我可以更改 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 下面的堆栈中。
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 下面的堆栈中。