mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Type annotations for instruction operands are omitted, e.g. ``` %3 = struct $S(%1, %2) ``` Operand types are redundant anyway and were only used for sanity checking in the SIL parser. But: operand types _are_ printed if the definition of the operand value was not printed yet. This happens: * if the block with the definition appears after the block where the operand's instruction is located * if a block or instruction is printed in isolation, e.g. in a debugger The old behavior can be restored with `-Xllvm -sil-print-types`. This option is added to many existing test files which check for operand types in their check-lines.
252 lines
8.5 KiB
Plaintext
252 lines
8.5 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -jumpthread-simplify-cfg | %FileCheck %s
|
|
//
|
|
// Test SimplifyCFG's handling of address-type phis. In other words, makes sure it doesn't create them at all!
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
class C {
|
|
@_hasStorage @_hasInitialValue var field: Int { get set }
|
|
deinit
|
|
init()
|
|
}
|
|
|
|
sil @getC : $@convention(thin) () -> C
|
|
|
|
// Test that jump threading sinks a ref_element_addr, generating a
|
|
// non-address phi for its operand.
|
|
//
|
|
// The retain on separate paths followed by a merged release, and
|
|
// target block with a conditional branch are necessary just to get
|
|
// jump threading to kick in.
|
|
//
|
|
// CHECK-LABEL: sil @testJumpThreadRefEltLoop : $@convention(thin) () -> () {
|
|
// CHECK: bb0
|
|
// CHECK: function_ref @getC : $@convention(thin) () -> C
|
|
// CHECK: cond_br undef, bb1, bb2
|
|
// CHECK: bb1:
|
|
// CHECK: [[C1:%.*]] = apply %0() : $@convention(thin) () -> C
|
|
// CHECK: strong_retain [[C1]] : $C
|
|
// CHECK: strong_release [[C1]] : $C
|
|
// CHECK: br bb3([[C1]] : $C)
|
|
// CHECK: bb2:
|
|
// CHECK: [[C2:%.*]] = apply %0() : $@convention(thin) () -> C
|
|
// CHECK: strong_retain [[C2]] : $C
|
|
// CHECK: strong_release [[C2]] : $C
|
|
// CHECK: br bb3([[C2]] : $C)
|
|
// CHECK: bb3([[PHI:%.*]] : $C):
|
|
// CHECK: [[ADR:%.*]] = ref_element_addr [[PHI]] : $C, #C.field
|
|
// CHECK: begin_access [read] [dynamic] [[ADR]] : $*Int
|
|
// CHECK: load
|
|
// CHECK: end_access
|
|
// CHECK-LABEL: } // end sil function 'testJumpThreadRefEltLoop'
|
|
sil @testJumpThreadRefEltLoop : $@convention(thin) () -> () {
|
|
bb0:
|
|
%f = function_ref @getC : $@convention(thin) () -> C
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%c1 = apply %f() : $@convention(thin) ()->C
|
|
strong_retain %c1 : $C
|
|
br bb3(%c1 : $C)
|
|
|
|
bb2:
|
|
%c2 = apply %f() : $@convention(thin) ()->C
|
|
strong_retain %c2 : $C
|
|
br bb3(%c2 : $C)
|
|
|
|
bb3(%arg : $C):
|
|
strong_release %arg : $C
|
|
%18 = ref_element_addr %arg : $C, #C.field
|
|
br bb4
|
|
|
|
bb4:
|
|
%19 = begin_access [read] [dynamic] %18 : $*Int
|
|
%20 = load %19 : $*Int
|
|
end_access %19 : $*Int
|
|
cond_br undef, bb4, bb5
|
|
|
|
bb5:
|
|
%z = tuple ()
|
|
return %z : $()
|
|
}
|
|
|
|
// Test that jump threading sinks a
|
|
// ref_tail_addr->index_addr->struct_element_addr chain and generates
|
|
// a phi for the index_addr's index operand.
|
|
//
|
|
// The retain on separate paths followed by a merged release, and
|
|
// target block with a conditional branch are necessary just to get
|
|
// jump threading to kick in.
|
|
//
|
|
// CHECK-LABEL: sil @testJumpThreadIndex : $@convention(thin) (__ContiguousArrayStorageBase, Builtin.Int64) -> Builtin.Int32 {
|
|
// CHECK: bb0(%0 : $__ContiguousArrayStorageBase, %1 : $Builtin.Int64):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: apply
|
|
// CHECK: strong_retain
|
|
// CHECK: strong_release
|
|
// CHECK: [[IDX2:%.*]] = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word
|
|
// CHECK: br bb3([[IDX2]] : $Builtin.Word)
|
|
// CHECK: bb2:
|
|
// CHECK: apply
|
|
// CHECK: strong_retain
|
|
// CHECK: strong_release
|
|
// CHECK: [[IDX1:%.*]] = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word
|
|
// CHECK: br bb3([[IDX1]] : $Builtin.Word)
|
|
// CHECK: bb3([[PHI:%.*]] : $Builtin.Word):
|
|
// CHECK: [[TAIL:%.*]] = ref_tail_addr %0 : $__ContiguousArrayStorageBase, $Int32
|
|
// CHECK: [[ELT:%.*]] = index_addr [[TAIL]] : $*Int32, %14 : $Builtin.Word
|
|
// CHECK: [[ADR:%.*]] = struct_element_addr [[ELT]] : $*Int32, #Int32._value
|
|
// CHECK: load [[ADR]] : $*Builtin.Int32
|
|
// CHECK: cond_br undef, bb4, bb5
|
|
// CHECK-LABEL: } // end sil function 'testJumpThreadIndex'
|
|
sil @testJumpThreadIndex : $@convention(thin) (__ContiguousArrayStorageBase, Builtin.Int64) -> Builtin.Int32 {
|
|
bb0(%0 : $__ContiguousArrayStorageBase, %1 : $Builtin.Int64):
|
|
%f = function_ref @getC : $@convention(thin) () -> C
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%c1 = apply %f() : $@convention(thin) ()->C
|
|
strong_retain %c1 : $C
|
|
br bb3(%c1 : $C)
|
|
|
|
bb2:
|
|
%c2 = apply %f() : $@convention(thin) ()->C
|
|
strong_retain %c2 : $C
|
|
br bb3(%c2 : $C)
|
|
|
|
bb3(%arg : $C):
|
|
strong_release %arg : $C
|
|
%tail = ref_tail_addr %0 : $__ContiguousArrayStorageBase, $Int32
|
|
%idx = builtin "truncOrBitCast_Int64_Word"(%1 : $Builtin.Int64) : $Builtin.Word
|
|
%elt = index_addr %tail : $*Int32, %idx : $Builtin.Word
|
|
%adr = struct_element_addr %elt : $*Int32, #Int32._value
|
|
br bb4
|
|
|
|
bb4:
|
|
%val = load %adr : $*Builtin.Int32
|
|
cond_br undef, bb4, bb5
|
|
|
|
bb5:
|
|
return %val : $Builtin.Int32
|
|
}
|
|
|
|
// Test that debug_value is not unnecessarily lost during address projection sinking.
|
|
public class CC<R> {
|
|
let r : R
|
|
init(_ _r: R) { r = _r }
|
|
}
|
|
|
|
sil @useAny : $@convention(thin) <V> (@in_guaranteed V) -> ()
|
|
|
|
// CHECK-LABEL: sil @testDebugValue : $@convention(method) <R><S> (@in_guaranteed S, @guaranteed CC<R>, Bool) -> () {
|
|
// CHECK: debug_value %0 : $*S, let, name "u", expr op_deref
|
|
// CHECK: apply %{{.*}}<S>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
// CHECK: [[FIELD:%.*]] = ref_element_addr %1 : $CC<R>, #CC.r
|
|
// CHECK: debug_value [[FIELD]] : $*R, let, name "u", expr op_deref
|
|
// CHECK: apply %{{.*}}<R>([[FIELD]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
// CHECK-LABEL: } // end sil function 'testDebugValue'
|
|
sil @testDebugValue : $@convention(method) <R><S> (@in_guaranteed S, @guaranteed CC<R>, Bool) -> () {
|
|
bb0(%0 : $*S, %1 : $CC<R>, %2 : $Bool):
|
|
%bool = struct_extract %2 : $Bool, #Bool._value
|
|
cond_br %bool, bb1, bb2
|
|
|
|
bb1:
|
|
debug_value %0 : $*S, let, name "u", expr op_deref
|
|
%f1 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
%call1 = apply %f1<S>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
br bb2
|
|
|
|
bb2:
|
|
%field = ref_element_addr %1 : $CC<R>, #CC.r
|
|
debug_value %field : $*R, let, name "t", expr op_deref
|
|
cond_br %bool, bb3, bb4
|
|
|
|
bb3:
|
|
debug_value %field : $*R, let, name "u", expr op_deref
|
|
%f2 = function_ref @useAny : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
%call2 = apply %f2<R>(%field) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
|
br bb4
|
|
|
|
bb4:
|
|
%z = tuple ()
|
|
return %z : $()
|
|
}
|
|
|
|
// Test multiple uses and cloned allocation.
|
|
//
|
|
// project_box and struct_extract_addr will be sunk into three
|
|
// different blocks, but only once per block.
|
|
struct S {
|
|
@_hasStorage @_hasInitialValue var x: Int { get set }
|
|
init(x: Int = 0)
|
|
init()
|
|
}
|
|
sil @doNothing : $@convention(thin) (@inout Int) -> ()
|
|
|
|
// CHECK-LABEL: sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () {
|
|
// CHECK: bb0(%0 : $Bool, %1 : $*Int):
|
|
// CHECK: cond_br %{{.*}}, bb1, bb2
|
|
// CHECK: bb1:
|
|
// CHECK: apply %{{.*}}(%1) : $@convention(thin) (@inout Int) -> ()
|
|
// CHECK: [[ALLOC2:%.*]] = alloc_box ${ var S }, var, name "s"
|
|
// CHECK: [[PROJ2:%.*]] = project_box [[ALLOC2]] : ${ var S }, 0
|
|
// CHECK: [[ADR2:%.*]] = struct_element_addr [[PROJ2]] : $*S, #S.x
|
|
// CHECK: store %{{.*}} to [[ADR2]] : $*Int
|
|
// CHECK: apply %{{.*}}([[ADR2]]) : $@convention(thin) (@inout Int) -> ()
|
|
// CHECK: br bb3([[ALLOC2]] : ${ var S })
|
|
// CHECK: bb2:
|
|
// CHECK: [[ALLOC1:%.*]] = alloc_box ${ var S }, var, name "s"
|
|
// CHECK: [[PROJ1:%.*]] = project_box [[ALLOC1]] : ${ var S }, 0
|
|
// CHECK: [[ADR1:%.*]] = struct_element_addr [[PROJ1]] : $*S, #S.x
|
|
// CHECK: store %{{.*}} to [[ADR1]] : $*Int
|
|
// CHECK: br bb3([[ALLOC1]] : ${ var S })
|
|
// CHECK: bb3([[BOXARG:%.*]] : ${ var S }):
|
|
// CHECK: [[PROJ3:%.*]] = project_box [[BOXARG]] : ${ var S }, 0
|
|
// CHECK: [[ADR3:%.*]] = struct_element_addr [[PROJ3]] : $*S, #S.x
|
|
// CHECK: apply %{{.*}}([[ADR3]]) : $@convention(thin) (@inout Int) -> ()
|
|
// CHECK: release_value [[BOXARG]] : ${ var S }
|
|
// CHECK-LABEL: } // end sil function 'testMultiUse'
|
|
sil @testMultiUse : $@convention(method) (Bool, @inout Int) -> () {
|
|
bb0(%0 : $Bool, %1 : $*Int):
|
|
%bool = struct_extract %0 : $Bool, #Bool._value
|
|
cond_br %bool, bb1, bb2
|
|
|
|
bb1:
|
|
%f1 = function_ref @doNothing : $@convention(thin) (@inout Int) -> ()
|
|
%call1 = apply %f1(%1) : $@convention(thin) (@inout Int) -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%box3 = alloc_box ${ var S }, var, name "s"
|
|
%proj3 = project_box %box3 : ${ var S }, 0
|
|
%adr3 = struct_element_addr %proj3 : $*S, #S.x
|
|
cond_br %bool, bb4, bb5
|
|
|
|
bb4:
|
|
%i4 = load %1 : $*Int
|
|
store %i4 to %adr3 : $*Int
|
|
%f2 = function_ref @doNothing : $@convention(thin) (@inout Int) -> ()
|
|
%call2 = apply %f2(%adr3) : $@convention(thin) (@inout Int) -> ()
|
|
br bb6
|
|
|
|
bb5:
|
|
%i5 = load %1 : $*Int
|
|
store %i5 to %adr3 : $*Int
|
|
br bb6
|
|
|
|
bb6:
|
|
%f6 = function_ref @doNothing : $@convention(thin) (@inout Int) -> ()
|
|
%call6 = apply %f6(%adr3) : $@convention(thin) (@inout Int) -> ()
|
|
release_value %box3 : ${ var S }
|
|
%z = tuple ()
|
|
return %z : $()
|
|
}
|