aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/CodeGen/CGExpr.cpp32
-rw-r--r--lib/CodeGen/TargetInfo.cpp17
-rw-r--r--lib/CodeGen/TargetInfo.h35
-rw-r--r--test/CodeGen/builtin-expect.c2
-rw-r--r--test/CodeGen/functions.c4
-rw-r--r--test/CodeGen/kr-call.c5
-rw-r--r--test/CodeGen/microsoft-call-conv.c2
-rw-r--r--test/CodeGen/mrtd.c2
-rw-r--r--test/CodeGen/stdcall-fastcall.c2
9 files changed, 90 insertions, 11 deletions
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index f4c552b085..62be62b79e 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -18,6 +18,7 @@
#include "CGDebugInfo.h"
#include "CGRecordLayout.h"
#include "CGObjCRuntime.h"
+#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Frontend/CodeGenOptions.h"
@@ -2414,8 +2415,35 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
CallArgList Args;
EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), ArgBeg, ArgEnd);
- return EmitCall(CGM.getTypes().getFunctionInfo(Args, FnType),
- Callee, ReturnValue, Args, TargetDecl);
+ const CGFunctionInfo &FnInfo = CGM.getTypes().getFunctionInfo(Args, FnType);
+
+ // C99 6.5.2.2p6:
+ // If the expression that denotes the called function has a type
+ // that does not include a prototype, [the default argument
+ // promotions are performed]. If the number of arguments does not
+ // equal the number of parameters, the behavior is undefined. If
+ // the function is defined with a type that includes a prototype,
+ // and either the prototype ends with an ellipsis (, ...) or the
+ // types of the arguments after promotion are not compatible with
+ // the types of the parameters, the behavior is undefined. If the
+ // function is defined with a type that does not include a
+ // prototype, and the types of the arguments after promotion are
+ // not compatible with those of the parameters after promotion,
+ // the behavior is undefined [except in some trivial cases].
+ // That is, in the general case, we should assume that a call
+ // through an unprototyped function type works like a *non-variadic*
+ // call. The way we make this work is to cast to the exact type
+ // of the promoted arguments.
+ if (isa<FunctionNoProtoType>(FnType) &&
+ !getTargetHooks().isNoProtoCallVariadic(FnType->getCallConv())) {
+ assert(cast<llvm::FunctionType>(Callee->getType()->getContainedType(0))
+ ->isVarArg());
+ llvm::Type *CalleeTy = getTypes().GetFunctionType(FnInfo, false);
+ CalleeTy = CalleeTy->getPointerTo();
+ Callee = Builder.CreateBitCast(Callee, CalleeTy, "callee.knr.cast");
+ }
+
+ return EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl);
}
LValue CodeGenFunction::
diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp
index 434e202f2e..bad8a1d928 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
@@ -98,6 +98,14 @@ unsigned TargetCodeGenInfo::getSizeOfUnwindException() const {
return 32;
}
+bool TargetCodeGenInfo::isNoProtoCallVariadic(CallingConv CC) const {
+ // The following conventions are known to require this to be false:
+ // x86_stdcall
+ // MIPS
+ // For everything else, we just prefer false unless we opt out.
+ return false;
+}
+
static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays);
/// isEmptyField - Return true iff a the field is "empty", that is it
@@ -959,6 +967,15 @@ public:
return X86AdjustInlineAsmType(CGF, Constraint, Ty);
}
+ bool isNoProtoCallVariadic(CallingConv CC) const {
+ // The default CC on x86-64 sets %al to the number of SSA
+ // registers used, and GCC sets this when calling an unprototyped
+ // function, so we override the default behavior.
+ if (CC == CC_Default || CC == CC_C) return true;
+
+ return TargetCodeGenInfo::isNoProtoCallVariadic(CC);
+ }
+
};
class WinX86_64TargetCodeGenInfo : public TargetCodeGenInfo {
diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h
index 22e6eb8383..8f90c7bdd9 100644
--- a/lib/CodeGen/TargetInfo.h
+++ b/lib/CodeGen/TargetInfo.h
@@ -16,6 +16,7 @@
#define CLANG_CODEGEN_TARGETINFO_H
#include "clang/Basic/LLVM.h"
+#include "clang/AST/Type.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
@@ -126,6 +127,40 @@ namespace clang {
virtual StringRef getARCRetainAutoreleasedReturnValueMarker() const {
return "";
}
+
+ /// Determine whether a call to an unprototyped functions under
+ /// the given calling convention should use the variadic
+ /// convention or the non-variadic convention.
+ ///
+ /// There's a good reason to make a platform's variadic calling
+ /// convention be different from its non-variadic calling
+ /// convention: the non-variadic arguments can be passed in
+ /// registers (better for performance), and the variadic arguments
+ /// can be passed on the stack (also better for performance). If
+ /// this is done, however, unprototyped functions *must* use the
+ /// non-variadic convention, because C99 states that a call
+ /// through an unprototyped function type must succeed if the
+ /// function was defined with a non-variadic prototype with
+ /// compatible parameters. Therefore, splitting the conventions
+ /// makes it impossible to call a variadic function through an
+ /// unprototyped type. Since function prototypes came out in the
+ /// late 1970s, this is probably an acceptable trade-off.
+ /// Nonetheless, not all platforms are willing to make it, and in
+ /// particularly x86-64 bends over backwards to make the
+ /// conventions compatible.
+ ///
+ /// The default is false. This is correct whenever:
+ /// - the conventions are exactly the same, because it does not
+ /// matter and the resulting IR will be somewhat prettier in
+ /// certain cases; or
+ /// - the conventions are substantively different in how they pass
+ /// arguments, because in this case using the variadic convention
+ /// will lead to C99 violations.
+ /// It is not necessarily correct when arguments are passed in the
+ /// same way and some out-of-band information is passed for the
+ /// benefit of variadic callees, as is the case for x86-64.
+ /// In this case the ABI should be consulted.
+ virtual bool isNoProtoCallVariadic(CallingConv CC) const;
};
}
diff --git a/test/CodeGen/builtin-expect.c b/test/CodeGen/builtin-expect.c
index 664c6b6a47..73b3b24be8 100644
--- a/test/CodeGen/builtin-expect.c
+++ b/test/CodeGen/builtin-expect.c
@@ -18,7 +18,7 @@ int main() {
}
// CHECK: call void @isigprocmask()
-// CHECK: [[C:%.*]] = call i64 (...)* @bar()
+// CHECK: [[C:%.*]] = call i64 bitcast (i64 (...)* @bar to i64 ()*)()
// CHECK: @test1
diff --git a/test/CodeGen/functions.c b/test/CodeGen/functions.c
index e51f93e574..28e4bd0c82 100644
--- a/test/CodeGen/functions.c
+++ b/test/CodeGen/functions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - -verify | FileCheck %s
+// RUN: %clang_cc1 %s -triple i386-unknown-unknown -emit-llvm -o - -verify | FileCheck %s
int g();
@@ -24,7 +24,7 @@ void f0() {}
void f1();
void f2(void) {
-// CHECK: call void @f1()
+// CHECK: call void bitcast (void ()* @f1 to void (i32, i32, i32)*)(i32 1, i32 2, i32 3)
f1(1, 2, 3);
}
// CHECK: define void @f1()
diff --git a/test/CodeGen/kr-call.c b/test/CodeGen/kr-call.c
index 97068bce0c..ea4e3d3d70 100644
--- a/test/CodeGen/kr-call.c
+++ b/test/CodeGen/kr-call.c
@@ -2,11 +2,10 @@
// Test that we don't crash. The s390x-unknown-linux target happens
// to need to set a sext argument attribute on this call, and we need
-// to make sure that rewriting it correctly drops that attribute when
-// also dropping the spurious argument.
+// to make sure that rewriting it correctly keeps that attribute.
void test0_helper();
void test0() {
- // CHECK: call void @test0_helper()
+ // CHECK: call void bitcast (void ()* @test0_helper to void (i32)*)(i32 signext 1)
test0_helper(1);
}
void test0_helper() {}
diff --git a/test/CodeGen/microsoft-call-conv.c b/test/CodeGen/microsoft-call-conv.c
index 95f5fa3f83..390c3be05e 100644
--- a/test/CodeGen/microsoft-call-conv.c
+++ b/test/CodeGen/microsoft-call-conv.c
@@ -46,5 +46,5 @@ int main(void) {
void __stdcall f7(foo) int foo; {}
void f8(void) {
f7(0);
- // CHECK: call x86_stdcallcc void (...)* bitcast
+ // CHECK: call x86_stdcallcc void @f7(i32 0)
}
diff --git a/test/CodeGen/mrtd.c b/test/CodeGen/mrtd.c
index 2cc71bb008..d7729a5250 100644
--- a/test/CodeGen/mrtd.c
+++ b/test/CodeGen/mrtd.c
@@ -4,7 +4,7 @@ void baz(int arg);
// CHECK: define x86_stdcallcc void @foo(i32 %arg) nounwind
void foo(int arg) {
-// CHECK: call x86_stdcallcc i32 (...)* @bar(i32
+// CHECK: call x86_stdcallcc i32 bitcast (i32 (...)* @bar to i32 (i32)*)(
bar(arg);
// CHECK: call x86_stdcallcc void @baz(i32
baz(arg);
diff --git a/test/CodeGen/stdcall-fastcall.c b/test/CodeGen/stdcall-fastcall.c
index 6f3b003287..3de7b6727b 100644
--- a/test/CodeGen/stdcall-fastcall.c
+++ b/test/CodeGen/stdcall-fastcall.c
@@ -46,5 +46,5 @@ int main(void) {
void __attribute((stdcall)) f7(foo) int foo; {}
void f8(void) {
f7(0);
- // CHECK: call x86_stdcallcc void (...)* bitcast
+ // CHECK: call x86_stdcallcc void @f7(i32 0)
}