Skip to content

Commit 6bc2696

Browse files
committed
[cxx-interop] Inline constexpr vars.
If a static variable can be evaluated at compile time, create an accessor using that value. This means static variables will always be inlined and removed. Note: currently this only works with numeric types.
1 parent 62e3896 commit 6bc2696

File tree

7 files changed

+113
-29
lines changed

7 files changed

+113
-29
lines changed

lib/AST/ASTScopeCreation.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ void ASTScope::
209209
}
210210

211211
void ASTScope::expandFunctionBody(AbstractFunctionDecl *AFD) {
212-
auto *const SF = AFD->getParentSourceFile();
213-
SF->getScope().expandFunctionBodyImpl(AFD);
212+
if (auto *const SF = AFD->getParentSourceFile())
213+
SF->getScope().expandFunctionBodyImpl(AFD);
214214
}
215215

216216
void ASTScope::expandFunctionBodyImpl(AbstractFunctionDecl *AFD) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4094,6 +4094,47 @@ namespace {
40944094
return nullptr;
40954095
}
40964096

4097+
AccessorDecl *tryCreateConstexprAccessor(const clang::VarDecl *clangVar,
4098+
VarDecl *swiftVar) {
4099+
assert(clangVar->isConstexpr());
4100+
clangVar->evaluateValue();
4101+
auto evaluated = clangVar->getEvaluatedValue();
4102+
if (!evaluated)
4103+
return nullptr;
4104+
4105+
// If we have a constexpr var with an evaluated value, try to create an
4106+
// accessor for it. If we can remove all references to the global (which
4107+
// we should be able to do for constexprs) then we can remove the global
4108+
// entirely.
4109+
auto accessor = AccessorDecl::create(
4110+
Impl.SwiftContext, SourceLoc(), SourceLoc(), AccessorKind::Get,
4111+
swiftVar, SourceLoc(), StaticSpellingKind::KeywordStatic,
4112+
/*throws=*/false, SourceLoc(), /*genericParams*/ nullptr,
4113+
ParameterList::createEmpty(Impl.SwiftContext), swiftVar->getType(),
4114+
swiftVar->getDeclContext());
4115+
Expr *value = nullptr;
4116+
// TODO: add non-numeric types.
4117+
if (evaluated->isInt()) {
4118+
value = IntegerLiteralExpr::createFromUnsigned(
4119+
Impl.SwiftContext,
4120+
static_cast<unsigned>(
4121+
clangVar->getEvaluatedValue()->getInt().getZExtValue()));
4122+
} else if (evaluated->isFloat()) {
4123+
auto floatStr = evaluated->getAsString(Impl.getClangASTContext(),
4124+
clang::QualType());
4125+
auto floatIdent = Impl.SwiftContext.getIdentifier(floatStr);
4126+
value = new (Impl.SwiftContext)
4127+
FloatLiteralExpr(floatIdent.str(), SourceLoc());
4128+
}
4129+
if (!value)
4130+
return nullptr;
4131+
auto returnStmt = new (Impl.SwiftContext) ReturnStmt(SourceLoc(), value);
4132+
auto body = BraceStmt::create(Impl.SwiftContext, SourceLoc(),
4133+
{returnStmt}, SourceLoc());
4134+
accessor->setBodyParsed(body);
4135+
return accessor;
4136+
}
4137+
40974138
Decl *VisitVarDecl(const clang::VarDecl *decl) {
40984139
// Variables are imported as... variables.
40994140
Optional<ImportedName> correctSwiftName;
@@ -4161,6 +4202,11 @@ namespace {
41614202
if (correctSwiftName)
41624203
markAsVariant(result, *correctSwiftName);
41634204

4205+
// For constexpr vars, we can create an accessor with a numeric literal.
4206+
if (decl->isConstexpr())
4207+
if (auto acc = tryCreateConstexprAccessor(decl, result))
4208+
result->setAccessors(SourceLoc(), {acc}, SourceLoc());
4209+
41644210
return result;
41654211
}
41664212

test/Interop/Cxx/static/Inputs/static-member-var.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@ class WithConstStaticMember {
2626
const static int definedOutOfLine;
2727
};
2828

29+
constexpr float getFloatValue() { return 42; }
30+
constexpr float getIntValue(int arg) { return 40 + arg; }
31+
2932
class WithConstexprStaticMember {
3033
public:
3134
constexpr static int definedInline = 139;
35+
constexpr static int definedInlineWithArg = getIntValue(2);
36+
constexpr static float definedInlineFloat = 139;
37+
constexpr static float definedInlineFromMethod = getFloatValue();
3238
};
3339

3440
class ClassA {

test/Interop/Cxx/static/static-member-var-irgen.swift

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010
// CHECK: @{{_ZN21WithConstStaticMember7definedE|"\?defined@WithConstStaticMember@@2HB"}} = {{available_externally|linkonce_odr}} {{(dso_local )?}}constant i32 48, {{(comdat, )?}}align 4
1111
// CHECK: @{{_ZN21WithConstStaticMember16definedOutOfLineE|"\?definedOutOfLine@WithConstStaticMember@@2HB"}} = external {{(dso_local )?}}constant i32, align 4
1212

13-
//TODO: This test uses only values of static const members, so it does not need
14-
//to depend on external definitions. However, our code generation pattern loads
15-
//the value dynamically. Instead, we should inline known constants. That would
16-
//allow Swift code to even read the value of WithIncompleteStaticMember::notDefined.
17-
// NOTE: we allow both available_externally and linkonce_odr as there are
18-
// differences in between MSVC and itanium model semantics where the constexpr
19-
// value is emitted into COMDAT.
20-
// CHECK: @{{_ZN25WithConstexprStaticMember13definedInlineE|"\?definedInline@WithConstexprStaticMember@@2HB"}} = {{available_externally|linkonce_odr}} {{(dso_local )?}}constant i32 139, {{(comdat, )?}}align 4
13+
// Make sure we remove constexpr globals after all uses have been inlined.
14+
// CHECK-NOT: _ZN25WithConstexprStaticMember13definedInlineE
15+
// CHECK-NOT: ?definedInline@WithConstexprStaticMember@@2HB
16+
// CHECK-NOT: @_ZN25WithConstexprStaticMember20definedInlineWithArgE
17+
// CHECK-NOT: @_ZN25WithConstexprStaticMember18definedInlineFloatE
18+
// CHECK-NOT: @_ZN25WithConstexprStaticMember23definedInlineFromMethodE
2119

2220
import StaticMemberVar
2321

@@ -73,10 +71,38 @@ public func readDefinedOutOfLineConstMember() -> CInt {
7371
// CHECK: [[VALUE:%.*]] = load i32, i32* getelementptr inbounds (%Ts5Int32V, %Ts5Int32V* bitcast (i32* @{{_ZN21WithConstStaticMember16definedOutOfLineE|"\?definedOutOfLine@WithConstStaticMember@@2HB"}} to %Ts5Int32V*), i32 0, i32 0), align 4
7472
// CHECK: ret i32 [[VALUE]]
7573

76-
public func readConstexprStaticMember() -> CInt {
77-
return WithConstexprStaticMember.definedInline
74+
public func readConstexprStaticIntMembers() {
75+
let x = WithConstexprStaticMember.definedInline
76+
let y = WithConstexprStaticMember.definedInlineWithArg
7877
}
7978

80-
// CHECK: define {{(protected |dllexport )?}}swiftcc i32 @"$s4main25readConstexprStaticMembers5Int32VyF"() #0
81-
// CHECK: [[VALUE:%.*]] = load i32, i32* getelementptr inbounds (%Ts5Int32V, %Ts5Int32V* bitcast (i32* @{{_ZN25WithConstexprStaticMember13definedInlineE|"\?definedInline@WithConstexprStaticMember@@2HB"}} to %Ts5Int32V*), i32 0, i32 0), align 4
82-
// CHECK: ret i32 [[VALUE]]
79+
// CHECK-LABEL: define {{(protected |dllexport )?}}swiftcc void @"$s4main29readConstexprStaticIntMembersyyF"()
80+
// CHECK: call swiftcc i32 @"$sSo25WithConstexprStaticMemberV13definedInlines5Int32VvgZ"()
81+
// CHECK: call swiftcc i32 @"$sSo25WithConstexprStaticMemberV013definedInlineA3Args5Int32VvgZ"()
82+
// CHECK: ret void
83+
84+
// CHECK-LABEL: define linkonce_odr {{.*}}swiftcc i32 @"$sSo25WithConstexprStaticMemberV13definedInlines5Int32VvgZ"()
85+
// CHECK-NEXT: entry
86+
// CHECK-NEXT: ret i32 139
87+
88+
// CHECK-LABEL: define linkonce_odr {{.*}}swiftcc i32 @"$sSo25WithConstexprStaticMemberV013definedInlineA3Args5Int32VvgZ"()
89+
// CHECK-NEXT: entry
90+
// CHECK-NEXT: ret i32 42
91+
92+
public func readConstexprStaticFloatMembers() {
93+
let x = WithConstexprStaticMember.definedInlineFloat
94+
let y = WithConstexprStaticMember.definedInlineFromMethod
95+
}
96+
97+
// CHECK-LABEL: define {{(protected |dllexport )?}}swiftcc void @"$s4main31readConstexprStaticFloatMembersyyF"()
98+
// CHECK: call swiftcc float @"$sSo25WithConstexprStaticMemberV18definedInlineFloatSfvgZ"()
99+
// CHECK: call swiftcc float @"$sSo25WithConstexprStaticMemberV23definedInlineFromMethodSfvgZ"()
100+
// CHECK: ret void
101+
102+
// CHECK-LABEL: define linkonce_odr {{.*}}swiftcc float @"$sSo25WithConstexprStaticMemberV18definedInlineFloatSfvgZ"()
103+
// CHECK-NEXT: entry
104+
// CHECK-NEXT: ret float 1.390000e+02
105+
106+
// CHECK-LABEL: define linkonce_odr {{.*}}swiftcc float @"$sSo25WithConstexprStaticMemberV23definedInlineFromMethodSfvgZ"()
107+
// CHECK-NEXT: entry
108+
// CHECK-NEXT: ret float 4.200000e+01

test/Interop/Cxx/static/static-member-var-silgen.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// CHECK: sil_global public_external [let] @{{_ZN21WithConstStaticMember7definedE|\?defined@WithConstStaticMember@@2HB}} : $Int32
99
// CHECK: // clang name: WithConstStaticMember::definedOutOfLine
1010
// CHECK: sil_global public_external [let] @{{_ZN21WithConstStaticMember16definedOutOfLineE|\?definedOutOfLine@WithConstStaticMember@@2HB}} : $Int32
11-
// CHECK: // clang name: WithConstexprStaticMember::definedInline
12-
// CHECK: sil_global public_external [let] @{{_ZN25WithConstexprStaticMember13definedInlineE|\?definedInline@WithConstexprStaticMember@@2HB}} : $Int32
1311

1412
import StaticMemberVar
1513

@@ -70,16 +68,20 @@ func readDefinedOutOfLineConstMember() -> CInt {
7068
return WithConstStaticMember.definedOutOfLine
7169
}
7270

73-
// CHECK: sil hidden @$s4main31readDefinedOutOfLineConstMembers5Int32VyF : $@convention(thin) () -> Int32
74-
// CHECK: [[ADDR:%.*]] = global_addr @{{_ZN21WithConstStaticMember16definedOutOfLineE|\?definedOutOfLine@WithConstStaticMember@@2HB}} : $*Int32
75-
// CHECK: [[VALUE:%.*]] = load [[ADDR]] : $*Int32
76-
// CHECK: return [[VALUE]] : $Int32
71+
// CHECK: sil hidden @$s4main25readConstexprStaticMembers5Int32VyF : $@convention(thin) () -> Int32
72+
// CHECK: [[META:%.*]] = metatype $@thin WithConstexprStaticMember.Type
73+
// CHECK: [[ACC:%.*]] = function_ref @$sSo25WithConstexprStaticMemberV13definedInlines5Int32VvgZ : $@convention(method) (@thin WithConstexprStaticMember.Type) -> Int32
74+
// CHECK: [[OUT:%.*]] = apply [[ACC]]([[META]]) : $@convention(method) (@thin WithConstexprStaticMember.Type) -> Int32
75+
// CHECK: return [[OUT]] : $Int32
76+
// CHECK-LABEL: end sil function '$s4main25readConstexprStaticMembers5Int32VyF'
77+
78+
// Make sure we also generate the accessor with a numeric literal.
79+
// CHECK-LABEL: sil shared [serializable] @$sSo25WithConstexprStaticMemberV13definedInlines5Int32VvgZ : $@convention(method) (@thin WithConstexprStaticMember.Type) -> Int32
80+
// CHECK: [[IL:%.*]] = integer_literal $Builtin.Int32, 139
81+
// CHECK: [[OUT:%.*]] = struct $Int32 ([[IL]] : $Builtin.Int32)
82+
// CHECK: return [[OUT]] : $Int32
83+
// CHECK-LABEL: end sil function '$sSo25WithConstexprStaticMemberV13definedInlines5Int32VvgZ'
7784

7885
func readConstexprStaticMember() -> CInt {
7986
return WithConstexprStaticMember.definedInline
8087
}
81-
82-
// CHECK: sil hidden @$s4main25readConstexprStaticMembers5Int32VyF : $@convention(thin) () -> Int32
83-
// CHECK: [[ADDR:%.*]] = global_addr @{{_ZN25WithConstexprStaticMember13definedInlineE|\?definedInline@WithConstexprStaticMember@@2HB}} : $*Int32
84-
// CHECK: [[VALUE:%.*]] = load [[ADDR]] : $*Int32
85-
// CHECK: return [[VALUE]] : $Int32

test/Interop/Cxx/static/static-var-irgen.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ public func initStaticVars() -> CInt {
88
+ staticConstexprNonTrivial.val
99
}
1010

11+
// Constexpr vars should be inlined and removed.
12+
// CHECK-NOT: ?staticConstexpr
13+
// CHECK-NOT: _ZL15staticConstexpr
14+
1115
// CHECK: @{{_ZL9staticVar|staticVar}} = internal global i32 2, align 4
1216
// CHECK: @{{_ZL13staticVarInit|staticVarInit}} = internal global i32 0, align 4
1317
// CHECK: @{{_ZL19staticVarInlineInit|staticVarInlineInit}} = internal global i32 0, align 4
1418
// CHECK: @{{_ZL11staticConst|staticConst}} = internal constant i32 4, align 4
1519
// CHECK: @{{_ZL15staticConstInit|staticConstInit}} = internal global i32 0, align 4
1620
// CHECK: @{{_ZL21staticConstInlineInit|staticConstInlineInit}} = internal global i32 0, align 4
17-
// CHECK: @{{_ZL15staticConstexpr|staticConstexpr}} = internal constant i32 32, align 4
1821
// CHECK: @{{_ZL16staticNonTrivial|staticNonTrivial}} = internal global %class.NonTrivial zeroinitializer, align 4
1922
// CHECK: @{{_ZL21staticConstNonTrivial|staticConstNonTrivial}} = internal global %class.NonTrivial zeroinitializer, align 4
2023
// CHECK: @{{_ZL25staticConstexprNonTrivial|staticConstexprNonTrivial}} = internal constant %class.NonTrivial { i32 8192 }, align 4

test/Interop/Cxx/static/static-var-silgen.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ func initStaticVars() -> CInt {
88
+ staticConstexprNonTrivial.val
99
}
1010

11+
// Constexpr globals should be inlined and removed.
12+
// CHECK-NOT: sil_global public_external [let] @staticConstexpr : $Int32
13+
1114
// CHECK: // clang name: staticVar
1215
// CHECK: sil_global public_external @staticVar : $Int32
1316
// CHECK: // clang name: staticVarInit
@@ -20,8 +23,6 @@ func initStaticVars() -> CInt {
2023
// CHECK: sil_global public_external [let] @staticConstInit : $Int32
2124
// CHECK: // clang name: staticConstInlineInit
2225
// CHECK: sil_global public_external [let] @staticConstInlineInit : $Int32
23-
// CHECK: // clang name: staticConstexpr
24-
// CHECK: sil_global public_external [let] @staticConstexpr : $Int32
2526
// CHECK: // clang name: staticNonTrivial
2627
// CHECK: sil_global public_external @staticNonTrivial : $NonTrivial
2728
// CHECK: // clang name: staticConstNonTrivial

0 commit comments

Comments
 (0)