2013-10-29 37 views
4

我有一个任务,我需要将装配转换为C.装配是x86。我注释了这个装配,并开始填充C中的空白,但我有些失去了一些东西,有人可以协助吗?请解释一下,不要只给出我想学习的答案。装配到C的帮助

大会:

x at %ebp+8, n at %ebp+12 

1 movl 8(%ebp), %esi //store x in esi 
2 movl 12(%ebp), %ebx //store n in ebx 
3 movl $-1, %edi  //result in edi 
4 movl $1, %edx  //i of loop in edx 
5 .L2: 
6 movl %edx, %eax  //move edx to eax 
7 andl %esi, %eax  //sum += 1 ...? i think 
8 xorl %eax, %edi  //results = results^(i & x) 
9 movl %ebx, %ecx  //store n in ecx 
10 sall %cl, %edx  //shift edx by %cl (low byte of ecx) 
11 testl %edx, %edx //check if zeroed out 
12 jne .L2   //jump to .L2 if flag 
13 movl %edi, %eax //move result to eax 

C代码:

int loop(int x, int n) { 
    int result = _______; 
    int mask; 
    for (mask = 1; mask != 0; mask = ______) { 
    result ^= mask & x; 
    } 
    return result; 
} 

回答

5

我开始是这样的:

movl 8(%ebp), %esi ;; esi := x 
movl 12(%ebp), %ebx ;; ebx := n 
movl $-1, %edi  ;; edi := -1 
movl $1, %edx  ;; edx := 1 
.L2: 
movl %edx, %eax  ;; eax := edx 
andl %esi, %eax  ;; eax &= esi (:= x) 
xorl %eax, %edi  ;; edi ^= eax 
movl %ebx, %ecx  ;; ecx := ebx (:= n) 
sall %cl, %edx  ;; edx <<= ecx & 0x000000FF 
testl %edx, %edx ;; set flags with edx & edx 
jne .L2    ;; loop if not ZF 
movl %edi, %eax  ;; eax := edi 

,并通过垂直向上平移进展到这一点,切割为几个小钱的我可以管理:

int x, n, foo = -1, bar = 1; 

do { 
    int baz = bar; 
    baz &= x; 
    foo ^= baz; 

    int qux = n; 
    bar <<= qux & 0xFF; 
} while (bar); 

// now do something with foo 

,然后删除不必要的临时变量,改变循环的友好形式,并添加函数体和return语句达到这一点:这里

int func(int x, int n) { 
    int result = -1; 

    for (int mask = 1; mask; mask <<= n) { 
     result ^= (mask & x); 
    } 
    return result; 
} 

请注意,我已经下降掩盖了所有扎下左边的n的字节。在评论中进行了一些讨论后,我已经解决了这个问题并添加了一个解释。 我们可以通过以下两种方式之一使用sal

  1. 立即sall $2, %edx ;; left shift EDX by 2
  2. 通过CL移位,例如, sall %cl, %edx ;; left shift EDX by the lower byte of ECX

由于由比特数中的整数或更多移是undefined behaviour(和一个字节是超过足以表示一个32位的数字明确定义转移更多),编译器没有义务以一种有用的方式处理它,所以任何被编译为通过CL移位的东西都不需要明确掩蔽。因此,不需要在C'翻译中显示明确的掩码,但是由于这是一项任务,我衷心推荐实际解释您的选择。

(感谢Peter Huene在意见提出这个问题。)

您也可以从生成的C代码生成x86汇编,看看你会得到什么。不要期望得到完全一样的东西,而是用它来学习。例如你可以检查<< n被编译到我们的循环中。像clang -O0 -S -mllvm --x86-asm-syntax=att filename.c这样的事情就可以做到。

我不太清楚你想要什么解释你有最东西整理已经(除movl $-1, %edi初始化结果-1andl %esi, %eax不是加)

+1

我想表达'N'0xFF'在这里误导,因为没有相应的程序集映射它,尽管在语义上是正确的(简单的演员可能会更有意义)。 'sal'指令只有两种表示形式:直接形式和通过'cl'移位(有趣的事实:Intel处理器也为'sal'指令屏蔽'cl'为5位)。将这一点与移位溢出未定义的事实相结合(即n> = 32),编译器知道它甚至不需要屏蔽最不重要的字节。 –

+0

在我这样表达它之前,我已经做了很多修改,但我确实同意你的看法,但提问者说这是一个任务,所以我选择在C代码中显示只有低位字节用于移位。我现在正在考虑添加一些细节。所有这一切都是为了回答,回想起来我本来应该做的。 你为什么认为演员会更清晰?我认为这可能更糟糕,因为(在C99中)'int'到'signed char'投射是在溢出的情况下实现定义的行为... –

+0

顺便说一句,英特尔将'cl'的'cl'掩盖为5位是可爱 - 谢谢你的提示,尽管我希望它实际上不是一个真实的信息(经常重复的一块不可靠的信息)。 –