简体   繁体   English

调用函数时 x86_64 不正确的调用约定

[英]x86_64 incorrect calling convention when calling function

I'm relatively new to LLVM, and I'm attempting to generate LLVM IR that calls a C function ( growDictionary ).我对 LLVM 比较陌生,我正在尝试生成调用 C 函数( growDictionary )的 LLVM IR。 This is on x86_64 Linux, using llvm 12:这是在 x86_64 Linux 上,使用 llvm 12:

$ llc-12 --version
Ubuntu LLVM version 12.0.1

  Optimized build.
  Default target: x86_64-pc-linux-gnu
  Host CPU: broadwell

The function (defined in C++ as extern "C" , compiled with clang 12):函数(在 C++ 中定义为extern "C" ,用 clang 12 编译):

struct StringDictionary {
    uint32_t* base;
    uint32_t elementSize;
    uint32_t rowCount;
    uint32_t wordsCapacity;
};

extern "C" {
StringDictionary growStringDictionary(StringDictionary dict,
                                      uint32_t neededWordsCapacity);
}

The function takes the StringDictionary object by value, but, according to the x86_64 ABI ( https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf , section 3.2.3, "Parameter Passing") should have it passed on the stack.该函数按值获取 StringDictionary 对象,但是,根据 x86_64 ABI ( https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf ,第 3.2.3 节, “参数传递”)应该让它在堆栈上传递。 (The object's size is greater than 2 eightbytes and neither of the eightbytes is in class SSE or SSEUP, so it turns into class MEMORY according to the "post merger cleanup" section.) A cursory look at the disassembly confirms that this is indeed the case: (对象的大小大于 2 个八字节,并且这八个字节都不属于 SSE 或 SSEUP 类,因此根据“合并后清理”部分,它变成了 MEMORY 类。)粗略查看反汇编确认这确实是案件:

Dump of assembler code for function growStringDictionary(rockset::jit::StringDictionary, uint32_t):
   0x00007ffff7f98f70 <+0>: push   %rbp
   0x00007ffff7f98f71 <+1>: mov    %rsp,%rbp
   0x00007ffff7f98f74 <+4>: push   %rbx
   0x00007ffff7f98f75 <+5>: and    $0xffffffffffffffe0,%rsp
   0x00007ffff7f98f79 <+9>: sub    $0x1c0,%rsp
   0x00007ffff7f98f80 <+16>:    mov    %rsp,%rbx
   0x00007ffff7f98f83 <+19>:    mov    %esi,0x15c(%rbx)
   0x00007ffff7f98f89 <+25>:    mov    %rdi,0x160(%rbx)
[...]

%rdi is the address where the return value will be written, %esi is the uint32_t neededWordsCapacity argument, no other argument passing registers are used. %rdi是返回值将被写入的地址, %esi是 uint32_t neededWordsCapacity 参数,没有使用其他参数传递寄存器。

This is all fine so far, but I'm now trying to call this function from my generated IR, and it tries to pass all arguments in registers.到目前为止一切都很好,但我现在试图从我生成的 IR 中调用这个函数,它试图在寄存器中传递所有参数。 Here are the relevant sections of code:以下是代码的相关部分:

  %83 = call { i32*, i32, i32, i32 } @growStringDictionary({ i32*, i32, i32, i32 } %70, i32 %73)
[...]
declare { i32*, i32, i32, i32 } @growStringDictionary({ i32*, i32, i32, i32 }, i32)

Note that the calling convention is default (not changed to something like fastcc).请注意,调用约定是默认的(未更改为类似 fastcc 的约定)。

The generated code (both the JIT I'm trying to use and llc produce the same result) os trying to pass the argument in registers, here's the output from llc -O0 ;生成的代码(我尝试使用的 JIT 和 llc 产生相同的结果)尝试在寄存器中传递参数,这是llc -O0的输出; -O3 is similar: -O3类似:

        movl    148(%rsp), %r9d                 # 4-byte Reload
        movl    140(%rsp), %r8d                 # 4-byte Reload
        movl    136(%rsp), %ecx                 # 4-byte Reload
        movl    132(%rsp), %edx                 # 4-byte Reload
        movq    120(%rsp), %rsi                 # 8-byte Reload
        leaq    376(%rsp), %rdi
        callq   growStringDictionary@PLT

Unsurprisingly, my code segfaults.不出所料,我的代码有段错误。

I'm surprised that llc generated code that doesn't match the ABI.我很惊讶 llc 生成的代码与 ABI 不匹配。 Are there any attributes I need to put on the function declaration, or on the type definition, or is there anything else that I'm missing?我是否需要在函数声明或类型定义上添加任何属性,或者还有什么我遗漏的吗?

It turns out that this part of the calling convention is handled by the frontend (together with, I presume, things like "this is a non-trivial C++ object").事实证明,调用约定的这一部分由前端处理(连同,我认为,诸如“这是一个非平凡的 C++ 对象”之类的事情)。

Take this example file:以这个示例文件为例:

#include <stdint.h>

struct A {
  uint32_t* p;
  uint32_t a;
  uint32_t b;
};

struct B {
  uint32_t* p;
  uint32_t a;
  uint32_t b;
  uint32_t c;
};

uint32_t addA(struct A x) {
  return x.a + x.b;
}

uint32_t addB(struct B x) {
  return x.a + x.b + x.c;
}

clang -S -emit-llvm says: clang -S -emit-llvm说:

%struct.A = type { i32*, i32, i32 }
%struct.B = type { i32*, i32, i32, i32 }

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @addA(i32* %0, i64 %1) #0 {
  %3 = alloca %struct.A, align 8
  %4 = bitcast %struct.A* %3 to { i32*, i64 }*
  %5 = getelementptr inbounds { i32*, i64 }, { i32*, i64 }* %4, i32 0, i32 0
  store i32* %0, i32** %5, align 8
  %6 = getelementptr inbounds { i32*, i64 }, { i32*, i64 }* %4, i32 0, i32 1
  store i64 %1, i64* %6, align 8
  %7 = getelementptr inbounds %struct.A, %struct.A* %3, i32 0, i32 1
  %8 = load i32, i32* %7, align 8
  %9 = getelementptr inbounds %struct.A, %struct.A* %3, i32 0, i32 2
  %10 = load i32, i32* %9, align 4
  %11 = add i32 %8, %10
  ret i32 %11
}

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @addB(%struct.B* byval(%struct.B) align 8 %0) #0 {
  %2 = getelementptr inbounds %struct.B, %struct.B* %0, i32 0, i32 1
  %3 = load i32, i32* %2, align 8
  %4 = getelementptr inbounds %struct.B, %struct.B* %0, i32 0, i32 2
  %5 = load i32, i32* %4, align 4
  %6 = add i32 %3, %5
  %7 = getelementptr inbounds %struct.B, %struct.B* %0, i32 0, i32 3
  %8 = load i32, i32* %7, align 8
  %9 = add i32 %6, %8
  ret i32 %9
}

Note that the argument to addB has become %struct.B* byval(%struct.B) indicating that this is passed on the stack.请注意,参数addB已成%struct.B* byval(%struct.B)表示,这是在栈上传递。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用LLVM进行编译时,Mac OS X上x86_64体系结构的未定义符号 - Undefined symbols for architecture x86_64 on Mac OS X when compiling using LLVM 构建万花筒llvm时,架构x86_64的未定义符号:“ std :: terminate()” - Undefined symbols for architecture x86_64: “std::terminate()”, when building kaleidoscope llvm 构建LLVM示例:体系结构x86_64的未定义符号 - build LLVM example: Undefined symbols for architecture x86_64 Xcode 9架构x86_64的未定义符号 - Xcode 9 undefined symbols for architecture x86_64 ld:找不到架构x86_64错误的符号 - ld: symbol(s) not found for architecture x86_64 error 具有llvm和clang的架构x86_64的未定义符号 - Undefined symbols for architecture x86_64 with llvm and clang x86_64上无用的jp / jnp汇编指令 - Useless jp / jnp assembly instruction on x86_64 架构 x86_64 (clang) 的未定义符号 - Undefined symbols for architecture x86_64 (clang) 在Linux x86_64上编译llvm:关于优化标志的建议? - compiling llvm on linux x86_64: suggestions on optimization flags? 编译器什么时候为x86_64生成间接跳转,用C语言? 我们可以告诉编译器禁用生成间接跳转吗? - When will compiler generate indirect jump for x86_64, in C language? Can we tell compilers to disable generating indirect jumps?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM