汇编翻译
Assembly translation
我正在尝试将以下 C 代码转换为汇编代码:
void write (int bitpos, unsigned short sample)
{
int pos = bitpos / 16;
int posA = bitpos - pos * 16;
unsigned short write1 = sample >> posA;
}
我总是在移位操作中出错。我看了一本书中的一些例子,但我不明白哪里出了问题。我想可能是因为我要转移的数量是一个变量。我想知道实现这一目标的正确方法是什么?
这是我尝试过的:
//int pos = bitpos / 16;
mov eax, 0
mov eax, [bitpos] // eax= bitpos
cdq
mov ecx, 16
idiv ecx //ecx = pos
//int posA = bitpos - pos * 16;
mov ebx, ecx //ebx = pos
imul ebx, 16 // ebx = pos*16
sub eax, ebx // eax = posA
//unsigned short write1 = sample >> posA;
mov bx, [sample]
shr bx, eax // This is the part that is not working.
错误说:错误的操作数类型。错误代码:C2415
你的 write()
函数没有 return 值,也没有副作用(没有写入任何全局变量,没有系统调用,只设置一些局部变量,当函数 return)。您可以而且应该将其优化为空函数 just like gcc does.
global write
write:
ret
假设您的函数 return 是 write1
变量,因此您必须计算它。
gcc -Og
(针对调试进行了优化)使可读性很好的 asm 不会一直从内存中 store/reload。 gcc -m32 -Og -fverbose-asm -masm=intel
emits:
# see the godbolt link for colour-coded mapping of source lines to asm lines
write(int, unsigned short):
mov edx, DWORD PTR [esp+4] # bitpos, bitpos
lea eax, [edx+15] # tmp98,
test edx, edx # bitpos
cmovns eax, edx # tmp98,, bitpos, bitpos
sar eax, 4 # tmp99,
neg eax # tmp101
sal eax, 4 # tmp102,
mov ecx, eax # tmp102, tmp102
add ecx, edx # posA, bitpos
movzx eax, WORD PTR [esp+8] # D.2591, sample
sar eax, cl # D.2591, posA
ret
注意它是如何从堆栈加载函数参数的,因为它们是函数参数,而不是全局参数。 (您的代码引用 [bitpos]
,一个全局的,而不是 return 地址 [esp+4]
之后堆栈上的第一个位置。)64 位 ABI 在寄存器中传递参数,因此您可以获得更清晰的代码。
有条件移动代码是因为负数的整数除法的 C 语义给出了与算术右移不同的结果(它们的舍入方式不同)。由于 idiv
与班次相比非常昂贵,因此仍然值得使用额外的指令来设置班次。如果 bitpos
是无符号的,它可以只使用 shr
.
通过全面优化,gcc 找到了一种更有效的处理方式,并将一些算术合并在一起。 (即除以 16,然后乘以 16,四舍五入到最接近的 16 的倍数,用单个 and
来屏蔽掉这些位。)
故事的寓意:您始终可以查看编译器 输出以获取有关如何做某事的灵感,并且经常会看到您最初没有想到的技巧。
我正在尝试将以下 C 代码转换为汇编代码:
void write (int bitpos, unsigned short sample)
{
int pos = bitpos / 16;
int posA = bitpos - pos * 16;
unsigned short write1 = sample >> posA;
}
我总是在移位操作中出错。我看了一本书中的一些例子,但我不明白哪里出了问题。我想可能是因为我要转移的数量是一个变量。我想知道实现这一目标的正确方法是什么?
这是我尝试过的:
//int pos = bitpos / 16;
mov eax, 0
mov eax, [bitpos] // eax= bitpos
cdq
mov ecx, 16
idiv ecx //ecx = pos
//int posA = bitpos - pos * 16;
mov ebx, ecx //ebx = pos
imul ebx, 16 // ebx = pos*16
sub eax, ebx // eax = posA
//unsigned short write1 = sample >> posA;
mov bx, [sample]
shr bx, eax // This is the part that is not working.
错误说:错误的操作数类型。错误代码:C2415
你的 write()
函数没有 return 值,也没有副作用(没有写入任何全局变量,没有系统调用,只设置一些局部变量,当函数 return)。您可以而且应该将其优化为空函数 just like gcc does.
global write
write:
ret
假设您的函数 return 是 write1
变量,因此您必须计算它。
gcc -Og
(针对调试进行了优化)使可读性很好的 asm 不会一直从内存中 store/reload。 gcc -m32 -Og -fverbose-asm -masm=intel
emits:
# see the godbolt link for colour-coded mapping of source lines to asm lines
write(int, unsigned short):
mov edx, DWORD PTR [esp+4] # bitpos, bitpos
lea eax, [edx+15] # tmp98,
test edx, edx # bitpos
cmovns eax, edx # tmp98,, bitpos, bitpos
sar eax, 4 # tmp99,
neg eax # tmp101
sal eax, 4 # tmp102,
mov ecx, eax # tmp102, tmp102
add ecx, edx # posA, bitpos
movzx eax, WORD PTR [esp+8] # D.2591, sample
sar eax, cl # D.2591, posA
ret
注意它是如何从堆栈加载函数参数的,因为它们是函数参数,而不是全局参数。 (您的代码引用 [bitpos]
,一个全局的,而不是 return 地址 [esp+4]
之后堆栈上的第一个位置。)64 位 ABI 在寄存器中传递参数,因此您可以获得更清晰的代码。
有条件移动代码是因为负数的整数除法的 C 语义给出了与算术右移不同的结果(它们的舍入方式不同)。由于 idiv
与班次相比非常昂贵,因此仍然值得使用额外的指令来设置班次。如果 bitpos
是无符号的,它可以只使用 shr
.
通过全面优化,gcc 找到了一种更有效的处理方式,并将一些算术合并在一起。 (即除以 16,然后乘以 16,四舍五入到最接近的 16 的倍数,用单个 and
来屏蔽掉这些位。)
故事的寓意:您始终可以查看编译器 输出以获取有关如何做某事的灵感,并且经常会看到您最初没有想到的技巧。