From 5c25104ef9eebc49e9ef631cc41a067f6adc6094 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 23 Nov 2015 10:09:48 -0800 Subject: [PATCH 1/8] Quiet CMake warning. --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 079fcf3cb5406..3b43734936540 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,7 +25,7 @@ function(swift_configure_lit_site_cfg source_path destination_path installed_nam endfunction() function(normalize_boolean_spelling var_name) - if("${var_name}") + if(${var_name}) set("${var_name}" TRUE PARENT_SCOPE) else() set("${var_name}" FALSE PARENT_SCOPE) From 178409ad1d5d7e8c5c579c7d70ba79b2a8ad403c Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 23 Nov 2015 11:47:09 -0800 Subject: [PATCH 2/8] SILGen: Pass heap captures by only box. Now that boxes are typed and projectable, the address no longer has to be passed separately. For now, this breaks capture promotion, DI, and debug info, which analyze uses of the address param. Will be addressed in upcoming commits: Swift :: DebugInfo/byref-capture.swift Swift :: DebugInfo/closure-args.swift Swift :: DebugInfo/closure-args2.swift Swift :: DebugInfo/inout.swift Swift :: DebugInfo/linetable.swift Swift :: SILPasses/capture_promotion.swift Swift :: SILPasses/definite_init_diagnostics.swift --- include/swift/AST/Types.h | 13 +++++-- lib/SIL/TypeLowering.cpp | 11 ++---- lib/SILGen/SILGenFunction.cpp | 4 -- lib/SILGen/SILGenProlog.cpp | 6 +-- test/DebugInfo/byref-capture.swift | 2 + test/DebugInfo/closure-args.swift | 2 + test/DebugInfo/closure-args2.swift | 2 + test/DebugInfo/inout.swift | 2 + test/DebugInfo/linetable.swift | 2 + test/IRGen/closure.swift | 12 +++--- test/SILGen/capture_inout.swift | 6 +-- test/SILGen/capture_typed_boxes.swift | 12 +++--- test/SILGen/closures.swift | 39 ++++++++++--------- test/SILGen/functions.swift | 12 +++--- test/SILGen/generic_closures.swift | 10 ++--- test/SILGen/properties.swift | 11 +++--- test/SILGen/weak.swift | 5 ++- test/SILPasses/capture_promotion.swift | 2 + .../SILPasses/definite_init_diagnostics.swift | 2 + 19 files changed, 86 insertions(+), 69 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 4fc48aeff04ef..77f77ba0293e9 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2498,10 +2498,17 @@ enum class ParameterConvention { Indirect_In_Guaranteed, /// This argument is passed indirectly, i.e. by directly passing the address - /// of an object in memory. The object is instantaneously valid on entry, and - /// it must be instantaneously valid on exit. The callee may assume that the - /// address does not alias any valid object. + /// of an object in memory. The object is always valid, but the callee may + /// assume that the address does not alias any valid object and reorder loads + /// stores to the parameter as long as the whole object remains valid. Invalid + /// single-threaded aliasing may produce inconsistent results, but should + /// remain memory safe. Indirect_Inout, + + /// This argument is passed indirectly, i.e. by directly passing the address + /// of an object in memory. The object is allowed to be aliased by other + /// well-typed references. + //TODO: Indirect_Aliased, /// This argument is passed indirectly, i.e. by directly passing the address /// of an uninitialized object in memory. The callee is responsible for diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 4352cdbd7db67..cb3f8380ae2e8 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -1952,11 +1952,9 @@ TypeConverter::getFunctionTypeWithCaptures(CanAnyFunctionType funcType, inputFields.push_back(TupleTypeElt(captureType)); break; case CaptureKind::Box: { - // Capture the owning NativeObject and the address of the value. + // Capture the owning box. CanType boxTy = SILBoxType::get(captureType); inputFields.push_back(boxTy); - auto lvType = CanInOutType::get(captureType); - inputFields.push_back(TupleTypeElt(lvType)); break; } } @@ -2021,6 +2019,8 @@ TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType, case CaptureKind::StorageAddress: // No-escape stored decls are captured by their raw address. + // FIXME: 'inout' is semantically incorrect for a capture, since + // it is allowed to alias captured references. inputFields.push_back(TupleTypeElt(CanInOutType::get(captureType))); break; @@ -2029,12 +2029,9 @@ TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType, inputFields.push_back(TupleTypeElt(captureType)); break; case CaptureKind::Box: { - // Capture the owning NativeObject and the address of the value. + // Capture the owning box. CanType boxTy = SILBoxType::get(captureType); - inputFields.push_back(boxTy); - auto lvType = CanInOutType::get(captureType); - inputFields.push_back(TupleTypeElt(lvType)); break; } } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 908e84a4be26d..3999acaa8db63 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -285,7 +285,6 @@ void SILGenFunction::emitCaptures(SILLocation loc, if (vl.box) { B.createStrongRetain(loc, vl.box); capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box)); - capturedArgs.push_back(ManagedValue::forLValue(vl.value)); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each @@ -297,7 +296,6 @@ void SILGenFunction::emitCaptures(SILLocation loc, auto boxAddress = SILValue(allocBox, 1); B.createCopyAddr(loc, vl.value, boxAddress, IsNotTake,IsInitialization); capturedArgs.push_back(emitManagedRValueWithCleanup(SILValue(allocBox, 0))); - capturedArgs.push_back(ManagedValue::forLValue(boxAddress)); } break; @@ -578,8 +576,6 @@ static void forwardCaptureArgs(SILGenFunction &gen, SILType boxTy = SILType::getPrimitiveObjectType( SILBoxType::get(ty.getSwiftRValueType())); addSILArgument(boxTy, vd); - // Forward the captured value address. - addSILArgument(ty, vd); break; } diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index c1d566e1549f4..9bdcd66a59971 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -457,13 +457,13 @@ static void emitCaptureArguments(SILGenFunction &gen, CapturedValue capture) { } case CaptureKind::Box: { - // LValues are captured as two arguments: a retained NativeObject that owns - // the captured value, and the address of the value itself. + // LValues are captured as a retained @box that owns + // the captured value. SILType ty = gen.getLoweredType(type).getAddressType(); SILType boxTy = SILType::getPrimitiveObjectType( SILBoxType::get(ty.getSwiftRValueType())); SILValue box = new (gen.SGM.M) SILArgument(gen.F.begin(), boxTy, VD); - SILValue addr = new (gen.SGM.M) SILArgument(gen.F.begin(), ty, VD); + SILValue addr = gen.B.createProjectBox(VD, box); gen.VarLocs[VD] = SILGenFunction::VarLoc::get(addr, box); gen.Cleanups.pushCleanup(box); break; diff --git a/test/DebugInfo/byref-capture.swift b/test/DebugInfo/byref-capture.swift index f941ec6ec1821..5a811b824038c 100644 --- a/test/DebugInfo/byref-capture.swift +++ b/test/DebugInfo/byref-capture.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | FileCheck %s +// XFAIL: * + func makeIncrementor(inc : Int64) -> () -> Int64 { var sum : Int64 = 0 diff --git a/test/DebugInfo/closure-args.swift b/test/DebugInfo/closure-args.swift index fa6f6495867b4..f659ec7018555 100644 --- a/test/DebugInfo/closure-args.swift +++ b/test/DebugInfo/closure-args.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | FileCheck %s +// XFAIL: * + import Swift func main() -> Void diff --git a/test/DebugInfo/closure-args2.swift b/test/DebugInfo/closure-args2.swift index 353d815540fe6..69f09c5f049a7 100644 --- a/test/DebugInfo/closure-args2.swift +++ b/test/DebugInfo/closure-args2.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | FileCheck %s +// XFAIL: * + func main () -> Void { diff --git a/test/DebugInfo/inout.swift b/test/DebugInfo/inout.swift index b5680dcaaa3ba..947c755824978 100644 --- a/test/DebugInfo/inout.swift +++ b/test/DebugInfo/inout.swift @@ -3,6 +3,8 @@ // RUN: cat %t.ll | FileCheck %s --check-prefix=PROMO-CHECK // RUN: cat %t.ll | FileCheck %s --check-prefix=FOO-CHECK +// XFAIL: * + // LValues are direct values, too. They are reference types, though. func Close(fn: () -> Int64) { fn() } diff --git a/test/DebugInfo/linetable.swift b/test/DebugInfo/linetable.swift index 0ad52c6c296a3..1fcb0eda08dc0 100644 --- a/test/DebugInfo/linetable.swift +++ b/test/DebugInfo/linetable.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | FileCheck %s // RUN: %target-swift-frontend %s -S -g -o - | FileCheck %s --check-prefix ASM-CHECK +// XFAIL: * + // REQUIRES: CPU=i386_or_x86_64 import Swift func markUsed(t: T) {} diff --git a/test/IRGen/closure.swift b/test/IRGen/closure.swift index 65f7527b3a3dd..d93d516457b16 100644 --- a/test/IRGen/closure.swift +++ b/test/IRGen/closure.swift @@ -26,25 +26,23 @@ func b(seq seq: T) -> (Int) -> Int { // CHECK: } // -- Closure entry point -// CHECK: define linkonce_odr hidden i64 @[[CLOSURE2:_TFF7closure1buRxS_9OrdinablerFT3seqx_FSiSiU_FSiSi]](i64, %swift.refcounted*, %swift.opaque* nocapture, %swift.type* %T, i8** %T.Ordinable) {{.*}} { +// CHECK: define linkonce_odr hidden i64 @[[CLOSURE2:_TFF7closure1buRxS_9OrdinablerFT3seqx_FSiSiU_FSiSi]](i64, %swift.refcounted*, %swift.type* %T, i8** %T.Ordinable) {{.*}} { // -- partial_apply stub // CHECK: define internal i64 @_TPA_[[CLOSURE2]](i64, %swift.refcounted*) {{.*}} { // CHECK: entry: -// CHECK: [[CONTEXT:%.*]] = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>* -// CHECK: [[BINDINGSADDR:%.*]] = getelementptr inbounds <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>, <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>* [[CONTEXT]], i32 0, i32 1 +// CHECK: [[CONTEXT:%.*]] = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [16 x i8], %swift.refcounted* }>* +// CHECK: [[BINDINGSADDR:%.*]] = getelementptr inbounds <{ %swift.refcounted, [16 x i8], %swift.refcounted* }>, <{ %swift.refcounted, [16 x i8], %swift.refcounted* }>* [[CONTEXT]], i32 0, i32 1 // CHECK: [[TYPEADDR:%.*]] = bitcast [16 x i8]* [[BINDINGSADDR]] // CHECK: [[TYPE:%.*]] = load %swift.type*, %swift.type** [[TYPEADDR]], align 8 // CHECK: [[WITNESSADDR_0:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[TYPEADDR]], i32 1 // CHECK: [[WITNESSADDR:%.*]] = bitcast %swift.type** [[WITNESSADDR_0]] // CHECK: [[WITNESS:%.*]] = load i8**, i8*** [[WITNESSADDR]], align 8 -// CHECK: [[BOXADDR:%.*]] = getelementptr inbounds <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>, <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>* [[CONTEXT]], i32 0, i32 2 +// CHECK: [[BOXADDR:%.*]] = getelementptr inbounds <{ %swift.refcounted, [16 x i8], %swift.refcounted* }>, <{ %swift.refcounted, [16 x i8], %swift.refcounted* }>* [[CONTEXT]], i32 0, i32 2 // CHECK: [[BOX:%.*]] = load %swift.refcounted*, %swift.refcounted** [[BOXADDR]], align 8 // CHECK: call void @swift_retain(%swift.refcounted* [[BOX]]) -// CHECK: [[ADDRADDR:%.*]] = getelementptr inbounds <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>, <{ %swift.refcounted, [16 x i8], %swift.refcounted*, %swift.opaque* }>* [[CONTEXT]], i32 0, i32 3 -// CHECK: [[ADDR:%.*]] = load %swift.opaque*, %swift.opaque** [[ADDRADDR]], align 8 // CHECK: call void @swift_release(%swift.refcounted* %1) -// CHECK: [[RES:%.*]] = tail call i64 @[[CLOSURE2]](i64 %0, %swift.refcounted* [[BOX]], %swift.opaque* nocapture [[ADDR]], %swift.type* [[TYPE]], i8** [[WITNESS]]) +// CHECK: [[RES:%.*]] = tail call i64 @[[CLOSURE2]](i64 %0, %swift.refcounted* [[BOX]], %swift.type* [[TYPE]], i8** [[WITNESS]]) // CHECK: ret i64 [[RES]] // CHECK: } diff --git a/test/SILGen/capture_inout.swift b/test/SILGen/capture_inout.swift index cf78357e7be8f..22205d44f5d03 100644 --- a/test/SILGen/capture_inout.swift +++ b/test/SILGen/capture_inout.swift @@ -5,10 +5,10 @@ typealias Int = Builtin.Int64 // CHECK: sil hidden @_TF13capture_inout3foo // CHECK: bb0([[X_INOUT:%.*]] : $*Builtin.Int64): // CHECK: [[X_LOCAL:%.*]] = alloc_box $Builtin.Int64 -// CHECK: [[FUNC:%.*]] = function_ref [[CLOSURE:@.*]] : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 -// CHECK: partial_apply [[FUNC]]([[X_LOCAL]]#0, [[X_LOCAL]]#1) +// CHECK: [[FUNC:%.*]] = function_ref [[CLOSURE:@.*]] : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 +// CHECK: partial_apply [[FUNC]]([[X_LOCAL]]#0) // CHECK: } -// CHECK: sil shared [[CLOSURE]] : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 +// CHECK: sil shared [[CLOSURE]] : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 func foo(inout x: Int) -> () -> Int { func bar() -> Int { return x diff --git a/test/SILGen/capture_typed_boxes.swift b/test/SILGen/capture_typed_boxes.swift index 1e76eea0d6def..2f3934fbba597 100644 --- a/test/SILGen/capture_typed_boxes.swift +++ b/test/SILGen/capture_typed_boxes.swift @@ -4,8 +4,8 @@ func foo(x: Int) -> () -> Int { var x = x return { x } } -// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes3fooFSiFT_SiU_FT_Si : $@convention(thin) (@owned @box Int, @inout Int) -> Int { -// CHECK: bb0(%0 : $@box Int, %1 : $*Int): +// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes3fooFSiFT_SiU_FT_Si : $@convention(thin) (@owned @box Int) -> Int { +// CHECK: bb0(%0 : $@box Int): func closure(f: Int -> Int) -> Int { var f = f @@ -15,8 +15,8 @@ func closure(f: Int -> Int) -> Int { return bar(0) } -// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes7closureFFSiSiSiL_3barfSiSi : $@convention(thin) (Int, @owned @box @callee_owned (Int) -> Int, @inout @callee_owned (Int) -> Int) -> Int { -// CHECK: bb0(%0 : $Int, %1 : $@box @callee_owned (Int) -> Int, %2 : $*@callee_owned (Int) -> Int): +// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes7closureFFSiSiSiL_3barfSiSi : $@convention(thin) (Int, @owned @box @callee_owned (Int) -> Int) -> Int { +// CHECK: bb0(%0 : $Int, %1 : $@box @callee_owned (Int) -> Int): func closure_generic(f: T -> T, x: T) -> T { var f = f @@ -26,6 +26,6 @@ func closure_generic(f: T -> T, x: T) -> T { return bar(x) } -// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes15closure_generic{{.*}} : $@convention(thin) (@out T, @in T, @owned @box @callee_owned (@out T, @in T) -> (), @inout @callee_owned (@out T, @in T) -> ()) -> () { -// CHECK-LABEL: bb0(%0 : $*T, %1 : $*T, %2 : $@box @callee_owned (@out T, @in T) -> (), %3 : $*@callee_owned (@out T, @in T) -> ()): +// CHECK-LABEL: sil shared @_TFF19capture_typed_boxes15closure_generic{{.*}} : $@convention(thin) (@out T, @in T, @owned @box @callee_owned (@out T, @in T) -> ()) -> () { +// CHECK-LABEL: bb0(%0 : $*T, %1 : $*T, %2 : $@box @callee_owned (@out T, @in T) -> ()): diff --git a/test/SILGen/closures.swift b/test/SILGen/closures.swift index a01b1a3db0a93..de08f1c8e0c74 100644 --- a/test/SILGen/closures.swift +++ b/test/SILGen/closures.swift @@ -27,14 +27,15 @@ func read_only_capture(x: Int) -> Int { } return cap() - // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures17read_only_capture.*]] : $@convention(thin) (@owned @box Int, @inout Int) -> Int - // CHECK: [[RET:%[0-9]+]] = apply [[CAP]]([[XBOX]]#0, [[XBOX]]#1) + // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures17read_only_capture.*]] : $@convention(thin) (@owned @box Int) -> Int + // CHECK: [[RET:%[0-9]+]] = apply [[CAP]]([[XBOX]]#0) // CHECK: release [[XBOX]]#0 // CHECK: return [[RET]] } // CHECK: sil shared @[[CAP_NAME]] -// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int, [[XADDR:%[0-9]+]] : $*Int): +// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int): +// CHECK: [[XADDR:%[0-9]+]] = project_box [[XBOX]] // CHECK: [[X:%[0-9]+]] = load [[XADDR]] // CHECK: release [[XBOX]] // CHECK: return [[X]] @@ -52,8 +53,8 @@ func write_to_capture(x: Int) -> Int { } scribble() - // CHECK: [[SCRIB:%[0-9]+]] = function_ref @[[SCRIB_NAME:_TFF8closures16write_to_capture.*]] : $@convention(thin) (@owned @box Int, @inout Int) -> () - // CHECK: apply [[SCRIB]]([[X2BOX]]#0, [[X2BOX]]#1) + // CHECK: [[SCRIB:%[0-9]+]] = function_ref @[[SCRIB_NAME:_TFF8closures16write_to_capture.*]] : $@convention(thin) (@owned @box Int) -> () + // CHECK: apply [[SCRIB]]([[X2BOX]]#0) // CHECK: [[RET:%[0-9]+]] = load [[X2BOX]]#1 // CHECK: release [[X2BOX]]#0 // CHECK: release [[XBOX]]#0 @@ -62,7 +63,8 @@ func write_to_capture(x: Int) -> Int { } // CHECK: sil shared @[[SCRIB_NAME]] -// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int, [[XADDR:%[0-9]+]] : $*Int): +// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int): +// CHECK: [[XADDR:%[0-9]+]] = project_box [[XBOX]] // CHECK: copy_addr {{%[0-9]+}} to [[XADDR]] // CHECK: release [[XBOX]] // CHECK: return @@ -75,9 +77,9 @@ func multiple_closure_refs(x: Int) -> (() -> Int, () -> Int) { } return (cap, cap) - // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned @box Int, @inout Int) -> Int + // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned @box Int) -> Int // CHECK: [[CAP_CLOSURE_1:%[0-9]+]] = partial_apply [[CAP]] - // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned @box Int, @inout Int) -> Int + // CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned @box Int) -> Int // CHECK: [[CAP_CLOSURE_2:%[0-9]+]] = partial_apply [[CAP]] // CHECK: [[RET:%[0-9]+]] = tuple ([[CAP_CLOSURE_1]] : {{.*}}, [[CAP_CLOSURE_2]] : {{.*}}) // CHECK: return [[RET]] @@ -91,20 +93,20 @@ func capture_local_func(x: Int) -> () -> () -> Int { func aleph() -> Int { return x } func beth() -> () -> Int { return aleph } - // CHECK: [[BETH_REF:%[0-9]+]] = function_ref @[[BETH_NAME:_TFF8closures18capture_local_funcFSiFT_FT_SiL_4bethfT_FT_Si]] : $@convention(thin) (@owned @box Int, @inout Int) -> @owned @callee_owned () -> Int - // CHECK: [[BETH_CLOSURE:%[0-9]+]] = partial_apply [[BETH_REF]]([[XBOX]]#0, [[XBOX]]#1) + // CHECK: [[BETH_REF:%[0-9]+]] = function_ref @[[BETH_NAME:_TFF8closures18capture_local_funcFSiFT_FT_SiL_4bethfT_FT_Si]] : $@convention(thin) (@owned @box Int) -> @owned @callee_owned () -> Int + // CHECK: [[BETH_CLOSURE:%[0-9]+]] = partial_apply [[BETH_REF]]([[XBOX]]#0) return beth // CHECK: release [[XBOX]]#0 // CHECK: return [[BETH_CLOSURE]] } // CHECK: sil shared @[[ALEPH_NAME:_TFF8closures18capture_local_funcFSiFT_FT_SiL_5alephfT_Si]] -// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int, [[XADDR:%[0-9]+]] : $*Int): +// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int): // CHECK: sil shared @[[BETH_NAME]] -// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int, [[XADDR:%[0-9]+]] : $*Int): -// CHECK: [[ALEPH_REF:%[0-9]+]] = function_ref @[[ALEPH_NAME]] : $@convention(thin) (@owned @box Int, @inout Int) -> Int -// CHECK: [[ALEPH_CLOSURE:%[0-9]+]] = partial_apply [[ALEPH_REF]]([[XBOX]], [[XADDR]]) +// CHECK: bb0([[XBOX:%[0-9]+]] : $@box Int): +// CHECK: [[ALEPH_REF:%[0-9]+]] = function_ref @[[ALEPH_NAME]] : $@convention(thin) (@owned @box Int) -> Int +// CHECK: [[ALEPH_CLOSURE:%[0-9]+]] = partial_apply [[ALEPH_REF]]([[XBOX]]) // CHECK: return [[ALEPH_CLOSURE]] // CHECK-LABEL: sil hidden @_TF8closures22anon_read_only_capture @@ -155,15 +157,16 @@ func small_closure_capture_with_argument(x: Int) -> (y: Int) -> Int { return { x + $0 } // -- func expression - // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures35small_closure_capture_with_argument.*]] : $@convention(thin) (Int, @owned @box Int, @inout Int) -> Int + // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures35small_closure_capture_with_argument.*]] : $@convention(thin) (Int, @owned @box Int) -> Int // CHECK: retain [[XBOX]]#0 - // CHECK: [[ANON_CLOSURE_APP:%[0-9]+]] = partial_apply [[ANON]]([[XBOX]]#0, [[XBOX]]#1) + // CHECK: [[ANON_CLOSURE_APP:%[0-9]+]] = partial_apply [[ANON]]([[XBOX]]#0) // -- return // CHECK: release [[XBOX]]#0 // CHECK: return [[ANON_CLOSURE_APP]] } -// CHECK: sil shared @[[CLOSURE_NAME]] : $@convention(thin) (Int, @owned @box Int, @inout Int) -> Int -// CHECK: bb0([[DOLLAR0:%[0-9]+]] : $Int, [[XBOX:%[0-9]+]] : $@box Int, [[XADDR:%[0-9]+]] : $*Int): +// CHECK: sil shared @[[CLOSURE_NAME]] : $@convention(thin) (Int, @owned @box Int) -> Int +// CHECK: bb0([[DOLLAR0:%[0-9]+]] : $Int, [[XBOX:%[0-9]+]] : $@box Int): +// CHECK: [[XADDR:%[0-9]+]] = project_box [[XBOX]] // CHECK: [[PLUS:%[0-9]+]] = function_ref @_TZFsoi1pFTSiSi_Si{{.*}} // CHECK: [[LHS:%[0-9]+]] = load [[XADDR]] // CHECK: [[RET:%[0-9]+]] = apply [[PLUS]]([[LHS]], [[DOLLAR0]]) diff --git a/test/SILGen/functions.swift b/test/SILGen/functions.swift index 5ade82dbbfb97..19b1a0be679e5 100644 --- a/test/SILGen/functions.swift +++ b/test/SILGen/functions.swift @@ -53,8 +53,8 @@ func curried_function_returns_function(x: Int)(y: Int) -> (z:Int) -> Int { return { z in standalone_function(standalone_function(x, y), z) } } // -- Local function has extra uncurry level with context -// CHECK-LABEL: sil shared @_TFF9functions33curried_function_returns_function{{.*}} : $@convention(thin) (Builtin.Int64, @owned @box Builtin.Int64, @inout Builtin.Int64, @owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 -// bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64, %2 : $@box Builtin.Int64, %3 : $*Builtin.Int64, %4 : $Builtin.Int64): +// CHECK-LABEL: sil shared @_TFF9functions33curried_function_returns_function{{.*}} : $@convention(thin) (Builtin.Int64, @owned @box Builtin.Int64, @owned @box Builtin.Int64) -> Builtin.Int64 +// bb0(%0 : $@box Builtin.Int64, %1 : $@box Builtin.Int64, %4 : $Builtin.Int64): struct SomeStruct { // -- Constructors and methods are uncurried in 'self' @@ -581,12 +581,12 @@ func testNoescape() { // CHECK-LABEL: functions.testNoescape () -> () // CHECK-NEXT: sil hidden @_TF9functions12testNoescapeFT_T_ : $@convention(thin) () -> () // CHECK: function_ref functions.(testNoescape () -> ()).(closure #1) -// CHECK-NEXT: function_ref @_TFF9functions12testNoescapeFT_T_U_FT_T_ : $@convention(thin) (@owned @box Int, @inout Int) -> () +// CHECK-NEXT: function_ref @_TFF9functions12testNoescapeFT_T_U_FT_T_ : $@convention(thin) (@owned @box Int) -> () // Despite being a noescape closure, this needs to capture 'a' by-box so it can // be passed to the capturing closure.closure // CHECK: functions.(testNoescape () -> ()).(closure #1) -// CHECK-NEXT: sil shared @_TFF9functions12testNoescapeFT_T_U_FT_T_ : $@convention(thin) (@owned @box Int, @inout Int) -> () { +// CHECK-NEXT: sil shared @_TFF9functions12testNoescapeFT_T_U_FT_T_ : $@convention(thin) (@owned @box Int) -> () { @@ -606,10 +606,10 @@ func testNoescape2() { // CHECK-LABEL: sil hidden @_TF9functions13testNoescape2FT_T_ : $@convention(thin) () -> () { // CHECK: // functions.(testNoescape2 () -> ()).(closure #1) -// CHECK-NEXT: sil shared @_TFF9functions13testNoescape2FT_T_U_FT_T_ : $@convention(thin) (@owned @box Int, @inout Int) -> () { +// CHECK-NEXT: sil shared @_TFF9functions13testNoescape2FT_T_U_FT_T_ : $@convention(thin) (@owned @box Int) -> () { // CHECK: // functions.(testNoescape2 () -> ()).(closure #1).(closure #1) -// CHECK-NEXT: sil shared @_TFFF9functions13testNoescape2FT_T_U_FT_T_U_FT_T_ : $@convention(thin) (@owned @box Int, @inout Int) -> () { +// CHECK-NEXT: sil shared @_TFFF9functions13testNoescape2FT_T_U_FT_T_U_FT_T_ : $@convention(thin) (@owned @box Int) -> () { enum PartialApplyEnumPayload { case Left(T) diff --git a/test/SILGen/generic_closures.swift b/test/SILGen/generic_closures.swift index 4e2bdc7bb23b2..2b714f03ece55 100644 --- a/test/SILGen/generic_closures.swift +++ b/test/SILGen/generic_closures.swift @@ -8,7 +8,7 @@ var zero: Int func generic_nondependent_context(x: T, y: Int) -> Int { var y = y func foo() -> Int { return y } - // CHECK: [[FOO:%.*]] = function_ref @_TFF16generic_closures28generic_nondependent_context{{.*}} : $@convention(thin) (@owned @box Int, @inout Int) -> Int + // CHECK: [[FOO:%.*]] = function_ref @_TFF16generic_closures28generic_nondependent_context{{.*}} : $@convention(thin) (@owned @box Int) -> Int // CHECK: [[FOO_CLOSURE:%.*]] = apply [[FOO]] return foo() } @@ -44,7 +44,7 @@ func generic_nocapture_existential(x: T, y: Concept) -> Bool { // CHECK-LABEL: sil hidden @_TF16generic_closures25generic_dependent_context{{.*}} func generic_dependent_context(x: T, y: Int) -> T { func foo() -> T { return x } - // CHECK: [[FOO:%.*]] = function_ref @_TFF16generic_closures25generic_dependent_context{{.*}} : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0, @inout τ_0_0) -> () + // CHECK: [[FOO:%.*]] = function_ref @_TFF16generic_closures25generic_dependent_context{{.*}} : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0) -> () // CHECK: [[FOO_CLOSURE:%.*]] = apply [[FOO]] return foo() } @@ -114,11 +114,11 @@ func local_properties(inout t: T) { } } - // CHECK: [[GETTER_REF:%[0-9]+]] = function_ref [[GETTER_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0, @inout τ_0_0) -> () + // CHECK: [[GETTER_REF:%[0-9]+]] = function_ref [[GETTER_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0) -> () // CHECK: apply [[GETTER_REF]] t = prop - // CHECK: [[SETTER_REF:%[0-9]+]] = function_ref [[SETTER_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@in τ_0_0, @owned @box τ_0_0, @inout τ_0_0) -> () + // CHECK: [[SETTER_REF:%[0-9]+]] = function_ref [[SETTER_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@in τ_0_0, @owned @box τ_0_0) -> () // CHECK: apply [[SETTER_REF]] prop = t @@ -131,7 +131,7 @@ func local_properties(inout t: T) { } } - // CHECK: [[GETTER2_REF:%[0-9]+]] = function_ref [[GETTER2_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0, @inout τ_0_0) -> () + // CHECK: [[GETTER2_REF:%[0-9]+]] = function_ref [[GETTER2_CLOSURE:@_TFF16generic_closures16local_properties.*]] : $@convention(thin) <τ_0_0> (@out τ_0_0, @owned @box τ_0_0) -> () // CHECK: apply [[GETTER2_REF]] t = prop2 diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 2d1790e12f6b3..756ed623e704c 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -667,15 +667,16 @@ func propertyWithDidSetTakingOldValue() { // CHECK: // properties.(propertyWithDidSetTakingOldValue () -> ()).(p #1).setter : Swift.Int // CHECK-NEXT: sil {{.*}} @_TFF10properties32propertyWithDidSetTakingOldValue -// CHECK: bb0(%0 : $Int, %1 : $@box Int, %2 : $*Int): +// CHECK: bb0(%0 : $Int, %1 : $@box Int): // CHECK-NEXT: debug_value %0 : $Int -// CHECK-NEXT: %4 = load %2 : $*Int +// CHECK-NEXT: %3 = project_box %1 +// CHECK-NEXT: %4 = load %3 : $*Int // CHECK-NEXT: debug_value %4 : $Int -// CHECK-NEXT: assign %0 to %2 : $*Int +// CHECK-NEXT: assign %0 to %3 : $*Int // CHECK-NEXT: strong_retain %1 : $@box Int // CHECK-NEXT: // function_ref -// CHECK-NEXT: %8 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int, @inout Int) -> () -// CHECK-NEXT: %9 = apply %8(%4, %1, %2) : $@convention(thin) (Int, @owned @box Int, @inout Int) -> () +// CHECK-NEXT: %8 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int) -> () +// CHECK-NEXT: %9 = apply %8(%4, %1) : $@convention(thin) (Int, @owned @box Int) -> () // CHECK-NEXT: strong_release %1 : $@box Int // CHECK-NEXT: %11 = tuple () // CHECK-NEXT: return %11 : $() diff --git a/test/SILGen/weak.swift b/test/SILGen/weak.swift index 18666fddd67b9..27eba8fb593e3 100644 --- a/test/SILGen/weak.swift +++ b/test/SILGen/weak.swift @@ -45,8 +45,9 @@ func test0(c c: C) { // silgen crashes on weak capture // CHECK: weak.(testClosureOverWeak () -> ()).(closure #1) -// CHECK-LABEL: sil shared @_TFF4weak19testClosureOverWeakFT_T_U_FT_Si : $@convention(thin) (@owned @box @sil_weak Optional, @inout @sil_weak Optional) -> Int { -// CHECK: bb0(%0 : $@box @sil_weak Optional, %1 : $*@sil_weak Optional): +// CHECK-LABEL: sil shared @_TFF4weak19testClosureOverWeakFT_T_U_FT_Si : $@convention(thin) (@owned @box @sil_weak Optional) -> Int { +// CHECK: bb0(%0 : $@box @sil_weak Optional): +// CHECK-NEXT: %1 = project_box %0 // CHECK-NEXT: %2 = alloc_stack $Optional // CHECK-NEXT: %3 = load_weak %1 : $*@sil_weak Optional // CHECK-NEXT: store %3 to %2#1 : $*Optional diff --git a/test/SILPasses/capture_promotion.swift b/test/SILPasses/capture_promotion.swift index ce3d891e5705b..2437bb280a29d 100644 --- a/test/SILPasses/capture_promotion.swift +++ b/test/SILPasses/capture_promotion.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend %s -emit-sil -o - -verify | FileCheck %s +// XFAIL: * + class Foo { func foo() -> Int { return 1 diff --git a/test/SILPasses/definite_init_diagnostics.swift b/test/SILPasses/definite_init_diagnostics.swift index 47880f0bd2cce..90193e0e8984c 100644 --- a/test/SILPasses/definite_init_diagnostics.swift +++ b/test/SILPasses/definite_init_diagnostics.swift @@ -3,6 +3,8 @@ // FIXME: rdar://problem/19648117 Needs splitting objc parts out // XFAIL: linux +// XFAIL: * + import Swift import gizmo From cdb4a20c45b2128b6ff3e23e8d34e0056c401d3a Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 23 Nov 2015 15:09:00 -0800 Subject: [PATCH 3/8] CapturePromotion: Update to handle lone box arguments. Match the new SILGen pattern, where only the box parameter is partially applied to the closure, and the address of the value is projected on the callee side. --- include/swift/Basic/Demangle.h | 2 +- include/swift/SIL/Mangle.h | 4 +- lib/Basic/Demangle.cpp | 8 +- lib/Basic/Remangle.cpp | 2 +- lib/SIL/Mangle.cpp | 6 +- lib/SILPasses/IPO/CapturePromotion.cpp | 190 +++++++++--------- test/Demangle/Inputs/manglings.txt | 4 +- test/SILPasses/capture_promotion.sil | 40 ++-- test/SILPasses/capture_promotion.swift | 6 +- .../capture_promotion_reachability.sil | 88 ++++---- 10 files changed, 186 insertions(+), 164 deletions(-) diff --git a/include/swift/Basic/Demangle.h b/include/swift/Basic/Demangle.h index dba1292b81309..580005ee3e720 100644 --- a/include/swift/Basic/Demangle.h +++ b/include/swift/Basic/Demangle.h @@ -78,7 +78,7 @@ enum class FunctionSigSpecializationParamKind : unsigned { ConstantPropFloat = 3, ConstantPropString = 4, ClosureProp = 5, - InOutToValue = 6, + BoxToValue = 6, // Option Set Flags use bits 6-31. This gives us 26 bits to use for option // flags. diff --git a/include/swift/SIL/Mangle.h b/include/swift/SIL/Mangle.h index bf7e54af6b323..e70a2ced3b9dc 100644 --- a/include/swift/SIL/Mangle.h +++ b/include/swift/SIL/Mangle.h @@ -148,7 +148,7 @@ class FunctionSignatureSpecializationMangler Unmodified=0, ConstantProp=1, ClosureProp=2, - InOutToValue=3, + BoxToValue=3, First_Option=0, Last_Option=31, // Option Set Space. 12 bits (i.e. 12 option). @@ -171,7 +171,7 @@ class FunctionSignatureSpecializationMangler void setArgumentDead(unsigned ArgNo); void setArgumentOwnedToGuaranteed(unsigned ArgNo); void setArgumentSROA(unsigned ArgNo); - void setArgumentInOutToValue(unsigned ArgNo); + void setArgumentBoxToValue(unsigned ArgNo); private: void mangleSpecialization(); diff --git a/lib/Basic/Demangle.cpp b/lib/Basic/Demangle.cpp index d12074982cd11..735af287aec5f 100644 --- a/lib/Basic/Demangle.cpp +++ b/lib/Basic/Demangle.cpp @@ -784,7 +784,7 @@ class Demangler { if (!demangleFuncSigSpecializationClosureProp(param)) return nullptr; } else if (Mangled.nextIf("i_")) { - auto result = FUNCSIGSPEC_CREATE_PARAM_KIND(InOutToValue); + auto result = FUNCSIGSPEC_CREATE_PARAM_KIND(BoxToValue); if (!result) return nullptr; param->addChild(result); @@ -2634,7 +2634,7 @@ unsigned NodePrinter::printFunctionSigSpecializationParam(NodePointer pointer, unsigned V = firstChild->getIndex(); auto K = FunctionSigSpecializationParamKind(V); switch (K) { - case FunctionSigSpecializationParamKind::InOutToValue: + case FunctionSigSpecializationParamKind::BoxToValue: print(pointer->getChild(Idx++)); return Idx; case FunctionSigSpecializationParamKind::ConstantPropFunction: @@ -3099,8 +3099,8 @@ void NodePrinter::print(NodePointer pointer, bool asContext, bool suppressType) return; switch (FunctionSigSpecializationParamKind(raw)) { - case FunctionSigSpecializationParamKind::InOutToValue: - Printer << "Value Promoted from InOut"; + case FunctionSigSpecializationParamKind::BoxToValue: + Printer << "Value Promoted from Box"; break; case FunctionSigSpecializationParamKind::ConstantPropFunction: Printer << "Constant Propagated Function"; diff --git a/lib/Basic/Remangle.cpp b/lib/Basic/Remangle.cpp index ed4ccf9a847e6..75bb643c2cda9 100644 --- a/lib/Basic/Remangle.cpp +++ b/lib/Basic/Remangle.cpp @@ -522,7 +522,7 @@ void Remangler::mangleFunctionSignatureSpecializationParam(Node *node) { } Out << '_'; return; - case FunctionSigSpecializationParamKind::InOutToValue: + case FunctionSigSpecializationParamKind::BoxToValue: Out << "i_"; return; default: diff --git a/lib/SIL/Mangle.cpp b/lib/SIL/Mangle.cpp index 5f0067f21ab8d..9bb682ca70453 100644 --- a/lib/SIL/Mangle.cpp +++ b/lib/SIL/Mangle.cpp @@ -123,8 +123,8 @@ setArgumentSROA(unsigned ArgNo) { void FunctionSignatureSpecializationMangler:: -setArgumentInOutToValue(unsigned ArgNo) { - Args[ArgNo].first = ArgumentModifierIntBase(ArgumentModifier::InOutToValue); +setArgumentBoxToValue(unsigned ArgNo) { + Args[ArgNo].first = ArgumentModifierIntBase(ArgumentModifier::BoxToValue); } void @@ -240,7 +240,7 @@ void FunctionSignatureSpecializationMangler::mangleArgument( return; } - if (ArgMod == ArgumentModifierIntBase(ArgumentModifier::InOutToValue)) { + if (ArgMod == ArgumentModifierIntBase(ArgumentModifier::BoxToValue)) { os << "i"; return; } diff --git a/lib/SILPasses/IPO/CapturePromotion.cpp b/lib/SILPasses/IPO/CapturePromotion.cpp index a3bb0741375fb..12918fd9927af 100644 --- a/lib/SILPasses/IPO/CapturePromotion.cpp +++ b/lib/SILPasses/IPO/CapturePromotion.cpp @@ -218,11 +218,12 @@ class ClosureCloner : public TypeSubstCloner { void visitStrongReleaseInst(StrongReleaseInst *Inst); void visitStructElementAddrInst(StructElementAddrInst *Inst); void visitLoadInst(LoadInst *Inst); + void visitProjectBoxInst(ProjectBoxInst *Inst); SILFunction *Orig; IndicesSet &PromotableIndices; llvm::DenseMap BoxArgumentMap; - llvm::DenseMap AddrArgumentMap; + llvm::DenseMap ProjectBoxArgumentMap; }; } // end anonymous namespace. @@ -351,24 +352,17 @@ computeNewArgInterfaceTypes(SILFunction *F, << (PromotableIndices.count(Index)?"yes":"no") << " Param: "; param.dump()); - // With that in mind, first check if we do not have a box address value... - if (Index == 0 || !PromotableIndices.count(Index - 1)) { - - // If we do not have a box address value, if we have a box container - // value, continue so we do not add it to the new closure's function type. - if (PromotableIndices.count(Index)) - continue; - - // Otherwise, we have a function argument not related to a promotable - // box. Just add it to the new signature and continue. + if (!PromotableIndices.count(Index)) { OutTys.push_back(param); continue; } - - // Otherwise, we have an address value of the box. Perform the proper - // conversions and then add it to the new parameter list for the type. - assert(param.getConvention() == ParameterConvention::Indirect_Inout); - auto ¶mTL = F->getModule().Types.getTypeLowering(param.getSILType()); + + // Perform the proper conversions and then add it to the new parameter list + // for the type. + assert(!isIndirectParameter(param.getConvention())); + auto paramBoxedTy = param.getSILType().castTo() + ->getBoxedAddressType(); + auto ¶mTL = F->getModule().Types.getTypeLowering(paramBoxedTy); ParameterConvention convention; if (paramTL.isPassedIndirectly()) { convention = ParameterConvention::Indirect_In; @@ -377,7 +371,8 @@ computeNewArgInterfaceTypes(SILFunction *F, } else { convention = ParameterConvention::Direct_Owned; } - OutTys.push_back(SILParameterInfo(param.getType(), convention)); + OutTys.push_back(SILParameterInfo(paramBoxedTy.getSwiftRValueType(), + convention)); } } @@ -394,14 +389,9 @@ static llvm::SmallString<64> getSpecializedName(SILFunction *F, ArrayRef Parameters = FTy->getParameters(); for (unsigned Index : indices(Parameters)) { - if (Index == 0 || !PromotableIndices.count(Index - 1)) { - if (!PromotableIndices.count(Index)) - continue; - FSSM.setArgumentDead(Index); + if (!PromotableIndices.count(Index)) continue; - } - - FSSM.setArgumentInOutToValue(Index); + FSSM.setArgumentBoxToValue(Index); } FSSM.mangle(); @@ -477,15 +467,19 @@ ClosureCloner::populateCloned() { auto I = OrigEntryBB->bbarg_begin(), E = OrigEntryBB->bbarg_end(); while (I != E) { if (PromotableIndices.count(ArgNo)) { - // Handle the case of a promoted capture argument - SILArgument *ReleaseArgument = *I++; + // Handle the case of a promoted capture argument. + auto BoxedTy = (*I)->getType().castTo()->getBoxedAddressType() + .getObjectType(); SILValue MappedValue = - new (M) SILArgument(ClonedEntryBB, - (*I)->getType().getObjectType(), - (*I)->getDecl()); - BoxArgumentMap.insert(std::make_pair(ReleaseArgument, MappedValue)); - AddrArgumentMap.insert(std::make_pair(*I, MappedValue)); - ++ArgNo; + new (M) SILArgument(ClonedEntryBB, BoxedTy, (*I)->getDecl()); + BoxArgumentMap.insert(std::make_pair(*I, MappedValue)); + + // Track the projections of the box. + for (auto *Use : (*I)->getUses()) { + if (auto Proj = dyn_cast(Use->getUser())) { + ProjectBoxArgumentMap.insert(std::make_pair(Proj, MappedValue)); + } + } } else { // Otherwise, create a new argument which copies the original argument SILValue MappedValue = @@ -539,26 +533,36 @@ ClosureCloner::visitStrongReleaseInst(StrongReleaseInst *Inst) { void ClosureCloner::visitStructElementAddrInst(StructElementAddrInst *Inst) { SILValue Operand = Inst->getOperand(); - if (SILArgument *A = dyn_cast(Operand)) { + if (auto *A = dyn_cast(Operand)) { assert(Operand.getResultNumber() == 0); - auto I = AddrArgumentMap.find(A); - if (I != AddrArgumentMap.end()) + auto I = ProjectBoxArgumentMap.find(A); + if (I != ProjectBoxArgumentMap.end()) return; } SILCloner::visitStructElementAddrInst(Inst); } +/// project_box of captured boxes can be eliminated. +void +ClosureCloner::visitProjectBoxInst(ProjectBoxInst *I) { + if (auto Arg = dyn_cast(I->getOperand())) + if (BoxArgumentMap.count(Arg)) + return; + + SILCloner::visitProjectBoxInst(I); +} + /// \brief Handle a load instruction during cloning of a closure; the two /// relevant cases are a direct load from a promoted address argument or a load /// of a struct_element_addr of a promoted address argument. void ClosureCloner::visitLoadInst(LoadInst *Inst) { SILValue Operand = Inst->getOperand(); - if (auto *A = dyn_cast(Operand)) { + if (auto *A = dyn_cast(Operand)) { assert(Operand.getResultNumber() == 0); - auto I = AddrArgumentMap.find(A); - if (I != AddrArgumentMap.end()) { + auto I = ProjectBoxArgumentMap.find(A); + if (I != ProjectBoxArgumentMap.end()) { // Loads of the address argument get eliminated completely; the uses of // the loads get mapped to uses of the new object type argument. ValueMap.insert(std::make_pair(Inst, I->second)); @@ -566,10 +570,10 @@ ClosureCloner::visitLoadInst(LoadInst *Inst) { } } else if (auto *SEAI = dyn_cast(Operand)) { assert(Operand.getResultNumber() == 0); - if (auto *A = dyn_cast(SEAI->getOperand())) { + if (auto *A = dyn_cast(SEAI->getOperand())) { assert(SEAI->getOperand().getResultNumber() == 0); - auto I = AddrArgumentMap.find(A); - if (I != AddrArgumentMap.end()) { + auto I = ProjectBoxArgumentMap.find(A); + if (I != ProjectBoxArgumentMap.end()) { // Loads of a struct_element_addr of an argument get replaced with // struct_extract of the new object type argument. SILBuilderWithPostProcess B(this, Inst); @@ -585,15 +589,10 @@ ClosureCloner::visitLoadInst(LoadInst *Inst) { SILCloner::visitLoadInst(Inst); } -static std::pair getBoxAndAddrFromIndex( - SILFunction *F, - unsigned Index) { +static SILArgument *getBoxFromIndex(SILFunction *F, unsigned Index) { assert(F->isDefinition() && "Expected definition not external declaration!"); auto &Entry = F->front(); - auto *Box = Entry.getBBArg(Index); - auto *Addr = Entry.getBBArg(Index + 1); - - return std::make_pair(Box, Addr); + return Entry.getBBArg(Index); } /// \brief Given a partial_apply instruction and the argument index into its @@ -601,28 +600,41 @@ static std::pair getBoxAndAddrFromIndex( /// for the address of the box's contents), return true if the closure is known /// not to mutate the captured variable. static bool -isNonmutatingCapture(SILArgument *BoxArg, SILArgument *AddrArg) { +isNonmutatingCapture(SILArgument *BoxArg) { + SmallVector Projections; + // Conservatively do not allow any use of the box argument other than a - // strong_release, since this is the pattern expected from SILGen. - for (auto *O : BoxArg->getUses()) - if (!isa(O->getUser())) - return false; + // strong_release or projection, since this is the pattern expected from + // SILGen. + for (auto *O : BoxArg->getUses()) { + if (isa(O->getUser())) + continue; + + if (auto Projection = dyn_cast(O->getUser())) { + Projections.push_back(Projection); + continue; + } + + return false; + } - // Only allow loads of the address argument, either directly or via + // Only allow loads of projections, either directly or via // struct_element_addr instructions. // // TODO: This seems overly limited. Why not projections of tuples and other // stuff? Also, why not recursive struct elements? This should be a helper // function that mirrors isNonEscapingUse. - for (auto *O : AddrArg->getUses()) { - if (auto *SEAI = dyn_cast(O->getUser())) { - for (auto *UO : SEAI->getUses()) - if (!isa(UO->getUser())) - return false; - continue; + for (auto *Projection : Projections) { + for (auto *O : Projection->getUses()) { + if (auto *SEAI = dyn_cast(O->getUser())) { + for (auto *UO : SEAI->getUses()) + if (!isa(UO->getUser())) + return false; + continue; + } + if (!isa(O->getUser())) + return false; } - if (!isa(O->getUser())) - return false; } return true; @@ -705,14 +717,10 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI, llvm::DenseMap &IM) { SmallVector Mutations; - // If the AllocBox is used by a mark_uninitialized, scan the MUI for - // interesting uses. - SILValue Addr = ABI->getAddressResult(); - if (Addr.hasOneUse()) - if (auto MUI = dyn_cast(Addr.use_begin()->getUser())) - Addr = SILValue(MUI); + // Scan the box for interesting uses. + SILValue Box = ABI->getContainerResult(); - for (Operand *O : Addr.getUses()) { + for (Operand *O : Box.getUses()) { if (auto *PAI = dyn_cast(O->getUser())) { unsigned OpNo = O->getOperandNumber(); assert(OpNo != 0 && "Alloc box used as callee of partial apply?"); @@ -723,11 +731,6 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI, if (IM.count(PAI)) return false; - // Verify that the previous operand of the partial apply is the refcount - // result of the alloc_box. - if (PAI->getOperand(OpNo - 1) != SILValue(ABI)) - return false; - auto Callee = PAI->getCallee(); auto CalleeTy = Callee.getType().castTo(); @@ -741,27 +744,26 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI, // Calculate the index into the closure's argument list of the captured // box pointer (the captured address is always the immediately following // index so is not stored separately); - unsigned Index = OpNo - 2 + closureType->getParameters().size(); + unsigned Index = OpNo - 1 + closureType->getParameters().size(); auto *Fn = PAI->getCalleeFunction(); if (!Fn || !Fn->isDefinition()) return false; - SILArgument *BoxArg; - SILArgument *AddrArg; - std::tie(BoxArg, AddrArg) = getBoxAndAddrFromIndex(Fn, Index); + SILArgument *BoxArg = getBoxFromIndex(Fn, Index); // For now, return false is the address argument is an address-only type, - // since we currently assume loadable types only. + // since we currently handle loadable types only. // TODO: handle address-only types SILModule &M = PAI->getModule(); - if (AddrArg->getType().isAddressOnly(M)) + if (BoxArg->getType().castTo()->getBoxedAddressType() + .isAddressOnly(M)) return false; // Verify that this closure is known not to mutate the captured value; if // it does, then conservatively refuse to promote any captures of this // value. - if (!isNonmutatingCapture(BoxArg, AddrArg)) + if (!isNonmutatingCapture(BoxArg)) return false; // Record the index and continue. @@ -774,6 +776,19 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI, if (!isNonescapingUse(O, Mutations)) return false; } + + // Check for mutations of the address component. + // If the AllocBox is used by a mark_uninitialized, scan the MUI for + // interesting uses. + SILValue Addr = ABI->getAddressResult(); + if (Addr.hasOneUse()) + if (auto MUI = dyn_cast(Addr.use_begin()->getUser())) + Addr = SILValue(MUI); + + for (Operand *O : Addr.getUses()) { + if (!isNonescapingUse(O, Mutations)) + return false; + } // Helper lambda function to determine if instruction b is strictly after // instruction a, assuming both are in the same basic block. @@ -883,13 +898,8 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices, unsigned Index = OpNo - 1 + FirstIndex; if (PromotableIndices.count(Index)) { SILValue BoxValue = PAI->getOperand(OpNo); - SILValue AddrValue = PAI->getOperand(OpNo + 1); - SILValue UnderlyingAddrValue = AddrValue; - if (auto *MUI = dyn_cast(AddrValue)) - UnderlyingAddrValue = MUI->getOperand(); - assert(BoxValue.getDef() == UnderlyingAddrValue.getDef() && - BoxValue.getResultNumber() == 0 && - UnderlyingAddrValue.getResultNumber() == 1); + assert(isa(BoxValue) && + BoxValue.getResultNumber() == 0); SILParameterInfo CPInfo = CalleePInfo[Index]; assert(CPInfo.getSILType() == BoxValue.getType() && @@ -900,10 +910,10 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices, // Load and copy from the address value, passing the result as an argument // to the new closure. + auto AddrValue = cast(BoxValue)->getAddressResult(); auto &typeLowering = M.getTypeLowering(AddrValue.getType()); Args.push_back( typeLowering.emitLoadOfCopy(B, PAI->getLoc(), AddrValue, IsNotTake)); - ++OpNo; ++NumCapturesPromoted; } else { Args.push_back(PAI->getOperand(OpNo)); @@ -942,7 +952,7 @@ constructMapFromPartialApplyToPromoteableIndices(SILFunction *F, IndexMap.clear(); if (examineAllocBoxInst(ABI, RS, IndexMap)) { // If we are able to promote at least one capture of the alloc_box, - // then add the promotable indices to the main map. + // then add the promotable index to the main map. for (auto &IndexPair : IndexMap) Map[IndexPair.first].insert(IndexPair.second); } diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index a1ce4a0c8dd51..36efbf6ed64e9 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -191,8 +191,8 @@ _TTSf0gs___TFVs11_StringCore15_invariantCheckfT_T_ ---> function signature speci _TTSf2g___TTSf2s_d___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore _TTSf2dg___TTSf2s_d___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore _TTSf2dgs___TTSf2s_d___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore -_TTSf3d_i_d_i_d_i___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore -_TTSf3d_i_n_i_d_i___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore +_TTSf3d_i_d_i_d_i___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore +_TTSf3d_i_n_i_d_i___TFVs11_StringCoreCfVs13_StringBufferS_ ---> function signature specialization of Swift._StringCore.init (Swift._StringBuffer) -> Swift._StringCore _TFIZvV8mangling10HasVarInit5stateSbiu_KT_Sb ---> static mangling.HasVarInit.(state : Swift.Bool).(variable initialization expression).(implicit closure #1) _TFFV23interface_type_mangling18GenericTypeContext23closureInGenericContexturFqd__T_L_3fooFTQd__Q__T_ ---> interface_type_mangling.GenericTypeContext.(closureInGenericContext (A1) -> ()).(foo #1) (A1, A) -> () _TFFV23interface_type_mangling18GenericTypeContextg31closureInGenericPropertyContextxL_3fooFT_Q_ ---> interface_type_mangling.GenericTypeContext.(closureInGenericPropertyContext.getter : A).(foo #1) () -> A diff --git a/test/SILPasses/capture_promotion.sil b/test/SILPasses/capture_promotion.sil index e0e0d9a3b5c8e..db993bc939301 100644 --- a/test/SILPasses/capture_promotion.sil +++ b/test/SILPasses/capture_promotion.sil @@ -49,7 +49,7 @@ bb0: store %15 to %11#1 : $*Int // CHECK-NOT: function_ref @closure0 : -// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2d_i_d_i_d_i__closure0 +// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2i_i_i__closure0 // CHECK-NOT: function_ref @closure0 : // The three strong retains are removed @@ -70,11 +70,11 @@ bb0: // previously used to capture and pass the variable by reference // CHECK-NEXT: {{.*}} = partial_apply [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]]) - %17 = function_ref @closure0 : $@convention(thin) (@owned @box Foo, @inout Foo, @owned @box Baz, @inout Baz, @owned @box Int, @inout Int) -> Int + %17 = function_ref @closure0 : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int strong_retain %1#0 : $@box Foo strong_retain %6#0 : $@box Baz strong_retain %11#0 : $@box Int - %21 = partial_apply %17(%1#0, %1#1, %6#0, %6#1, %11#0, %11#1) : $@convention(thin) (@owned @box Foo, @inout Foo, @owned @box Baz, @inout Baz, @owned @box Int, @inout Int) -> Int + %21 = partial_apply %17(%1#0, %6#0, %11#0) : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int strong_release %11#0 : $@box Int strong_release %6#0 : $@box Baz @@ -82,7 +82,7 @@ bb0: return %21 : $@callee_owned () -> Int } -// CHECK-LABEL: sil private @_TTSf2d_i_d_i_d_i__closure0 : $@convention(thin) (@owned Foo, @owned Baz, Int) -> Int +// CHECK-LABEL: sil private @_TTSf2i_i_i__closure0 : $@convention(thin) (@owned Foo, @owned Baz, Int) -> Int // CHECK: [[DUMMY_FUNC:%.*]] = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int // The load of %1 is removed, and its uses replaced with the Foo argument @@ -107,8 +107,11 @@ bb0: // CHECK-NEXT: return [[RETVAL]] : $Int -sil private @closure0 : $@convention(thin) (@owned @box Foo, @inout Foo, @owned @box Baz, @inout Baz, @owned @box Int, @inout Int) -> Int { -bb0(%0 : $@box Foo, %1 : $*Foo, %2 : $@box Baz, %3 : $*Baz, %4 : $@box Int, %5 : $*Int): +sil private @closure0 : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int { +bb0(%0 : $@box Foo, %2 : $@box Baz, %4 : $@box Int): + %1 = project_box %0 : $@box Foo + %3 = project_box %2 : $@box Baz + %5 = project_box %4 : $@box Int %6 = tuple () // function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int %7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int @@ -137,18 +140,19 @@ bb0: %3 = metatype $@thick Foo.Type %4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo store %4 to %1#1 : $*Foo - %17 = function_ref @closure1 : $@convention(thin) (@box Foo, @inout Foo) -> Int + %17 = function_ref @closure1 : $@convention(thin) (@box Foo) -> Int strong_retain %1#0 : $@box Foo - // CHECK: partial_apply {{%.*}}({{%.*}}#0, {{%.*}}#1) - %21 = partial_apply %17(%1#0, %1#1) : $@convention(thin) (@box Foo, @inout Foo) -> Int + // CHECK: partial_apply {{%.*}}({{%.*}}#0) + %21 = partial_apply %17(%1#0) : $@convention(thin) (@box Foo) -> Int strong_release %1#0 : $@box Foo return %21 : $@callee_owned () -> Int } sil @mutate_foo : $@convention(thin) (@inout Foo) -> () -sil private @closure1 : $@convention(thin) (@box Foo, @inout Foo) -> Int { -bb0(%0 : $@box Foo, %1 : $*Foo): +sil private @closure1 : $@convention(thin) (@box Foo) -> Int { +bb0(%0 : $@box Foo): + %1 = project_box %0 : $@box Foo %6 = tuple () // function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int %7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int @@ -173,12 +177,12 @@ bb0(%0 : $*Int, %1 : $*Int): %4 = alloc_box $Int copy_addr %1 to [initialization] %4#1 : $*Int %6 = function_ref @apply : $@convention(thin) (@owned @callee_owned () -> ()) -> () - // CHECK: [[PROMOTED:%[0-9a-zA-Z]+]] = function_ref @_TTSf2n_n_d_i__closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned @box Int, @inout Int, Int) -> () - %7 = function_ref @closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned @box Int, @inout Int, @owned @box Int, @inout Int) -> () + // CHECK: [[PROMOTED:%[0-9a-zA-Z]+]] = function_ref @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned @box Int, Int) -> () + %7 = function_ref @closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned @box Int, @owned @box Int) -> () strong_retain %4#0 : $@box Int strong_retain %2#0 : $@box Int // CHECK: partial_apply [[PROMOTED]]<{{[^>]+}}>( - %10 = partial_apply %7(%4#0, %4#1, %2#0, %2#1) : $@convention(thin) <τ_0_0> (@owned @box Int, @inout Int, @owned @box Int, @inout Int) -> () + %10 = partial_apply %7(%4#0, %2#0) : $@convention(thin) <τ_0_0> (@owned @box Int, @owned @box Int) -> () %11 = apply %6(%10) : $@convention(thin) (@owned @callee_owned () -> ()) -> () copy_addr %4#1 to %1 : $*Int strong_release %4#0 : $@box Int @@ -188,9 +192,11 @@ bb0(%0 : $*Int, %1 : $*Int): return %16 : $() } -// CHECK: sil @_TTSf2n_n_d_i__closureWithGenericSignature : $@convention(thin) <{{[^>]+}}> (@owned @box Int, @inout Int, Int) -> () -sil @closureWithGenericSignature : $@convention(thin) (@owned @box Int, @inout Int, @owned @box Int, @inout Int) -> () { -bb0(%0 : $@box Int, %1 : $*Int, %2 : $@box Int, %3 : $*Int): +// CHECK: sil @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <{{[^>]+}}> (@owned @box Int, Int) -> () +sil @closureWithGenericSignature : $@convention(thin) (@owned @box Int, @owned @box Int) -> () { +bb0(%0 : $@box Int, %2 : $@box Int): + %1 = project_box %0 : $@box Int + %3 = project_box %2 : $@box Int %4 = function_ref @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int %5 = load %3 : $*Int %6 = load %3 : $*Int diff --git a/test/SILPasses/capture_promotion.swift b/test/SILPasses/capture_promotion.swift index 2437bb280a29d..6fd1949dfa183 100644 --- a/test/SILPasses/capture_promotion.swift +++ b/test/SILPasses/capture_promotion.swift @@ -1,7 +1,5 @@ // RUN: %target-swift-frontend %s -emit-sil -o - -verify | FileCheck %s -// XFAIL: * - class Foo { func foo() -> Int { return 1 @@ -24,11 +22,11 @@ func test_capture_promotion() -> () -> Int { // CHECK-NOT: alloc_box -// CHECK: [[CLOSURE0_PROMOTE0:%.*]] = function_ref @_TTSf2d_i_d_i_d_i___TFF17capture_promotion22test_capture_promotionFT_FT_SiU_FT_Si +// CHECK: [[CLOSURE0_PROMOTE0:%.*]] = function_ref @_TTSf2i_i_i___TFF17capture_promotion22test_capture_promotionFT_FT_SiU_FT_Si // CHECK: partial_apply [[CLOSURE0_PROMOTE0]]({{%[0-9]*}}, {{%[0-9]*}}, {{%[0-9]*}}) return { x + y.foo() + z.x } } -// CHECK: sil shared @_TTSf2d_i_d_i_d_i___TFF17capture_promotion22test_capture_promotionFT_FT_SiU_FT_Si : $@convention(thin) (Int, @owned Foo, @owned Baz) -> Int +// CHECK: sil shared @_TTSf2i_i_i___TFF17capture_promotion22test_capture_promotionFT_FT_SiU_FT_Si : $@convention(thin) (Int, @owned Foo, @owned Baz) -> Int diff --git a/test/SILPasses/capture_promotion_reachability.sil b/test/SILPasses/capture_promotion_reachability.sil index 095aec3daf2a6..df48ea33bb8d5 100644 --- a/test/SILPasses/capture_promotion_reachability.sil +++ b/test/SILPasses/capture_promotion_reachability.sil @@ -43,21 +43,21 @@ bb1: bb2: %14 = alloc_box $@callee_owned () -> Builtin.Int64 - // CHECK: [[CLOSURE0_PROMOTE0:%.*]] = function_ref @_TTSf2d_i__closure0 : - %15 = function_ref @closure0 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: [[CLOSURE0_PROMOTE0:%.*]] = function_ref @_TTSf2i__closure0 : + %15 = function_ref @closure0 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 // CHECK: partial_apply [[CLOSURE0_PROMOTE0]]({{%.*}}) - %17 = partial_apply %15(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %17 = partial_apply %15(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 store %17 to %14#1 : $*@callee_owned () -> Builtin.Int64 %19 = load %3#1 : $*Builtin.Int1 cond_br %19, bb3, bb4 bb3: - // CHECK: [[CLOSURE1_PROMOTE0:%.*]] = function_ref @_TTSf2d_i__closure1 : - %21 = function_ref @closure1 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: [[CLOSURE1_PROMOTE0:%.*]] = function_ref @_TTSf2i__closure1 : + %21 = function_ref @closure1 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 // CHECK: partial_apply [[CLOSURE1_PROMOTE0]]({{%.*}}) - %23 = partial_apply %21(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %23 = partial_apply %21(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 assign %23 to %14#1 : $*@callee_owned () -> Builtin.Int64 br bb4 @@ -71,8 +71,9 @@ bb4: } // closure0 -sil private @closure0 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure0 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -80,8 +81,9 @@ bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): } // closure1 -sil private @closure1 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure1 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -111,10 +113,10 @@ bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int64, %2 : $Builtin.Int64): store %2 to %5#1 : $*Builtin.Int64 %9 = alloc_box $@callee_owned () -> Builtin.Int64 // CHECK: [[CLOSURE2:%.*]] = function_ref @closure2 : - %10 = function_ref @closure2 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %10 = function_ref @closure2 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 - // CHECK: partial_apply [[CLOSURE2]]({{%.*}}, {{%.*}}) - %12 = partial_apply %10(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: partial_apply [[CLOSURE2]]({{%.*}}) + %12 = partial_apply %10(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 store %12 to %9#1 : $*@callee_owned () -> Builtin.Int64 %14 = load %3#1 : $*Builtin.Int1 cond_br %14, bb1, bb2 @@ -129,11 +131,11 @@ bb2: cond_br %19, bb3, bb4 bb3: - // CHECK: [[CLOSURE3_PROMOTE0:%.*]] = function_ref @_TTSf2d_i__closure3 : - %21 = function_ref @closure3 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: [[CLOSURE3_PROMOTE0:%.*]] = function_ref @_TTSf2i__closure3 : + %21 = function_ref @closure3 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 // CHECK: partial_apply [[CLOSURE3_PROMOTE0]]({{%.*}}) - %23 = partial_apply %21(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %23 = partial_apply %21(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 assign %23 to %9#1 : $*@callee_owned () -> Builtin.Int64 br bb4 @@ -147,8 +149,9 @@ bb4: } // closure2 -sil private @closure2 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure2 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -156,8 +159,9 @@ bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): } // closure3 -sil private @closure3 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure3 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -187,20 +191,20 @@ bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int64, %2 : $Builtin.Int64): store %2 to %5#1 : $*Builtin.Int64 %9 = alloc_box $@callee_owned () -> Builtin.Int64 // CHECK: [[CLOSURE4:%.*]] = function_ref @closure4 : - %10 = function_ref @closure4 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %10 = function_ref @closure4 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 - // CHECK: partial_apply [[CLOSURE4]]({{%.*}}, {{%.*}}) - %12 = partial_apply %10(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: partial_apply [[CLOSURE4]]({{%.*}}) + %12 = partial_apply %10(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 store %12 to %9#1 : $*@callee_owned () -> Builtin.Int64 %14 = load %3#1 : $*Builtin.Int1 cond_br %14, bb1, bb2 bb1: // CHECK: [[CLOSURE5:%.*]] = function_ref @closure5 : - %16 = function_ref @closure5 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %16 = function_ref @closure5 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 - // CHECK: partial_apply [[CLOSURE5]]({{%.*}}, {{%.*}}) - %18 = partial_apply %16(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: partial_apply [[CLOSURE5]]({{%.*}}) + %18 = partial_apply %16(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 assign %18 to %9#1 : $*@callee_owned () -> Builtin.Int64 br bb2 @@ -223,8 +227,9 @@ bb4: } // closure4 -sil private @closure4 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure4 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -232,8 +237,9 @@ bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): } // closure5 -sil private @closure5 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure5 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -261,10 +267,10 @@ bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int64, %2 : $Builtin.Int64): store %2 to %5#1 : $*Builtin.Int64 %9 = alloc_box $@callee_owned () -> Builtin.Int64 // CHECK: [[CLOSURE6:%.*]] = function_ref @closure6 : - %10 = function_ref @closure6 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %10 = function_ref @closure6 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 - // CHECK: partial_apply [[CLOSURE6]]({{%.*}}, {{%.*}}) - %12 = partial_apply %10(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: partial_apply [[CLOSURE6]]({{%.*}}) + %12 = partial_apply %10(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 store %12 to %9#1 : $*@callee_owned () -> Builtin.Int64 br bb1 @@ -276,10 +282,10 @@ bb2: %17 = load %5#1 : $*Builtin.Int64 assign %17 to %4#1 : $*Builtin.Int64 // CHECK: [[CLOSURE7:%.*]] = function_ref @closure7 : - %19 = function_ref @closure7 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + %19 = function_ref @closure7 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 strong_retain %4#0 : $@box Builtin.Int64 - // CHECK: partial_apply [[CLOSURE7]]({{%.*}}, {{%.*}}) - %21 = partial_apply %19(%4#0, %4#1) : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 + // CHECK: partial_apply [[CLOSURE7]]({{%.*}}) + %21 = partial_apply %19(%4#0) : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 assign %21 to %9#1 : $*@callee_owned () -> Builtin.Int64 br bb1 @@ -293,8 +299,9 @@ bb3: } // closure6 -sil private @closure6 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure6 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 @@ -302,8 +309,9 @@ bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): } // closure7 -sil private @closure7 : $@convention(thin) (@owned @box Builtin.Int64, @inout Builtin.Int64) -> Builtin.Int64 { -bb0(%0 : $@box Builtin.Int64, %1 : $*Builtin.Int64): +sil private @closure7 : $@convention(thin) (@owned @box Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $@box Builtin.Int64): + %1 = project_box %0 : $@box Builtin.Int64 %2 = tuple () %3 = load %1 : $*Builtin.Int64 strong_release %0 : $@box Builtin.Int64 From 513780d1dd790117e9a3ca4a10f6a3e84d5562ed Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 24 Nov 2015 10:24:44 -0800 Subject: [PATCH 4/8] SILGen: Emit mark_function_escape when capturing variable boxes. There's no longer a direct use of the variable's address when we invoke the closure, but we have a handy mark_function_escape instruction to mark the use without requiring merging analysis of the box and its contents. This also gives us a slightly more accurate error message when a variable is prematurely captured, noting the variable was captured by a closure instead of just generically used. --- include/swift/AST/DiagnosticsSIL.def | 4 ++-- lib/SILGen/SILGenFunction.cpp | 11 +++++++++++ lib/SILPasses/EarlySIL/DefiniteInitialization.cpp | 12 ++++++++---- lib/SILPasses/IPO/CapturePromotion.cpp | 7 ++++++- test/SILGen/properties.swift | 9 +++++---- test/SILPasses/definite_init_diagnostics.swift | 4 +--- 6 files changed, 33 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index c795e28c20691..a75690c391399 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -97,7 +97,7 @@ ERROR(variable_used_before_initialized,sil_analysis,none, ERROR(variable_inout_before_initialized,sil_analysis,none, "%select{variable|constant}1 '%0' passed by reference before being" " initialized", (StringRef, bool)) -ERROR(variable_escape_before_initialized,sil_analysis,none, +ERROR(variable_closure_use_uninit,sil_analysis,none, "%select{variable|constant}1 '%0' captured by a closure before being" " initialized", (StringRef, bool)) @@ -145,7 +145,7 @@ ERROR(return_from_init_without_initing_stored_properties,sil_analysis,none, "return from initializer without initializing all" " stored properties", ()) -ERROR(global_variable_function_use_uninit,sil_analysis,none, +ERROR(variable_function_use_uninit,sil_analysis,none, "%select{variable|constant}1 '%0' used by function definition before" " being initialized", (StringRef, bool)) diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 3999acaa8db63..b512eb966655f 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -223,6 +223,10 @@ void SILGenFunction::emitCaptures(SILLocation loc, AnyFunctionRef TheClosure, SmallVectorImpl &capturedArgs) { auto captureInfo = SGM.Types.getLoweredLocalCaptures(TheClosure); + // For boxed captures, we need to mark the contained variables as having + // escaped for DI diagnostics. + SmallVector escapesToMark; + for (auto capture : captureInfo.getCaptures()) { auto *vd = capture.getDecl(); @@ -285,6 +289,7 @@ void SILGenFunction::emitCaptures(SILLocation loc, if (vl.box) { B.createStrongRetain(loc, vl.box); capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box)); + escapesToMark.push_back(vl.value); } else { // Address only 'let' values are passed by box. This isn't great, in // that a variable captured by multiple closures will be boxed for each @@ -302,6 +307,12 @@ void SILGenFunction::emitCaptures(SILLocation loc, } } } + + // Mark box addresses as captured for DI purposes. The values must have + // been fully initialized before we close over them. + if (!escapesToMark.empty()) { + B.createMarkFunctionEscape(loc, escapesToMark); + } } ManagedValue diff --git a/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp b/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp index d67143af19118..b072c6c5b27e5 100644 --- a/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp +++ b/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp @@ -1089,10 +1089,14 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { } Diag DiagMessage; - if (isa(Inst)) - DiagMessage = diag::global_variable_function_use_uninit; - else - DiagMessage = diag::variable_escape_before_initialized; + if (isa(Inst)) { + if (Inst->getLoc().isASTNode()) + DiagMessage = diag::variable_closure_use_uninit; + else + DiagMessage = diag::variable_function_use_uninit; + } else { + DiagMessage = diag::variable_closure_use_uninit; + } diagnoseInitError(Use, DiagMessage); } diff --git a/lib/SILPasses/IPO/CapturePromotion.cpp b/lib/SILPasses/IPO/CapturePromotion.cpp index 12918fd9927af..464b20056fb8e 100644 --- a/lib/SILPasses/IPO/CapturePromotion.cpp +++ b/lib/SILPasses/IPO/CapturePromotion.cpp @@ -632,7 +632,8 @@ isNonmutatingCapture(SILArgument *BoxArg) { return false; continue; } - if (!isa(O->getUser())) + if (!isa(O->getUser()) && + !isa(O->getUser())) return false; } } @@ -647,6 +648,10 @@ isNonmutatingCapture(SILArgument *BoxArg) { static bool isNonescapingUse(Operand *O, SmallVectorImpl &Mutations) { auto *U = O->getUser(); + // Marking the boxed value as escaping is OK. It's just a DI annotation. + if (isa(U)) + return true; + // A store or assign is ok if the alloc_box is the destination. if (isa(U) || isa(U)) { if (O->getOperandNumber() != 1) diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 756ed623e704c..ed9ddbc1ba36a 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -674,12 +674,13 @@ func propertyWithDidSetTakingOldValue() { // CHECK-NEXT: debug_value %4 : $Int // CHECK-NEXT: assign %0 to %3 : $*Int // CHECK-NEXT: strong_retain %1 : $@box Int +// CHECK-NEXT: mark_function_escape %3 // CHECK-NEXT: // function_ref -// CHECK-NEXT: %8 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int) -> () -// CHECK-NEXT: %9 = apply %8(%4, %1) : $@convention(thin) (Int, @owned @box Int) -> () +// CHECK-NEXT: %9 = function_ref @_TFF10properties32propertyWithDidSetTakingOldValueFT_T_WL_1pSi : $@convention(thin) (Int, @owned @box Int) -> () +// CHECK-NEXT: %10 = apply %9(%4, %1) : $@convention(thin) (Int, @owned @box Int) -> () // CHECK-NEXT: strong_release %1 : $@box Int -// CHECK-NEXT: %11 = tuple () -// CHECK-NEXT: return %11 : $() +// CHECK-NEXT: %12 = tuple () +// CHECK-NEXT: return %12 : $() // CHECK-NEXT:} diff --git a/test/SILPasses/definite_init_diagnostics.swift b/test/SILPasses/definite_init_diagnostics.swift index 90193e0e8984c..be1766639bd28 100644 --- a/test/SILPasses/definite_init_diagnostics.swift +++ b/test/SILPasses/definite_init_diagnostics.swift @@ -3,8 +3,6 @@ // FIXME: rdar://problem/19648117 Needs splitting objc parts out // XFAIL: linux -// XFAIL: * - import Swift import gizmo @@ -59,7 +57,7 @@ func test2() { // expected-warning @+1 {{variable 'b1' was never mutated}} {{3-6=let}} var b1 : Int // expected-note {{variable defined here}} - takes_closure { // expected-error {{variable 'b1' used before being initialized}} + takes_closure { // expected-error {{variable 'b1' captured by a closure before being initialized}} markUsed(b1) } From d201b133db5dd359ae3365ee6869c06ad0a80fba Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 25 Nov 2015 12:41:45 -0800 Subject: [PATCH 5/8] SIL: Introduce a new @inout_aliasable parameter convention. Modeling nonescaping captures as @inout parameters is wrong, because captures are allowed to share state, unlike 'inout' parameters, which are allowed to assume to some degree that there are no aliases during the parameter's scope. To model this, introduce a new @inout_aliasable parameter convention to indicate an indirect parameter that can be written to, not only by the current function, but by well-typed, well-synchronized aliasing accesses too. (This is unrelated to our discussions of adding a "type-unsafe-aliasable" annotation to pointer_to_address to allow for safe pointer punning.) --- include/swift/AST/Attr.def | 1 + include/swift/AST/Types.h | 29 +++++++++----- include/swift/Serialization/ModuleFormat.h | 5 ++- lib/AST/ASTPrinter.cpp | 2 + lib/AST/Mangle.cpp | 2 + lib/IRGen/GenClangType.cpp | 7 ++-- lib/IRGen/GenFunc.cpp | 26 +++++++++---- lib/IRGen/GenObjC.cpp | 1 + lib/IRGen/GenProto.cpp | 1 + lib/SIL/AbstractionPattern.cpp | 4 +- lib/SIL/SILFunctionType.cpp | 4 ++ lib/SIL/SILModule.cpp | 2 +- lib/SIL/TypeLowering.cpp | 14 +++---- lib/SIL/Verifier.cpp | 9 +++-- lib/SILGen/SILGenApply.cpp | 7 ++-- lib/SILGen/SILGenBridging.cpp | 7 +++- lib/SILGen/SILGenLValue.cpp | 2 +- lib/SILGen/SILGenPoly.cpp | 5 +++ lib/SILGen/SILGenProlog.cpp | 1 + .../EarlySIL/DIMemoryUseCollector.cpp | 3 +- .../EarlySIL/DefiniteInitialization.cpp | 9 ++++- lib/SILPasses/IPO/CapturePromotion.cpp | 13 +++++-- .../SILCombiner/SILCombinerApplyVisitors.cpp | 3 +- lib/SILPasses/Scalar/AllocBoxToStack.cpp | 2 +- lib/SILPasses/Scalar/CopyForwarding.cpp | 2 + lib/SILPasses/Utils/Local.cpp | 2 +- lib/Sema/TypeCheckType.cpp | 2 + lib/Serialization/Deserialization.cpp | 1 + lib/Serialization/Serialization.cpp | 1 + test/IRGen/objc_block_storage.sil | 38 +++++++++---------- test/SIL/Parser/basic.sil | 8 ++-- test/SILGen/closures.swift | 16 ++++---- test/SILGen/generic_closures.swift | 4 +- test/SILGen/objc_thunks.swift | 2 +- test/SILGen/statements.swift | 2 +- .../SILPasses/definite_init_diagnostics.swift | 6 +++ test/Serialization/Inputs/def_basic.sil | 8 ++-- 37 files changed, 162 insertions(+), 89 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 9f74e9e5fa195..3cf4e8803722a 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -45,6 +45,7 @@ TYPE_ATTR(error) TYPE_ATTR(out) TYPE_ATTR(in) TYPE_ATTR(inout) +TYPE_ATTR(inout_aliasable) TYPE_ATTR(in_guaranteed) TYPE_ATTR(noescape) // Only valid in sil mode. TYPE_ATTR(owned) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 77f77ba0293e9..c502532ad90e9 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2507,8 +2507,9 @@ enum class ParameterConvention { /// This argument is passed indirectly, i.e. by directly passing the address /// of an object in memory. The object is allowed to be aliased by other - /// well-typed references. - //TODO: Indirect_Aliased, + /// well-typed references, but is not allowed to be escaped. This is the + /// convention used by mutable captures in @noescape closures. + Indirect_InoutAliasable, /// This argument is passed indirectly, i.e. by directly passing the address /// of an uninitialized object in memory. The callee is responsible for @@ -2538,6 +2539,7 @@ inline bool isIndirectParameter(ParameterConvention conv) { switch (conv) { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In_Guaranteed: return true; @@ -2557,6 +2559,7 @@ inline bool isConsumedParameter(ParameterConvention conv) { return true; case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Direct_Unowned: case ParameterConvention::Direct_Guaranteed: @@ -2577,6 +2580,7 @@ inline bool isGuaranteedParameter(ParameterConvention conv) { return true; case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In: case ParameterConvention::Direct_Unowned: @@ -2594,6 +2598,7 @@ inline bool isDeallocatingParameter(ParameterConvention conv) { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Direct_Unowned: @@ -2606,19 +2611,20 @@ inline bool isDeallocatingParameter(ParameterConvention conv) { /// A parameter type and the rules for passing it. class SILParameterInfo { - llvm::PointerIntPair TypeAndConvention; + CanType Ty; + ParameterConvention Convention; public: - SILParameterInfo() = default; + SILParameterInfo() : Ty(), Convention((ParameterConvention)0) {} SILParameterInfo(CanType type, ParameterConvention conv) - : TypeAndConvention(type, conv) { + : Ty(type), Convention(conv) { assert(type->isLegalSILType() && "SILParameterInfo has illegal SIL type"); } CanType getType() const { - return TypeAndConvention.getPointer(); + return Ty; } ParameterConvention getConvention() const { - return TypeAndConvention.getInt(); + return Convention; } bool isIndirect() const { return isIndirectParameter(getConvention()); @@ -2631,6 +2637,10 @@ class SILParameterInfo { bool isIndirectInOut() const { return getConvention() == ParameterConvention::Indirect_Inout; } + bool isIndirectMutating() const { + return getConvention() == ParameterConvention::Indirect_Inout + || getConvention() == ParameterConvention::Indirect_InoutAliasable; + } bool isIndirectResult() const { return getConvention() == ParameterConvention::Indirect_Out; } @@ -2682,7 +2692,8 @@ class SILParameterInfo { } void profile(llvm::FoldingSetNodeID &id) { - id.AddPointer(TypeAndConvention.getOpaqueValue()); + id.AddPointer(Ty.getPointer()); + id.AddInteger((unsigned)Convention); } void dump() const; @@ -2696,7 +2707,7 @@ class SILParameterInfo { } bool operator==(SILParameterInfo rhs) const { - return TypeAndConvention == rhs.TypeAndConvention; + return Ty == rhs.Ty && Convention == rhs.Convention; } bool operator!=(SILParameterInfo rhs) const { return !(*this == rhs); diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 1a5abb62bd3a9..69e91a4c81d29 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -51,7 +51,7 @@ const uint16_t VERSION_MAJOR = 0; /// To ensure that two separate changes don't silently get merged into one /// in source control, you should also update the comment to briefly /// describe what change you made. -const uint16_t VERSION_MINOR = 222; // Last change: @_fixed_layout +const uint16_t VERSION_MINOR = 223; // Last change: SIL @inout_aliasable using DeclID = Fixnum<31>; using DeclIDField = BCFixed<31>; @@ -179,13 +179,14 @@ enum class ParameterConvention : uint8_t { Indirect_In, Indirect_Out, Indirect_Inout, + Indirect_InoutAliasable, Direct_Owned, Direct_Unowned, Direct_Guaranteed, Indirect_In_Guaranteed, Direct_Deallocating, }; -using ParameterConventionField = BCFixed<3>; +using ParameterConventionField = BCFixed<4>; // These IDs must \em not be renumbered or reordered without incrementing // VERSION_MAJOR. diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 91287a3e481aa..1b0ae277336cb 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2968,6 +2968,7 @@ class TypePrinter : public TypeVisitor { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_In_Guaranteed: llvm_unreachable("callee convention cannot be indirect"); } @@ -3218,6 +3219,7 @@ static StringRef getStringForParameterConvention(ParameterConvention conv) { case ParameterConvention::Indirect_Out: return "@out "; case ParameterConvention::Indirect_In_Guaranteed: return "@in_guaranteed "; case ParameterConvention::Indirect_Inout: return "@inout "; + case ParameterConvention::Indirect_InoutAliasable: return "@inout_aliasable "; case ParameterConvention::Direct_Owned: return "@owned "; case ParameterConvention::Direct_Unowned: return ""; case ParameterConvention::Direct_Guaranteed: return "@guaranteed "; diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 4db65d6b25f88..8be2d575bf11a 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -1108,6 +1108,7 @@ void Mangler::mangleType(Type type, ResilienceExpansion explosion, // ::= 'e' // direct, deallocating // ::= 'i' // indirect, ownership transfer // ::= 'l' // indirect, inout + // ::= 'L' // indirect, inout, aliasable // ::= 'g' // direct, guaranteed // ::= 'G' // indirect, guaranteed // ::= 'z' // error result @@ -1129,6 +1130,7 @@ void Mangler::mangleType(Type type, ResilienceExpansion explosion, case ParameterConvention::Indirect_In: return 'i'; case ParameterConvention::Indirect_Out: return 'i'; case ParameterConvention::Indirect_Inout: return 'l'; + case ParameterConvention::Indirect_InoutAliasable: return 'L'; case ParameterConvention::Indirect_In_Guaranteed: return 'G'; case ParameterConvention::Direct_Owned: return 'o'; case ParameterConvention::Direct_Unowned: return 'd'; diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index c7f67847abdb3..9d4277a144d3e 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -547,6 +547,7 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { llvm_unreachable("block takes owned parameter"); case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In_Guaranteed: llvm_unreachable("block takes indirect parameter"); @@ -720,9 +721,9 @@ clang::CanQualType IRGenModule::getClangType(SILType type) { clang::CanQualType IRGenModule::getClangType(SILParameterInfo params) { auto clangType = getClangType(params.getSILType()); - // @block_storage types must be wrapped in an @inout and have special - // lowering - if (params.isIndirectInOut() && + // @block_storage types must be @inout_aliasable and have + // special lowering + if (params.isIndirectMutating() && !params.getSILType().is()) { return getClangASTContext().getPointerType(clangType); } diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index 2859fabc84e24..b36518f37d436 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -176,11 +176,12 @@ static void addIndirectValueParameterAttributes(IRGenModule &IGM, static void addInoutParameterAttributes(IRGenModule &IGM, llvm::AttributeSet &attrs, const TypeInfo &ti, - unsigned argIndex) { + unsigned argIndex, + bool aliasable) { llvm::AttrBuilder b; // Aliasing inouts is unspecified, but we still want aliasing to be memory- // safe, so we can't mark inouts as noalias at the LLVM level. - // They can't be captured without doing unsafe stuff, though. + // They still can't be captured without doing unsafe stuff, though. b.addAttribute(llvm::Attribute::NoCapture); // The inout must reference dereferenceable memory of the type. addDereferenceableAttributeToBuilder(IGM, b, ti); @@ -1316,7 +1317,7 @@ llvm::Type *SignatureExpansion::expandExternalSignatureTypes() { void SignatureExpansion::expand(SILParameterInfo param) { auto &ti = IGM.getTypeInfo(param.getSILType()); - switch (param.getConvention()) { + switch (auto conv = param.getConvention()) { case ParameterConvention::Indirect_Out: assert(ParamIRTypes.empty()); addIndirectReturnAttributes(IGM, Attrs); @@ -1331,7 +1332,9 @@ void SignatureExpansion::expand(SILParameterInfo param) { return; case ParameterConvention::Indirect_Inout: - addInoutParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size()); + case ParameterConvention::Indirect_InoutAliasable: + addInoutParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size(), + conv == ParameterConvention::Indirect_InoutAliasable); addPointerParameter(IGM.getStorageType(param.getSILType())); return; @@ -2879,9 +2882,9 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee, case clang::CodeGen::ABIArgInfo::Direct: { auto toTy = AI.getCoerceToType(); - // inout parameters are bridged as Clang pointer types. For now, this + // Mutating parameters are bridged as Clang pointer types. For now, this // only ever comes up with Clang-generated accessors. - if (params[i - firstParam].isIndirectInOut()) { + if (params[i - firstParam].isIndirectMutating()) { assert(paramType.isAddress() && "SIL type is not an address?"); auto addr = in.claimNext(); @@ -3372,6 +3375,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, case ParameterConvention::Direct_Deallocating: llvm_unreachable("callables do not have destructors"); case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: @@ -3441,6 +3445,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, case ParameterConvention::Direct_Deallocating: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: llvm_unreachable("should never happen!"); } @@ -3506,7 +3511,8 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM, dependsOnContextLifetime = true; break; case ParameterConvention::Indirect_Inout: - // Load the add ress of the inout parameter. + case ParameterConvention::Indirect_InoutAliasable: + // Load the address of the inout parameter. cast(fieldTI).loadAsCopy(subIGF, fieldAddr, param); break; case ParameterConvention::Direct_Guaranteed: @@ -3727,6 +3733,7 @@ void irgen::emitFunctionPartialApplication(IRGenFunction &IGF, // Capture inout parameters by pointer. case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: argLoweringTy = argType.getSwiftType(); break; @@ -3834,7 +3841,9 @@ void irgen::emitFunctionPartialApplication(IRGenFunction &IGF, // still need to build a thunk, but we don't need to allocate anything. if ((hasSingleSwiftRefcountedContext == Yes || hasSingleSwiftRefcountedContext == Thunkable) && - *singleRefcountedConvention != ParameterConvention::Indirect_Inout) { + *singleRefcountedConvention != ParameterConvention::Indirect_Inout && + *singleRefcountedConvention != + ParameterConvention::Indirect_InoutAliasable) { assert(bindings.empty()); assert(args.size() == 1); @@ -3905,6 +3914,7 @@ void irgen::emitFunctionPartialApplication(IRGenFunction &IGF, case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Direct_Deallocating: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: cast(fieldLayout.getType()) .initialize(IGF, args, fieldAddr); break; diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 6f2b1f283290c..39253de50dbf4 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -823,6 +823,7 @@ static llvm::Function *emitObjCPartialApplicationForwarder(IRGenModule &IGM, case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: llvm_unreachable("self passed indirectly?!"); } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 8e50ed0230edd..54bb8afd1362f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2317,6 +2317,7 @@ namespace { case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: if (!isSelfParameter) return; if (type->getNominalOrBoundGenericNominal()) { considerNewTypeSource(SourceKind::GenericLValueMetadata, diff --git a/lib/SIL/AbstractionPattern.cpp b/lib/SIL/AbstractionPattern.cpp index c2914d7dd1361..fa01c67ea23d9 100644 --- a/lib/SIL/AbstractionPattern.cpp +++ b/lib/SIL/AbstractionPattern.cpp @@ -463,10 +463,10 @@ AbstractionPattern AbstractionPattern::getLValueObjectType() const { return *this; case Kind::Type: return AbstractionPattern(getGenericSignature(), - cast(getType()).getObjectType()); + getType().getLValueOrInOutObjectType()); case Kind::ClangType: return AbstractionPattern(getGenericSignature(), - cast(getType()).getObjectType(), + getType().getLValueOrInOutObjectType(), getClangType()); } llvm_unreachable("bad kind"); diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 1cdd51a4eea0f..114936b30524d 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -359,6 +359,9 @@ enum class ConventionsKind : uint8_t { if (isa(substType)) { assert(origType.isOpaque() || origType.getAs()); convention = ParameterConvention::Indirect_Inout; + } else if (isa(substType)) { + assert(origType.isOpaque() || origType.getAs()); + convention = ParameterConvention::Indirect_InoutAliasable; } else if (isPassedIndirectly(origType, substType, substTL)) { convention = Convs.getIndirectParameter(origParamIndex, origType); assert(isIndirectParameter(convention)); @@ -1625,6 +1628,7 @@ namespace { case ParameterConvention::Direct_Deallocating: case ParameterConvention::Direct_Unowned: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: return orig; diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index ab0a535ed18ec..ae35a08f0f4a5 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -306,7 +306,7 @@ static bool verifySILSelfParameterType(SILDeclRef DeclRef, // Otherwise, if this function type has a guaranteed self parameter type, // make sure that we have a +0 self param. return !FTy->getExtInfo().hasGuaranteedSelfParam() || - PInfo.isGuaranteed() || PInfo.isIndirectInOut(); + PInfo.isGuaranteed() || PInfo.isIndirectMutating(); } SILFunction *SILModule::getOrCreateFunction(SILLocation loc, diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index cb3f8380ae2e8..8176b38a6cba0 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -1456,11 +1456,11 @@ TypeConverter::getTypeLowering(AbstractionPattern origType, assert(uncurryLevel == 0); - // inout types are a special case for lowering, because they get + // inout and lvalue types are a special case for lowering, because they get // completely removed and represented as 'address' SILTypes. - if (auto substInOutType = dyn_cast(substType)) { + if (isa(substType) || isa(substType)) { // Derive SILType for InOutType from the object type. - CanType substObjectType = substInOutType.getObjectType(); + CanType substObjectType = substType.getLValueOrInOutObjectType(); AbstractionPattern origObjectType = origType.getLValueObjectType(); SILType loweredType = getLoweredType(origObjectType, substObjectType, @@ -1944,7 +1944,7 @@ TypeConverter::getFunctionTypeWithCaptures(CanAnyFunctionType funcType, case CaptureKind::StorageAddress: // No-escape stored decls are captured by their raw address. - inputFields.push_back(TupleTypeElt(CanInOutType::get(captureType))); + inputFields.push_back(TupleTypeElt(CanLValueType::get(captureType))); break; case CaptureKind::Constant: @@ -2019,9 +2019,9 @@ TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType, case CaptureKind::StorageAddress: // No-escape stored decls are captured by their raw address. - // FIXME: 'inout' is semantically incorrect for a capture, since - // it is allowed to alias captured references. - inputFields.push_back(TupleTypeElt(CanInOutType::get(captureType))); + // Unlike 'inout', captures are allowed to have well-typed, synchronized + // aliasing references, so capture the raw lvalue type instead. + inputFields.push_back(TupleTypeElt(CanLValueType::get(captureType))); break; case CaptureKind::Constant: diff --git a/lib/SIL/Verifier.cpp b/lib/SIL/Verifier.cpp index 5c6847119733e..095b93a37f2d1 100644 --- a/lib/SIL/Verifier.cpp +++ b/lib/SIL/Verifier.cpp @@ -2665,8 +2665,10 @@ class SILVerifier : public SILVerifierBase { require(invokeTy->getParameters().size() >= 1, "invoke function must take at least one parameter"); auto storageParam = invokeTy->getParameters()[0]; - require(storageParam.getConvention() == ParameterConvention::Indirect_Inout, - "invoke function must take block storage as @inout parameter"); + require(storageParam.getConvention() == + ParameterConvention::Indirect_InoutAliasable, + "invoke function must take block storage as @inout_aliasable " + "parameter"); require(storageParam.getType() == storageTy, "invoke function must take block storage type as first parameter"); @@ -2772,12 +2774,13 @@ class SILVerifier : public SILVerifierBase { return false; case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Indirect_In_Guaranteed: return true; } }), - "entry point address argument must have a nonaliasing calling " + "entry point address argument must have an indirect calling " "convention"); } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 0ae22a041f513..271c426bcacfa 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -729,7 +729,7 @@ static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc, assert(address.getType().is()); auto formalTy = address.getType().getSwiftRValueType(); - if (getSelfParameter().isIndirectInOut()) { + if (getSelfParameter().isIndirectMutating()) { // Be sure not to consume the cleanup for an inout argument. auto selfLV = ManagedValue::forLValue(address.getValue()); selfValue = ArgumentSource(loc, @@ -1964,6 +1964,7 @@ ManagedValue SILGenFunction::emitApply( case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: // We may need to support this at some point, but currently only imported // objc methods are returns_inner_pointer. @@ -3611,6 +3612,7 @@ ArgumentSource SILGenFunction::prepareAccessorBaseArg(SILLocation loc, // If the accessor wants the value 'inout', always pass the // address we were given. This is semantically required. case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: return false; // If the accessor wants the value 'in', we have to copy if the @@ -3688,13 +3690,12 @@ ArgumentSource SILGenFunction::prepareAccessorBaseArg(SILLocation loc, return (p && !p->requiresClass()); }; #endif - assert((!selfParam.isIndirectInOut() || + assert((!selfParam.isIndirectMutating() || (baseFormalType->isAnyClassReferenceType() && isNonClassProtocolMember(accessor.getDecl()))) && "passing unmaterialized r-value as inout argument"); base = emitMaterializeIntoTemporary(*this, loc, base); - if (selfParam.isIndirectInOut()) { // Drop the cleanup if we have one. auto baseLV = ManagedValue::forLValue(base.getValue()); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 96d734e0216b7..1e3d4cbd3b572 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -213,6 +213,7 @@ static void buildFuncToBlockInvokeBody(SILGenFunction &gen, case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: llvm_unreachable("indirect params to blocks not supported"); } @@ -241,6 +242,7 @@ static void buildFuncToBlockInvokeBody(SILGenFunction &gen, case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: llvm_unreachable("indirect arguments to blocks not supported"); } @@ -297,8 +299,8 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, // Build the invoke function type. SmallVector params; - params.push_back( - SILParameterInfo(storageTy, ParameterConvention::Indirect_Inout)); + params.push_back(SILParameterInfo(storageTy, + ParameterConvention::Indirect_InoutAliasable)); std::copy(blockTy->getParameters().begin(), blockTy->getParameters().end(), std::back_inserter(params)); @@ -1080,6 +1082,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) { param = ManagedValue::forUnmanaged(paramValue); break; case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: param = ManagedValue::forUnmanaged(paramValue); break; case ParameterConvention::Indirect_In: diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index c943a15df66ef..a735380d2ce14 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -724,7 +724,7 @@ namespace { AccessKind kind) const override { SILDeclRef accessor = getAccessor(gen, kind); auto accessorType = gen.SGM.Types.getConstantFunctionType(accessor); - if (accessorType->getSelfParameter().isIndirectInOut()) { + if (accessorType->getSelfParameter().isIndirectMutating()) { return AccessKind::ReadWrite; } else { return AccessKind::Read; diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index a25e568ebd47b..b53eaa6317504 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -640,6 +640,7 @@ static ManagedValue manageParam(SILGenFunction &gen, return gen.emitManagedBufferWithCleanup(copy); } case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: return ManagedValue::forLValue(paramValue); case ParameterConvention::Indirect_In: return gen.emitManagedBufferWithCleanup(paramValue); @@ -1124,6 +1125,10 @@ namespace { Outputs.push_back(temp->getManagedAddress()); return; } + case ParameterConvention::Indirect_InoutAliasable: { + llvm_unreachable("abstraction difference in aliasable argument not " + "allowed"); + } } llvm_unreachable("Covered switch isn't covered?!"); diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index 9bdcd66a59971..aa02e12e424d7 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -98,6 +98,7 @@ class EmitBBArguments : public CanTypeVisitor(Inst) && Inst->getLoc().isASTNode()) + diagnoseInitError(Use, diag::variable_closure_use_uninit); + else + diagnoseInitError(Use, diag::variable_used_before_initialized); } /// handleSuperInitUse - When processing a 'self' argument on a class, this is diff --git a/lib/SILPasses/IPO/CapturePromotion.cpp b/lib/SILPasses/IPO/CapturePromotion.cpp index 464b20056fb8e..7e034f650f11e 100644 --- a/lib/SILPasses/IPO/CapturePromotion.cpp +++ b/lib/SILPasses/IPO/CapturePromotion.cpp @@ -915,10 +915,17 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices, // Load and copy from the address value, passing the result as an argument // to the new closure. - auto AddrValue = cast(BoxValue)->getAddressResult(); - auto &typeLowering = M.getTypeLowering(AddrValue.getType()); + SILValue Addr = cast(BoxValue)->getAddressResult(); + // If the address is marked uninitialized, load through the mark, so that + // DI can reason about it. + if (Addr.hasOneUse()) + if (auto MUI = dyn_cast( + Addr.use_begin()->getUser())) + Addr = SILValue(MUI); + + auto &typeLowering = M.getTypeLowering(Addr.getType()); Args.push_back( - typeLowering.emitLoadOfCopy(B, PAI->getLoc(), AddrValue, IsNotTake)); + typeLowering.emitLoadOfCopy(B, PAI->getLoc(), Addr, IsNotTake)); ++NumCapturesPromoted; } else { Args.push_back(PAI->getOperand(OpNo)); diff --git a/lib/SILPasses/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILPasses/SILCombiner/SILCombinerApplyVisitors.cpp index 0c235beeb8eee..9e13a82b628e7 100644 --- a/lib/SILPasses/SILCombiner/SILCombinerApplyVisitors.cpp +++ b/lib/SILPasses/SILCombiner/SILCombinerApplyVisitors.cpp @@ -202,7 +202,7 @@ void PartialApplyCombiner::allocateTemporaries() { for (unsigned AI = 0, AE = Args.size(); AI != AE; ++AI) { SILValue Arg = Args[AI]; SILParameterInfo Param = Params[AI + Delta]; - if (Param.isIndirectInOut()) + if (Param.isIndirectMutating()) continue; // Create a temporary and copy the argument into it, if: // - the argument stems from an alloc_stack @@ -539,6 +539,7 @@ void SILCombiner::eraseApply(FullApplySite FAS, const UserListTy &Users) { break; case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_Out: case ParameterConvention::Direct_Unowned: case ParameterConvention::Direct_Deallocating: diff --git a/lib/SILPasses/Scalar/AllocBoxToStack.cpp b/lib/SILPasses/Scalar/AllocBoxToStack.cpp index 7cbb459633202..959aed4ec72b6 100644 --- a/lib/SILPasses/Scalar/AllocBoxToStack.cpp +++ b/lib/SILPasses/Scalar/AllocBoxToStack.cpp @@ -418,7 +418,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI, for (auto UI : ASI->getAddressResult().getUses()) if (auto *MUI = dyn_cast(UI->getUser())) { assert(ASI->getAddressResult().hasOneUse() && - "alloc_stack used by mark_uninialized, but not exclusively!"); + "alloc_stack used by mark_uninitialized, but not exclusively!"); PointerResult = MUI; break; } diff --git a/lib/SILPasses/Scalar/CopyForwarding.cpp b/lib/SILPasses/Scalar/CopyForwarding.cpp index 74afd3e2e8631..d095e4b031200 100644 --- a/lib/SILPasses/Scalar/CopyForwarding.cpp +++ b/lib/SILPasses/Scalar/CopyForwarding.cpp @@ -183,6 +183,7 @@ class AnalyzeForwardUse return true; case ParameterConvention::Indirect_In_Guaranteed: case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: return false; case ParameterConvention::Indirect_Out: llvm_unreachable("copy_addr not released before reinitialization"); @@ -278,6 +279,7 @@ class AnalyzeBackwardUse case ParameterConvention::Indirect_Out: return true; case ParameterConvention::Indirect_Inout: + case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_In_Guaranteed: return false; case ParameterConvention::Indirect_In: diff --git a/lib/SILPasses/Utils/Local.cpp b/lib/SILPasses/Utils/Local.cpp index c818a6cbc77be..ae1f3d6fa620f 100644 --- a/lib/SILPasses/Utils/Local.cpp +++ b/lib/SILPasses/Utils/Local.cpp @@ -899,7 +899,7 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &Builder, SILLocation Loc, // not need to destroy it here. This is something that is implicit in the // partial_apply design that will be revisited when partial_apply is // redesigned. - if (PInfo.isIndirectInOut()) + if (PInfo.isIndirectMutating()) return; if (isa(Arg)) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 436a98916fe6b..d4a96aec19430 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1936,6 +1936,8 @@ SILParameterInfo TypeResolver::resolveSILParameter( checkFor(TypeAttrKind::TAK_in, ParameterConvention::Indirect_In); checkFor(TypeAttrKind::TAK_out, ParameterConvention::Indirect_Out); checkFor(TypeAttrKind::TAK_inout, ParameterConvention::Indirect_Inout); + checkFor(TypeAttrKind::TAK_inout_aliasable, + ParameterConvention::Indirect_InoutAliasable); checkFor(TypeAttrKind::TAK_owned, ParameterConvention::Direct_Owned); checkFor(TypeAttrKind::TAK_guaranteed, ParameterConvention::Direct_Guaranteed); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 62b719464de90..8a208c54c7634 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3152,6 +3152,7 @@ Optional getActualParameterConvention(uint8_t raw) { CASE(Indirect_In) CASE(Indirect_Out) CASE(Indirect_Inout) + CASE(Indirect_InoutAliasable) CASE(Indirect_In_Guaranteed) CASE(Direct_Owned) CASE(Direct_Unowned) diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 09d09dc5ee37c..864d9c6e091b0 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2572,6 +2572,7 @@ static uint8_t getRawStableParameterConvention(swift::ParameterConvention pc) { SIMPLE_CASE(ParameterConvention, Indirect_In_Guaranteed) SIMPLE_CASE(ParameterConvention, Indirect_Out) SIMPLE_CASE(ParameterConvention, Indirect_Inout) + SIMPLE_CASE(ParameterConvention, Indirect_InoutAliasable) SIMPLE_CASE(ParameterConvention, Direct_Owned) SIMPLE_CASE(ParameterConvention, Direct_Unowned) SIMPLE_CASE(ParameterConvention, Direct_Guaranteed) diff --git a/test/IRGen/objc_block_storage.sil b/test/IRGen/objc_block_storage.sil index 6430d6f469a3a..085d86320df2b 100644 --- a/test/IRGen/objc_block_storage.sil +++ b/test/IRGen/objc_block_storage.sil @@ -42,7 +42,7 @@ import gizmo // CHECK-NEXT: %2 = load i8*, i8** %1, align 8 // CHECK-NEXT: ret i8* %2 // CHECK-NEXT: } -sil @project_block_storage : $@convention(thin) (@inout @block_storage Builtin.RawPointer) -> Builtin.RawPointer { +sil @project_block_storage : $@convention(thin) (@inout_aliasable @block_storage Builtin.RawPointer) -> Builtin.RawPointer { entry(%0 : $*@block_storage Builtin.RawPointer): %c = project_block_storage %0 : $*@block_storage Builtin.RawPointer %p = load %c : $*Builtin.RawPointer @@ -55,7 +55,7 @@ entry(%0 : $*@block_storage Builtin.RawPointer): // CHECK-NEXT: %2 = load fp128, fp128* %1, align 16 // CHECK-NEXT: ret fp128 %2 // CHECK-NEXT: } -sil @overaligned_project_block_storage : $@convention(thin) (@inout @block_storage Builtin.FPIEEE128) -> Builtin.FPIEEE128 { +sil @overaligned_project_block_storage : $@convention(thin) (@inout_aliasable @block_storage Builtin.FPIEEE128) -> Builtin.FPIEEE128 { entry(%0 : $*@block_storage Builtin.FPIEEE128): %c = project_block_storage %0 : $*@block_storage Builtin.FPIEEE128 %p = load %c : $*Builtin.FPIEEE128 @@ -74,34 +74,34 @@ entry(%0 : $*@block_storage Builtin.FPIEEE128): // CHECK: }, %objc_block* %1 // CHECK: %2 = bitcast {{.*}} %0 to %objc_block* // CHECK: ret %objc_block* %2 -sil @init_block_header_trivial : $@convention(thin) (@inout @block_storage Builtin.RawPointer) -> @convention(block) () -> () { +sil @init_block_header_trivial : $@convention(thin) (@inout_aliasable @block_storage Builtin.RawPointer) -> @convention(block) () -> () { entry(%0 : $*@block_storage Builtin.RawPointer): - %i = function_ref @invoke_trivial : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> () - %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> (), type $@convention(block) () -> () + %i = function_ref @invoke_trivial : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> () + %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> (), type $@convention(block) () -> () return %b : $@convention(block) () -> () } // CHECK-LABEL: define void @invoke_trivial(void (...)*) {{.*}} { // CHECK: %1 = bitcast void (...)* %0 to { %objc_block, i8* }* // CHECK: %2 = getelementptr inbounds { %objc_block, i8* }, { %objc_block, i8* }* %1, i32 0, i32 1 -sil @invoke_trivial : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> () { +sil @invoke_trivial : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> () { entry(%0 : $*@block_storage Builtin.RawPointer): %c = project_block_storage %0 : $*@block_storage Builtin.RawPointer return undef : $() } -sil @init_block_header_trivial_block_param : $@convention(thin) (@inout @block_storage Builtin.RawPointer) -> @convention(block) (@convention(block) (Int) -> Int) -> () { +sil @init_block_header_trivial_block_param : $@convention(thin) (@inout_aliasable @block_storage Builtin.RawPointer) -> @convention(block) (@convention(block) (Int) -> Int) -> () { entry(%0 : $*@block_storage Builtin.RawPointer): - %i = function_ref @invoke_trivial_block_param : $@convention(c) (@inout @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> () - %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> (), type $@convention(block) (@convention(block) (Int) -> Int) -> () + %i = function_ref @invoke_trivial_block_param : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> () + %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> (), type $@convention(block) (@convention(block) (Int) -> Int) -> () return %b : $@convention(block) (@convention(block) (Int) -> Int) -> () } -sil @invoke_trivial_block_param : $@convention(c) (@inout @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> () +sil @invoke_trivial_block_param : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer, @convention(block) (Int) -> Int) -> () // CHECK-LABEL: define i64 @invoke_trivial_with_arg(void (...)*, i64) {{.*}} { // CHECK: ret i64 %1 -sil @invoke_trivial_with_arg : $@convention(c) (@inout @block_storage Builtin.RawPointer, Int) -> Int { +sil @invoke_trivial_with_arg : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer, Int) -> Int { entry(%0 : $*@block_storage Builtin.RawPointer, %1 : $Int): return %1 : $Int } @@ -115,10 +115,10 @@ entry(%0 : $*@block_storage Builtin.RawPointer, %1 : $Int): // CHECK: i8* {{.*}} @invoke_nontrivial // CHECK: i8* bitcast {{.*}} [[NONTRIVIAL_BLOCK_DESCRIPTOR]] // CHECK: }, %objc_block* %1 -sil @init_block_header_nontrivial : $@convention(thin) (@inout @block_storage Builtin.NativeObject) -> @convention(block) () -> () { +sil @init_block_header_nontrivial : $@convention(thin) (@inout_aliasable @block_storage Builtin.NativeObject) -> @convention(block) () -> () { entry(%0 : $*@block_storage Builtin.NativeObject): - %i = function_ref @invoke_nontrivial : $@convention(c) (@inout @block_storage Builtin.NativeObject) -> () - %b = init_block_storage_header %0 : $*@block_storage Builtin.NativeObject, invoke %i : $@convention(c) (@inout @block_storage Builtin.NativeObject) -> (), type $@convention(block) () -> () + %i = function_ref @invoke_nontrivial : $@convention(c) (@inout_aliasable @block_storage Builtin.NativeObject) -> () + %b = init_block_storage_header %0 : $*@block_storage Builtin.NativeObject, invoke %i : $@convention(c) (@inout_aliasable @block_storage Builtin.NativeObject) -> (), type $@convention(block) () -> () return %b : $@convention(block) () -> () } @@ -138,7 +138,7 @@ entry(%0 : $*@block_storage Builtin.NativeObject): // CHECK-NEXT: call void @swift_release(%swift.refcounted* %toDestroy) {{#[0-9]+}} // CHECK-NEXT: ret void -sil public_external @invoke_nontrivial : $@convention(c) (@inout @block_storage Builtin.NativeObject) -> () +sil public_external @invoke_nontrivial : $@convention(c) (@inout_aliasable @block_storage Builtin.NativeObject) -> () // CHECK-LABEL: define %objc_block* @init_block_header_stret({ %objc_block, i8* }* nocapture dereferenceable({{.*}})) {{.*}} { // CHECK: store %objc_block { @@ -149,11 +149,11 @@ sil public_external @invoke_nontrivial : $@convention(c) (@inout @block_storage // CHECK: i8* {{.*}} @invoke_stret // CHECK: i8* bitcast {{.*}} [[STRET_BLOCK_DESCRIPTOR]] // CHECK: }, %objc_block* %1 -sil @init_block_header_stret : $@convention(thin) (@inout @block_storage Builtin.RawPointer) -> @convention(block) () -> NSRect { +sil @init_block_header_stret : $@convention(thin) (@inout_aliasable @block_storage Builtin.RawPointer) -> @convention(block) () -> NSRect { entry(%0 : $*@block_storage Builtin.RawPointer): - %i = function_ref @invoke_stret : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> NSRect - %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> NSRect, type $@convention(block) () -> NSRect + %i = function_ref @invoke_stret : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> NSRect + %b = init_block_storage_header %0 : $*@block_storage Builtin.RawPointer, invoke %i : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> NSRect, type $@convention(block) () -> NSRect return %b : $@convention(block) () -> NSRect } -sil public_external @invoke_stret : $@convention(c) (@inout @block_storage Builtin.RawPointer) -> NSRect +sil public_external @invoke_stret : $@convention(c) (@inout_aliasable @block_storage Builtin.RawPointer) -> NSRect diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index f9a1cc07ed958..d2ed921d1a1fb 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1160,16 +1160,16 @@ entry(%0 : $Int): // CHECK: store %0 to [[PROJECT]] store %0 to %c : $*Int // CHECK: [[FUNC:%.*]] = function_ref - %f = function_ref @block_invoke : $@convention(c) (@inout @block_storage Int) -> () - // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORAGE]]#1 : $*@block_storage Int, invoke [[FUNC]] : $@convention(c) (@inout @block_storage Int) -> (), type $@convention(block) () -> () - %b = init_block_storage_header %s#1 : $*@block_storage Int, invoke %f : $@convention(c) (@inout @block_storage Int) -> (), type $@convention(block) () -> () + %f = function_ref @block_invoke : $@convention(c) (@inout_aliasable @block_storage Int) -> () + // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORAGE]]#1 : $*@block_storage Int, invoke [[FUNC]] : $@convention(c) (@inout_aliasable @block_storage Int) -> (), type $@convention(block) () -> () + %b = init_block_storage_header %s#1 : $*@block_storage Int, invoke %f : $@convention(c) (@inout_aliasable @block_storage Int) -> (), type $@convention(block) () -> () // CHECK: dealloc_stack [[STORAGE]]#0 : $*@local_storage @block_storage Int dealloc_stack %s#0 : $*@local_storage @block_storage Int // CHECK: return [[BLOCK]] : $@convention(block) () -> () return %b : $@convention(block) () -> () } -sil @block_invoke : $@convention(c) (@inout @block_storage Int) -> () { +sil @block_invoke : $@convention(c) (@inout_aliasable @block_storage Int) -> () { entry(%0 : $*@block_storage Int): return undef : $() } diff --git a/test/SILGen/closures.swift b/test/SILGen/closures.swift index de08f1c8e0c74..056e5c3da785f 100644 --- a/test/SILGen/closures.swift +++ b/test/SILGen/closures.swift @@ -117,7 +117,7 @@ func anon_read_only_capture(x: Int) -> Int { return ({ x })() // -- func expression - // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures22anon_read_only_capture.*]] : $@convention(thin) (@inout Int) -> Int + // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures22anon_read_only_capture.*]] : $@convention(thin) (@inout_aliasable Int) -> Int // -- apply expression // CHECK: [[RET:%[0-9]+]] = apply [[ANON]]([[XBOX]]#1) // -- cleanup @@ -137,7 +137,7 @@ func small_closure_capture(x: Int) -> Int { return { x }() // -- func expression - // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures21small_closure_capture.*]] : $@convention(thin) (@inout Int) -> Int + // CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures21small_closure_capture.*]] : $@convention(thin) (@inout_aliasable Int) -> Int // -- apply expression // CHECK: [[RET:%[0-9]+]] = apply [[ANON]]([[XBOX]]#1) // -- cleanup @@ -209,8 +209,8 @@ class SomeClass { class SomeGenericClass { deinit { var i: Int = zero - // CHECK: [[C1REF:%[0-9]+]] = function_ref @_TFFC8closures16SomeGenericClassdU_FT_Si : $@convention(thin) (@inout Int) -> Int - // CHECK: apply [[C1REF]]([[IBOX:%[0-9]+]]#1) : $@convention(thin) (@inout Int) -> Int + // CHECK: [[C1REF:%[0-9]+]] = function_ref @_TFFC8closures16SomeGenericClassdU_FT_Si : $@convention(thin) (@inout_aliasable Int) -> Int + // CHECK: apply [[C1REF]]([[IBOX:%[0-9]+]]#1) : $@convention(thin) (@inout_aliasable Int) -> Int var x = { i + zero } () // CHECK: [[C2REF:%[0-9]+]] = function_ref @_TFFC8closures16SomeGenericClassdU0_FT_Si : $@convention(thin) () -> Int @@ -222,7 +222,7 @@ class SomeGenericClass { var z = { _ = T.self } () } - // CHECK-LABEL: sil shared @_TFFC8closures16SomeGenericClassdU_FT_Si : $@convention(thin) (@inout Int) -> Int + // CHECK-LABEL: sil shared @_TFFC8closures16SomeGenericClassdU_FT_Si : $@convention(thin) (@inout_aliasable Int) -> Int // CHECK-LABEL: sil shared @_TFFC8closures16SomeGenericClassdU0_FT_Si : $@convention(thin) () -> Int @@ -332,11 +332,11 @@ struct StructWithMutatingMethod { // CHECK: bb0(%0 : $*StructWithMutatingMethod): // CHECK-NEXT: %1 = alloc_box $StructWithMutatingMethod // var self // users: %2, %5, %7, %8 // CHECK-NEXT: copy_addr %0 to [initialization] %1#1 : $*StructWithMutatingMethod // id: %2 -// CHECK: [[CLOSURE:%[0-9]+]] = function_ref @_TFFV8closures24StructWithMutatingMethod14mutatingMethod{{.*}} : $@convention(thin) (@inout StructWithMutatingMethod) -> Int -// CHECK: partial_apply [[CLOSURE]](%1#1) : $@convention(thin) (@inout StructWithMutatingMethod) -> Int +// CHECK: [[CLOSURE:%[0-9]+]] = function_ref @_TFFV8closures24StructWithMutatingMethod14mutatingMethod{{.*}} : $@convention(thin) (@inout_aliasable StructWithMutatingMethod) -> Int +// CHECK: partial_apply [[CLOSURE]](%1#1) : $@convention(thin) (@inout_aliasable StructWithMutatingMethod) -> Int // Check that the closure body only takes the pointer. -// CHECK-LABEL: sil shared @_TFFV8closures24StructWithMutatingMethod14mutatingMethod{{.*}} : $@convention(thin) (@inout StructWithMutatingMethod) -> Int { +// CHECK-LABEL: sil shared @_TFFV8closures24StructWithMutatingMethod14mutatingMethod{{.*}} : $@convention(thin) (@inout_aliasable StructWithMutatingMethod) -> Int { // CHECK: bb0(%0 : $*StructWithMutatingMethod): class SuperBase { diff --git a/test/SILGen/generic_closures.swift b/test/SILGen/generic_closures.swift index 2b714f03ece55..1eff96f42dd3c 100644 --- a/test/SILGen/generic_closures.swift +++ b/test/SILGen/generic_closures.swift @@ -95,9 +95,9 @@ var f: (Int) -> () = generic_curried_function(zero) // CHECK: sil hidden @_TF16generic_closures25nested_closure_in_generic{{.*}} : $@convention(thin) (@out T, @in T) -> () // CHECK: function_ref [[OUTER_CLOSURE:@_TFF16generic_closures25nested_closure_in_genericurFxxU_FT_Q_]] - // CHECK: sil shared [[OUTER_CLOSURE]] : $@convention(thin) (@out T, @inout T) -> () + // CHECK: sil shared [[OUTER_CLOSURE]] : $@convention(thin) (@out T, @inout_aliasable T) -> () // CHECK: function_ref [[INNER_CLOSURE:@_TFFF16generic_closures25nested_closure_in_genericurFxxU_FT_Q_U_FT_Q_]] - // CHECK: sil shared [[INNER_CLOSURE]] : $@convention(thin) (@out T, @inout T) -> () { + // CHECK: sil shared [[INNER_CLOSURE]] : $@convention(thin) (@out T, @inout_aliasable T) -> () { func nested_closure_in_generic(x:T) -> T { return { { x }() }() } diff --git a/test/SILGen/objc_thunks.swift b/test/SILGen/objc_thunks.swift index 0b8bb7003417e..f24a56b7d1a8d 100644 --- a/test/SILGen/objc_thunks.swift +++ b/test/SILGen/objc_thunks.swift @@ -401,7 +401,7 @@ func registerAnsible() { // FIXME: would be nice if we didn't need to re-abstract as much here. -// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFo_oGSQFT_T___dT__XFdCb_dGSQbT_T___dT__ : $@convention(c) (@inout @block_storage @callee_owned (@owned ImplicitlyUnwrappedOptional<() -> ()>) -> (), ImplicitlyUnwrappedOptional<@convention(block) () -> ()>) -> () +// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFo_oGSQFT_T___dT__XFdCb_dGSQbT_T___dT__ : $@convention(c) (@inout_aliasable @block_storage @callee_owned (@owned ImplicitlyUnwrappedOptional<() -> ()>) -> (), ImplicitlyUnwrappedOptional<@convention(block) () -> ()>) -> () // CHECK: [[HEAP_BLOCK_IUO:%.*]] = copy_block %1 // CHECK: select_enum [[HEAP_BLOCK_IUO]] // CHECK: bb1: diff --git a/test/SILGen/statements.swift b/test/SILGen/statements.swift index 4cfdc52c65bc8..4886350ecb07a 100644 --- a/test/SILGen/statements.swift +++ b/test/SILGen/statements.swift @@ -515,7 +515,7 @@ func defer_mutable(x: Int) { var x = x // CHECK: [[BOX:%.*]] = alloc_box $Int // CHECK-NOT: [[BOX]]#0 - // CHECK: function_ref @_TFF10statements13defer_mutableFSiT_L_6$deferfT_T_ : $@convention(thin) (@inout Int) -> () + // CHECK: function_ref @_TFF10statements13defer_mutableFSiT_L_6$deferfT_T_ : $@convention(thin) (@inout_aliasable Int) -> () // CHECK-NOT: [[BOX]]#0 // CHECK: strong_release [[BOX]]#0 defer { _ = x } diff --git a/test/SILPasses/definite_init_diagnostics.swift b/test/SILPasses/definite_init_diagnostics.swift index be1766639bd28..e619272cd28c6 100644 --- a/test/SILPasses/definite_init_diagnostics.swift +++ b/test/SILPasses/definite_init_diagnostics.swift @@ -61,6 +61,12 @@ func test2() { markUsed(b1) } + var b1a : Int // expected-note {{variable defined here}} + takes_closure { // expected-error {{variable 'b1a' captured by a closure before being initialized}} + b1a += 1 + markUsed(b1a) + } + var b2 = 4 takes_closure { // ok. markUsed(b2) diff --git a/test/Serialization/Inputs/def_basic.sil b/test/Serialization/Inputs/def_basic.sil index a4c25374f10c6..e8f2fedbf088a 100644 --- a/test/Serialization/Inputs/def_basic.sil +++ b/test/Serialization/Inputs/def_basic.sil @@ -961,9 +961,9 @@ entry(%0 : $Int): // CHECK: store %0 to [[PROJECT]] store %0 to %c : $*Int // CHECK: [[FUNC:%.*]] = function_ref - %f = function_ref @block_invoke : $@convention(c) (@inout @block_storage Int) -> () - // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORAGE]]#1 : $*@block_storage Int, invoke [[FUNC]] : $@convention(c) (@inout @block_storage Int) -> (), type $@convention(block) () -> () - %b = init_block_storage_header %s#1 : $*@block_storage Int, invoke %f : $@convention(c) (@inout @block_storage Int) -> (), type $@convention(block) () -> () + %f = function_ref @block_invoke : $@convention(c) (@inout_aliasable @block_storage Int) -> () + // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORAGE]]#1 : $*@block_storage Int, invoke [[FUNC]] : $@convention(c) (@inout_aliasable @block_storage Int) -> (), type $@convention(block) () -> () + %b = init_block_storage_header %s#1 : $*@block_storage Int, invoke %f : $@convention(c) (@inout_aliasable @block_storage Int) -> (), type $@convention(block) () -> () // CHECK: dealloc_stack [[STORAGE]]#0 : $*@local_storage @block_storage Int dealloc_stack %s#0 : $*@local_storage @block_storage Int // CHECK: return [[BLOCK]] : $@convention(block) () -> () @@ -1007,7 +1007,7 @@ entry: return %p : $Protocol } -sil [fragile] @block_invoke : $@convention(c) (@inout @block_storage Int) -> () +sil [fragile] @block_invoke : $@convention(c) (@inout_aliasable @block_storage Int) -> () // Test try_apply and throws From 01ba1eb54746451d0518140be0a1c717efdf72ca Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 25 Nov 2015 13:31:22 -0800 Subject: [PATCH 6/8] SILCombine: Add a project_box (alloc_box#0) -> alloc_box#1 peephole. This unblocks existing load/store optimizations when a closure is inlined and the original boxed value is exposed. --- lib/SILAnalysis/SimplifyInstruction.cpp | 11 +++++++++++ test/SILPasses/sil_combine.sil | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/SILAnalysis/SimplifyInstruction.cpp b/lib/SILAnalysis/SimplifyInstruction.cpp index c9add68356041..f5587baa2d597 100644 --- a/lib/SILAnalysis/SimplifyInstruction.cpp +++ b/lib/SILAnalysis/SimplifyInstruction.cpp @@ -29,6 +29,7 @@ namespace { public: SILValue visitSILInstruction(SILInstruction *I) { return SILValue(); } + SILValue visitProjectBoxInst(ProjectBoxInst *PBI); SILValue visitTupleExtractInst(TupleExtractInst *TEI); SILValue visitStructExtractInst(StructExtractInst *SEI); SILValue visitEnumInst(EnumInst *EI); @@ -129,6 +130,16 @@ SILValue InstSimplifier::visitTupleInst(TupleInst *TI) { return SILValue(); } +SILValue InstSimplifier::visitProjectBoxInst(ProjectBoxInst *PBI) { + // project_box(alloc_box#0) -> alloc_box#1 + if (auto TheBox = dyn_cast(PBI->getOperand())) { + assert(PBI->getOperand().getResultNumber() == 0 + && "should only be able to project box result of alloc_box"); + return TheBox->getAddressResult(); + } + return SILValue(); +} + SILValue InstSimplifier::visitTupleExtractInst(TupleExtractInst *TEI) { // tuple_extract(tuple(x, y), 0) -> x if (TupleInst *TheTuple = dyn_cast(TEI->getOperand())) diff --git a/test/SILPasses/sil_combine.sil b/test/SILPasses/sil_combine.sil index 581a455739ee0..b11558693a579 100644 --- a/test/SILPasses/sil_combine.sil +++ b/test/SILPasses/sil_combine.sil @@ -2992,3 +2992,16 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): return %90 : $() } +// CHECK-LABEL: sil @project_alloc_box +sil @project_alloc_box : $@convention(thin) () -> Builtin.Int32 { +entry: + // CHECK: [[BOX:%.*]] = alloc_box + %b = alloc_box $Builtin.Int32 + %0 = integer_literal $Builtin.Int32, 0 + store %0 to %b#1 : $*Builtin.Int32 + %p = project_box %b#0 : $@box Builtin.Int32 + // CHECK: [[RET:%.*]] = load [[BOX]]#1 + // CHECK: return [[RET]] + %r = load %p : $*Builtin.Int32 + return %r : $Builtin.Int32 +} From 05214faaa4212aaec1b32eb942e5d4697545c4d3 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 30 Nov 2015 11:50:33 -0800 Subject: [PATCH 7/8] SIL.rst: Describe `@inout_aliasable` arg semantics and clarify `@inout`. --- docs/SIL.rst | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/SIL.rst b/docs/SIL.rst index b181489a3f816..5be2f82fb7b45 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -449,8 +449,24 @@ number of ways: the value held there. - An ``@inout`` parameter is indirect. The address must be of an - initialized object, and the function must leave an initialized - object there upon exit. + initialized object. The memory must remain initialized for the duration + of the call until the function returns. The function may mutate the + pointee, and furthermore may weakly assume that there are no aliasing + reads from or writes to the argument, though must preserve a valid + value at the argument so that well-ordered aliasing violations do not + compromise memory safety. This allows for optimizations such as local + load and store propagation, introduction or elimination of temporary + copies, and promotion of the ``@inout`` parameter to an ``@owned`` direct + parameter and result pair, but does not admit "take" optimization out + of the parameter or other optimization that would leave memory in an + uninitialized state. + + - An ``@inout_aliasable`` parameter is indirect. The address must be of an + initialized object. The memory must remain initialized for the duration + of the call until the function returns. The function may mutate the + pointee, and must assume that other aliases may mutate it as well. These + aliases however can be assumed to be well-typed and well-ordered; ill-typed + accesses and data races to the parameter are still undefined. - An ``@out`` parameter is indirect. The address must be of an uninitialized object; the function is responsible for initializing From d74ffa8f88a723a2c3922c2f292d09c8edc3f0b6 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 30 Nov 2015 14:57:47 -0800 Subject: [PATCH 8/8] Update computeNewArgInterfaceTypes doc comment. --- lib/SILPasses/IPO/CapturePromotion.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/SILPasses/IPO/CapturePromotion.cpp b/lib/SILPasses/IPO/CapturePromotion.cpp index 7e034f650f11e..f02fe66d35d12 100644 --- a/lib/SILPasses/IPO/CapturePromotion.cpp +++ b/lib/SILPasses/IPO/CapturePromotion.cpp @@ -319,23 +319,11 @@ ClosureCloner::ClosureCloner(SILFunction *Orig, StringRef ClonedName, /// Compute the SILParameterInfo list for the new cloned closure. /// -/// SILGen always closes over boxes such that the container address is -/// first. Thus we know that: -/// -/// 1. By assumption, all indices that is a box container value is in -/// PromotableIndices. -/// 2. All box address values must have the box container value previous to -/// it implying that PromotableIndices.count(ParamIndex - 1) will be true. -/// 3. The first parameter can *never* be a box address value since there -/// does not exist any previous box container that is able to be -/// associated with it. -/// /// Our goal as a result of this transformation is to: /// /// 1. Let through all arguments not related to a promotable box. -/// 2. Do not add any container box value arguments to the cloned closure. -/// 3. Add the address box value argument to the cloned closure with the -/// appropriate transformations. +/// 2. Replace container box value arguments for the cloned closure with the +/// transformed address or value argument. static void computeNewArgInterfaceTypes(SILFunction *F, IndicesSet &PromotableIndices,