summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Majnemer <david.majnemer@gmail.com>2014-06-25 08:15:07 +0000
committerDavid Majnemer <david.majnemer@gmail.com>2014-06-25 08:15:07 +0000
commitab6dc444704d6f49ebc9ed60cc9fec0ec034b384 (patch)
tree4694247893b5188579763cd59234fcf5fe657f8e
parent9f074fb6303750e5405d0192115cc53aeca3e262 (diff)
downloadclang-ab6dc444704d6f49ebc9ed60cc9fec0ec034b384.tar.gz
clang-ab6dc444704d6f49ebc9ed60cc9fec0ec034b384.tar.bz2
clang-ab6dc444704d6f49ebc9ed60cc9fec0ec034b384.tar.xz
AST: Initialization with dllimport functions in C
The C++ language requires that the address of a function be the same across all translation units. To make __declspec(dllimport) useful, this means that a dllimported function must also obey this rule. MSVC implements this by dynamically querying the import address table located in the linked executable. This means that the address of such a function in C++ is not constant (which violates other rules). However, the C language has no notion of ODR nor does it permit dynamic initialization whatsoever. This requires implementations to _not_ dynamically query the import address table and instead utilize a wrapper function that will be synthesized by the linker which will eventually query the import address table. The effect this has is, to say the least, perplexing. Consider the following C program: __declspec(dllimport) void f(void); typedef void (*fp)(void); static const fp var = &f; const fp fun() { return &f; } int main() { return fun() == var; } MSVC will statically initialize "var" with the address of the wrapper function and "fun" returns the address of the actual imported function. This means that "main" will return false! Note that LLVM's optimizers are strong enough to figure out that "main" should return true. However, this result is dependent on having optimizations enabled! N.B. This change also permits the usage of dllimport declarators inside of template arguments; they are sufficiently constant for such a purpose. Add tests to make sure we don't regress here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@211677 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/AST/ExprConstant.cpp35
-rw-r--r--test/CodeGen/dllimport.c5
-rw-r--r--test/Sema/dllimport.c8
-rw-r--r--test/SemaCXX/PR19955.cpp8
4 files changed, 47 insertions, 9 deletions
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index c57a802512..5897f2c485 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -1269,11 +1269,36 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
LVal.getLValueCallIndex() == 0) &&
"have call index for global lvalue");
- // Check if this is a thread-local variable.
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
if (const VarDecl *Var = dyn_cast<const VarDecl>(VD)) {
+ // Check if this is a thread-local variable.
if (Var->getTLSKind())
return false;
+
+ // Check if this is a dllimport variable. Fail evaluation if we care
+ // about side effects; a dllimport variable rarely acts like a constant
+ // except in places like template arguments. It never acts like a
+ // constant in C.
+ if ((!Info.getLangOpts().CPlusPlus ||
+ !Info.keepEvaluatingAfterSideEffect()) &&
+ Var->hasAttr<DLLImportAttr>())
+ return false;
+ }
+ if (const auto *FD = dyn_cast<const FunctionDecl>(VD)) {
+ // __declspec(dllimport) must be handled very carefully:
+ // We must never initialize an expression with the thunk in C++.
+ // Doing otherwise would allow the same id-expression to yield
+ // different addresses for the same function in different translation
+ // units. However, this means that we must dynamically initialize the
+ // expression with the contents of the import address table at runtime.
+ //
+ // The C language has no notion of ODR; furthermore, it has no notion of
+ // dynamic initialization. This means that we are permitted to
+ // perform initialization with the address of the thunk.
+ if (Info.getLangOpts().CPlusPlus &&
+ !Info.keepEvaluatingAfterSideEffect() &&
+ FD->hasAttr<DLLImportAttr>())
+ return false;
}
}
@@ -4373,11 +4398,8 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info) {
}
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl())) {
- if (FD->hasAttr<DLLImportAttr>())
- return ZeroInitialization(E);
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl()))
return Success(FD);
- }
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
return VisitVarDecl(E, VD);
return Error(E);
@@ -4393,9 +4415,6 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
Result.set(VD, Frame->Index);
return true;
}
- // The address of __declspec(dllimport) variables aren't constant.
- if (VD->hasAttr<DLLImportAttr>())
- return ZeroInitialization(E);
return Success(VD);
}
diff --git a/test/CodeGen/dllimport.c b/test/CodeGen/dllimport.c
index 485f6e21b2..32ee81f859 100644
--- a/test/CodeGen/dllimport.c
+++ b/test/CodeGen/dllimport.c
@@ -53,7 +53,10 @@ USEVAR(GlobalRedecl3)
// Import function declaration.
// CHECK-DAG: declare dllimport void @decl()
__declspec(dllimport) void decl(void);
-USE(decl)
+
+// Initialize use_decl with the address of the thunk.
+// CHECK-DAG: @use_decl = global void ()* @decl
+void (*use_decl)(void) = &decl;
// Import inline function.
// CHECK-DAG: declare dllimport void @inlineFunc()
diff --git a/test/Sema/dllimport.c b/test/Sema/dllimport.c
index f4d26fb8c8..2702453b61 100644
--- a/test/Sema/dllimport.c
+++ b/test/Sema/dllimport.c
@@ -25,6 +25,9 @@ __declspec(dllimport) int GlobalDecl;
int **__attribute__((dllimport))* GlobalDeclChunkAttr;
int GlobalDeclAttr __attribute__((dllimport));
+// Address of variables can't be used for initialization in C language modes.
+int *VarForInit = &GlobalDecl; // expected-error{{initializer element is not a compile-time constant}}
+
// Not allowed on definitions.
__declspec(dllimport) extern int ExternGlobalInit = 1; // expected-error{{definition of dllimport data}}
__declspec(dllimport) int GlobalInit1 = 1; // expected-error{{definition of dllimport data}}
@@ -96,6 +99,11 @@ __declspec(dllimport) void decl1B();
void __attribute__((dllimport)) decl2A();
void __declspec(dllimport) decl2B();
+// Address of functions can be used for initialization in C language modes.
+// However, the address of the thunk wrapping the function is used instead of
+// the address in the import address table.
+void (*FunForInit)() = &decl2A;
+
// Not allowed on function definitions.
__declspec(dllimport) void def() {} // expected-error{{dllimport cannot be applied to non-inline function definition}}
diff --git a/test/SemaCXX/PR19955.cpp b/test/SemaCXX/PR19955.cpp
index e0d4618f2c..4596e5a459 100644
--- a/test/SemaCXX/PR19955.cpp
+++ b/test/SemaCXX/PR19955.cpp
@@ -5,3 +5,11 @@ constexpr int *varp = &var; // expected-error {{must be initialized by a constan
extern __attribute__((dllimport)) void fun();
constexpr void (*funp)(void) = &fun; // expected-error {{must be initialized by a constant expression}}
+
+template <void (*)()>
+struct S {};
+S<&fun> x;
+
+template <int *>
+struct U {};
+U<&var> y;