Merge pull request #14163 from gottesmm/swift-4.1-branch_rdar36509461
[4.1][sil] When expanding aggregate instructions, do so consistently based…
diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h
index 64273b8..fbd2c0e 100644
--- a/include/swift/SIL/TypeLowering.h
+++ b/include/swift/SIL/TypeLowering.h
@@ -259,14 +259,6 @@
enum class LoweringStyle { Shallow, Deep };
- /// Given the result of the expansion heuristic,
- /// return appropriate lowering style.
- static LoweringStyle getLoweringStyle(bool shouldExpand) {
- if (shouldExpand)
- return TypeLowering::LoweringStyle::Deep;
- return TypeLowering::LoweringStyle::Shallow;
- }
-
//===--------------------------------------------------------------------===//
// DestroyValue
//===--------------------------------------------------------------------===//
diff --git a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
index 3d8267f..235ec2b 100644
--- a/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
+++ b/lib/SILOptimizer/Transforms/SILLowerAggregateInstrs.cpp
@@ -112,8 +112,11 @@
// retain_value %new : $*T
IsTake_t IsTake = CA->isTakeOfSrc();
if (IsTake_t::IsNotTake == IsTake) {
- TL.emitLoweredCopyValue(Builder, CA->getLoc(), New,
- TypeLowering::getLoweringStyle(expand));
+ if (expand) {
+ TL.emitLoweredCopyValueDeep(Builder, CA->getLoc(), New);
+ } else {
+ TL.emitCopyValue(Builder, CA->getLoc(), New);
+ }
}
// If we are not initializing:
@@ -121,8 +124,11 @@
// *or*
// release_value %old : $*T
if (Old) {
- TL.emitLoweredDestroyValue(Builder, CA->getLoc(), Old,
- TypeLowering::getLoweringStyle(expand));
+ if (expand) {
+ TL.emitLoweredDestroyValueDeep(Builder, CA->getLoc(), Old);
+ } else {
+ TL.emitDestroyValue(Builder, CA->getLoc(), Old);
+ }
}
}
@@ -155,8 +161,11 @@
LoadInst *LI = Builder.createLoad(DA->getLoc(), Addr,
LoadOwnershipQualifier::Unqualified);
auto &TL = Module.getTypeLowering(Type);
- TL.emitLoweredDestroyValue(Builder, DA->getLoc(), LI,
- TypeLowering::getLoweringStyle(expand));
+ if (expand) {
+ TL.emitLoweredDestroyValueDeep(Builder, DA->getLoc(), LI);
+ } else {
+ TL.emitDestroyValue(Builder, DA->getLoc(), LI);
+ }
}
++NumExpand;
diff --git a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp
index 1dfc54d..b0e98bb 100644
--- a/lib/SILOptimizer/Transforms/SILMem2Reg.cpp
+++ b/lib/SILOptimizer/Transforms/SILMem2Reg.cpp
@@ -350,8 +350,12 @@
bool expand = shouldExpand(DAI->getModule(),
DAI->getOperand()->getType().getObjectType());
- TL.emitLoweredDestroyValue(Builder, DAI->getLoc(), NewValue,
- Lowering::TypeLowering::getLoweringStyle(expand));
+ if (expand) {
+ TL.emitLoweredDestroyValueDeep(Builder, DAI->getLoc(), NewValue);
+ } else {
+ TL.emitDestroyValue(Builder, DAI->getLoc(), NewValue);
+ }
+
DAI->eraseFromParent();
}
diff --git a/test/Executable/Inputs/arc_36509461.h b/test/Executable/Inputs/arc_36509461.h
new file mode 100644
index 0000000..48820f8
--- /dev/null
+++ b/test/Executable/Inputs/arc_36509461.h
@@ -0,0 +1,11 @@
+
+#ifndef ARC36509461_H
+#define ARC36509461_H
+
+#include <stdbool.h>
+
+typedef bool (^fake_apply_t)(const char *key, id value);
+
+bool fake_apply(id obj, fake_apply_t applier);
+
+#endif
diff --git a/test/Executable/Inputs/arc_36509461.m b/test/Executable/Inputs/arc_36509461.m
new file mode 100644
index 0000000..b88b1f3
--- /dev/null
+++ b/test/Executable/Inputs/arc_36509461.m
@@ -0,0 +1,6 @@
+
+#import "arc_36509461.h"
+
+bool fake_apply(id obj, fake_apply_t applier) {
+ return false;
+}
diff --git a/test/Executable/arc_36509461.swift b/test/Executable/arc_36509461.swift
new file mode 100644
index 0000000..37da207
--- /dev/null
+++ b/test/Executable/arc_36509461.swift
@@ -0,0 +1,55 @@
+// RUN: %empty-directory(%T)
+// RUN: %target-clang -x objective-c -c %S/Inputs/arc_36509461.m -o %T/arc_36509461.m.o
+// RUN: %target-swift-frontend -c -O -import-objc-header %S/Inputs/arc_36509461.h -sanitize=address %s -o %T/arc_36509461.swift.o
+// RUN: %target-build-swift %T/arc_36509461.m.o %T/arc_36509461.swift.o -sanitize=address -o %t
+// RUN: %t
+
+// REQUIRES: executable_test
+// REQUIRES: asan_runtime
+// REQUIRES: objc_interop
+
+import Foundation
+
+struct FakeUUID {
+ var bigTuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
+
+ init() {
+ bigTuple = (0, 0, 0, 0, 0, 0, 0, 0)
+ }
+}
+
+struct Record {
+ let name: String
+ let uuid: FakeUUID
+ let storage: NSObject
+
+ init(storage: NSObject, name: String, uuid: FakeUUID) {
+ self.name = name
+ self.uuid = uuid
+ self.storage = storage
+ }
+
+ func copy() -> Record {
+ let copiedNSObject = NSObject()
+
+ fake_apply(self.storage) { (key, value) -> Bool in
+ let x = copiedNSObject
+ return true
+ }
+
+ var record = Record(storage: copiedNSObject, name: self.name, uuid: self.uuid)
+ return record
+ }
+}
+
+@inline(never)
+func foo(record: Record) -> Record {
+ return record.copy()
+}
+
+func main() {
+ let record = Record(storage: NSObject(), name: "", uuid: FakeUUID())
+ _ = foo(record: record)
+}
+
+main()
diff --git a/test/SILOptimizer/loweraggregateinstrs.sil b/test/SILOptimizer/loweraggregateinstrs.sil
index 600c963..db385d1 100644
--- a/test/SILOptimizer/loweraggregateinstrs.sil
+++ b/test/SILOptimizer/loweraggregateinstrs.sil
@@ -1,5 +1,9 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -enable-expand-all %s -lower-aggregate-instrs | %FileCheck %s
+// This file makes sure that the mechanics of expanding aggregate instructions
+// work. With that in mind, we expand all structs here ignoring code-size
+// trade-offs.
+
sil_stage canonical
import Swift
diff --git a/test/SILOptimizer/loweraggregateinstrs_codesize.sil b/test/SILOptimizer/loweraggregateinstrs_codesize.sil
new file mode 100644
index 0000000..3f0aae1
--- /dev/null
+++ b/test/SILOptimizer/loweraggregateinstrs_codesize.sil
@@ -0,0 +1,126 @@
+// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -lower-aggregate-instrs | %FileCheck %s
+
+// This file makes sure that given the current code-size metric we properly
+// expand operations for small structs and not for large structs in a consistent
+// way for all operations we expand.
+
+sil_stage canonical
+
+import Swift
+import Builtin
+
+class C1 {
+ var data : Builtin.Int64
+ init()
+}
+
+class C2 {
+ var data : Builtin.FPIEEE32
+ init()
+}
+
+class C3 {
+ var data : Builtin.FPIEEE64
+ init()
+}
+
+struct S2 {
+ var cls1 : C1
+ var cls2 : C2
+ var trivial : Builtin.FPIEEE32
+}
+
+struct S {
+ var trivial : Builtin.Int64
+ var cls : C3
+ var inner_struct : S2
+}
+
+enum E {
+ case NoElement()
+ case TrivialElement(Builtin.Int64)
+ case ReferenceElement(C1)
+ case StructNonTrivialElt(S)
+ case TupleNonTrivialElt((Builtin.Int64, S, C3))
+}
+
+// This struct is larger than our current code-size limit (> 6 leaf nodes).
+struct LargeStruct {
+ var trivial1 : Builtin.Int64
+ var cls : S
+ var trivial2 : Builtin.Int64
+ var trivial3 : Builtin.Int64
+}
+
+///////////
+// Tests //
+///////////
+
+// This test makes sure that we /do not/ expand retain_value, release_value and
+// promote copy_addr/destroy_value to non-expanded retain_value, release_value.
+// CHECK-LABEL: sil @large_struct_test : $@convention(thin) (@owned LargeStruct, @in LargeStruct) -> () {
+// CHECK: bb0([[ARG0:%.*]] : $LargeStruct, [[ARG1:%.*]] : $*LargeStruct):
+// CHECK: retain_value [[ARG0]]
+// CHECK: release_value [[ARG0]]
+// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $LargeStruct
+// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
+// CHECK: [[LOADED_OLD_VAL:%.*]] = load [[ALLOC_STACK]]
+// CHECK: retain_value [[LOADED_ARG1]]
+// CHECK: release_value [[LOADED_OLD_VAL]]
+// CHECK: store [[LOADED_ARG1]] to [[ALLOC_STACK]]
+// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
+// CHECK: release_value [[LOADED_ARG1]]
+// CHECK: dealloc_stack [[ALLOC_STACK]]
+// CHECK: } // end sil function 'large_struct_test'
+sil @large_struct_test : $@convention(thin) (@owned LargeStruct, @in LargeStruct) -> () {
+bb0(%0 : $LargeStruct, %1 : $*LargeStruct):
+ retain_value %0 : $LargeStruct
+ release_value %0 : $LargeStruct
+ %2 = alloc_stack $LargeStruct
+ copy_addr %1 to %2 : $*LargeStruct
+ destroy_addr %1 : $*LargeStruct
+ dealloc_stack %2 : $*LargeStruct
+ %9999 = tuple()
+ return %9999 : $()
+}
+
+// CHECK-LABEL: sil @small_struct_test : $@convention(thin) (@owned S2, @in S2) -> () {
+// CHECK: bb0([[ARG0:%.*]] : $S2, [[ARG1:%.*]] : $*S2):
+// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls1
+// CHECK: strong_retain [[ARG0cls1]] : $C1
+// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls2
+// CHECK: strong_retain [[ARG0cls2]] : $C2
+// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls1
+// CHECK: strong_release [[ARG0cls1]] : $C1
+// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]] : $S2, #S2.cls2
+// CHECK: strong_release [[ARG0cls2]] : $C2
+//
+// CHECK: [[ALLOC_STACK:%.*]] = alloc_stack $S2
+// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
+// CHECK: [[LOADED_OLDVALUE:%.*]] = load [[ALLOC_STACK]]
+// CHECK: [[LOADED_ARG1cls1:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls1
+// CHECK: strong_retain [[LOADED_ARG1cls1]] : $C1
+// CHECK: [[LOADED_ARG1cls2:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls2
+// CHECK: strong_retain [[LOADED_ARG1cls2]] : $C2
+// CHECK: [[LOADED_OLDVALUEcls1:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls1
+// CHECK: strong_release [[LOADED_OLDVALUEcls1]] : $C1
+// CHECK: [[LOADED_OLDVALUEcls2:%.*]] = struct_extract [[LOADED_OLDVALUE]] : $S2, #S2.cls2
+// CHECK: strong_release [[LOADED_OLDVALUEcls2]] : $C2
+//
+// CHECK: [[LOADED_ARG1:%.*]] = load [[ARG1]]
+// CHECK: [[LOADED_ARG1cls1:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls1
+// CHECK: strong_release [[LOADED_ARG1cls1]] : $C1
+// CHECK: [[LOADED_ARG1cls2:%.*]] = struct_extract [[LOADED_ARG1]] : $S2, #S2.cls2
+// CHECK: strong_release [[LOADED_ARG1cls2]] : $C2
+// CHECK: } // end sil function 'small_struct_test'
+sil @small_struct_test : $@convention(thin) (@owned S2, @in S2) -> () {
+bb0(%0 : $S2, %1 : $*S2):
+ retain_value %0 : $S2
+ release_value %0 : $S2
+ %2 = alloc_stack $S2
+ copy_addr %1 to %2 : $*S2
+ destroy_addr %1 : $*S2
+ dealloc_stack %2 : $*S2
+ %9999 = tuple()
+ return %9999 : $()
+}
diff --git a/test/SILOptimizer/mem2reg.sil b/test/SILOptimizer/mem2reg.sil
index be9a350..0529921 100644
--- a/test/SILOptimizer/mem2reg.sil
+++ b/test/SILOptimizer/mem2reg.sil
@@ -3,6 +3,29 @@
import Builtin
import Swift
+//////////////////
+// Declarations //
+//////////////////
+
+class Klass {}
+
+struct SmallCodesizeStruct {
+ var cls1 : Klass
+ var cls2 : Klass
+}
+
+struct LargeCodesizeStruct {
+ var s1: SmallCodesizeStruct
+ var s2: SmallCodesizeStruct
+ var s3: SmallCodesizeStruct
+ var s4: SmallCodesizeStruct
+ var s5: SmallCodesizeStruct
+}
+
+///////////
+// Tests //
+///////////
+
// CHECK-LABEL: sil @store_only_allocas
// CHECK-NOT: alloc_stack
// CHECK: return
@@ -356,3 +379,36 @@
%4 = tuple ()
return %4 : $()
}
+
+// Make sure that we do expand destroy_addr appropriately for code-size
+// trade-offs.
+// CHECK-LABEL: sil @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () {
+// CHECK: bb0([[ARG0:%.*]] : $LargeCodesizeStruct):
+// CHECK: release_value [[ARG0]]
+// CHECK: } // end sil function 'large_struct_test'
+sil @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () {
+bb0(%0 : $LargeCodesizeStruct):
+ %1 = alloc_stack $LargeCodesizeStruct
+ store %0 to %1 : $*LargeCodesizeStruct
+ destroy_addr %1 : $*LargeCodesizeStruct
+ dealloc_stack %1 : $*LargeCodesizeStruct
+ %7 = tuple ()
+ return %7 : $()
+}
+
+// CHECK-LABEL: sil @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
+// CHECK: bb0([[ARG0:%.*]] : $SmallCodesizeStruct):
+// CHECK: [[ARG0cls1:%.*]] = struct_extract [[ARG0]]
+// CHECK: strong_release [[ARG0cls1]]
+// CHECK: [[ARG0cls2:%.*]] = struct_extract [[ARG0]]
+// CHECK: strong_release [[ARG0cls2]]
+// CHECK: } // end sil function 'small_struct_test'
+sil @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
+bb0(%0 : $SmallCodesizeStruct):
+ %1 = alloc_stack $SmallCodesizeStruct
+ store %0 to %1 : $*SmallCodesizeStruct
+ destroy_addr %1 : $*SmallCodesizeStruct
+ dealloc_stack %1 : $*SmallCodesizeStruct
+ %7 = tuple ()
+ return %7 : $()
+}