Skip to content

Commit fbed1c1

Browse files
committed
Skip meta instructions for Builder.init with automatic location
1 parent 4b1ce86 commit fbed1c1

File tree

3 files changed

+100
-8
lines changed

3 files changed

+100
-8
lines changed

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,34 @@ extension Type {
429429
// Builder initialization
430430
//===----------------------------------------------------------------------===//
431431

432+
private extension Instruction {
433+
/// Returns self, unless self is a meta instruction, in which case the next
434+
/// non-meta instruction is returned. Returns nil if there are no non-meta
435+
/// instructions in the basic block.
436+
var nextNonMetaInstruction: Instruction? {
437+
for inst in InstructionList(first: self) where !(inst is MetaInstruction) {
438+
return inst
439+
}
440+
return nil
441+
}
442+
443+
/// Returns the next interesting location. As it is impossible to set a
444+
/// breakpoint on a meta instruction, those are skipped.
445+
/// However, we don't want to take a location with different inlining
446+
/// information than this instruction, so in that case, we will return the
447+
/// location of the meta instruction. If the meta instruction is the only
448+
/// instruction in the basic block, we also take its location.
449+
var locationOfNextNonMetaInstruction: Location {
450+
let location = self.location
451+
guard !location.isInlined,
452+
let nextLocation = nextNonMetaInstruction?.location,
453+
!nextLocation.isInlined else {
454+
return location
455+
}
456+
return nextLocation
457+
}
458+
}
459+
432460
extension Builder {
433461
/// Creates a builder which inserts _before_ `insPnt`, using a custom `location`.
434462
init(before insPnt: Instruction, location: Location, _ context: some MutatingContext) {
@@ -437,8 +465,23 @@ extension Builder {
437465
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
438466
}
439467

440-
/// Creates a builder which inserts _before_ `insPnt`, using the location of `insPnt`.
468+
/// Creates a builder which inserts before `insPnt`, using `insPnt`'s next
469+
/// non-meta instruction's location.
470+
/// This function should be used when moving a code to an unknown insert point,
471+
/// when we want to inherit the location of the closest non-meta instruction.
472+
/// For replacing an existing meta instruction with another, use
473+
/// ``Builder.init(replacing:_:)``.
441474
init(before insPnt: Instruction, _ context: some MutatingContext) {
475+
context.verifyIsTransforming(function: insPnt.parentFunction)
476+
self.init(insertAt: .before(insPnt),
477+
location: insPnt.locationOfNextNonMetaInstruction,
478+
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
479+
}
480+
481+
/// Creates a builder which inserts _before_ `insPnt`, using the exact location of `insPnt`,
482+
/// for the purpose of replacing that meta instruction with an equivalent instruction.
483+
/// This function does not delete `insPnt`.
484+
init(replacing insPnt: MetaInstruction, _ context: some MutatingContext) {
442485
context.verifyIsTransforming(function: insPnt.parentFunction)
443486
self.init(insertAt: .before(insPnt), location: insPnt.location,
444487
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
@@ -456,10 +499,10 @@ extension Builder {
456499
}
457500
}
458501

459-
/// Creates a builder which inserts _after_ `insPnt`, using the location of `insPnt`.
502+
/// Creates a builder which inserts _after_ `insPnt`, using `insPnt`'s next
503+
/// non-meta instruction's location.
460504
init(after insPnt: Instruction, _ context: some MutatingContext) {
461-
context.verifyIsTransforming(function: insPnt.parentFunction)
462-
self.init(after: insPnt, location: insPnt.location, context)
505+
self.init(after: insPnt, location: insPnt.locationOfNextNonMetaInstruction, context)
463506
}
464507

465508
/// Creates a builder which inserts at the end of `block`, using a custom `location`.
@@ -478,11 +521,12 @@ extension Builder {
478521
}
479522

480523
/// Creates a builder which inserts at the begin of `block`, using the location of the first
481-
/// instruction of `block`.
524+
/// non-meta instruction of `block`.
482525
init(atBeginOf block: BasicBlock, _ context: some MutatingContext) {
483526
context.verifyIsTransforming(function: block.parentFunction)
484527
let firstInst = block.instructions.first!
485-
self.init(insertAt: .before(firstInst), location: firstInst.location,
528+
self.init(insertAt: .before(firstInst),
529+
location: firstInst.locationOfNextNonMetaInstruction,
486530
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
487531
}
488532

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,16 @@ public protocol DebugVariableInstruction : VarDeclInstruction {
418418
var debugVariable: DebugVariable { get }
419419
}
420420

421-
final public class DebugValueInst : Instruction, UnaryInstruction, DebugVariableInstruction {
421+
/// A meta instruction is an instruction whose location is not interesting as
422+
/// it is impossible to set a breakpoint on it.
423+
/// That could be because the instruction does not generate code (such as
424+
/// `debug_value`), or because the generated code would be in the prologue
425+
/// (`alloc_stack`).
426+
/// When we are moving code onto an unknown instruction (such as the start of a
427+
/// basic block), we want to ignore any meta instruction that might be there.
428+
public protocol MetaInstruction: Instruction {}
429+
430+
final public class DebugValueInst : Instruction, UnaryInstruction, DebugVariableInstruction, MetaInstruction {
422431
public var varDecl: VarDecl? {
423432
VarDecl(bridged: bridged.DebugValue_getDecl())
424433
}
@@ -1127,7 +1136,7 @@ final public class InitBlockStorageHeaderInst: SingleValueInstruction {}
11271136

11281137
public protocol Allocation : SingleValueInstruction { }
11291138

1130-
final public class AllocStackInst : SingleValueInstruction, Allocation, DebugVariableInstruction {
1139+
final public class AllocStackInst : SingleValueInstruction, Allocation, DebugVariableInstruction, MetaInstruction {
11311140
public var hasDynamicLifetime: Bool { bridged.AllocStackInst_hasDynamicLifetime() }
11321141

11331142
public var varDecl: VarDecl? {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=checked_cast_br -sil-print-debuginfo | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
import Swift
6+
import Builtin
7+
8+
protocol P : AnyObject {}
9+
class B : P {}
10+
class X : B {}
11+
12+
// CHECK-LABEL: sil [ossa] @test_ossa :
13+
// CHECK: %0 = alloc_ref
14+
// CHECK-NEXT: checked_cast_br AnyObject in %0 : $X
15+
// CHECK: bb2([[A:%.*]] : @owned $X):
16+
// CHECK-NEXT: [[U:%.*]] = upcast [[A]] : $X to $B, loc "takethis":1:1
17+
// CHECK-NEXT: [[E:%.*]] = init_existential_ref [[U]] {{.+}}, loc "takethis":1:1
18+
// CHECK-NEXT: debug_value [[E]] : $AnyObject, let, name "me", loc "ignore-this-meta-inst":1:1
19+
// CHECK-NEXT: destroy_value [[E]] : $AnyObject, loc "takethis":1:1
20+
// CHECK: } // end sil function 'test_ossa'
21+
sil [ossa] @test_ossa : $@convention(thin) () -> @owned X {
22+
bb0:
23+
%0 = alloc_ref $X
24+
%1 = upcast %0 : $X to $B
25+
%2 = init_existential_ref %1 : $B : $B, $AnyObject
26+
debug_value %2 : $AnyObject, name "x"
27+
checked_cast_br AnyObject in %2 : $AnyObject to X, bb1, bb2
28+
29+
bb1(%5 : @owned $X):
30+
return %5 : $X
31+
32+
bb2(%7 : @owned $AnyObject):
33+
debug_value %7 : $AnyObject, let, name "me", loc "ignore-this-meta-inst":1:1
34+
destroy_value %7: $AnyObject, loc "takethis":1:1
35+
unreachable
36+
}
37+
38+
sil_vtable X {
39+
}

0 commit comments

Comments
 (0)