Skip to content

Commit

Permalink
[IRGen] Evaluate constant static variables referenced through member
Browse files Browse the repository at this point in the history
expressions

C++ allows us to reference static variables through member expressions. Prior to
this commit, non-integer static variables that were referenced using a member
expression were always emitted using lvalue loads. The old behaviour introduced
an inconsistency between regular uses of static variables and member expressions
uses. For example, the following program compiled and linked successfully:

struct Foo {
   constexpr static const char *name = "foo";
};
int main() {
  return Foo::name[0] == 'f';
}

but this program failed to link because "Foo::name" wasn't found:

struct Foo {
   constexpr static const char *name = "foo";
};
int main() {
  Foo f;
  return f.name[0] == 'f';
}

This commit ensures that constant static variables referenced through member
expressions are emitted in the same way as ordinary static variable references.

rdar://33942261

Differential Revision: https://reviews.llvm.org/D36876

llvm-svn: 311772
  • Loading branch information
hyp committed Aug 25, 2017
1 parent bc3e993 commit 6cc8317
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 44 deletions.
27 changes: 24 additions & 3 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -1339,6 +1339,25 @@ CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
return ConstantEmission::forValue(C);
}

static DeclRefExpr *tryToConvertMemberExprToDeclRefExpr(CodeGenFunction &CGF,
const MemberExpr *ME) {
if (auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {
// Try to emit static variable member expressions as DREs.
return DeclRefExpr::Create(
CGF.getContext(), NestedNameSpecifierLoc(), SourceLocation(), VD,
/*RefersToEnclosingVariableOrCapture=*/false, ME->getExprLoc(),
ME->getType(), ME->getValueKind());
}
return nullptr;
}

CodeGenFunction::ConstantEmission
CodeGenFunction::tryEmitAsConstant(const MemberExpr *ME) {
if (DeclRefExpr *DRE = tryToConvertMemberExprToDeclRefExpr(*this, ME))
return tryEmitAsConstant(DRE);
return ConstantEmission();
}

llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
SourceLocation Loc) {
return EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatile(),
Expand Down Expand Up @@ -3540,6 +3559,11 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) {
}

LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
if (DeclRefExpr *DRE = tryToConvertMemberExprToDeclRefExpr(*this, E)) {
EmitIgnoredExpr(E->getBase());
return EmitDeclRefLValue(DRE);
}

Expr *BaseExpr = E->getBase();
// If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar.
LValue BaseLV;
Expand All @@ -3566,9 +3590,6 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
return LV;
}

if (auto *VD = dyn_cast<VarDecl>(ND))
return EmitGlobalVarDeclLValue(*this, E, VD);

if (const auto *FD = dyn_cast<FunctionDecl>(ND))
return EmitFunctionDeclLValue(*this, E, FD);

Expand Down
19 changes: 1 addition & 18 deletions clang/lib/CodeGen/CGExprAgg.cpp
Expand Up @@ -124,24 +124,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
}

// l-values.
void VisitDeclRefExpr(DeclRefExpr *E) {
// For aggregates, we should always be able to emit the variable
// as an l-value unless it's a reference. This is due to the fact
// that we can't actually ever see a normal l2r conversion on an
// aggregate in C++, and in C there's no language standard
// actively preventing us from listing variables in the captures
// list of a block.
if (E->getDecl()->getType()->isReferenceType()) {
if (CodeGenFunction::ConstantEmission result
= CGF.tryEmitAsConstant(E)) {
EmitFinalDestCopy(E->getType(), result.getReferenceLValue(CGF, E));
return;
}
}

EmitAggLoadOfLValue(E);
}

void VisitDeclRefExpr(DeclRefExpr *E) { EmitAggLoadOfLValue(E); }
void VisitMemberExpr(MemberExpr *ME) { EmitAggLoadOfLValue(ME); }
void VisitUnaryDeref(UnaryOperator *E) { EmitAggLoadOfLValue(E); }
void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); }
Expand Down
31 changes: 21 additions & 10 deletions clang/lib/CodeGen/CGExprComplex.cpp
Expand Up @@ -120,18 +120,22 @@ class ComplexExprEmitter
return Visit(E->getSubExpr());
}

ComplexPairTy emitConstant(const CodeGenFunction::ConstantEmission &Constant,
Expr *E) {
assert(Constant && "not a constant");
if (Constant.isReference())
return EmitLoadOfLValue(Constant.getReferenceLValue(CGF, E),
E->getExprLoc());

llvm::Constant *pair = Constant.getValue();
return ComplexPairTy(pair->getAggregateElement(0U),
pair->getAggregateElement(1U));
}

// l-values.
ComplexPairTy VisitDeclRefExpr(DeclRefExpr *E) {
if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) {
if (result.isReference())
return EmitLoadOfLValue(result.getReferenceLValue(CGF, E),
E->getExprLoc());

llvm::Constant *pair = result.getValue();
return ComplexPairTy(pair->getAggregateElement(0U),
pair->getAggregateElement(1U));
}
if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E))
return emitConstant(Constant, E);
return EmitLoadOfLValue(E);
}
ComplexPairTy VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
Expand All @@ -141,7 +145,14 @@ class ComplexExprEmitter
return CGF.EmitObjCMessageExpr(E).getComplexVal();
}
ComplexPairTy VisitArraySubscriptExpr(Expr *E) { return EmitLoadOfLValue(E); }
ComplexPairTy VisitMemberExpr(const Expr *E) { return EmitLoadOfLValue(E); }
ComplexPairTy VisitMemberExpr(MemberExpr *ME) {
if (CodeGenFunction::ConstantEmission Constant =
CGF.tryEmitAsConstant(ME)) {
CGF.EmitIgnoredExpr(ME->getBase());
return emitConstant(Constant, ME);
}
return EmitLoadOfLValue(ME);
}
ComplexPairTy VisitOpaqueValueExpr(OpaqueValueExpr *E) {
if (E->isGLValue())
return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E), E->getExprLoc());
Expand Down
33 changes: 20 additions & 13 deletions clang/lib/CodeGen/CGExprScalar.cpp
Expand Up @@ -428,14 +428,19 @@ class ScalarExprEmitter
return CGF.getOpaqueRValueMapping(E).getScalarVal();
}

Value *emitConstant(const CodeGenFunction::ConstantEmission &Constant,
Expr *E) {
assert(Constant && "not a constant");
if (Constant.isReference())
return EmitLoadOfLValue(Constant.getReferenceLValue(CGF, E),
E->getExprLoc());
return Constant.getValue();
}

// l-values.
Value *VisitDeclRefExpr(DeclRefExpr *E) {
if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) {
if (result.isReference())
return EmitLoadOfLValue(result.getReferenceLValue(CGF, E),
E->getExprLoc());
return result.getValue();
}
if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E))
return emitConstant(Constant, E);
return EmitLoadOfLValue(E);
}

Expand Down Expand Up @@ -1299,13 +1304,15 @@ Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) {
}

Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
llvm::APSInt Value;
if (E->EvaluateAsInt(Value, CGF.getContext(), Expr::SE_AllowSideEffects)) {
if (E->isArrow())
CGF.EmitScalarExpr(E->getBase());
else
EmitLValue(E->getBase());
return Builder.getInt(Value);
if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) {
CGF.EmitIgnoredExpr(E->getBase());
return emitConstant(Constant, E);
} else {
llvm::APSInt Value;
if (E->EvaluateAsInt(Value, CGF.getContext(), Expr::SE_AllowSideEffects)) {
CGF.EmitIgnoredExpr(E->getBase());
return Builder.getInt(Value);
}
}

return EmitLoadOfLValue(E);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Expand Up @@ -3161,6 +3161,7 @@ class CodeGenFunction : public CodeGenTypeCache {
};

ConstantEmission tryEmitAsConstant(DeclRefExpr *refExpr);
ConstantEmission tryEmitAsConstant(const MemberExpr *ME);

RValue EmitPseudoObjectRValue(const PseudoObjectExpr *e,
AggValueSlot slot = AggValueSlot::ignored());
Expand Down
104 changes: 104 additions & 0 deletions clang/test/CodeGenCXX/member-expr-references-variable.cpp
@@ -0,0 +1,104 @@
// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s

struct Agg { const char * x; const char * y; constexpr Agg() : x(0), y(0) {} };

struct Struct {
constexpr static const char *name = "foo";

constexpr static __complex float complexValue = 42.0;

static constexpr const Agg &agg = Agg();

Struct();
Struct(int x);
};

void use(int n, const char *c);

Struct *getPtr();

// CHECK: @[[STR:.*]] = private unnamed_addr constant [4 x i8] c"foo\00", align 1

void scalarStaticVariableInMemberExpr(Struct *ptr, Struct &ref) {
use(1, Struct::name);
// CHECK: call void @_Z3useiPKc(i32 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
Struct s;
use(2, s.name);
// CHECK: call void @_Z3useiPKc(i32 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
use(3, ptr->name);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: call void @_Z3useiPKc(i32 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
use(4, ref.name);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: call void @_Z3useiPKc(i32 4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
use(5, Struct(2).name);
// CHECK: call void @_ZN6StructC1Ei(%struct.Struct* %{{.*}}, i32 2)
// CHECK: call void @_Z3useiPKc(i32 5, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
use(6, getPtr()->name);
// CHECK: call %struct.Struct* @_Z6getPtrv()
// CHECK: call void @_Z3useiPKc(i32 6, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
}

void use(int n, __complex float v);

void complexStaticVariableInMemberExpr(Struct *ptr, Struct &ref) {
use(1, Struct::complexValue);
// CHECK: store float 4.200000e+01, float* %[[coerce0:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce0]].{{.*}}, align 4
// CHECK: %[[cast0:.*]] = bitcast { float, float }* %[[coerce0]] to <2 x float>*
// CHECK: %[[vector0:.*]] = load <2 x float>, <2 x float>* %[[cast0]], align 4
// CHECK: call void @_Z3useiCf(i32 1, <2 x float> %[[vector0]])
Struct s;
use(2, s.complexValue);
// CHECK: store float 4.200000e+01, float* %[[coerce1:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce1]].{{.*}}, align 4
// CHECK: %[[cast1:.*]] = bitcast { float, float }* %[[coerce1]] to <2 x float>*
// CHECK: %[[vector1:.*]] = load <2 x float>, <2 x float>* %[[cast1]], align 4
// CHECK: call void @_Z3useiCf(i32 2, <2 x float> %[[vector1]])
use(3, ptr->complexValue);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: store float 4.200000e+01, float* %[[coerce2:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce2]].{{.*}}, align 4
// CHECK: %[[cast2:.*]] = bitcast { float, float }* %[[coerce2]] to <2 x float>*
// CHECK: %[[vector2:.*]] = load <2 x float>, <2 x float>* %[[cast2]], align 4
// CHECK: call void @_Z3useiCf(i32 3, <2 x float> %[[vector2]])
use(4, ref.complexValue);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: store float 4.200000e+01, float* %[[coerce3:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce3]].{{.*}}, align 4
// CHECK: %[[cast3:.*]] = bitcast { float, float }* %[[coerce3]] to <2 x float>*
// CHECK: %[[vector3:.*]] = load <2 x float>, <2 x float>* %[[cast3]], align 4
// CHECK: call void @_Z3useiCf(i32 4, <2 x float> %[[vector3]])
use(5, Struct(2).complexValue);
// CHECK: call void @_ZN6StructC1Ei(%struct.Struct* %{{.*}}, i32 2)
// CHECK: store float 4.200000e+01, float* %[[coerce4:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce4]].{{.*}}, align 4
// CHECK: %[[cast4:.*]] = bitcast { float, float }* %[[coerce4]] to <2 x float>*
// CHECK: %[[vector4:.*]] = load <2 x float>, <2 x float>* %[[cast4]], align 4
// CHECK: call void @_Z3useiCf(i32 5, <2 x float> %[[vector4]])
use(6, getPtr()->complexValue);
// CHECK: call %struct.Struct* @_Z6getPtrv()
// CHECK: store float 4.200000e+01, float* %[[coerce5:.*]].{{.*}}, align 4
// CHECK: store float 0.000000e+00, float* %[[coerce5]].{{.*}}, align 4
// CHECK: %[[cast5:.*]] = bitcast { float, float }* %[[coerce5]] to <2 x float>*
// CHECK: %[[vector5:.*]] = load <2 x float>, <2 x float>* %[[cast5]], align 4
// CHECK: call void @_Z3useiCf(i32 6, <2 x float> %[[vector5]])
}

void aggregateRefInMemberExpr(Struct *ptr, Struct &ref) {
use(1, Struct::agg.x);
// CHECK: %[[value0:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
// CHECK: call void @_Z3useiPKc(i32 1, i8* %[[value0]])
Struct s;
use(2, s.agg.x);
// CHECK: %[[value1:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
// CHECK: call void @_Z3useiPKc(i32 2, i8* %[[value1]])
use(3, ptr->agg.x);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: %[[value2:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
// CHECK: call void @_Z3useiPKc(i32 3, i8* %[[value2]])
use(4, ref.agg.x);
// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
// CHECK: %[[value3:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
// CHECK: call void @_Z3useiPKc(i32 4, i8* %[[value3]])
}

0 comments on commit 6cc8317

Please sign in to comment.