我可以更改 clang 中参数评估的顺序吗?

Can I change the order of argument evaluation in clang?

Clang 从左到右计算其参数,gcc 从右到左计算。 (根据 C 和 C++ 语言规范,两者都可以,另请参阅 g++ vs intel/clang argument passing order?)有没有办法更改 clang 中参数评估的顺序?如果不是通过 pragma 或编译器开关,也许有人可以将我指向 clang 代码库中的正确位置?


一些背景信息:我有一个巨大的(第 3 方,没有很好记录的)代码库,我正试图将其从 gcc 移植到 clang,但我看到了一些奇怪的问题。根据以前的经验,我认为至少部分问题是参数顺序评估。能够在两种模式之间来回切换而不混合两个完全不同的编译器(因此可能引入许多其他问题来源)将非常有助于将问题一分为二。

clang 中没有选项或编译指示来颠倒函数参数求值的顺序。但是存在支持 MSVC ABI 的现有代码(这似乎需要从右到左的参数评估)。以下 hack(针对当前 clang svn trunk 的补丁)可用于根据环境变量 CLANG_REVERSE_ARGS 的值反转参数评估的顺序。 1 的值反转顺序,0 的值保持原样。

Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp  (revision 229661)
+++ lib/CodeGen/CGCall.cpp  (working copy)
@@ -2676,9 +2676,20 @@
                                    CallExpr::const_arg_iterator ArgEnd,
                                    const FunctionDecl *CalleeDecl,
                                    unsigned ParamsToSkip) {
+  bool ForceReverseArgs = false;
+  const char *p = getenv("CLANG_REVERSE_ARGS");
+  if (p != nullptr) {
+    if (!strcmp(p, "1"))
+      ForceReverseArgs = true;
+    else if (strcmp(p, "0")) {
+      fprintf(stderr, "Expected $CLANG_REVERSE_ARGS to be '0' or '1'!\n");
+      exit(1);
+    }
+  }
+
   // We *have* to evaluate arguments from right to left in the MS C++ ABI,
   // because arguments are destroyed left to right in the callee.
-  if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
+  if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee() || ForceReverseArgs) {
     // Insert a stack save if we're going to need any inalloca args.
     bool HasInAllocaArgs = false;
     for (ArrayRef<QualType>::iterator I = ArgTypes.begin(), E = ArgTypes.end();

它甚至似乎有效:

$ cat > demo.c << EOT
#include <stdio.h>

int a() {
    printf("a\n");
    return 1;
}

int b() {
    printf("b\n");
    return 2;
}

int main() {
    printf("%d%d\n", a(), b());
    return 0;
}
EOT

$ CLANG_REVERSE_ARGS=0 Debug+Asserts/bin/clang demo.c && ./a.out 
a
b
12

$ CLANG_REVERSE_ARGS=1 Debug+Asserts/bin/clang demo.c && ./a.out 
b
a
12

我构建了一个大型 C++ 项目,在 "frankenstein mode" 中有一个很大的测试台:一半的对象 CLANG_REVERSE_ARGS=1 和一半的对象 CLANG_REVERSE_ARGS=0。生成的二进制文件通过了项目的测试平台。

我认为 clang 仍然从右到左计算参数。

考虑这段代码:

int add(int a, int b) { return a+b; }

在 clang 后编译时:

080483c0 <添加>: 80483c0: 55 推 %ebp 80483c1: 89 e5 mov %esp,%ebp 80483c3: 83 ec 08 sub $0x8,%esp 80483c6: 8b 45 0c mov 0xc(%ebp),%eax 80483c9: 8b 4d 08 mov 0x8(%ebp),%ecx 80483cc: 89 4d fc mov %ecx,-0x4(%ebp) 80483cf: 89 45 f8 mov %eax,-0x8(%ebp) 80483d2: 8b 45 fc mov -0x4(%ebp),%eax 80483d5: 03 45 f8 添加-0x8(%ebp),%eax 80483d8: 83 c4 08 添加 $0x8,%esp 80483db: 5d pop %ebp 80483dc: c3 ret<br> 80483dd: 0f 1f 00 nopl (%eax)

让我们想想这个反编译代码:

mov 将 args 复制到堆栈

`

+---------+
|high     |
+---------+
|101      |<-arg2
+---------+
|99       |<-arg1
+---------+
|ret      |
+---------+
|ebp      |
+---------+
|99       |<-a
+---------+
|101      |<-b
+---------+
|low      |
+---------+

`

在 gdb 中打印 &a 或 &b 时,arg1 的地址似乎比 arg2 高。

准确地说,a 和 b 现在在 ebp 下面的堆栈中。