Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3070,7 +3070,13 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
return CGF.MakeAddrLValue(Addr, T, AlignmentSource::Decl);
}

llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
// For static data members with in-class initializers, ensure we emit a
// definition if one doesn't exist yet. This is necessary for interpreters
// where the member's address might be taken after the class definition,
// requiring the symbol to be materialized on demand.
const VarDecl *DefinitionVD = CGF.CGM.materializeStaticDataMember(VD);

llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(DefinitionVD);

if (VD->getTLSKind() != VarDecl::TLS_None)
V = CGF.Builder.CreateThreadLocalAddress(V);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
}

if (const auto *VD = dyn_cast<VarDecl>(D)) {
VD = CGM.materializeStaticDataMember(VD);
// We can never refer to a variable with local storage.
if (!VD->hasLocalStorage()) {
if (VD->isFileVarDecl() || VD->hasExternalStorage())
Expand Down
29 changes: 29 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6228,6 +6228,35 @@ CodeGenModule::getLLVMLinkageVarDefinition(const VarDecl *VD) {
return getLLVMLinkageForDeclarator(VD, Linkage);
}

const VarDecl *CodeGenModule::materializeStaticDataMember(const VarDecl *VD) {
if (!VD->isStaticDataMember())
return VD;

const VarDecl *InitVD = nullptr;
if (!VD->getAnyInitializer(InitVD) || !InitVD)
return VD;

StringRef MangledName = getMangledName(InitVD);
auto needsEmission = [](llvm::GlobalValue *GV) {
if (!GV || GV->isDeclaration())
return true;
if (auto *GVVar = llvm::dyn_cast<llvm::GlobalVariable>(GV))
return GVVar->hasAvailableExternallyLinkage();
return false;
};

llvm::GlobalValue *GV = GetGlobalValue(MangledName);
if (needsEmission(GV)) {
EmitGlobalVarDefinition(InitVD, /*IsTentative=*/false);
GV = GetGlobalValue(MangledName);
if (auto *GVVar = llvm::dyn_cast_or_null<llvm::GlobalVariable>(GV))
if (GVVar->hasAvailableExternallyLinkage())
GVVar->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
}

return InitVD;
}

/// Replace the uses of a function that was declared with a non-proto type.
/// We want to silently drop extra arguments from call sites
static void replaceUsesOfNonProtoConstant(llvm::Constant *old,
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,13 @@ class CodeGenModule : public CodeGenTypeCache {
return TrapReasonBuilder(&getDiags(), DiagID, TR);
}

void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false);

public:
/// Ensure a static data member with an in-class initializer is materialized
/// and return the declaration that owns the emitted definition.
const VarDecl *materializeStaticDataMember(const VarDecl *VD);

private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;

Expand Down Expand Up @@ -1882,7 +1889,6 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV);
void EmitMultiVersionFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV);

void EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative = false);
void EmitAliasDefinition(GlobalDecl GD);
void emitIFuncDefinition(GlobalDecl GD);
void emitCPUDispatchDefinition(GlobalDecl GD);
Expand Down
37 changes: 37 additions & 0 deletions clang/test/Interpreter/static-const-member.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Test for static const member address issue
// see https://github.com/llvm/llvm-project/issues/146956

// RUN: cat %s | clang-repl | FileCheck %s

extern "C" int printf(const char*, ...);

// Test 1: Static const member with in-class initializer
struct Foo {
static int const bar { 5 };
};

// Taking the address should work
int const * p = &Foo::bar;
printf("Address test: %d\n", *p);
// CHECK: Address test: 5

// Test 2: Direct value access (this should already work)
int const i = Foo::bar;
printf("Value test: %d\n", i);
// CHECK-NEXT: Value test: 5

// Test 3: Multiple accesses to the same static member
int const * p2 = &Foo::bar;
printf("Second address test: %d\n", *p2);
// CHECK-NEXT: Second address test: 5

// Test 4: Different types
struct Bar {
static double const pi { 3.14159 };
};

double const * pi_ptr = &Bar::pi;
printf("Pi test: %.2f\n", *pi_ptr);
// CHECK-NEXT: Pi test: 3.14

%quit
15 changes: 15 additions & 0 deletions clang/unittests/Interpreter/InterpreterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,19 @@ TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) {
sema.getASTContext().getTranslationUnitDecl()->getCanonicalDecl());
}

TEST_F(InterpreterTest, StaticConstMemberAddress) {
std::unique_ptr<Interpreter> Interp = createInterpreter();

// Test taking the address of a static const member with in-class initializer
llvm::cantFail(
Interp->ParseAndExecute("struct Foo { static int const bar { 5 }; };"));

Value V;
llvm::cantFail(
Interp->ParseAndExecute("int const * p = &Foo::bar; *p", &V));

// The value should be 5
EXPECT_EQ(V.getInt(), 5);
}

} // end anonymous namespace