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 : $()
+}