Merge pull request #20207 from atrick/fix-letprop
Fix static initializer analysis, used in LetPropertiesOpt.
diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp
index a0b2d19..9ee51ec 100644
--- a/lib/SILOptimizer/IPO/GlobalOpt.cpp
+++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp
@@ -292,13 +292,10 @@
auto V = Store->getSrc();
SmallVector<SILInstruction *, 8> Insts;
- Insts.push_back(Store);
- Insts.push_back(cast<SingleValueInstruction>(Store->getDest()));
if (!analyzeStaticInitializer(V, Insts))
return nullptr;
-
- // Produce a correct order of instructions.
- std::reverse(Insts.begin(), Insts.end());
+ Insts.push_back(cast<SingleValueInstruction>(Store->getDest()));
+ Insts.push_back(Store);
auto *GetterF = getGlobalGetterFunction(FunctionBuilder,
Store->getModule(),
diff --git a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp
index 5de9e22..39512cd 100644
--- a/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp
+++ b/lib/SILOptimizer/IPO/LetPropertiesOpts.cpp
@@ -401,7 +401,6 @@
// Analyze the init value being stored by the instruction into a property.
bool
LetPropertiesOpt::analyzeInitValue(SILInstruction *I, VarDecl *Property) {
- SmallVector<SILInstruction *, 8> ReverseInsns;
SILValue value;
if (auto SI = dyn_cast<StructInst>(I)) {
value = SI->getFieldValue(Property);
@@ -422,16 +421,10 @@
}
// Bail if a value of a property is not a statically known constant init.
- if (!analyzeStaticInitializer(value, ReverseInsns))
- return false;
-
- // Fill in the InitSequence by reversing the instructions and
- // setting the result index.
InitSequence sequence;
- while (!ReverseInsns.empty()) {
- sequence.Instructions.push_back(ReverseInsns.pop_back_val());
- }
sequence.Result = value;
+ if (!analyzeStaticInitializer(value, sequence.Instructions))
+ return false;
auto &cachedSequence = InitMap[Property];
if (cachedSequence.isValid() &&
diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp
index de1ddd2..559912c 100644
--- a/lib/SILOptimizer/Utils/Local.cpp
+++ b/lib/SILOptimizer/Utils/Local.cpp
@@ -1411,53 +1411,94 @@
return true;
}
-/// Check if the value of V is computed by means of a simple initialization.
-/// Store the actual SILValue into Val and the reversed list of instructions
-/// initializing it in Insns.
-///
-/// The check is performed by recursively walking the computation of the SIL
-/// value being analyzed.
-bool
-swift::analyzeStaticInitializer(SILValue V,
- SmallVectorImpl<SILInstruction *> &Insts) {
- // Save every instruction we see.
- // TODO: MultiValueInstruction?
- if (auto *I = dyn_cast<SingleValueInstruction>(V))
- Insts.push_back(I);
+// Encapsulate the state used for recursive analysis of a static
+// initializer. Discover all the instruction in a use-def graph and return them
+// in topological order.
+//
+// TODO: We should have a DFS utility for this sort of thing so it isn't
+// recursive.
+class StaticInitializerAnalysis {
+ SmallVectorImpl<SILInstruction *> &postOrderInstructions;
+ llvm::SmallDenseSet<SILValue, 8> visited;
+ int recursionLevel = 0;
- if (auto *SI = dyn_cast<StructInst>(V)) {
- // If it is not a struct which is a simple type, bail.
- if (!isSimpleType(SI->getType(), SI->getModule()))
- return false;
- return llvm::all_of(SI->getAllOperands(), [&](Operand &Op) -> bool {
- return analyzeStaticInitializer(Op.get(), Insts);
- });
+public:
+ StaticInitializerAnalysis(
+ SmallVectorImpl<SILInstruction *> &postOrderInstructions)
+ : postOrderInstructions(postOrderInstructions) {}
+
+ // Perform a recursive DFS on on the use-def graph rooted at `V`. Insert
+ // values in the `visited` set in preorder. Insert values in
+ // `postOrderInstructions` in postorder so that the instructions are
+ // topologically def-use ordered (in execution order).
+ bool analyze(SILValue RootValue) {
+ return recursivelyAnalyzeOperand(RootValue);
}
- if (auto *TI = dyn_cast<TupleInst>(V)) {
- // If it is not a tuple which is a simple type, bail.
- if (!isSimpleType(TI->getType(), TI->getModule()))
+protected:
+ bool recursivelyAnalyzeOperand(SILValue V) {
+ if (!visited.insert(V).second)
+ return true;
+
+ if (++recursionLevel > 50)
return false;
- return llvm::all_of(TI->getAllOperands(), [&](Operand &Op) -> bool {
- return analyzeStaticInitializer(Op.get(), Insts);
- });
+
+ // TODO: For multi-result instructions, we could simply insert all result
+ // values in the visited set here.
+ auto *I = dyn_cast<SingleValueInstruction>(V);
+ if (!I)
+ return false;
+
+ if (!recursivelyAnalyzeInstruction(I))
+ return false;
+
+ postOrderInstructions.push_back(I);
+ --recursionLevel;
+ return true;
}
- if (auto *bi = dyn_cast<BuiltinInst>(V)) {
- switch (bi->getBuiltinInfo().ID) {
- case BuiltinValueKind::FPTrunc:
- if (auto *LI = dyn_cast<LiteralInst>(bi->getArguments()[0])) {
- return analyzeStaticInitializer(LI, Insts);
- }
- return false;
- default:
- return false;
+ bool recursivelyAnalyzeInstruction(SILInstruction *I) {
+ if (auto *SI = dyn_cast<StructInst>(I)) {
+ // If it is not a struct which is a simple type, bail.
+ if (!isSimpleType(SI->getType(), SI->getModule()))
+ return false;
+
+ return llvm::all_of(SI->getAllOperands(), [&](Operand &Op) -> bool {
+ return recursivelyAnalyzeOperand(Op.get());
+ });
}
- }
+ if (auto *TI = dyn_cast<TupleInst>(I)) {
+ // If it is not a tuple which is a simple type, bail.
+ if (!isSimpleType(TI->getType(), TI->getModule()))
+ return false;
- return isa<IntegerLiteralInst>(V)
- || isa<FloatLiteralInst>(V)
- || isa<StringLiteralInst>(V);
+ return llvm::all_of(TI->getAllOperands(), [&](Operand &Op) -> bool {
+ return recursivelyAnalyzeOperand(Op.get());
+ });
+ }
+ if (auto *bi = dyn_cast<BuiltinInst>(I)) {
+ switch (bi->getBuiltinInfo().ID) {
+ case BuiltinValueKind::FPTrunc:
+ if (auto *LI = dyn_cast<LiteralInst>(bi->getArguments()[0])) {
+ return recursivelyAnalyzeOperand(LI);
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+ return isa<IntegerLiteralInst>(I) || isa<FloatLiteralInst>(I)
+ || isa<StringLiteralInst>(I);
+ }
+};
+
+/// Check if the value of V is computed by means of a simple initialization.
+/// Populate `forwardInstructions` with references to all the instructions
+/// that participate in the use-def graph required to compute `V`. The
+/// instructions will be in def-use topological order.
+bool swift::analyzeStaticInitializer(
+ SILValue V, SmallVectorImpl<SILInstruction *> &forwardInstructions) {
+ return StaticInitializerAnalysis(forwardInstructions).analyze(V);
}
/// Replace load sequence which may contain
diff --git a/test/SILOptimizer/let_properties_opts.sil b/test/SILOptimizer/let_properties_opts.sil
new file mode 100644
index 0000000..3ff5296
--- /dev/null
+++ b/test/SILOptimizer/let_properties_opts.sil
@@ -0,0 +1,77 @@
+// RUN: %target-sil-opt -let-properties-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s | %FileCheck %s
+
+sil_stage canonical
+
+import Builtin
+import Swift
+
+// Test initialization of a constant aggregate "let".
+// <rdar://problem/45691574> [SR-9146] IBAnimatable - SILCloner crash in LetPropertiesOpt.
+
+struct Point {
+ var x: Int64
+ var y: Int64
+}
+class HasCenter {
+
+ let centerPoint: Point = Point(x: 0, y: 0)
+
+ public func getCenter() -> Int64
+}
+
+sil hidden @$s19let_properties_opts5PointV1x1yACSi_SitcfC : $@convention(method) (Int64, Int64, @thin Point.Type) -> Point {
+bb0(%0 : $Int64, %1 : $Int64, %2 : $@thin Point.Type):
+ %3 = struct $Point (%0 : $Int64, %1 : $Int64)
+ return %3 : $Point
+}
+
+// variable initialization expression of HasCenter.centerPoint
+sil hidden [transparent] @$s19let_properties_opts9HasCenterC11centerPointAA0E0Vvpfi : $@convention(thin) () -> Point {
+bb0:
+ %0 = integer_literal $Builtin.Int64, 0
+ %1 = struct $Int64 (%0 : $Builtin.Int64)
+ %2 = struct $Point (%1 : $Int64, %1 : $Int64)
+ return %2 : $Point
+}
+
+// HasCenter.centerPoint.getter
+// CHECK-LABEL: sil hidden [transparent] @$s19let_properties_opts9HasCenterC11centerPointAA0E0Vvg : $@convention(method) (@guaranteed HasCenter) -> Point {
+// CHECK: bb0(%0 : $HasCenter):
+// CHECK: [[LIT:%.*]] = integer_literal $Builtin.Int64, 0
+// CHECK: [[INT:%.*]] = struct $Int64 ([[LIT]] : $Builtin.Int64)
+// CHECK: [[PNT:%.*]] = struct $Point ([[INT]] : $Int64, [[INT]] : $Int64)
+// CHECK: return [[PNT]] : $Point
+// CHECK-LABEL: } // end sil function '$s19let_properties_opts9HasCenterC11centerPointAA0E0Vvg'
+sil hidden [transparent] @$s19let_properties_opts9HasCenterC11centerPointAA0E0Vvg : $@convention(method) (@guaranteed HasCenter) -> Point {
+bb0(%0 : $HasCenter):
+ %1 = ref_element_addr %0 : $HasCenter, #HasCenter.centerPoint
+ %2 = load %1 : $*Point
+ return %2 : $Point
+}
+
+// HasCenter.getCenter()
+// CHECK-LABEL: sil hidden @$s19let_properties_opts9HasCenterC9getCenterSiyF : $@convention(method) (@guaranteed HasCenter) -> Int64 {
+// CHECK: [[LIT:%.*]] = integer_literal $Builtin.Int64, 0
+// CHECK: [[INT:%.*]] = struct $Int64 ([[LIT]] : $Builtin.Int64)
+// CHECK: [[PNT:%.*]] = struct $Point ([[INT]] : $Int64, [[INT]] : $Int64)
+// CHECK: [[X:%.*]] = struct_extract [[PNT]] : $Point, #Point.x
+// CHECK: return [[X]] : $Int64
+// CHECK-LABEL: } // end sil function '$s19let_properties_opts9HasCenterC9getCenterSiyF'
+sil hidden @$s19let_properties_opts9HasCenterC9getCenterSiyF : $@convention(method) (@guaranteed HasCenter) -> Int64 {
+bb0(%0 : $HasCenter):
+ %1 = ref_element_addr %0 : $HasCenter, #HasCenter.centerPoint
+ %2 = struct_element_addr %1 : $*Point, #Point.x
+ %3 = load %2 : $*Int64
+ return %3 : $Int64
+}
+
+// HasCenter.init()
+sil hidden @$s19let_properties_opts9HasCenterCACycfc : $@convention(method) (@owned HasCenter) -> @owned HasCenter {
+bb0(%0 : $HasCenter):
+ %1 = integer_literal $Builtin.Int64, 0
+ %2 = struct $Int64 (%1 : $Builtin.Int64)
+ %3 = struct $Point (%2 : $Int64, %2 : $Int64)
+ %4 = ref_element_addr %0 : $HasCenter, #HasCenter.centerPoint
+ store %3 to %4 : $*Point
+ return %0 : $HasCenter
+}