为何编译器对无符号整数代码生成idiv与cdq指令?
无符号整数代码为何生成有符号除法指令?
我十分好奇,为什么下面这段全是无符号整数的代码,编译后会用到属于有符号整数操作的cdq和idiv指令?我猜测这可能是一种优化,想知道具体原因。
typedef struct mfiu { struct { uint32_t field_code : 24; uint32_t inc_size : 1; uint32_t reserved_0 : 7; }; uint32_t raw; } mfiu_t; typedef struct mls { mfiu_t field_id; uint16_t num_of_fields; uint16_t num_of_elem; uint32_t offset; } mls_t; typedef struct lis { mfiu_t field_id; } lis_t; int foo(lis_t* lookup_context, mls_t* entry){ uint16_t element_stride = (uint16_t)entry->field_id.inc_size + 1; uint32_t field_num = (lookup_context->field_id.field_code - entry->field_id.field_code) / (entry->num_of_elem * element_stride); uint32_t elem_num = (lookup_context->field_id.field_code - entry->field_id.field_code) / element_stride; return 0; }
以Clang 9.0为例,两处除法操作均通过idiv实现:
第一段汇编:
mov eax, r9d ; entry->num_of_elem * element_stride cdq ; lookup_context->field_id.field_code - entry->field_id.field_code 被符号扩展 idiv r10d
第二段汇编:
movzx r9d, word ptr [rbp - 18] ; element_stride cdq ; lookup_context->field_id.field_code - entry->field_id.field_code 同样被符号扩展为64位整数 idiv r9d
核心原因:特定场景下idiv与div等价,编译器借此优化代码生成
x86架构中,编译器选择用有符号除法指令idiv实现无符号除法,本质是利用了特定条件下两类除法的结果一致性,同时简化代码生成逻辑、减少指令路径分支。
1. 被除数的特性决定了符号扩展的安全性
你的代码中,field_code是24位无符号位域,它的值会被零扩展为32位uint32_t(高8位全为0)。当计算lookup_context->field_id.field_code - entry->field_id.field_code时:
- 若
lookup_context->field_id.field_code >= entry->field_id.field_code,差值的32位表示中最高位(第31位)为0,此时cdq指令会将EDX寄存器置为0(符号扩展后高32位全0)。 - 此时
idiv处理的是EDX:EAX组成的64位有符号数(等价于无符号数,因为高32位为0),和div处理64位无符号数的结果完全一致。
2. 编译器的逻辑复用与代码优化
编译器的代码生成器会尽量复用指令逻辑,减少不同场景下的代码分支:
cdq和idiv的组合,在被除数最高位为0时,效果完全等价于xor edx, edx(无符号除法前需将EDX置0)+div,且两者的指令字节数相同,编译器无需额外维护两套除法生成逻辑。- 若编译器通过数据流分析判定你的代码中差值必然非负(即
lookup_context->field_id.field_code >= entry->field_id.field_code),则这种替换是绝对安全的,不会影响计算结果。
3. 关于差值为负的情况
如果lookup_context->field_id.field_code < entry->field_id.field_code,无符号减法会得到一个高位全1的32位值,此时cdq会将EDX置为0xFFFFFFFF,idiv会将被除数视为有符号负数,得到的商是负数。但将这个负数赋值给uint32_t时,会被转换为无符号数,这和直接做无符号除法的结果并不一致——但编译器默认你的代码逻辑中不会出现这种情况,或者认为这种场景无需处理(代码中未对该分支做特殊逻辑)。
内容的提问来源于stack exchange,提问作者Shpongle
相关产品推荐
相关产品推荐

