Skip to content

Commit 597bea3

Browse files
committed
SILGen: Peephole Optional-to-Optional conversions where the input value is 'none'
Fixes <rdar://problem/38545956>.
1 parent d118654 commit 597bea3

File tree

3 files changed

+121
-7
lines changed

3 files changed

+121
-7
lines changed

lib/SILGen/SILGenConvert.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,23 @@ SILGenFunction::emitOptionalToOptional(SILLocation loc,
365365
SILType resultTy,
366366
ValueTransformRef transformValue,
367367
SGFContext C) {
368+
auto &Ctx = getASTContext();
369+
370+
// If the input is known to be 'none' just emit a 'none' value of the right
371+
// result type right away.
372+
auto &resultTL = getTypeLowering(resultTy);
373+
374+
if (auto *EI = dyn_cast<EnumInst>(input.getValue())) {
375+
if (EI->getElement() == Ctx.getOptionalNoneDecl()) {
376+
if (!(resultTL.isAddressOnly() && silConv.useLoweredAddresses())) {
377+
SILValue none = B.createEnum(loc, SILValue(), EI->getElement(),
378+
resultTy);
379+
return emitManagedRValueWithCleanup(none);
380+
}
381+
}
382+
}
383+
384+
// Otherwise perform a dispatch.
368385
auto contBB = createBasicBlock();
369386
auto isNotPresentBB = createBasicBlock();
370387
auto isPresentBB = createBasicBlock();
@@ -377,8 +394,7 @@ SILGenFunction::emitOptionalToOptional(SILLocation loc,
377394
assert(noOptResultTy);
378395

379396
// Create a temporary for the output optional.
380-
auto &resultTL = getTypeLowering(resultTy);
381-
397+
//
382398
// If the result is address-only, we need to return something in memory,
383399
// otherwise the result is the BBArgument in the merge point.
384400
// TODO: use the SGFContext passed in.
@@ -399,7 +415,7 @@ SILGenFunction::emitOptionalToOptional(SILLocation loc,
399415
// possible.
400416
if (getTypeLowering(input.getType()).isAddressOnly() &&
401417
silConv.useLoweredAddresses()) {
402-
auto *someDecl = B.getASTContext().getOptionalSomeDecl();
418+
auto *someDecl = Ctx.getOptionalSomeDecl();
403419
input = B.createUncheckedTakeEnumDataAddr(
404420
loc, input, someDecl, input.getType().getOptionalObjectType());
405421
}

test/SILGen/nil_literal.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// REQUIRES: plus_one_runtime
2+
3+
// RUN: %target-swift-frontend -emit-silgen -enable-sil-ownership %s | %FileCheck %s
4+
5+
func takesOptionalFunction(_: (() -> ())?) {}
6+
7+
struct CustomNull : ExpressibleByNilLiteral {
8+
init(nilLiteral: ()) {}
9+
}
10+
11+
func takesANull(_: CustomNull) {}
12+
13+
// CHECK-LABEL: sil hidden @$S11nil_literal4testyyF : $@convention(thin) () -> ()
14+
func test() {
15+
// CHECK: [[NIL:%.*]] = enum $Optional<@callee_guaranteed () -> ()>, #Optional.none!enumelt
16+
// CHECK: [[FN:%.*]] = function_ref @$S11nil_literal21takesOptionalFunctionyyyycSgF
17+
// CHECK: apply [[FN]]([[NIL]])
18+
_ = takesOptionalFunction(nil)
19+
20+
// CHECK: [[METATYPE:%.*]] = metatype $@thin CustomNull.Type
21+
// CHECK: [[NIL_FN:%.*]] = function_ref @$S11nil_literal10CustomNullV0A7LiteralACyt_tcfC
22+
// CHECK: [[NIL:%.*]] = apply [[NIL_FN]]([[METATYPE]])
23+
// CHECK: [[FN:%.*]] = function_ref @$S11nil_literal10takesANullyyAA10CustomNullVF
24+
// CHECK: apply [[FN]]([[NIL]])
25+
_ = takesANull(nil)
26+
}

test/SILGen/objc_blocks_bridging.swift

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,84 @@ func bridgeNonnullBlockResult() {
174174
nonnullStringBlockResult { return "test" }
175175
}
176176

177-
// CHECK-LABEL: sil hidden @{{.*}}bridgeNoescapeBlock{{.*}}
178-
func bridgeNoescapeBlock() {
179-
// CHECK: function_ref @$SIg_IyB_TR
177+
// CHECK-LABEL: sil hidden @$S20objc_blocks_bridging19bridgeNoescapeBlock2fnyyyXE_tF
178+
func bridgeNoescapeBlock(fn: () -> ()) {
179+
// CHECK: [[CLOSURE_FN:%.*]] = function_ref @$S20objc_blocks_bridging19bridgeNoescapeBlock2fnyyyXE_tFyyXEfU_
180+
// CHECK: [[CONV_FN:%.*]] = convert_function [[CLOSURE_FN]]
181+
// CHECK: [[THICK_FN:%.*]] = thin_to_thick_function [[CONV_FN]]
182+
// CHECK: [[BLOCK_ALLOC:%.*]] = alloc_stack $@block_storage @noescape @callee_guaranteed () -> ()
183+
// CHECK: [[BLOCK_ADDR:%.*]] = project_block_storage [[BLOCK_ALLOC]]
184+
// CHECK: store [[THICK_FN]] to [trivial] [[BLOCK_ADDR]]
185+
// CHECK: [[THUNK:%.*]] = function_ref @$SIg_IyB_TR : $@convention(c) (@inout_aliasable @block_storage @noescape @callee_guaranteed () -> ()) -> ()
186+
// CHECK: [[BLOCK_STACK:%.*]] = init_block_storage_header [[BLOCK_ALLOC]] : {{.*}}, invoke [[THUNK]] : {{.*}}
187+
188+
// FIXME: We're passing the block as a no-escape -- so we don't have to copy it
189+
// CHECK: [[BLOCK:%.*]] = copy_block [[BLOCK_STACK]]
190+
191+
// CHECK: [[SOME_BLOCK:%.*]] = enum $Optional<@convention(block) @noescape () -> ()>, #Optional.some!enumelt.1, [[BLOCK]]
192+
// CHECK: dealloc_stack [[BLOCK_ALLOC]]
193+
// CHECK: [[FN:%.*]] = function_ref @noescapeBlock : $@convention(c) (Optional<@convention(block) @noescape () -> ()>) -> ()
194+
// CHECK: apply [[FN]]([[SOME_BLOCK]])
195+
noescapeBlock { }
196+
// CHECK: destroy_value [[SOME_BLOCK]]
197+
198+
// CHECK: [[BLOCK_ALLOC:%.*]] = alloc_stack $@block_storage @noescape @callee_guaranteed () -> ()
199+
// CHECK: [[BLOCK_ADDR:%.*]] = project_block_storage [[BLOCK_ALLOC]]
200+
// CHECK: store %0 to [trivial] [[BLOCK_ADDR]]
201+
// CHECK: [[THUNK:%.*]] = function_ref @$SIg_IyB_TR : $@convention(c) (@inout_aliasable @block_storage @noescape @callee_guaranteed () -> ()) -> ()
202+
// CHECK: [[BLOCK_STACK:%.*]] = init_block_storage_header [[BLOCK_ALLOC]] : {{.*}}, invoke [[THUNK]] : {{.*}}
203+
204+
// FIXME: We're passing the block as a no-escape -- so we don't have to copy it
205+
// CHECK: [[BLOCK:%.*]] = copy_block [[BLOCK_STACK]]
206+
207+
// CHECK: [[SOME_BLOCK:%.*]] = enum $Optional<@convention(block) @noescape () -> ()>, #Optional.some!enumelt.1, [[BLOCK]]
208+
// CHECK: dealloc_stack [[BLOCK_ALLOC]]
209+
// CHECK: [[FN:%.*]] = function_ref @noescapeBlock : $@convention(c) (Optional<@convention(block) @noescape () -> ()>) -> ()
210+
// CHECK: apply [[FN]]([[SOME_BLOCK]])
211+
noescapeBlock(fn)
212+
// CHECK: destroy_value [[SOME_BLOCK]]
213+
214+
// CHECK: [[NIL_BLOCK:%.*]] = enum $Optional<@convention(block) @noescape () -> ()>, #Optional.none!enumelt
215+
// CHECK: [[FN:%.*]] = function_ref @noescapeBlock : $@convention(c) (Optional<@convention(block) @noescape () -> ()>) -> ()
216+
// CHECK: apply [[FN]]([[NIL_BLOCK]])
217+
noescapeBlock(nil)
218+
219+
// CHECK: [[CLOSURE_FN:%.*]] = function_ref @$S20objc_blocks_bridging19bridgeNoescapeBlock2fnyyyXE_tFyyXEfU0_
220+
// CHECK: [[CONV_FN:%.*]] = convert_function [[CLOSURE_FN]]
221+
// CHECK: [[THICK_FN:%.*]] = thin_to_thick_function [[CONV_FN]]
222+
// CHECK: [[BLOCK_ALLOC:%.*]] = alloc_stack $@block_storage @noescape @callee_guaranteed () -> ()
223+
// CHECK: [[BLOCK_ADDR:%.*]] = project_block_storage [[BLOCK_ALLOC]]
224+
// CHECK: store [[THICK_FN]] to [trivial] [[BLOCK_ADDR]]
225+
// CHECK: [[THUNK:%.*]] = function_ref @$SIg_IyB_TR : $@convention(c) (@inout_aliasable @block_storage @noescape @callee_guaranteed () -> ()) -> ()
226+
// CHECK: [[BLOCK_STACK:%.*]] = init_block_storage_header [[BLOCK_ALLOC]] : {{.*}}, invoke [[THUNK]] : {{.*}}
227+
228+
// FIXME: We're passing the block as a no-escape -- so we don't have to copy it
229+
// CHECK: [[BLOCK:%.*]] = copy_block [[BLOCK_STACK]]
230+
231+
// CHECK: [[FN:%.*]] = function_ref @noescapeNonnullBlock : $@convention(c) (@convention(block) @noescape () -> ()) -> ()
232+
// CHECK: apply [[FN]]([[BLOCK]])
233+
noescapeNonnullBlock { }
234+
// CHECK: destroy_value [[BLOCK]]
235+
236+
// CHECK: [[BLOCK_ALLOC:%.*]] = alloc_stack $@block_storage @noescape @callee_guaranteed () -> ()
237+
// CHECK: [[BLOCK_ADDR:%.*]] = project_block_storage [[BLOCK_ALLOC]]
238+
// CHECK: store %0 to [trivial] [[BLOCK_ADDR]]
239+
// CHECK: [[THUNK:%.*]] = function_ref @$SIg_IyB_TR : $@convention(c) (@inout_aliasable @block_storage @noescape @callee_guaranteed () -> ()) -> ()
240+
// CHECK: [[BLOCK_STACK:%.*]] = init_block_storage_header [[BLOCK_ALLOC]] : {{.*}}, invoke [[THUNK]] : {{.*}}
241+
242+
// FIXME: We're passing the block as a no-escape -- so we don't have to copy it
243+
// CHECK: [[BLOCK:%.*]] = copy_block [[BLOCK_STACK]]
244+
245+
// CHECK: [[FN:%.*]] = function_ref @noescapeNonnullBlock : $@convention(c) (@convention(block) @noescape () -> ()) -> ()
246+
// CHECK: apply [[FN]]([[BLOCK]])
247+
noescapeNonnullBlock(fn)
248+
180249
noescapeBlockAlias { }
181-
// CHECK: function_ref @$SIg_IyB_TR
250+
noescapeBlockAlias(fn)
251+
noescapeBlockAlias(nil)
252+
182253
noescapeNonnullBlockAlias { }
254+
noescapeNonnullBlockAlias(fn)
183255
}
184256

185257
class ObjCClass : NSObject {}

0 commit comments

Comments
 (0)