依据国密SM3散列算法的标准文档,很容易写出其消息扩展函数的C语言实现:
#include
void expand_data(uint32_t W[68], uint32_t data[16])
{
uint32_tj, P1, X;
for(j = 0; j < 16; j++) W[j] = data[j];
for(j = 16; j < 68; j++) {
X = W[j-16] ^ W[j-9] ^ ((W[j-3]<<15) | (W[j-3]>>17));
P1 = X ^ ((X<<15)|(X>>17)) ^((X<<23)|(X>>9));
W[j] = ((W[j-13]<<7)|(W[j-13]>>25)) ^ W[j-6] ^ P1;
}
}
然而经过GCC编译成汇编语言之后,更接近下面的C语言实现
#include
void expand_data(uint32_t W[68], uint32_t data[16])
{
uint32_tj, X;
for(j = 0; j < 16; j++) W[j] = data[j];
for(j = 0; j < 52; j++) {
X = W[j] ^ W[j+7] ^ ((W[j+13]<<15) | (W[j+13]>>17));
W[j+16] = ((W[j+3]<<7)|(W[j+3]>>25)) ^ W[j+10] ^ X ^ ((X<<15)|(X>>17)) ^((X<<23)|(X>>9));
}
}
上述两份C语言代码编译之后得到的汇编代码是完全相同的:
.file"expand_data.c"
.text
.p2align 4,,15
.globl expand_data
.typeexpand_data, @function
expand_data:
.LFB0:
.cfi_startproc
xorl%eax, %eax#j = 0
.p2align 4,,10
.p2align 3
.L2:
movl(%rsi,%rax), %edx#edx = data[j]
movl%edx, (%rdi,%rax)#W[j] = edx
addq$4, %rax#j++
cmpq$64, %rax#(j == 16)
jne.L2#if (!=) goto .L2
xorl%edx, %edx#j = 0
.p2align 4,,10
.p2align 3
.L3:
movl28(%rdi,%rdx), %ecx#ecx = W[j+7]
movl52(%rdi,%rdx), %eax#eax = W[j+13]
xorl(%rdi,%rdx), %ecx#ecx ^ W[j+0]
rorl$17, %eax#eax = W[j+13]>>>17
xorl%eax, %ecx#ecx ^ W[j+13]>>>17
movl12(%rdi,%rdx), %eax#eax = W[j+3]
movl%ecx, %esi#esi = X
rorl$17, %esi#esi = X>>>17
rorl$25, %eax#eax = W[j+3]>>>25
xorl40(%rdi,%rdx), %eax#eax ^ W[j+10]
xorl%ecx, %eax#eax ^ X
rorl$9, %ecx#ecx = X>>>9
xorl%esi, %eax#eax ^ X>>>17
xorl%ecx, %eax#eax ^ X>>>9
movl%eax, 64(%rdi,%rdx)#W[j+16] = eax
addq$4, %rdx#j++
cmpq$208, %rdx#(j == 52)
jne.L3#if (!=) goto .L3
rep; ret
.cfi_endproc
.LFE0:
.sizeexpand_data, .-expand_data
.ident"GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)"
.section.note.GNU-stack,"",@progbits
阅读GCC产生的汇编代码可知:
1、((x >> 15) | (x << 17))这种会自动变成xor指令,无须使用宏夹杂内联汇编代码污染C语言代码的可读性;
2、GCC会根据需要将临时变量进行寄存器优化,为了可读性多用几个临时变量没啥坏处;
3、GCC似乎不喜欢减法,所以在C语言程序编写时,循环变量也好,数组下标也好,尽量使用加法而不是减法;