]> granicus.if.org Git - clang/commitdiff
ANSI C requires that a call to an unprototyped function type succeed
authorJohn McCall <rjmccall@apple.com>
Wed, 21 Sep 2011 08:08:30 +0000 (08:08 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 21 Sep 2011 08:08:30 +0000 (08:08 +0000)
if the definition has a non-variadic prototype with compatible
parameters.  Therefore, the default rule for such calls must be to
use a non-variadic convention.  Achieve this by casting the callee to
the function type with which it is required to be compatible, unless
the target specifically opts out and insists that unprototyped calls
should use the variadic rules.  The only case of that I'm aware of is
the x86-64 convention, which passes arguments the same way in both
cases but also sets a small amount of extra information;  here we seek
to maintain compatibility with GCC, which does set this when calling
an unprototyped function.

Addresses PR10810 and PR10713.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@140241 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGExpr.cpp
lib/CodeGen/TargetInfo.cpp
lib/CodeGen/TargetInfo.h
test/CodeGen/builtin-expect.c
test/CodeGen/functions.c
test/CodeGen/kr-call.c
test/CodeGen/microsoft-call-conv.c
test/CodeGen/mrtd.c
test/CodeGen/stdcall-fastcall.c

index f4c552b0850fac4bcbbabd377be6579b56202d11..62be62b79eda5c6520fa17bb16d278af6ec2d7c8 100644 (file)
@@ -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::
index 434e202f2e97d7497e164dc108f1ee18738b4295..bad8a1d928a9adf643331cadd9991aff8473bd6a 100644 (file)
@@ -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 {
index 22e6eb8383b7c30b6358c7248fee8c7bec06d265..8f90c7bdd92d1dd7a656c9dc7e9042ac7658541a 100644 (file)
@@ -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;
   };
 }
 
index 664c6b6a470ecb45c8bf458927d1886431460d0e..73b3b24be8f1fe67a649d851e0b12b9ef3a7e011 100644 (file)
@@ -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
index e51f93e57411208e1585ff7588ab341f7a985793..28e4bd0c8296df582cd8d166c55e5e85ba907777 100644 (file)
@@ -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()
index 97068bce0cdb79efb161b2a856fa8c940a15b5df..ea4e3d3d70f07eff5038884764c1a16fb77203e8 100644 (file)
@@ -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() {}
index 95f5fa3f83b599cff17eba4cf53befb9d6d37296..390c3be05e61fc4efac418a5aa6567fead77b41f 100644 (file)
@@ -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)
 }
index 2cc71bb0086fa17a38050ed1d2e0cf800453b782..d7729a525068d3836de7d6337a1e4b517e027473 100644 (file)
@@ -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);
index 6f3b003287719ae7f700cdd256a19fe5509f680b..3de7b6727bc24cc68865a8bbff69d22686a53c68 100644 (file)
@@ -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)
 }