diff options
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 32 | ||||
-rw-r--r-- | lib/CodeGen/TargetInfo.cpp | 17 | ||||
-rw-r--r-- | lib/CodeGen/TargetInfo.h | 35 | ||||
-rw-r--r-- | test/CodeGen/builtin-expect.c | 2 | ||||
-rw-r--r-- | test/CodeGen/functions.c | 4 | ||||
-rw-r--r-- | test/CodeGen/kr-call.c | 5 | ||||
-rw-r--r-- | test/CodeGen/microsoft-call-conv.c | 2 | ||||
-rw-r--r-- | test/CodeGen/mrtd.c | 2 | ||||
-rw-r--r-- | test/CodeGen/stdcall-fastcall.c | 2 |
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) } |