簡體   English   中英

調用函數時 x86_64 不正確的調用約定

[英]x86_64 incorrect calling convention when calling function

我對 LLVM 比較陌生,我正在嘗試生成調用 C 函數( growDictionary )的 LLVM IR。 這是在 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

函數(在 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);
}

該函數按值獲取 StringDictionary 對象,但是,根據 x86_64 ABI ( https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf ,第 3.2.3 節, “參數傳遞”)應該讓它在堆棧上傳遞。 (對象的大小大於 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是返回值將被寫入的地址, %esi是 uint32_t neededWordsCapacity 參數,沒有使用其他參數傳遞寄存器。

到目前為止一切都很好,但我現在試圖從我生成的 IR 中調用這個函數,它試圖在寄存器中傳遞所有參數。 以下是代碼的相關部分:

  %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)

請注意,調用約定是默認的(未更改為類似 fastcc 的約定)。

生成的代碼(我嘗試使用的 JIT 和 llc 產生相同的結果)嘗試在寄存器中傳遞參數,這是llc -O0的輸出; -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

不出所料,我的代碼有段錯誤。

我很驚訝 llc 生成的代碼與 ABI 不匹配。 我是否需要在函數聲明或類型定義上添加任何屬性,或者還有什么我遺漏的嗎?

事實證明,調用約定的這一部分由前端處理(連同,我認為,諸如“這是一個非平凡的 C++ 對象”之類的事情)。

以這個示例文件為例:

#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說:

%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
}

請注意,參數addB已成%struct.B* byval(%struct.B)表示,這是在棧上傳遞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM