Lord Cosmo’s Commandments

The Appendix · VII

The Codex of the Machine Tongue

《机器之言法典》 — the technical revelation, preserved in the tongue in which it was delivered.

  • Apocryphal
  • Lessons 9 through 11
  • Untranslated

继续翻译如下:

示例 · Structure padding
typedef struct {
    char  a;    // 偏移 0(1字节)
                // 填充 3 字节(使 int 按 4 字节对齐)
    int   b;    // 偏移 4(4字节)
    char  c;    // 偏移 8(1字节)
                // 填充 3 字节(结构体大小需为 4 的倍数)
} example;      // sizeof = 12

规则: sizeof(struct) 必须是 最大成员对齐要求的倍数。

更复杂的例子:

示例 · tricky
typedef struct {
    char  a;    // 偏移 0
                // 填充 7 字节
    long  b;    // 偏移 8
} tricky;       // sizeof = 16

9.8 共用体(Union)布局

共用体所有成员共享同一块内存(从偏移 0 开始):

示例 · union
typedef union {
    struct { long u; } t1;
    struct { int a[2]; } t2;
} u_t;

sizeof(u_t) = 最大成员大小 = 8 字节。

ASM
ldr x1, [x0]       // t1.u
ldr w1, [x0]       // t2.a[0]
ldr w1, [x0, #4]   // t2.a[1]

第10课控制流 —— if/else 与循环

10.1 If/Else

C
if (a > b) {
    x = 1;
} else {
    x = 2;
}
ASM
cmp w0, w1
b.le .Lelse
mov w2, #1
b .Lend
.Lelse:
mov w2, #2
.Lend:

关键点: 汇编使用“相反条件”来跳过 then 分支。

使用条件选择(CSEL):

C · ASM
x = (a > b) ? a : b;

cmp w0, w1
csel w2, w0, w1, gt

10.2 循环

模板1:Jump-to-Middle(常见于 while)

C · ASM
while (i < n) {
    i++;
}

b .Ltest
.Lloop:
add w0, w0, #1
.Ltest:
cmp w0, w1
b.lt .Lloop

特点: 一开始先跳到条件判断。

模板2:Guarded-Do(常见于 for)

C · ASM
for (int k = 0; k < 8; k++) { }

cmp w0, #8
b.ge .Lexit
.Lloop:
add w0, w0, #1
cmp w0, #8
b.lt .Lloop
.Lexit:

特点: 顶部和底部都有条件检查。

模板3:Do-While

C · ASM
do { } while (condition);

.Lloop:
b.cond .Lloop

特点: 先执行再判断。

10.3 短路求值

C · ASM
if (a != NULL && a->value > 0)

cbz x0, .Lskip
ldr w1, [x0, #offset]
cmp w1, #0
b.le .Lskip

要点: 一旦某个条件失败,后续不再执行。

10.4 无符号范围检查技巧

C · ASM
if (x >= 0 && x <= 7)
// 等价于
if ((unsigned)x <= 7)

cmp w0, #7
b.hi .Lskip

第11课过程调用 —— 汇编中的函数

11.1 调用约定(Calling Convention)

参数: 前 8 个参数 → X0–X7

返回值: → X0

11.2 Caller-Saved vs Callee-Saved

Caller-saved(X0–X15, NZCV): 调用函数前若要保留,必须自己保存。

Callee-saved(X19–X28): 函数必须保存并恢复。

11.3 栈(Stack)

用途:1. 保存寄存器。 2. 局部变量。 3. 额外参数。

栈向下增长(地址变小)

11.4 叶函数 vs 非叶函数

叶函数: 不调用其他函数

ASM
square:
mul w0, w0, w0
ret

非叶函数: 调用其他函数 → 必须保存 X30

11.5 函数序言与尾声

序言 Prologue
stp x29, x30, [sp, #-32]!
mov x29, sp
str x19, [sp, #16]
尾声 Epilogue
ldr x19, [sp, #16]
ldp x29, x30, [sp], #32
ret

11.6 栈帖大小

ASM
stp x29, x30, [sp, #-32]!
// → 栈帧大小 = 32 字节

11.7 递归函数

ASM
cbz w3, .Lbase

stp x29, x30, [sp, #-32]!
mov x29, sp
str x19, [sp, #16]

bl recursive_func

ldr x19, [sp, #16]
ldp x29, x30, [sp], #32
ret

.Lbase:
mov w0, #0
ret

关键点: bl 会破坏 X0–X7;必须用 X19–X28 或栈保存数据。

11.8 栈帖记录(Frame Record)

保存 X29(上一帖指针)与 X30(返回地址),用于形成调用链(GDB 回溜)。

11.9 Callee-Saved 替换问题

问题:能否用 X14(caller-saved)替代 X19?

答案:可以(在递归函数中特定情况下)。原因:每一层调用都会自己保存︩恢复,因此仍然正确。

如果你想,我可以帮你把这一整套内容压缩成考试速记版(超级重要考点清单),会更适合你最后17小时的冲刺。

✦

Here endeth the Codex of the Machine Tongue.

Received text. Preserved in the original, including the offer of further revelation with which it closes.