为什么 gcc 以不同的方式编译 f(1199) 和 f(1200)?
Why does gcc compile f(1199) and f(1200) differently?
是什么原因导致 ARM 上的 GCC 7.2.1 对某些常量使用内存加载 (lr
),而在其他一些情况下使用立即数 (mov
)?具体来说,我看到了以下内容:
GCC 7.2.1 for ARM 编译:
extern void abc(int);
int test() { abc(1199); return 0; }
…进入那个:
test():
push {r4, lr}
ldr r0, .L4 // ??!
bl abc(int)
mov r0, #0
pop {r4, lr}
bx lr
.L4:
.word 1199
还有这个:
extern void abc(int);
int test() { abc(1200); return 0; }
…进入那个:
test():
push {r4, lr}
mov r0, #1200 // OK
bl abc(int)
mov r0, #0
pop {r4, lr}
bx lr
起初我预计 1200 是某种独特的截止值,但在 1024 处还有其他类似的截止值(1024 产生 mov r0, #1024
,而 1025 使用 ldr
)并且在其他值。
为什么 GCC 使用内存加载来获取常量,而不是使用立即数?
这与常量操作数在 ARM 指令集中的编码方式有关。它们被编码为一个(无符号的)8 位常量和一个 4 位旋转字段——8 位值将旋转 4 位字段中值的 2 倍。所以任何适合该形式的值都可以用作常量参数。
常量1200
二进制为10010110000,所以可以编码为8位常量01001011循环4.
常量 1199
的二进制为 10010101111,因此无法将其放入 ARM 常量操作数。
是什么原因导致 ARM 上的 GCC 7.2.1 对某些常量使用内存加载 (lr
),而在其他一些情况下使用立即数 (mov
)?具体来说,我看到了以下内容:
GCC 7.2.1 for ARM 编译:
extern void abc(int);
int test() { abc(1199); return 0; }
…进入那个:
test():
push {r4, lr}
ldr r0, .L4 // ??!
bl abc(int)
mov r0, #0
pop {r4, lr}
bx lr
.L4:
.word 1199
还有这个:
extern void abc(int);
int test() { abc(1200); return 0; }
…进入那个:
test():
push {r4, lr}
mov r0, #1200 // OK
bl abc(int)
mov r0, #0
pop {r4, lr}
bx lr
起初我预计 1200 是某种独特的截止值,但在 1024 处还有其他类似的截止值(1024 产生 mov r0, #1024
,而 1025 使用 ldr
)并且在其他值。
为什么 GCC 使用内存加载来获取常量,而不是使用立即数?
这与常量操作数在 ARM 指令集中的编码方式有关。它们被编码为一个(无符号的)8 位常量和一个 4 位旋转字段——8 位值将旋转 4 位字段中值的 2 倍。所以任何适合该形式的值都可以用作常量参数。
常量1200
二进制为10010110000,所以可以编码为8位常量01001011循环4.
常量 1199
的二进制为 10010101111,因此无法将其放入 ARM 常量操作数。