Files
swift-mirror/test/SILOptimizer/definite_init_root_class.swift
Erik Eckstein 5bc036661c SIL optimizer: add the LetPropertyLowering pass
It lowers let property accesses of classes.
Lowering consists of two tasks:

* In class initializers, insert `end_init_let_ref` instructions at places where all let-fields are initialized.
  This strictly separates the life-range of the class into a region where let fields are still written during
  initialization and a region where let fields are truly immutable.

* Add the `[immutable]` flag to all `ref_element_addr` instructions (for let-fields) which are in the "immutable"
  region. This includes the region after an inserted `end_init_let_ref` in an class initializer, but also all
  let-field accesses in other functions than the initializer and the destructor.

This pass should run after DefiniteInitialization but before RawSILInstLowering (because it relies on `mark_uninitialized` still present in the class initializer).
Note that it's not mandatory to run this pass. If it doesn't run, SIL is still correct.

Simplified example (after lowering):

  bb0(%0 : @owned C):                           // = self of the class initializer
    %1 = mark_uninitialized %0
    %2 = ref_element_addr %1, #C.l              // a let-field
    store %init_value to %2
    %3 = end_init_let_ref %1                    // inserted by lowering
    %4 = ref_element_addr [immutable] %3, #C.l  // set to immutable by lowering
    %5 = load %4
2023-09-19 15:10:30 +02:00

223 lines
9.5 KiB
Swift

// RUN: %target-swift-frontend -emit-sil -enable-copy-propagation=false %s | %FileCheck %s
// Using -enable-copy-propagation=false to pattern match against older SIL
// output. At least until -enable-copy-propagation has been around
// long enough in the same form to be worth rewriting CHECK lines.
// REQUIRES: swift_in_compiler
class OtherClass {}
class FirstClass {
var x: OtherClass
// CHECK-LABEL: sil hidden @$s24definite_init_root_class10FirstClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned FirstClass) -> @owned Optional<FirstClass>
init?(n: Int32) {
// CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int1
// CHECK: [[EI:%.*]] = end_init_let_ref %1
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0
// CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int1
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ZERO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb1, bb2
if n == 0 {
return nil
}
// CHECK: bb1:
// CHECK: br bb5
// CHECK: bb2:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $FirstClass, #FirstClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [init] [static] [[X_ADDR]] : $*OtherClass
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int1
// CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
x = OtherClass()
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 1
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ONE]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb3, bb4
if n == 1 {
return nil
}
// CHECK: bb3:
// CHECK: br bb5
// CHECK: bb4:
// CHECK: [[RESULT:%.*]] = enum $Optional<FirstClass>, #Optional.some!enumelt, [[EI]] : $FirstClass
// CHECK: br bb12([[RESULT]] : $Optional<FirstClass>)
// CHECK: bb5:
// CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1
// CHECK: cond_br [[BIT]], bb6, bb7
// CHECK: bb6:
// CHECK: strong_release [[EI]] : $FirstClass
// CHECK: br bb11
// CHECK: bb7:
// CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1
// CHECK: cond_br [[BIT]], bb8, bb9
// CHECK: bb8:
// CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $FirstClass, #FirstClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
// CHECK: br bb10
// CHECK: bb9:
// CHECK: br bb10
// CHECK: [[METATYPE:%.*]] = metatype $@thick FirstClass.Type
// CHECK: dealloc_partial_ref [[EI]] : $FirstClass, [[METATYPE]] : $@thick FirstClass.Type
// CHECK: br bb11
// CHECK: bb11:
// CHECK: [[NIL:%.*]] = enum $Optional<FirstClass>, #Optional.none!enumelt
// CHECK: br bb12([[NIL]] : $Optional<FirstClass>)
// CHECK: bb12([[RESULT:%.*]] : $Optional<FirstClass>):
// CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int1
// CHECK: return [[RESULT]] : $Optional<FirstClass>
}
}
class SecondClass {
var x: OtherClass
var y: OtherClass
// CHECK-LABEL: sil hidden @$s24definite_init_root_class11SecondClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned SecondClass) -> @owned Optional<SecondClass> {
init?(n: Int32) {
// CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int2
// CHECK: [[EI:%.*]] = end_init_let_ref %1
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0
// CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ZERO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb1, bb2
if n == 0 {
return nil
}
// CHECK: bb1:
// CHECK: br bb7
// CHECK: bb2:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [init] [static] [[X_ADDR]] : $*OtherClass
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1
// CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
x = OtherClass()
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 1
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ONE]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb3, bb4
if n == 1 {
return nil
}
// CHECK: bb3:
// CHECK: br bb7
// CHECK: bb4:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[Y_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.y
// CHECK: [[Y_ACCESS:%.*]] = begin_access [init] [static] [[Y_ADDR]] : $*OtherClass
// CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1
// CHECK: store [[THREE]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: store [[OTHER]] to [[Y_ACCESS]] : $*OtherClass
// CHECK: end_access [[Y_ACCESS]] : $*OtherClass
y = OtherClass()
// CHECK: [[TWO:%.*]] = integer_literal $Builtin.Int32, 2
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[TWO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb5, bb6
if n == 2 {
return nil
}
// CHECK: bb5:
// CHECK: br bb7
// CHECK: bb6:
// CHECK: [[RESULT:%.*]] = enum $Optional<SecondClass>, #Optional.some!enumelt, [[EI]] : $SecondClass
// CHECK: br bb17([[RESULT]] : $Optional<SecondClass>)
// CHECK: bb7:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1
// CHECK: [[BIT:%.*]] = builtin "cmp_eq_Int2"([[BITS]] : $Builtin.Int2, [[THREE]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb8, bb9
// CHECK: bb8:
// CHECK: strong_release [[EI]] : $SecondClass
// CHECK: br bb16
// CHECK: bb9:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[BITS]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb10, bb11
// CHECK: bb10:
// CHECK: [[X_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
// CHECK: br bb12
// CHECK: bb11:
// CHECK: br bb12
// CHECK: bb12:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1
// CHECK: [[TMP:%.*]] = builtin "lshr_Int2"([[BITS]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) : $Builtin.Int2
// CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[TMP]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb13, bb14
// CHECK: bb13:
// CHECK: [[Y_ADDR:%.*]] = ref_element_addr [[EI]] : $SecondClass, #SecondClass.y
// CHECK: [[Y_ACCESS:%.*]] = begin_access [deinit] [static] [[Y_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[Y_ACCESS]] : $*OtherClass
// CHECK: end_access [[Y_ACCESS]] : $*OtherClass
// CHECK: br bb15
// CHECK: bb14:
// CHECK: br bb15
// CHECK: bb15:
// CHECK: [[METATYPE:%.*]] = metatype $@thick SecondClass.Type
// CHECK: dealloc_partial_ref [[EI]] : $SecondClass, [[METATYPE]] : $@thick SecondClass.Type
// CHECK: br bb16
// CHECK: bb16:
// CHECK: [[NIL:%.*]] = enum $Optional<SecondClass>, #Optional.none!enumelt
// CHECK: br bb17([[NIL]] : $Optional<SecondClass>)
// CHECK: bb17([[RESULT:%.*]] : $Optional<SecondClass>):
// CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int2
// CHECK: return [[RESULT]] : $Optional<SecondClass>
}
}