Skip to content

Commit

Permalink
Ensure that debugger calls to signature-less functions default to
Browse files Browse the repository at this point in the history
passing arguments in the fixed style.

We have an abstraction for deciding this, but it's (1) deep in
IR-generation, (2) necessarily tied to exact argument lists, and
(3) triggered by unprototyped function types, which we can't
legitimately make in C++ mode.  So this solution, wherein Sema
rewrites the function type to an exact prototype but leaves the
variadic bit enabled so as to request x86-64-like platforms to
pass the extra variadic info, is very much a hack, but it's one
that works in practice on the platforms that LLDB will support
in the medium term --- the only place we know of where it's a
problem is instance methods in Windows, where variadic functions
are implicitly cdecl.  We may have a more abstracted base on which
to build a solution by then.

rdar://13731520

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@185112 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rjmccall committed Jun 27, 2013
1 parent 267fad8 commit 02a01fa
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 25 deletions.
7 changes: 7 additions & 0 deletions lib/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ namespace clang {
/// arguments in %al. On these platforms, it is desireable to
/// call unprototyped functions using the variadic convention so
/// that unprototyped calls to varargs functions still succeed.
///
/// Relatedly, platforms which pass the fixed arguments to this:
/// A foo(B, C, D);
/// differently than they would pass them to this:
/// A foo(B, C, D, ...);
/// may need to adjust the debugger-support code in Sema to do the
/// right thing when calling a function with no know signature.
virtual bool isNoProtoCallVariadic(const CodeGen::CallArgList &args,
const FunctionNoProtoType *fnType) const;

Expand Down
45 changes: 42 additions & 3 deletions lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12200,12 +12200,49 @@ ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) {
assert(E->getObjectKind() == OK_Ordinary);

// Rebuild the function type, replacing the result type with DestType.
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FnType))
DestType = S.Context.getFunctionType(DestType, Proto->getArgTypes(),
const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FnType);
if (Proto) {
// __unknown_anytype(...) is a special case used by the debugger when
// it has no idea what a function's signature is.
//
// We want to build this call essentially under the K&R
// unprototyped rules, but making a FunctionNoProtoType in C++
// would foul up all sorts of assumptions. However, we cannot
// simply pass all arguments as variadic arguments, nor can we
// portably just call the function under a non-variadic type; see
// the comment on IR-gen's TargetInfo::isNoProtoCallVariadic.
// However, it turns out that in practice it is generally safe to
// call a function declared as "A foo(B,C,D);" under the prototype
// "A foo(B,C,D,...);". The only known exception is with the
// Windows ABI, where any variadic function is implicitly cdecl
// regardless of its normal CC. Therefore we change the parameter
// types to match the types of the arguments.
//
// This is a hack, but it is far superior to moving the
// corresponding target-specific code from IR-gen to Sema/AST.

ArrayRef<QualType> ParamTypes = Proto->getArgTypes();
SmallVector<QualType, 8> ArgTypes;
if (ParamTypes.empty() && Proto->isVariadic()) { // the special case
ArgTypes.reserve(E->getNumArgs());
for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) {
Expr *Arg = E->getArg(i);
QualType ArgType = Arg->getType();
if (E->isLValue()) {
ArgType = S.Context.getLValueReferenceType(ArgType);
} else if (E->isXValue()) {
ArgType = S.Context.getRValueReferenceType(ArgType);
}
ArgTypes.push_back(ArgType);
}
ParamTypes = ArgTypes;
}
DestType = S.Context.getFunctionType(DestType, ParamTypes,
Proto->getExtProtoInfo());
else
} else {
DestType = S.Context.getFunctionNoProtoType(DestType,
FnType->getExtInfo());
}

// Rebuild the appropriate pointer-to-function type.
switch (Kind) {
Expand Down Expand Up @@ -12338,6 +12375,8 @@ ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) {
return ExprError();
}

// Modifying the declaration like this is friendly to IR-gen but
// also really dangerous.
VD->setType(DestType);
E->setType(Type);
E->setValueKind(ValueKind);
Expand Down
62 changes: 40 additions & 22 deletions test/CodeGenCXX/unknown-anytype.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -funknown-anytype -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -funknown-anytype -emit-llvm -o %t %s
// RUN: FileCheck -check-prefix COMMON %s < %t
// RUN: FileCheck -check-prefix X86_64 %s < %t
// RUN: %clang_cc1 -triple i386-apple-darwin10 -funknown-anytype -emit-llvm -o %t %s
// RUN: FileCheck -check-prefix COMMON %s < %t
// RUN: FileCheck -check-prefix I386 %s < %t

// x86-64 is the special case here because of its variadic convention.
// We want to ensure that it always uses a variadic convention even if
// other platforms do not.
// rdar://13731520

int test0() {
extern __unknown_anytype test0_any;
// CHECK: load i32* @test0_any
// COMMON: load i32* @test0_any
return (int) test0_any;
}

int test1() {
extern __unknown_anytype test1_any();
// CHECK: call i32 @_Z9test1_anyv()
// COMMON: call i32 @_Z9test1_anyv()
return (int) test1_any();
}

extern "C" __unknown_anytype test2_any(...);
float test2() {
// CHECK: call float (...)* @test2_any(double {{[^,]+}})
// X86_64: call float (double, ...)* @test2_any(double {{[^,]+}})
// I386: call float (double, ...)* @test2_any(double {{[^,]+}})
return (float) test2_any(0.5f);
}

extern "C" __unknown_anytype test2a_any(...);
float test2a() {
// CHECK: call float (...)* @test2a_any(float {{[^,]+}})
// X86_64: call float (float, ...)* @test2a_any(float {{[^,]+}})
// I386: call float (float, ...)* @test2a_any(float {{[^,]+}})
return (float) test2a_any((float) 0.5f);
}

float test3() {
extern __unknown_anytype test3_any;
// CHECK: [[FN:%.*]] = load float (i32)** @test3_any,
// CHECK: call float [[FN]](i32 5)
// COMMON: [[FN:%.*]] = load float (i32)** @test3_any,
// COMMON: call float [[FN]](i32 5)
return ((float(*)(int)) test3_any)(5);
}

Expand All @@ -36,30 +48,30 @@ namespace test4 {
extern __unknown_anytype test4_any2;

int test() {
// CHECK: load i32* @_ZN5test410test4_any1E
// CHECK: load i8* @_ZN5test410test4_any2E
// COMMON: load i32* @_ZN5test410test4_any1E
// COMMON: load i8* @_ZN5test410test4_any2E
return (int) test4_any1 + (char) test4_any2;
}
}

extern "C" __unknown_anytype test5_any();
void test5() {
// CHECK: call void @test5_any()
// COMMON: call void @test5_any()
return (void) test5_any();
}

extern "C" __unknown_anytype test6_any(float *);
long test6() {
// CHECK: call i64 @test6_any(float* null)
return (long) test6_any(0);
// COMMON: call i64 @test6_any(float* null)
return (long long) test6_any(0);
}

struct Test7 {
~Test7();
};
extern "C" __unknown_anytype test7_any(int);
Test7 test7() {
// CHECK: call void @test7_any({{%.*}}* sret {{%.*}}, i32 5)
// COMMON: call void @test7_any({{%.*}}* sret {{%.*}}, i32 5)
return (Test7) test7_any(5);
}

Expand All @@ -71,29 +83,35 @@ struct Test8 {
};
void Test8::test() {
float f;
// CHECK: call i32 @_ZN5Test83fooEv(
// COMMON: call i32 @_ZN5Test83fooEv(
f = (int) foo();
// CHECK: call i32 @_ZN5Test83fooEi(
// COMMON: call i32 @_ZN5Test83fooEi(
f = (int) foo(5);
// CHECK: call i32 @_ZN5Test83fooEv(
// COMMON: call i32 @_ZN5Test83fooEv(
f = (float) this->foo();
// CHECK: call i32 @_ZN5Test83fooEi(
// COMMON: call i32 @_ZN5Test83fooEi(
f = (float) this->foo(5);
}
void test8(Test8 *p) {
double d;
// CHECK: call i32 @_ZN5Test83fooEv(
// COMMON: call i32 @_ZN5Test83fooEv(
d = (double) p->foo();
// CHECK: call i32 @_ZN5Test83fooEi(
// COMMON: call i32 @_ZN5Test83fooEi(
d = (double) p->foo(5);
// CHECK: call i32 @_ZN5Test83fooEv(
// COMMON: call i32 @_ZN5Test83fooEv(
d = (bool) (*p).foo();
// CHECK: call i32 @_ZN5Test83fooEi(
// COMMON: call i32 @_ZN5Test83fooEi(
d = (bool) (*p).foo(5);
}

extern "C" __unknown_anytype test9_foo;
void *test9() {
// CHECK: ret i8* bitcast (i32* @test9_foo to i8*)
// COMMON: ret i8* bitcast (i32* @test9_foo to i8*)
return (int*) &test9_foo;
}

// Don't explode on this.
extern "C" __unknown_anytype test10_any(...);
void test10() {
(void) test10_any(), (void) test10_any();
}

0 comments on commit 02a01fa

Please sign in to comment.