64位GCC混合使用32位和64位指针

5
尽管代码能够正常工作,但我对编译器似乎混合使用相同类型的32位和64位参数感到困惑。具体地说,我有一个接收三个char指针的函数。查看汇编代码,其中三个指针中的两个被传递为64位指针(如预期所示),而第三个是本地常量,但仍然是字符字符串,并被传递为32位指针。我不明白当第3个参数不是完全加载的64位指针时,我的函数怎么可能知道。显然,只要高位为0,这并不重要,但我没有看到它努力确保这一点。在这个例子中,RDX的高位可以是任何值。我错过了什么?顺便说一下,接收函数假定它是一个完整的64位指针,并在进入时包括此代码:
     movq    %rdx, -24(%rbp)

以下是相关代码:

.LC4
    .string "My Silly String"

    .text
    .globl funky_funk
    .type  funky_funk, @function
    funky_funk:
        pushq     %rbp
            movq      %rsp, %rbp
            pushq     %rbx
            subq      $16, %rsp
            movq      %rdi, -16(%rbp)          ;char *dst 64-bit
            movl      %esi, -20(%rbp)          ;int len, 32 bits OK

            movl      $.LC4, %edx              ;<<<<---- why is it not RDX?

            movl      -20(%rbp), %ecx          ;int len 32-bits OK
            movq      -16(%rbp), %rbx          ;char *dst 64-bit
            movq      -16(%rbp), %rax          ;char *dst 64-bit
            movq      %rbx, %rsi               ;char *dst 64-bit
            movq      %rax, %rdi               ;char *dst 64-bit
            call      edc_function


    void funky_funk(char *dst, int len)
    {                                             //how will function know when 
         edc_function(dst, dst, STRING_LC4, len); //a str passed in 3rd parm
    }                                             //is 32-bit ptr vs 64-bit ptr?

    void edc_function(char *dst, char *src, char *key, int len)
    {
         //so, is key a 32-bit ptr? or is key a 64-bit ptr?
    }

只要编译器能够确定.LC4在前4GB内,它就可以这样做。不过看起来像是一个bug。%edx将被加载为LC4地址的32位,并将上位位置为零,因此当调用edc_function()时,它可以使用完整的64位,只要地址在低4GB内,就可以正常工作。但对我来说,这看起来很奇怪。 - Mats Petersson
好的,我不知道CPU会对负载进行零扩展。哎呀!这让我今晚能够安心睡觉了... - Gary
请注意,在无法进行此优化的情况下,下一个最佳选择是lea .LC4(%rip),%rdx。而不是使用64位绝对地址的10字节mov指令。如何在GNU汇编器中将函数或标签的地址加载到寄存器中 - Peter Cordes
2个回答

5

当在寄存器中加载32位值时,该值会被零扩展。您可能是在编译器知道代码在低32位可寻址内存中的模式下工作。

GCC针对x64有几种内存模型,其中两种具有该属性。引自GCC文档:

`-mcmodel=small'
     Generate code for the small code model: the program and its
     symbols must be linked in the lower 2 GB of the address space.
     Pointers are 64 bits.  Programs can be statically or dynamically
     linked.  This is the default code model.
`-mcmodel=medium'
     Generate code for the medium model: The program is linked in the
     lower 2 GB of the address space.  Small symbols are also placed
     there.  Symbols with sizes larger than `-mlarge-data-threshold'
     are put into large data or bss sections and can be located above
     2GB.  Programs can be statically or dynamically linked.

(其他的是内核,类似于small但在地址空间的上部/负2GB,并且没有限制的large。)


据我所知,我正在使用mcmodel = small,因为它是默认值(除了-O2之外,我没有编译器选项。“-mcmodel = small生成小代码模型的代码:程序及其符号必须链接到地址空间的低2 GB中。指针为64位。程序可以静态或动态链接。这是默认的代码模型。”但你关于零扩展和编译器知道这个特定字符串位置的说法是正确的。 - Gary
@GarysTampaOfficeOfficeTampa,问题已解决。我一开始在寻找一个不存在的东西,而忽略了证明就在我的起点上方;)我(回顾时感到很愚蠢)假设小型是类似于-mx32,具有64位长。 - AProgrammer
你的主要答案仍然是正确的:在64位模式下,32位寄存器被零扩展。 - Gary

4

作为答案添加,因为它包含了原始问题的“拼图”部分:

只要编译器能够确定(例如通过指定满足此条件的内存模型).LC4在前4GB内,它就可以这样做。%edx将加载LC4地址的32位,并将上位位置零,因此当调用edc_function()时,它可以使用%rdx的完整64位,只要地址在低4GB内,它就能正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接