X86程序集 - 处理IDIV指令

ely*_*bhy 23 c compiler-construction x86 assembly

我目前正在编写一个简单的C编译器,它将.c文件作为输入并生成汇编代码(X86,AT&T语法).一切都很好,但是当我尝试执行IDIVQ指令时,我得到一个浮点异常.这是我的意见:

int mymain(int x){
  int d;
  int e;
  d = 3;
  e = 6 / d;
  return e;
}
Run Code Online (Sandbox Code Playgroud)

这是我生成的代码:

mymain:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    movq    %rdi, -40(%rbp)
    movq    $3, -8(%rbp)
    movq    $6, %rax
    movq    -8(%rbp), %rdx
    movq    %rdx, %rbx
    idivq   %rbx
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE1:
    .size mymain, .-mymain
Run Code Online (Sandbox Code Playgroud)

http://www.cs.virginia.edu/~evans/cs216/guides/x86.html,idivq%RBX应该产生在6/d(商)%RAX.但我得到一个浮点异常,我似乎无法找到问题.

任何帮助都感激不尽!

Gun*_*iez 24

Mysticials的第一部分答案是正确的,idiv进行128/64位除法,因此rdx保持被除数的高64位的值不得包含随机值.但零扩展是错误的方法.

正如你所签署的变量,你需要签署延长raxrdx:rax.在AT&T和Intel语法中有一个特定的指令cqto(将quad转换为oct)cqo.AFAIK更新版本的气体接受这两个名称.

movq    %rdx, %rbx
cqto                  # sign extend rax to rdx:rax
idivq   %rbx
Run Code Online (Sandbox Code Playgroud)


Mys*_*ial 11

idivq指令rdx:rax用操作数除以128位整数().

  • rax 保持红利的低64位.
  • rdx 保持红利的高64位.

当商不符合64位时,它将抛出该浮点异常.

所以你需要做的是零rdx:

movq    %rdx, %rbx
xorq    %rdx, %rdx    # zero "rdx"
idivq   %rbx
Run Code Online (Sandbox Code Playgroud)

如果你正在处理的有符号整数,您还需要签署延长raxrdx:rax,这意味着复制rax符号位的每一位rdx,并与CQO别名cqto完成:

movq    %rdx, %rbx
cqo
idivq   %rbx
Run Code Online (Sandbox Code Playgroud)

  • 归零rdx将与正数一起使用,但在负rax的情况下,可能需要rdx = -1 ......不是吗? (5认同)
  • 我认为marekb是对的 - 不应该将`xorq` instrcution作为`cqo`指令来将`rax`扩展为`rdx:rax`吗? (5认同)
  • 无符号:`xor%rdx,%rdx` /`div`.签名:`cqo` /`idiv`.归零rdx后使用idiv仅适用于你的除数已签名但你的被除数是无符号的.(而且你想要一个签名的结果).在将`uint64_t`转换为`__int128_t`之后,AC编译器应该仅将其生成为优化,此时它可以证明不存在除法异常.另请注意,在最近的Intel CPU上,64位`div`比64位`idiv`快一些. (2认同)