敏感肌是什么| 拖什么东西最轻松| 绦是什么意思| 便秘吃什么最快排便小孩| 试管进周期是什么意思| 大脑供血不足头晕吃什么药最好| 内伤湿滞什么意思| 中国什么姓氏人口最多| 天干是什么| 楞严经讲的是什么| 感统失调挂什么科| 短发女人吸引什么男人| 什么是慈悲| 6月20号是什么星座| 怀孕第一个月有什么特征| 尿常规隐血弱阳性什么意思| 娘惹是什么意思| 苦瓜吃多了有什么坏处| 脖子为什么有颈纹| 白介素是什么| 鹤顶红是什么| 烟酰胺是什么东西| 世事无常什么意思| 什么不导电| 又什么又什么的词语| 长此以往什么意思| 妊娠纹长什么样| 今天什么日| 前列腺炎吃什么药| 心脏搭桥后最怕什么| 肝功七项查的是什么| 肠胃炎吃什么水果比较好| bp是什么意思| 水瓶座女生和什么星座男生最配| 尿蛋白是什么病| 什么菜不能吃| 空腔是什么意思| 千叶豆腐是什么做的| 运动喝什么水补充能量| 什么是横纹肌溶解症| 针灸的原理是什么| 粟米是什么米| 点痣后用什么修复最好| 日晡潮热是什么意思| 吃什么容易结石| 怜惜是什么意思| 没出息什么意思| 什么情况下做肾穿刺| 喝茶水对身体有什么好处| 苏联什么时候解体| 七月半是什么日子| 尿酸高要吃什么药| 腹腔积水是什么原因造成的| 1993属什么生肖| 蜜饯是什么意思| 为什么眼睛有红血丝| 途明是什么档次的包| legrand是什么牌子| 头皮脂溢性皮炎用什么药| 什么是配速| 经常口腔溃疡挂什么科| 全员加速中什么时候播| 70年属狗的是什么命| 你的名字讲的什么故事| 出汗有异味是什么原因| 火龙果有什么好处| cocoon是什么品牌| 悄悄的什么| 觉是什么偏旁| 3m是什么意思| 阴虱用什么药最有效| 罗汉是什么意思| 惊弓之鸟告诉我们什么道理| 什么是高情商| 乳腺炎吃什么消炎药| 低盐饮食有利于预防什么| 脚趾长痣代表什么意思| 什么药治牙疼最快| 吃完饭恶心想吐是什么原因| 小孩眼屎多是什么原因引起的| 曹操的脸谱是什么颜色| hpv亚临床感染是什么意思| 吃什么可以让卵泡长得快| 精神病的前兆是什么| 什么食物含硒| 乙肝两对半阴性是什么意思| original是什么牌子| 上海九院是什么医院| 好无奈是什么意思| 老是犯困想睡觉是什么原因| 慢性浅表性胃炎吃什么药好| 正常人为什么传导阻滞| 石英岩玉是什么| 放鸽子是什么意思| 二五八万是什么意思| 夜宵和宵夜有什么区别| 西兰花是什么季节的蔬菜| 思源名字的寓意是什么| 执迷不悟是什么生肖| 孙五行属什么| 为什么头发会变白| 什么叫闰年| 蝉蜕有什么功效| 1996五行属什么| 精子是什么味道| 浮萍是什么植物| 四眼狗有什么迷信说法| 治甲沟炎用什么药膏好| 香槟是什么| 蓄谋已久什么意思| 早搏吃什么药效果好| 日值上朔是什么意思| 36周岁属什么| 吃什么补免疫力最快| 忌廉是什么东西| 脑震荡是什么症状| 花生不能和什么食物一起吃| 佬是什么意思| 梦见坐牢是什么预兆| 中央处理器由什么组成| 血压低头晕是什么原因导致的| 食道不舒服挂什么科| 肾结石吃什么| 骨结核是什么病| 黄泉是什么意思| 什么药补血效果最好最快| 片状低回声区什么意思| pt是什么| 脚底疼是什么原因| 明天代表什么生肖| 什么是铅中毒| 双鱼座是什么象星座| 广东属于什么气候| 老人喝什么牛奶好| 什么东西可以止痒| 抖m是什么意思| 手心脚心发热吃什么药| 男人遗精是什么原因造成的| 什么叫西米| epa是什么营养物质| 痔疮是什么引起的| 白花花的什么| 手发痒是什么原因| 梦见过生日是什么意思| 什么花喜欢磷酸二氢钾| 南京的简称是什么| 磕头虫吃什么| 什么是热感冒| 为什么会有高血压| 中国国粹是什么| 下午2点是什么时辰| 抬头是什么| 手指指尖发麻是什么原因| 坐阵是什么意思| 伏羲和女娲是什么关系| 熊猫血有什么好处| 拉肚子去医院挂什么科| 拿东西手抖是什么原因| 印度尼西亚是什么人种| 胃痛吃什么食物| 俄罗斯是什么人种| 警察为什么叫条子| 小孩子不吃饭是什么原因引起的| 道什么意思| 杜比全景声是什么意思| 沙果是什么水果| 什么是沉香| 正切是什么| 一什么骆驼| 五十知天命是什么意思| 小生化是检查什么项目| 什么叫二氧化碳| 溜肉段用什么肉| 杆鱼是什么鱼| 猫吃什么食物除了猫粮| 吃什么最容易减肥| 西米是什么字| 什么鱼炖豆腐好吃| 吃什么降血糖最快| 京东自营是什么意思| 炸东西用什么淀粉| 自然堂适合什么年龄| 什么的瞬间作文| 脚浮肿是什么原因引起的| 高密度脂蛋白胆固醇偏低什么意思| 桑葚和什么泡酒壮阳| 贲临是什么意思| 手背上有痣代表什么| 日文上下结构念什么| 反酸水是什么原因| 始于初见止于终老是什么意思| 男人吃女人有什么好处| 阴囊是什么部位| cache是什么意思| 大便一粒一粒的是什么原因| mic是什么| lp0是什么意思| 癸丑五行属什么| mcn是什么意思| 中性粒细胞计数偏高是什么意思| 鼻塞有脓鼻涕吃什么药| 脑梗适合吃什么食物| 什么叫保守治疗| 党员有什么好处| 比干是什么神| 罗汉果有什么功效和作用| 关节炎吃什么药最好| 心阳虚吃什么药| 什么时候会怀孕| 嗓子痒痒是什么原因| 男性生殖系统感染吃什么药| 玉鸟吃什么饲料好| 肺结节吃什么| 血管狭窄吃什么食物好| 宝字五行属什么| 脑梗前兆是什么症状| 田字出头是什么字| 肥波是什么品种的猫| 蔡英文是什么党派| 你有什么| 三生万物是什么意思| 苦荞茶适合什么人喝| 中心句是什么意思| 人为什么会有头皮屑| 红细胞偏低是什么意思| 怀孕两个月有什么症状| 喝酒对身体有什么危害| 上环后同房要注意什么| 什么是员额制| 牙疼吃什么药| 天厨贵人是什么意思| 71年属猪是什么命| 眼震是什么症状| 夏季吃桃子有什么好处| 解辣喝什么| 孕妇吃什么水果对胎儿好| 衢是什么意思| 萎缩性胃炎可以吃什么水果| 肚子左侧是什么器官| aigner是什么牌子| 情商是什么意思| 瞌睡多是什么原因| 脾肾阳虚吃什么药| 相敬如宾是什么生肖| 一直想吐是什么原因| 什么牌子洗发水好| 走路腿软没劲是什么原因引起的| 心肌标志物是查什么的| 流金是什么字| 前列腺增生用什么药好| 高血压降不下来是什么原因| 东山再起是什么生肖| 痛风病人吃什么菜| 为什么会得艾滋病| 小孩子睡觉流口水是什么原因| 车票改签是什么意思| 81年属什么生肖| 轭是什么意思| 师公是什么意思| 胃反流是什么原因引起的| 女性内分泌失调有什么症状| 月经一直不停有什么办法止血| 两个务必是什么| 婴儿放屁臭是什么原因| 百度コンテンツにスキップ

霍金警告再升级:人工智能对人类的威胁正快速上升

出典: フリー教科書『ウィキブックス(Wikibooks)』
百度 这就是进行完8小时长途飞行返回机场,20名维护人员要干48小时维修工作。

機械語(きかいご、machine language)とは、コンピュータの中央処理装置(CPU)が直接解釈して実行できる命令の集合のことを指します。これは、コンピュータの最も基本的なプログラム言語であり、0と1のビット列(バイナリ形式)で表されます。

主な特徴

[編集]
  1. CPU固有
    • 機械語はCPUのアーキテクチャに依存しており、異なるCPU(例: x86、ARM)では異なる機械語が用いられます。
  2. 直接実行可能
    • 機械語は、中間的な翻訳や解釈を必要とせず、CPUがそのまま実行します。
  3. 低水準言語
    • 高水準言語(例: C、Python)とは異なり、機械語はハードウェアに近い抽象度の低い言語です。プログラミングには非常に細かい作業が必要です。
  4. 命令セットアーキテクチャ(ISA)に基づく
    • 機械語は、特定のCPUがサポートする命令セットアーキテクチャ(Instruction Set Architecture)に準拠しています。例えば、「データをロードする」「加算する」「分岐する」などの命令が含まれます。

機械語の例(x86-64アーキテクチャの場合)

[編集]

以下は、add eax, 1(EAXレジスタの値に1を加算する)の機械語表現です:

  • バイナリ: 00000001 11000000
  • 16進数: 01 C0

機械語と関連する他の言語

[編集]
  • アセンブリ言語
    機械語に対して、人間が理解しやすい表現(ニーモニック)を使った低水準言語。アセンブリ言語をアセンブラで翻訳すると機械語になります。
    • 例: mov eax, 1
  • 高水準言語
    機械語やアセンブリ言語よりも人間にとって理解しやすい言語。コンパイラやインタプリタによって、最終的に機械語に変換されます。
    • 例: x = x + 1(C言語の場合)

機械語の利点と欠点

[編集]
利点
  • 高速な実行: 中間翻訳が不要なため、直接CPUで実行できます。
  • 細かい制御: ハードウェアの動作を詳細に制御できます。
欠点
  • 難解さ: 人間には理解しにくく、プログラミングが非常に難しい。
  • 非移植性: CPU固有のため、異なるCPU間で互換性がありません。

機械語の使用例

[編集]

現代では、プログラマーが直接機械語を書くことはほとんどありませんが、次のような場面で関わります:

  • 組み込みシステムの最適化
  • OSのカーネルやドライバの設計
  • セキュリティ関連の研究(例: バイナリ解析やリバースエンジニアリング)

以上が、機械語の基本的な説明です。興味があれば、具体的なCPUアーキテクチャ(例: x86-64、ARM)の命令セットを学ぶと、より深く理解できます。

ソースコードからどんな機械語が生成されるか

[編集]
フィボナッチ数を返す関数(C言語)
int fibo(int n) {
*:   if (n == 0 || n == 1)
*:     return n;
*:   return fibo(n-1) + fibo(n-2);
}

32bitARMプロセッサーの例

[編集]
コンパイラーによって生成されたコード
$ clang-19 --target=arm -mfloat-abi=soft -c -Oz -g fibo.c
$ llvm-objdump-19  --triple=arm -S fibo.o

fibo.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <fibo>:
; int fibo(int n) {
*:      0: e92d4830      push    {r4, r5, r11, lr}
*:      4: e28db008      add     r11, sp, #8
*:      8: e1a04000      mov     r4, r0
*:      c: e3a05000      mov     r5, #0
;     if (n == 0 || n == 1)
*:     10: e3540002      cmp     r4, #2
; }
*:     14: 30840005      addlo   r0, r4, r5
*:     18: 38bd8830      poplo   {r4, r5, r11, pc}
;     return fibo(n-1) + fibo(n-2);
*:     1c: e2440001      sub     r0, r4, #1
*:     20: ebfffffe      bl      0x20 <fibo+0x20>        @ imm = #-0x8
*:     24: e0805005      add     r5, r0, r5
*:     28: e2444002      sub     r4, r4, #2
*:     2c: eafffff7      b       0x10 <fibo+0x10>        @ imm = #-0x24

このコードは32bitARMプロセッサをターゲットとしたもので、すべての命令が32ビット長なのでアセンブラーの初学者向きです。 また、ARMアーキテクチャは多くのスマートフォンやタブレットで採用されていたり、組込み用途での採用も多いので最も普及しているコンピュータ?アーキテクチャの1つです。

各命令の説明

[編集]

出力されたアセンブリの各行を詳細に見ていきます。

*:      0: e92d4830      push    {r4, r5, r11, lr}
  • 命令: push {r4, r5, r11, lr}
  • 解説: 関数のエントリポイントです。レジスタr4, r5, r11, lr(リンクレジスタ)をスタックに保存して、lrは関数の復帰先アドレスを保持します。この命令によりスタックフレームが作成され、レジスタの内容が保存されます。
*:      4: e28db008      add     r11, sp, #8
  • 命令: add r11, sp, #8
  • 解説: フレームポインタr11を現在のスタックポインタにオフセットを追加した値に設定し、スタックフレームの開始位置を決めます。
*:      8: e1a04000      mov     r4, r0
  • 命令: mov r4, r0
  • 解説: 引数nr0に格納されている)をレジスタr4に移動します。このr4が以降で引数nとして使われます。
*:      c: e3a05000      mov     r5, #0
  • 命令: mov r5, #0
  • 解説: レジスタr50を設定します。r5はこの後の計算結果の累積値を保持します。
*:     10: e3540002      cmp     r4, #2
  • 命令: cmp r4, #2
  • 解説: n < 2を判定するため、r42を比較しています。
*:     14: 30840005      addlo   r0, r4, r5
*:     18: 38bd8830      poplo   {r4, r5, r11, pc}
  • 命令: addlo r0, r4, r5
    • 解説: n01のとき、つまりn < 2の場合、r0r4r5の和を返します[1]
  • 命令: poplo {r4, r5, r11, pc}
    • 解説: 条件が成立(n < 2)した場合、関数から復帰します。スタックからレジスタr4, r5, r11, pc(プログラムカウンタ)を復元し、pcに復帰アドレスが入ることで呼び出し元に戻ります。
*:     1c: e2440001      sub     r0, r4, #1
*:     20: ebfffffe      bl      0x20 <fibo+0x20>
  • 命令: sub r0, r4, #1
    • 解説: n-1を計算し、再帰呼び出しのためにr0にセットします。
  • 命令: bl 0x20 <fibo+0x20>
    • 解説: fibo(n-1)を再帰的に呼び出します。このとき、リンクレジスタlrに次の命令アドレスが保存されます。
*:     24: e0805005      add     r5, r0, r5
  • 命令: add r5, r0, r5
  • 解説: fibo(n-1)の戻り値がr0に格納されているため、それを累積値r5に加算します。
*:     28: e2444002      sub     r4, r4, #2
  • 命令: sub r4, r4, #2
  • 解説: n-2を計算し、次の再帰呼び出しのためにr4にセットします。
*:     2c: eafffff7      b       0x10 <fibo+0x10>
  • 命令: b 0x10 <fibo+0x10>
  • 解説: ラベル0x10(再帰処理の比較部分)に無条件分岐します。

Thumb命令の例

[編集]

ARMプロセッサはThumbと呼ばれるコード効率の向上を意図した16ビット長のThumb命令モードを持っています。

コンパイラーによって生成されたコード
$ clang-19 --target=thumb -mfloat-abi=soft -mthumb -c -Oz -g fibo.c
$ llvm-objdump-19 --triple=thumb -S fibo.o

fibo.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <fibo>:
; int fibo(int n) {
*:      0: b5b0          push    {r4, r5, r7, lr}
*:      2: af02          add     r7, sp, #0x8
*:      4: 0004          movs    r4, r0
*:      6: 2500          movs    r5, #0x0
;     if (n == 0 || n == 1)
*:      8: 2c02          cmp     r4, #0x2
*:      a: d305          blo     0x18 <fibo+0x18>        @ imm = #0xa
;     return fibo(n-1) + fibo(n-2);
*:      c: 1e60          subs    r0, r4, #0x1
*:      e: f7ff fffe     bl      0xe <fibo+0xe>          @ imm = #-0x4
*:     12: 1945          adds    r5, r0, r5
*:     14: 1ea4          subs    r4, r4, #0x2
*:     16: e7f7          b       0x8 <fibo+0x8>          @ imm = #-0x12
; }
*:     18: 1960          adds    r0, r4, r5
*:     1a: bdb0          pop     {r4, r5, r7, pc}

各命令の説明

[編集]

出力されたアセンブリの各行を詳細に見ていきます。

00000000 <fibo>:
; int fibo(int n) {
*:      0: b5b0          push    {r4, r5, r7, lr}
  • 命令 push {r4, r5, r7, lr}: レジスタ r4r5r7lr (リンクレジスタ) をスタックに保存し、再帰的な呼び出しでもレジスタの値を保持します。
*:      2: af02          add     r7, sp, #0x8
  • 命令 add r7, sp, #0x8: フレームポインタレジスタ r7 をスタックポインタから8バイト分ずらして設定します。ローカル変数や引数へのアクセスを簡単にするために使用されます。
*:      4: 0004          movs    r4, r0
*:      6: 2500          movs    r5, #0x0
  • 命令 movs r4, r0: 引数 n をレジスタ r4 に保存します。
  • 命令 movs r5, #0x0: レジスタ r5 に0を設定します。これはフィボナッチ計算の一部で、計算結果を保存していくためのレジスタです。
; if (n == 0 || n == 1)
*:      8: 2c02          cmp     r4, #0x2
*:      a: d305          blo     0x18 <fibo+0x18>
  • 命令 cmp r4, #0x2: n の値 (r4) を2と比較します。
  • 命令 blo 0x18 <fibo+0x18>: n が2未満(すなわち0か1)なら、条件分岐して0x18番地にジャンプします。ここで関数の終了処理に進みます。
; return fibo(n-1) + fibo(n-2);
*:      c: 1e60          subs    r0, r4, #0x1
*:      e: f7ff fffe     bl      0xe <fibo+0xe>
*:     12: 1945          adds    r5, r0, r5
*:     14: 1ea4          subs    r4, r4, #0x2
*:     16: e7f7          b       0x8 <fibo+0x8>
  • 命令 subs r0, r4, #0x1: r4 から1を引き、fibo(n-1) を計算するための引数を設定します。
  • 命令 bl 0xe <fibo+0xe>: fibo 関数を再帰的に呼び出します。この再帰呼び出しは fibo(n-1) を求めます。
  • 命令 adds r5, r0, r5: 呼び出しの結果が r0 に格納され、これを r5 に加えます。
  • 命令 subs r4, r4, #0x2: n の値から2を引き、fibo(n-2) を求める準備をします。
  • 命令 b 0x8 <fibo+0x8>: 再度 fibo 関数に戻ってループを繰り返し、 fibo(n-1) + fibo(n-2) を計算します。
*:     18: 1960          adds    r0, r4, r5
*:     1a: bdb0          pop     {r4, r5, r7, pc}
  • 命令 adds r0, r4, r5: 最終的な結果を r0 に格納し、戻り値として設定します。
  • 命令 pop {r4, r5, r7, pc}: スタックからレジスタの内容を復元し、関数の実行を終了します。

Thumb命令は概ね命令長は16ビットで、長いオペランドが必要な命令(この場合は bl)だけが追加のオペランドを持ちます。

64bitARMプロセッサーの例

[編集]

ARMアーキテクチャーは、ARMv8-Aから64ビットモードアーキテクチャーAArch64を採用してます。 AArch64は、32本の64ビットレジスター(うち1本はスタックポインター兼ゼロレジスタ、1本は戻り番地を保持するリンクレジスタ)を持ち、Xnnレジスターは64ビットレジスターのnn本目、WnnはXnnレジスターの下位32ビットです。

コンパイラーによって生成されたコード
$ clang-19 --target=aarch64 -c -Oz -g fibo.c
$ llvm-objdump-19 --triple=aarch64 -S fibo.o

fibo.o: file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <fibo>:
; int fibo(int n) {
*:      0: a9be7bfd      stp     x29, x30, [sp, #-0x20]!
*:      4: a9014ff4      stp     x20, x19, [sp, #0x10]
*:      8: 910003fd      mov     x29, sp
*:      c: 2a1f03f3      mov     w19, wzr
;     if (n == 0 || n == 1)
*:     10: 71000814      subs    w20, w0, #0x2
*:     14: 540000e3      b.lo    0x30 <fibo+0x30>
;     return fibo(n-1) + fibo(n-2);
*:     18: 51000400      sub     w0, w0, #0x1
*:     1c: 94000000      bl      0x1c <fibo+0x1c>
*:     20: 2a0003e8      mov     w8, w0
*:     24: 2a1403e0      mov     w0, w20
*:     28: 0b130113      add     w19, w8, w19
*:     2c: 17fffff9      b       0x10 <fibo+0x10>
; }
*:     30: 0b130000      add     w0, w0, w19
*:     34: a9414ff4      ldp     x20, x19, [sp, #0x10]
*:     38: a8c27bfd      ldp     x29, x30, [sp], #0x20
*:     3c: d65f03c0      ret

このコードは、AArch64アーキテクチャ用にコンパイルされた再帰的なフィボナッチ関数 fibo のアセンブリリストです。以下で各部分を解説します。

0: a9be7bfd      stp     x29, x30, [sp, #-0x20]!
4: a9014ff4      stp     x20, x19, [sp, #0x10]
8: 910003fd      mov     x29, sp
c: 2a1f03f3      mov     w19, wzr
  • スタックフレームの設定stp 命令でリンクレジスタ (x30) とフレームポインタ (x29) をスタックに保存し、sp(スタックポインタ)を更新しています。また、x20x19 の値もスタックに保存しています。これにより、関数が呼び出されるたびにスタック上に新しいフレームが作成され、レジスタの状態が保持されます。
  • 初期化mov w19, wzr によって、w19 にゼロ (wzrはゼロレジスタ) を設定しています。これは n = 0 または n = 1 の場合の初期戻り値として使用されます。
10: 71000814      subs    w20, w0, #0x2
14: 540000e3      b.lo    0x30 <fibo+0x30>
  • 条件チェック:引数 nw0に格納)を使って n < 2 かどうかをチェックしています。subs w20, w0, #0x2 によって、n - 2 の結果が w20 に格納され、b.lo 命令で n < 2 の場合に 0x30 のラベルにジャンプします。これにより n が 0 または 1 の場合は直接 n を返す処理に移行します。
18: 51000400      sub     w0, w0, #0x1
1c: 94000000      bl      0x1c <fibo+0x1c>
20: 2a0003e8      mov     w8, w0
24: 2a1403e0      mov     w0, w20
28: 0b130113      add     w19, w8, w19
2c: 17fffff9      b       0x10 <fibo+0x10>
  • 再帰呼び出しw0n-1 として設定して fibo(n-1) を呼び出します。この結果が w0 に返され、mov w8, w0 で一時保存します。その後 w0n-2 を設定し、再度 fibo(n-2) を呼び出し、その結果を w19 に加算します。b 0x10 によってループに戻り、これを繰り返して fibo を計算します。
30: 0b130000      add     w0, w0, w19
34: a9414ff4      ldp     x20, x19, [sp, #0x10]
38: a8c27bfd      ldp     x29, x30, [sp], #0x20
3c: d65f03c0      ret
  • 結果を返す:計算が完了したら、結果(w0)を返します。スタック上に保存していた x20x19x29、および x30 を復元し、ret で関数から戻ります。

wzrはゼロレジスターを32bitで参照しています、spはスタックポインターでアドレス演算の文脈と左辺値の文脈ではスタックポインター、右辺値の場合はゼロレジスターになりレジスタインデックス(31番)を共有しています。 aarch64 では32bitARMと違って全ての命令に条件フラッグ参照が着くわけではないので、どちらかというと Thumb に似ていますが最小命令サイズは32bitです。

amd64プロセッサーの例

[編集]

amd64(X86-64とも)の命令は最小単位は1バイトで、バイト数あたりの操作が多いのが特徴です。 逆アッセンブルされたコードを読む限り不便は感じませんが、ハンドディスアッセンブルする場合は1バイトずれるとまるで違った意味になるのが厄介で、プロセッサーの中でも数命令先の命令を読み込み実行効率を上げる為に命令の切れ目を探すことが性能向上のボトルネックになっています(ARMなら次の命令は4バイト先と決まっているので深い先読みが相対的に容易)。

同じコードをamd64向けにコンパイル
$ clang-19 --target=amd64 -c -Oz -g fibo.c
$ llvm-objdump-19 --triple=amd64 -S fibo.o

fibo.o: file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <fibo>:
; int fibo(int n) {
*:      0: 55                            pushq   %rbp
*:      1: 48 89 e5                      movq    %rsp, %rbp
*:      4: 41 56                         pushq   %r14
*:      6: 53                            pushq   %rbx
*:      7: 89 fb                         movl    %edi, %ebx
*:      9: 45 31 f6                      xorl    %r14d, %r14d
;     if (n == 0 || n == 1)
*:      c: 83 fb 02                      cmpl    $0x2, %ebx
*:      f: 72 10                         jb      0x21 <fibo+0x21>
;     return fibo(n-1) + fibo(n-2);
*:     11: 8d 7b ff                      leal    -0x1(%rbx), %edi
*:     14: e8 00 00 00 00                callq   0x19 <fibo+0x19>
*:     19: 41 01 c6                      addl    %eax, %r14d
*:     1c: 83 c3 fe                      addl    $-0x2, %ebx
*:     1f: eb eb                         jmp     0xc <fibo+0xc>
; }
*:     21: 44 01 f3                      addl    %r14d, %ebx
*:     24: 89 d8                         movl    %ebx, %eax
*:     26: 5b                            popq    %rbx
*:     27: 41 5e                         popq    %r14
*:     29: 5d                            popq    %rbp
*:     2a: c3                            retq

このアセンブリコードは、x86-64 アーキテクチャ用にコンパイルされた再帰的なフィボナッチ関数 fibo の内容を示しています。各命令が何をしているか解説します。

0: 55                            pushq   %rbp
1: 48 89 e5                      movq    %rsp, %rbp
4: 41 56                         pushq   %r14
6: 53                            pushq   %rbx
7: 89 fb                         movl    %edi, %ebx
9: 45 31 f6                      xorl    %r14d, %r14d
  • スタックフレームの設定pushq %rbpmovq %rsp, %rbp によってスタックフレームを設定しています。また、関数内で使用する %r14%rbx レジスタをスタックに退避させ、関数終了時に復元できるようにしています。
  • 引数の格納と初期化:引数 n%edi レジスタに格納されているため、これを %ebx に移しています(movl %edi, %ebx)。また、%r14d をゼロクリアして初期化しています。この %r14 は、最終的な戻り値のための一時的な蓄積レジスタとして使用されます。
c: 83 fb 02                      cmpl    $0x2, %ebx
f: 72 10                         jb      0x21 <fibo+0x21>
  • 条件チェック:引数 n の値が 2 未満 (n < 2) かどうかを比較しています。cmpl $0x2, %ebx によって %ebxn の値)と定数 2 を比較し、jb(jump if below)命令によって n < 2 の場合は、アドレス 0x21 にジャンプします。これは、フィボナッチ数列の初期条件に当たるケースです。
11: 8d 7b ff                      leal    -0x1(%rbx), %edi
14: e8 00 00 00 00                callq   0x19 <fibo+0x19>
19: 41 01 c6                      addl    %eax, %r14d
1c: 83 c3 fe                      addl    $-0x2, %ebx
1f: eb eb                         jmp     0xc <fibo+0xc>
  • 再帰呼び出し
    • leal -0x1(%rbx), %edi によって、%edin - 1 を設定し、fibo(n-1) を呼び出します。
    • callq 命令の後で %eax に返り値が格納されます。この %eax の値(fibo(n-1) の結果)を %r14d に加算して、合計を蓄積します。
    • 次に、%ebxn の値)を n - 2 に更新し、再び jmp 0xc によって n - 2 での再帰を行い、計算が完了するまで繰り返します。
21: 44 01 f3                      addl    %r14d, %ebx
24: 89 d8                         movl    %ebx, %eax
26: 5b                            popq    %rbx
27: 41 5e                         popq    %r14
29: 5d                            popq    %rbp
2a: c3                            retq
  • 戻り値の設定と終了処理:ここで蓄積していた %r14d%ebx に加算し、最終的な戻り値を %eax に格納します。そして、保存しておいたレジスタ %rbx%r14%rbp を復元し、retq 命令で関数から戻ります。

このコードは再帰的なフィボナッチ関数を最適化していますが、各再帰呼び出しの後に部分的な結果を加算していくため、再帰が深くなるとスタックの使用量が増えます。このコードは、コンパイラの最適化オプション -Oz によりコードサイズが最小化されています。

x86(32ビット)のコード

[編集]
x86(32ビット)のコード
$ clang-19 --target=i686 -c -Oz -g fibo.c
$ llvm-objdump-19 --triple=i686 -S fibo.o

fibo.o: file format elf32-i386

Disassembly of section .text:

00000000 <fibo>:
; int fibo(int n) {
*:      0: 55                            pushl   %ebp
*:      1: 89 e5                         movl    %esp, %ebp
*:      3: 57                            pushl   %edi
*:      4: 56                            pushl   %esi
*:      5: 31 ff                         xorl    %edi, %edi
*:      7: 8b 75 08                      movl    0x8(%ebp), %esi
;     if (n == 0 || n == 1)
*:      a: 83 fe 02                      cmpl    $0x2, %esi
*:      d: 72 11                         jb      0x20 <fibo+0x20>
;     return fibo(n-1) + fibo(n-2);
*:      f: 8d 46 ff                      leal    -0x1(%esi), %eax
*:     12: 50                            pushl   %eax
*:     13: e8 fc ff ff ff                calll   0x14 <fibo+0x14>
*:     18: 59                            popl    %ecx
*:     19: 01 c7                         addl    %eax, %edi
*:     1b: 83 c6 fe                      addl    $-0x2, %esi
*:     1e: eb ea                         jmp     0xa <fibo+0xa>
; }
*:     20: 01 fe                         addl    %edi, %esi
*:     22: 89 f0                         movl    %esi, %eax
*:     24: 5e                            popl    %esi
*:     25: 5f                            popl    %edi
*:     26: 5d                            popl    %ebp
*:     27: c3                            retl

このアセンブリコードは、i386(32ビット)アーキテクチャ用にコンパイルされた再帰的なフィボナッチ関数 fibo の内容を示しています。各命令が何をしているかを解説します。

0: 55                            pushl   %ebp
1: 89 e5                         movl    %esp, %ebp
3: 57                            pushl   %edi
4: 56                            pushl   %esi
5: 31 ff                         xorl    %edi, %edi
7: 8b 75 08                      movl    0x8(%ebp), %esi
  • スタックフレームの設定pushl %ebpmovl %esp, %ebp で新しいスタックフレームを作成します。また、関数内で使用するレジスタ %edi%esi をスタックに保存して、関数終了時に復元できるようにしています。
  • 初期化xorl %edi, %edi によって %edi をゼロクリアしています。このレジスタは結果の一時的な蓄積に使われます。
  • 引数の取得:引数 n はスタックから取り出して %esi に格納しています(movl 0x8(%ebp), %esi)。これは、fibo 関数の引数 n です。
a: 83 fe 02                      cmpl    $0x2, %esi
d: 72 11                         jb      0x20 <fibo+0x20>
  • 条件チェックcmpl $0x2, %esi%esi(引数 n)と 2 を比較し、jb(jump if below)命令によって n < 2 の場合にアドレス 0x20 にジャンプします。これは、フィボナッチ数列の初期条件をチェックする部分です。
f: 8d 46 ff                      leal    -0x1(%esi), %eax
12: 50                            pushl   %eax
13: e8 fc ff ff ff                calll   0x14 <fibo+0x14>
18: 59                            popl    %ecx
19: 01 c7                         addl    %eax, %edi
1b: 83 c6 fe                      addl    $-0x2, %esi
1e: eb ea                         jmp     0xa <fibo+0xa>
  • 再帰呼び出し
    • leal -0x1(%esi), %eax によって、%eaxn - 1 を設定し、次の再帰呼び出しの引数とします。
    • pushl %eax で引数をスタックにプッシュし、calll によって fibo(n-1) を再帰呼び出しします。
    • 呼び出しが終わったら %eax に戻り値が格納されます。これを %edi に加算して、合計を蓄積します。
    • addl $-0x2, %esi により %esin - 2 に更新し、jmp 0xan - 2 の場合の計算を再び行います。
20: 01 fe                         addl    %edi, %esi
22: 89 f0                         movl    %esi, %eax
24: 5e                            popl    %esi
25: 5f                            popl    %edi
26: 5d                            popl    %ebp
27: c3                            retl
  • 戻り値の設定と終了処理:最終的に %edifibo(n-1) の結果の蓄積)が %esi に加算され、戻り値として %eax に格納されます。スタックから %esi%edi%ebp を復元し、retl によって関数から戻ります。

このコードも再帰的なフィボナッチ関数を実装しています。再帰呼び出しでの部分的な計算結果が %edi に蓄積され、スタックの使用が少ないように最適化されています。

まとめ

[編集]
  • 異なるプロセッサーでは、同じ高級言語のコードが全く違う機械語命令列にコンパイルされる。
  • 同じプロセッサーでも命令モードによって、同じ高級言語のコードが全く違う機械語命令列にコンパイルされる。
  • 再帰などのプログラム構造もコンパイラーによって(意味解析され)等価のより速度的に(あるいはメモリーフットプリント的に)優れたコードに置き換えられる(事がある)。
機械語 = バイナリーデータ?
機械語はバイナリーデータですが、全てのバイナリーデータが機械語ではありません。

例えば、画像データや映像データはバイナリーデータですが機械語ではありません。

実行ファイルもバイナリーデータですが、オペレーティングシステムが「どの様に配置するのか?」「どの位置から実行するのか?」あるいは「初期化済みのデータ領域の値」など機械語以外の付帯的な情報(一般にヘッダーと呼ばれます)を重層的に持っているので、機械語を含んでいますが機械語そのものではありません。

機械語は、アセンブラのニーモニックに一対一で対応するコードと理解するとわかりやすいと思います。

用語集

[編集]

機械語に関する用語集

  • オペコード(Opcode) - 機械語命令の操作コード。特定の動作を実行するための識別子。
  • オペランド(Operand) - オペコードによって指定された操作対象。演算の対象となる数値やアドレス。
  • レジスタ(Register) - CPU内にある高速なメモリ領域で、演算に使用されるデータやアドレスを格納するために使用される。
  • フラグ(Flag) - CPUの状態を示すビットで、演算の結果を表す。フラグは、CPU内部で条件分岐を行うために使用される。
  • メモリアドレス(Memory Address) - メモリ内の特定の場所を示す番号。機械語のオペランドとして使用されることが多い。
  • メモリマップドI/O(Memory-mapped I/O) - I/Oデバイスを制御するために、メモリアドレスを使用する方法。
  • ファイルI/O(File I/O) - ディスクやネットワーク上のファイルを操作するために使用される命令。
  • 命令ポインタ(Instruction Pointer) - CPUが次に実行する機械語命令のアドレスを示すレジスタ。
  • サブルーチン(Subroutine) - 他の部分から呼び出される、独立した機械語のブロック。
  • スタック(Stack) - プログラム内で一時的なデータを格納するためのメモリ領域。
  • エンディアン(Endian) - データの並び順を示す方法。リトルエンディアンは、最下位バイトから順にデータを配置する方式。ビッグエンディアンは、最上位バイトから順にデータを配置する方式。
  • マシンサイクル(Machine Cycle) - CPUが一つの命令を実行するために必要なサイクル数。
  • 命令セット(Instruction Set) - 特定のCPUが実行できる機械語命令の集合。
  • アセンブラ(Assembler) - アセンブリ言語を機械語に変換するプログラム。
  • リンカ(Linker) - 複数のオブジェクトファイルを結合して、実行可能なプログラムを生成するプログラム。
  • デバッガ(Debugger) - プログラムの実行中に、機械語命令やレジスタの値を監視し、プログラムの動作を解析するツール。
  • トレース(Trace) - プログラムの実行中に、実行された命令やメモリアクセスなどの履歴を保存すること。
  • ブレークポイント(Breakpoint) - プログラムの実行中に、特定の命令の実行を一時停止し、デバッグのために命令の内容やレジスタの値を確認するために使用されるポイント。
  • クロスコンパイラ(Cross-compiler) - 特定のCPU向けに、異なるプラットフォームでコンパイルするためのコンパイラ。
  • リバースエンジニアリング(Reverse Engineering) - プログラムやハードウェアの動作を解析して、仕様や設計図を作成するプロセス。
  • コンパイル(Compile) - 高水準言語で書かれたプログラムを、機械語に変換するプロセス。
  • リンク(Link) - コンパイルされた複数のオブジェクトファイルを、実行可能なプログラムに結合するプロセス。
  • アセンブル(Assemble) - アセンブリ言語で書かれたプログラムを、機械語に変換するプロセス。
  • ローダ(Loader) - プログラムをメモリに読み込み、実行可能な状態にするプログラム。
  • バイトコード(Bytecode) - 実行環境に依存しない、仮想マシン上で実行される機械語。
  • オーバーフロー(Overflow) - データ型の最大値を超える演算が行われた場合に発生する、予期しない結果のこと。
  • セグメンテーション違反(Segmentation Fault) - プログラムがメモリの範囲外をアクセスしようとした場合に発生するエラー。
  • プロセス(Process) - プログラムの実行中に割り当てられる、メモリやレジスタ、実行状態などのリソースの集合。
  • マルチプロセッシング(Multiprocessing) - 複数のプロセッサを使用して、プログラムを並列に処理する方式。

脚註

[編集]
  1. ^ ARMアーキテクチャでは、多くの命令でキャリーなどのコンディションコードによって実行する?しないを制御できるのが大きな特徴で、他のアーキテクチャではジャンプ命令以外でコンディションコードによって実行する?しないを制御できるのは稀です(他にはIA-64がプレディケート可能です)。
吃什么东西补充胶原蛋白 什么人容易得癌症 谷草谷丙偏高是什么原因 牢固的近义词是什么 豚的右边念什么
猜疑是什么意思 溃疡是什么 死库水什么意思 手什么脚什么 蜱虫最怕什么药
什么的李逵 白化病是一种什么病 ab型血和o型血的孩子是什么血型 厦门有什么好吃的 志五行属什么
脑门发黑是什么原因 孕前检查挂什么科 拐子是什么意思 吸血鬼怕什么 肝血管瘤是什么病
什么是牛蒡hcv8jop7ns5r.cn 三心二意是指什么生肖hcv9jop5ns7r.cn 身上长肉疙瘩是什么原因hcv9jop5ns6r.cn 6月28日是什么星座cj623037.com 猪肚炖什么好吃hcv9jop0ns3r.cn
1974年属什么hcv7jop4ns8r.cn 胆在什么位置图片hcv7jop5ns5r.cn 1958年属狗的是什么命hcv8jop0ns0r.cn 针灸是什么hcv9jop8ns1r.cn 826是什么星座hcv8jop6ns0r.cn
临界值是什么意思aiwuzhiyu.com 赭是什么颜色hanqikai.com 通奸是什么意思travellingsim.com 单核细胞百分比偏高什么原因travellingsim.com 家和万事兴是什么意思hcv9jop3ns9r.cn
痛风急性发作期吃什么药zhongyiyatai.com 头昏是什么原因hcv9jop8ns0r.cn 维生素b吃多了有什么副作用hcv8jop8ns8r.cn 孩子干咳吃什么药效果好hlguo.com 嘴唇发麻是什么病兆hcv9jop2ns5r.cn
百度