611 lines · plain
1// RUN: mlir-opt %s -convert-scf-to-cf -test-arm-sme-tile-allocation -split-input-file -verify-diagnostics | FileCheck %s2// RUN: mlir-opt %s -convert-scf-to-cf -test-arm-sme-tile-allocation=dump-tile-live-ranges -mlir-disable-threading -split-input-file -verify-diagnostics 2>&1 >/dev/null | FileCheck %s --check-prefix=CHECK-LIVE-RANGE3 4// This file tests some simple aspects of using liveness in the SME tile allocator.5// Note: We use -convert-scf-to-cf first as the tile allocator expects CF, but6// some of these tests are written in SCF (to make things easier to follow).7 8// CHECK-LIVE-RANGE-LABEL: @constant_with_multiple_users9// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:10// CHECK-LIVE-RANGE: ^bb0:11// CHECK-LIVE-RANGE: S arm_sme.zero12// CHECK-LIVE-RANGE-NEXT: |S arm_sme.insert_tile_slice13// CHECK-LIVE-RANGE-NEXT: || arm_sme.insert_tile_slice14// CHECK-LIVE-RANGE-NEXT: |E test.some_use15// CHECK-LIVE-RANGE-NEXT: E test.some_use16 17// CHECK-LABEL: @constant_with_multiple_users(18// CHECK-SAME: %[[VECTOR_A:.*]]: vector<[4]xf32>, %[[VECTOR_B:.*]]: vector<[4]xf32>19func.func @constant_with_multiple_users(%a: vector<[4]xf32>, %b: vector<[4]xf32>, %index: index) {20 // CHECK-NEXT: %[[ZERO_TILE_0:.*]] = arm_sme.zero {tile_id = 0 : i32} : vector<[4]x[4]xf32>21 // CHECK-NEXT: %[[ZERO_TILE_1:.*]] = arm_sme.zero {tile_id = 1 : i32} : vector<[4]x[4]xf32>22 // CHECK-NEXT: %[[INSERT_TILE_1:.*]] = arm_sme.insert_tile_slice %[[VECTOR_A]], %[[ZERO_TILE_1]][%{{.*}}] {tile_id = 1 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>23 // CHECK-NEXT: %[[INSERT_TILE_0:.*]] = arm_sme.insert_tile_slice %[[VECTOR_B]], %[[ZERO_TILE_0]][%{{.*}}] {tile_id = 0 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>24 %zero = arm_sme.zero : vector<[4]x[4]xf32>25 %tile_a = arm_sme.insert_tile_slice %a, %zero[%index] : vector<[4]xf32> into vector<[4]x[4]xf32>26 %tile_b = arm_sme.insert_tile_slice %b, %zero[%index] : vector<[4]xf32> into vector<[4]x[4]xf32>27 "test.some_use"(%tile_a) : (vector<[4]x[4]xf32>) -> ()28 "test.some_use"(%tile_b) : (vector<[4]x[4]xf32>) -> ()29 return30}31 32// -----33 34// CHECK-LIVE-RANGE-LABEL: @value_with_multiple_users35// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:36// CHECK-LIVE-RANGE: ^bb0:37// CHECK-LIVE-RANGE-NEXT: |S arm_sme.insert_tile_slice38// CHECK-LIVE-RANGE-NEXT: || arm_sme.insert_tile_slice39// CHECK-LIVE-RANGE-NEXT: |E test.some_use40// CHECK-LIVE-RANGE-NEXT: E test.some_use41 42// expected-note@below {{tile operand is: <block argument> of type 'vector<[4]x[4]xf32>'}}43func.func @value_with_multiple_users(%tile: vector<[4]x[4]xf32>, %a: vector<[4]xf32>, %b: vector<[4]xf32>, %index: index) {44 // expected-error@below {{op tile operand allocated to different SME virtial tile (move required)}}45 %tile_a = arm_sme.insert_tile_slice %a, %tile[%index] : vector<[4]xf32> into vector<[4]x[4]xf32>46 %tile_b = arm_sme.insert_tile_slice %b, %tile[%index] : vector<[4]xf32> into vector<[4]x[4]xf32>47 "test.some_use"(%tile_a) : (vector<[4]x[4]xf32>) -> ()48 "test.some_use"(%tile_b) : (vector<[4]x[4]xf32>) -> ()49 return50}51 52// -----53 54// CHECK-LIVE-RANGE-LABEL: @reuse_tiles_after_initial_use55// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:56// CHECK-LIVE-RANGE: ^bb0:57// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile58// CHECK-LIVE-RANGE-NEXT: |S arm_sme.get_tile59// CHECK-LIVE-RANGE-NEXT: ||S arm_sme.get_tile60// CHECK-LIVE-RANGE-NEXT: |||S arm_sme.get_tile61// CHECK-LIVE-RANGE-NEXT: |||| test.dummy62// CHECK-LIVE-RANGE-NEXT: |||| test.dummy63// CHECK-LIVE-RANGE-NEXT: |||| test.dummy64// CHECK-LIVE-RANGE-NEXT: E||| test.some_use65// CHECK-LIVE-RANGE-NEXT: E|| test.some_use66// CHECK-LIVE-RANGE-NEXT: E| test.some_use67// CHECK-LIVE-RANGE-NEXT: E test.some_use68// CHECK-LIVE-RANGE-NEXT: S arm_sme.zero69// CHECK-LIVE-RANGE-NEXT: |S arm_sme.zero70// CHECK-LIVE-RANGE-NEXT: ||S arm_sme.zero71// CHECK-LIVE-RANGE-NEXT: |||S arm_sme.zero72// CHECK-LIVE-RANGE-NEXT: |||| test.dummy73// CHECK-LIVE-RANGE-NEXT: |||| test.dummy74// CHECK-LIVE-RANGE-NEXT: |||| test.dummy75// CHECK-LIVE-RANGE-NEXT: E||| test.some_use76// CHECK-LIVE-RANGE-NEXT: E|| test.some_use77// CHECK-LIVE-RANGE-NEXT: E| test.some_use78// CHECK-LIVE-RANGE-NEXT: E test.some_use79 80// CHECK-LABEL: @reuse_tiles_after_initial_use81func.func @reuse_tiles_after_initial_use() {82 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}83 // CHECK: arm_sme.get_tile {tile_id = 1 : i32}84 // CHECK: arm_sme.get_tile {tile_id = 2 : i32}85 // CHECK: arm_sme.get_tile {tile_id = 3 : i32}86 %tile_a = arm_sme.get_tile : vector<[4]x[4]xf32>87 %tile_b = arm_sme.get_tile : vector<[4]x[4]xf32>88 %tile_c = arm_sme.get_tile : vector<[4]x[4]xf32>89 %tile_d = arm_sme.get_tile : vector<[4]x[4]xf32>90 "test.dummy"(): () -> ()91 "test.dummy"(): () -> ()92 "test.dummy"(): () -> ()93 "test.some_use"(%tile_a) : (vector<[4]x[4]xf32>) -> ()94 "test.some_use"(%tile_b) : (vector<[4]x[4]xf32>) -> ()95 "test.some_use"(%tile_c) : (vector<[4]x[4]xf32>) -> ()96 "test.some_use"(%tile_d) : (vector<[4]x[4]xf32>) -> ()97 // CHECK: arm_sme.zero {tile_id = 0 : i32}98 // CHECK: arm_sme.zero {tile_id = 1 : i32}99 // CHECK: arm_sme.zero {tile_id = 2 : i32}100 // CHECK: arm_sme.zero {tile_id = 3 : i32}101 %tile_1 = arm_sme.zero : vector<[4]x[4]xf32>102 %tile_2 = arm_sme.zero : vector<[4]x[4]xf32>103 %tile_3 = arm_sme.zero : vector<[4]x[4]xf32>104 %tile_4 = arm_sme.zero : vector<[4]x[4]xf32>105 "test.dummy"(): () -> ()106 "test.dummy"(): () -> ()107 "test.dummy"(): () -> ()108 "test.some_use"(%tile_1) : (vector<[4]x[4]xf32>) -> ()109 "test.some_use"(%tile_2) : (vector<[4]x[4]xf32>) -> ()110 "test.some_use"(%tile_3) : (vector<[4]x[4]xf32>) -> ()111 "test.some_use"(%tile_4) : (vector<[4]x[4]xf32>) -> ()112 return113}114 115// -----116 117// CHECK-LIVE-RANGE-LABEL: @tile_live_ins118// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:119// CHECK-LIVE-RANGE: ^bb0:120// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile121// CHECK-LIVE-RANGE-NEXT: |S arm_sme.zero122// CHECK-LIVE-RANGE-NEXT: EE cf.br123// CHECK-LIVE-RANGE-NEXT: ^bb1:124// CHECK-LIVE-RANGE-NEXT: || test.dummy125// CHECK-LIVE-RANGE-NEXT: || test.dummy126// CHECK-LIVE-RANGE-NEXT: EE cf.br127// CHECK-LIVE-RANGE-NEXT: ^bb2:128// CHECK-LIVE-RANGE-NEXT: || test.dummy129// CHECK-LIVE-RANGE-NEXT: || test.dummy130// CHECK-LIVE-RANGE-NEXT: EE cf.br131// CHECK-LIVE-RANGE-NEXT: ^bb3:132// CHECK-LIVE-RANGE-NEXT: E| test.some_use133// CHECK-LIVE-RANGE-NEXT: E test.some_use134 135// CHECK-LABEL: @tile_live_ins136func.func @tile_live_ins()137{138 // CHECK: arm_sme.get_tile {tile_id = 0 : i32} : vector<[4]x[4]xf32>139 // CHECK: arm_sme.zero {tile_id = 1 : i32} : vector<[4]x[4]xf32>140 %tile_1 = arm_sme.get_tile : vector<[4]x[4]xf32>141 %tile_2 = arm_sme.zero : vector<[4]x[4]xf32>142 cf.br ^bb1143^bb1:144 "test.dummy"(): () -> ()145 "test.dummy"(): () -> ()146 cf.br ^bb2147^bb2:148 "test.dummy"(): () -> ()149 "test.dummy"(): () -> ()150 cf.br ^bb3151^bb3:152 "test.some_use"(%tile_1) : (vector<[4]x[4]xf32>) -> ()153 "test.some_use"(%tile_2) : (vector<[4]x[4]xf32>) -> ()154 return155}156 157// -----158 159// This is basically the same test as tile_live_ins but shows that the order of160// the blocks within the source does not relate to the liveness, which is based161// on successors and predecessors (not textual order).162//163// So %tile_1 is live on the path bb0 -> bb2 -> bb1 (and dies in bb1). The164// 'hole' when looking at the live range dump comes from the textual order165// (and would disappear if bb1 was moved before bb2 in the source).166//167// When looking at the live range dump (outside of straight-line code) it168// normally makes more sense to consider blocks in isolation (and how they169// relate to the CFG).170 171// CHECK-LIVE-RANGE-LABEL: @non_sequential_live_ins172// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:173// CHECK-LIVE-RANGE: ^bb0:174// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile175// CHECK-LIVE-RANGE-NEXT: | test.dummy176// CHECK-LIVE-RANGE-NEXT: E cf.br177// CHECK-LIVE-RANGE-NEXT: ^bb1:178// CHECK-LIVE-RANGE-NEXT: E| test.some_use179// CHECK-LIVE-RANGE-NEXT: | test.dummy180// CHECK-LIVE-RANGE-NEXT: E cf.br181// CHECK-LIVE-RANGE-NEXT: ^bb2:182// CHECK-LIVE-RANGE-NEXT: |S arm_sme.zero183// CHECK-LIVE-RANGE-NEXT: || test.dummy184// CHECK-LIVE-RANGE-NEXT: EE cf.cond_br185// CHECK-LIVE-RANGE-NEXT: ^bb3:186// CHECK-LIVE-RANGE-NEXT: | test.dummy187// CHECK-LIVE-RANGE-NEXT: E test.some_use188// CHECK-LIVE-RANGE-NEXT: func.return189 190// CHECK-LABEL: @non_sequential_live_ins191func.func @non_sequential_live_ins(%cond: i1) {192 // CHECK: arm_sme.get_tile {tile_id = 0 : i32} : vector<[4]x[4]xf32>193 // CHECK: arm_sme.zero {tile_id = 1 : i32} : vector<[4]x[4]xf32>194 %tile_1 = arm_sme.get_tile : vector<[4]x[4]xf32>195 "test.dummy"(): () -> ()196 cf.br ^bb2197^bb1:198 "test.some_use"(%tile_1) : (vector<[4]x[4]xf32>) -> ()199 "test.dummy"(): () -> ()200 cf.br ^bb3201^bb2:202 %tile_2 = arm_sme.zero : vector<[4]x[4]xf32>203 "test.dummy"(): () -> ()204 cf.cond_br %cond, ^bb1, ^bb3205^bb3:206 "test.dummy"(): () -> ()207 "test.some_use"(%tile_2) : (vector<[4]x[4]xf32>) -> ()208 return209}210 211// -----212 213// CHECK-LIVE-RANGE-LABEL: @non_overlapping_branches214// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:215// CHECK-LIVE-RANGE: ^bb1:216// CHECK-LIVE-RANGE-NEXT: S arm_sme.zero217// CHECK-LIVE-RANGE-NEXT: | arm_sme.copy_tile218// CHECK-LIVE-RANGE-NEXT: E cf.br219// CHECK-LIVE-RANGE-NEXT: ^bb2:220// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile221// CHECK-LIVE-RANGE-NEXT: | arm_sme.copy_tile222// CHECK-LIVE-RANGE-NEXT: E cf.br223 224// CHECK-LABEL: @non_overlapping_branches225func.func @non_overlapping_branches(%cond: i1) {226 // CHECK: arm_sme.zero {tile_id = 0 : i32} : vector<[4]x[4]xf32>227 // CHECK: arm_sme.get_tile {tile_id = 0 : i32} : vector<[4]x[4]xf32>228 %tile = scf.if %cond -> vector<[4]x[4]xf32> {229 // ^bb1:230 %zero = arm_sme.zero : vector<[4]x[4]xf32>231 scf.yield %zero : vector<[4]x[4]xf32>232 } else {233 // ^bb2:234 %undef = arm_sme.get_tile : vector<[4]x[4]xf32>235 scf.yield %undef : vector<[4]x[4]xf32>236 }237 "test.some_use"(%tile) : (vector<[4]x[4]xf32>) -> ()238 return239}240 241// -----242 243// Here %vecA and %vecB are not merged into the same live range (as they are unknown values).244// This means that %vecA and %vecB are both allocated to different tiles (which is not legal).245 246// expected-note@below {{tile operand is: <block argument> of type 'vector<[4]x[4]xf32>'}}247func.func @overlapping_branches(%cond: i1, %vecA: vector<[4]x[4]xf32>, %vecB: vector<[4]x[4]xf32>) {248 // expected-error@below {{op tile operand allocated to different SME virtial tile (move required)}}249 %tile = scf.if %cond -> vector<[4]x[4]xf32> {250 scf.yield %vecA : vector<[4]x[4]xf32>251 } else {252 scf.yield %vecB : vector<[4]x[4]xf32>253 }254 "test.some_use"(%tile) : (vector<[4]x[4]xf32>) -> ()255 return256}257 258// -----259 260// CHECK-LIVE-RANGE-LABEL: @run_out_of_tiles_but_avoid_spill261// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:262// CHECK-LIVE-RANGE: ^bb2:263// CHECK-LIVE-RANGE-NEXT: |S arm_sme.copy_tile264// CHECK-LIVE-RANGE-NEXT: ||S arm_sme.copy_tile265// CHECK-LIVE-RANGE-NEXT: |||S arm_sme.copy_tile266// CHECK-LIVE-RANGE-NEXT: ||||S arm_sme.copy_tile267// CHECK-LIVE-RANGE-NEXT: EEEEE cf.br268 269// Note in the live ranges (above) there is five tile values, but we only have four tiles.270// There is no 'real' spill as we spill the `arm_sme.zero` but are then able to clone it271// at each of its uses.272 273// CHECK-LABEL: @run_out_of_tiles_but_avoid_spill274func.func @run_out_of_tiles_but_avoid_spill(%a: vector<[4]xf32>, %b: vector<[4]xf32>, %c: vector<[4]xf32>, %d: vector<[4]xf32>) {275 %init = arm_sme.zero : vector<[4]x[4]xf32>276 %c0 = arith.constant 0 : index277 %c1 = arith.constant 1 : index278 %c10 = arith.constant 10 : index279 // Live = %init280 scf.for %i = %c0 to %c10 step %c1 {281 // CHECK: arm_sme.zero {tile_id = 1 : i32}282 // CHECK: arm_sme.zero {tile_id = 2 : i32}283 // CHECK: arm_sme.zero {tile_id = 3 : i32}284 // CHECK: arm_sme.zero {tile_id = 0 : i32}285 %tile_a, %tile_b, %tile_c, %tile_d = scf.for %j = %c0 to %c10 step %c1286 iter_args(%iter_a = %init, %iter_b = %init, %iter_c = %init, %iter_d = %init)287 -> (vector<[4]x[4]xf32>, vector<[4]x[4]xf32> , vector<[4]x[4]xf32> , vector<[4]x[4]xf32>) {288 // ^bb2:289 // CHECK: arm_sme.insert_tile_slice {{.*}} {tile_id = 1 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>290 // CHECK: arm_sme.insert_tile_slice {{.*}} {tile_id = 2 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>291 // CHECK: arm_sme.insert_tile_slice {{.*}} {tile_id = 3 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>292 // CHECK: arm_sme.insert_tile_slice {{.*}} {tile_id = 0 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>293 %new_a = arm_sme.insert_tile_slice %a, %iter_a[%i] : vector<[4]xf32> into vector<[4]x[4]xf32>294 %new_b = arm_sme.insert_tile_slice %b, %iter_b[%i] : vector<[4]xf32> into vector<[4]x[4]xf32>295 %new_c = arm_sme.insert_tile_slice %c, %iter_c[%i] : vector<[4]xf32> into vector<[4]x[4]xf32>296 %new_d = arm_sme.insert_tile_slice %d, %iter_d[%i] : vector<[4]xf32> into vector<[4]x[4]xf32>297 scf.yield %new_a, %new_b, %new_c, %new_d : vector<[4]x[4]xf32>, vector<[4]x[4]xf32>, vector<[4]x[4]xf32>, vector<[4]x[4]xf32>298 }299 // Live = %init, %tile_a, %tile_b, %tile_c, %tile_d (out of tiles!)300 // This should be resolved by duplicating the arm_sme.zero (from folding301 // arm_sme.copy_tile operations inserted by the tile allocator).302 "test.some_use"(%tile_a) : (vector<[4]x[4]xf32>) -> ()303 "test.some_use"(%tile_b) : (vector<[4]x[4]xf32>) -> ()304 "test.some_use"(%tile_c) : (vector<[4]x[4]xf32>) -> ()305 "test.some_use"(%tile_d) : (vector<[4]x[4]xf32>) -> ()306 }307 return308}309 310// -----311 312// We should be able to avoid spills like this, but logic handling this case is313// not implemented yet. Note tile ID >= 16 means a spill/in-memory tile.314 315// CHECK-LIVE-RANGE-LABEL: @avoidable_spill316// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:317// CHECK-LIVE-RANGE: ^bb2:318// CHECK-LIVE-RANGE-NEXT: || test.some_use319// CHECK-LIVE-RANGE-NEXT: ||S arm_sme.insert_tile_slice320// CHECK-LIVE-RANGE-NEXT: |||S arm_sme.insert_tile_slice321// CHECK-LIVE-RANGE-NEXT: ||||S arm_sme.insert_tile_slice322// CHECK-LIVE-RANGE-NEXT: |||||S arm_sme.insert_tile_slice323// CHECK-LIVE-RANGE-NEXT: ||E||| test.some_use324// CHECK-LIVE-RANGE-NEXT: || E|| test.some_use325// CHECK-LIVE-RANGE-NEXT: || E| test.some_use326// CHECK-LIVE-RANGE-NEXT: || E test.some_use327// CHECK-LIVE-RANGE-NEXT: || arith.addi328// CHECK-LIVE-RANGE-NEXT: EE cf.br329 330// Note in the live ranges (above) there is two constant live-ins (first two ranges),331// which gives six overlapping live ranges (at the point where %tile_d is defined).332// The allocator currently will spill the first constant (which results in a real333// spill at it's use), however, this could be avoided by using the knowledge that334// at the first "test.some_use" there's actually only two live ranges (so we can335// fix this be duplicating the constant).336 337// CHECK-LABEL: @avoidable_spill338func.func @avoidable_spill(%a: vector<[4]xf32>, %b: vector<[4]xf32>, %c: vector<[4]xf32>, %d: vector<[4]xf32>) {339 // CHECK: arm_sme.zero {tile_id = 16 : i32} : vector<[4]x[4]xf32>340 %zero = arm_sme.zero : vector<[4]x[4]xf32>341 %tile = arm_sme.get_tile : vector<[4]x[4]xf32>342 %c0 = arith.constant 0 : index343 %c1 = arith.constant 1 : index344 %c10 = arith.constant 10 : index345 scf.for %i = %c0 to %c10 step %c1 {346 // So spilled here (unnecessarily).347 // The arm_sme.zero op could be moved into the loop to avoid this.348 "test.some_use"(%zero) : (vector<[4]x[4]xf32>) -> ()349 %tile_a = arm_sme.insert_tile_slice %a, %tile[%c0] : vector<[4]xf32> into vector<[4]x[4]xf32>350 %tile_b = arm_sme.insert_tile_slice %b, %tile[%c0] : vector<[4]xf32> into vector<[4]x[4]xf32>351 %tile_c = arm_sme.insert_tile_slice %c, %tile[%c0] : vector<[4]xf32> into vector<[4]x[4]xf32>352 %tile_d = arm_sme.insert_tile_slice %d, %tile[%c0] : vector<[4]xf32> into vector<[4]x[4]xf32>353 // %zero is still live here (due the the backedge)354 "test.some_use"(%tile_a) : (vector<[4]x[4]xf32>) -> ()355 "test.some_use"(%tile_b) : (vector<[4]x[4]xf32>) -> ()356 "test.some_use"(%tile_c) : (vector<[4]x[4]xf32>) -> ()357 "test.some_use"(%tile_d) : (vector<[4]x[4]xf32>) -> ()358 }359 return360}361 362// -----363 364// This test is a follow up to the test of the same name in `tile-allocation-copies.mlir`.365// This shows the live ranges (which are why we need to split the conditional branch).366 367// CHECK-LIVE-RANGE-LABEL: @cond_branch_with_backedge368// CHECK-LIVE-RANGE: ^bb1:369// CHECK-LIVE-RANGE-NEXT: ||| | arith.cmpi370// CHECK-LIVE-RANGE-NEXT: EEE E cf.cond_br371//372// CHECK-LIVE-RANGE-NEXT: ^[[BB3_COPIES:[[:alnum:]]+]]:373// CHECK-LIVE-RANGE-NEXT: ||| ES arm_sme.copy_tile374// CHECK-LIVE-RANGE-NEXT: E|| |S arm_sme.copy_tile375// CHECK-LIVE-RANGE-NEXT: E| ||S arm_sme.copy_tile376// CHECK-LIVE-RANGE-NEXT: E |||S arm_sme.copy_tile377// CHECK-LIVE-RANGE-NEXT: EEEE cf.br378//379// It is important to note that the first three live ranges in ^bb1 do not end380// at the `cf.cond_br` they are live-out via the backedge bb1 -> bb2 -> bb1.381// This means that if we placed the `arm_sme.tile_copies` before the `cf.cond_br`382// then those live ranges would not end at the copies, resulting in unwanted383// overlapping live ranges (and hence tile spills).384//385// With the conditional branch split and the copies placed in the BB3_COPIES386// block the first three live ranges end at the copy operations (as the387// BB3_COPIES block is on the path out of the loop and has no backedge). This388// means there is no overlaps and the live ranges all merge, as shown below.389//390// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:391// CHECK-LIVE-RANGE: ^bb1:392// CHECK-LIVE-RANGE-NEXT: |||| arith.cmpi393// CHECK-LIVE-RANGE-NEXT: EEEE cf.cond_br394//395// CHECK-LIVE-RANGE-NEXT: ^[[BB3_COPIES]]:396// CHECK-LIVE-RANGE-NEXT: |||| arm_sme.copy_tile397// CHECK-LIVE-RANGE-NEXT: |||| arm_sme.copy_tile398// CHECK-LIVE-RANGE-NEXT: |||| arm_sme.copy_tile399// CHECK-LIVE-RANGE-NEXT: |||| arm_sme.copy_tile400// CHECK-LIVE-RANGE-NEXT: EEEE cf.br401 402// CHECK-LABEL: @cond_branch_with_backedge403// CHECK-NOT: tile_id = 16404// CHECK: arm_sme.get_tile {tile_id = 0 : i32} : vector<[4]x[4]xf32>405// CHECK: arm_sme.get_tile {tile_id = 1 : i32} : vector<[4]x[4]xf32>406// CHECK: arm_sme.get_tile {tile_id = 2 : i32} : vector<[4]x[4]xf32>407// CHECK: arm_sme.get_tile {tile_id = 3 : i32} : vector<[4]x[4]xf32>408// CHECK: arm_sme.insert_tile_slice {{.*}} {tile_id = 0 : i32} : vector<[4]xf32> into vector<[4]x[4]xf32>409// CHECK-NOT: tile_id = 16410func.func @cond_branch_with_backedge(%slice: vector<[4]xf32>) {411 %tileA = arm_sme.get_tile : vector<[4]x[4]xf32>412 %tileB = arm_sme.get_tile : vector<[4]x[4]xf32>413 %tileC = arm_sme.get_tile : vector<[4]x[4]xf32>414 %tileD = arm_sme.get_tile : vector<[4]x[4]xf32>415 %c0 = arith.constant 0 : index416 %c1 = arith.constant 1 : index417 %c10 = arith.constant 10 : index418 // Live here: %tileA, %tileB, %tileC, %tileD419 cf.br ^bb1(%c0, %tileA : index, vector<[4]x[4]xf32>)420^bb1(%currentIndex: index, %iterTile: vector<[4]x[4]xf32>):421 %continueLoop = arith.cmpi slt, %currentIndex, %c10 : index422 // Live here: %iterTile, %tileB, %tileC, %tileD423 cf.cond_br %continueLoop, ^bb2, ^bb3(%iterTile, %tileB, %tileC, %tileD : vector<[4]x[4]xf32>, vector<[4]x[4]xf32>, vector<[4]x[4]xf32>, vector<[4]x[4]xf32>)424^bb2:425 // Live here: %iterTile, %tileB, %tileC, %tileD426 %nextTile = arm_sme.insert_tile_slice %slice, %iterTile[%currentIndex] : vector<[4]xf32> into vector<[4]x[4]xf32>427 %nextIndex = arith.addi %currentIndex, %c1 : index428 cf.br ^bb1(%nextIndex, %nextTile : index, vector<[4]x[4]xf32>)429^bb3(%finalTileA: vector<[4]x[4]xf32>, %finalTileB: vector<[4]x[4]xf32>, %finalTileC: vector<[4]x[4]xf32>, %finalTileD: vector<[4]x[4]xf32>):430 // Live here: %finalTileA, %finalTileB, %finalTileC, %finalTileD431 return432}433 434// -----435 436// CHECK-LIVE-RANGE-LABEL: @fill_holes_in_tile_liveness437// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:438// CHECK-LIVE-RANGE: ^bb0:439// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile440// CHECK-LIVE-RANGE-NEXT: E cf.cond_br441// CHECK-LIVE-RANGE-NEXT: ^bb1:442// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile443// CHECK-LIVE-RANGE-NEXT: | test.dummy444// CHECK-LIVE-RANGE-NEXT: E test.some_use445// CHECK-LIVE-RANGE-NEXT: cf.br446// CHECK-LIVE-RANGE-NEXT: ^bb2:447// CHECK-LIVE-RANGE-NEXT: | test.dummy448// CHECK-LIVE-RANGE-NEXT: | test.dummy449// CHECK-LIVE-RANGE-NEXT: | test.dummy450// CHECK-LIVE-RANGE-NEXT: E test.some_use451// CHECK-LIVE-RANGE-NEXT: cf.br452 453// Here there's a 'hole' in the liveness of %tileA (in bb1) where another value454// can reuse the tile ID assigned to %tileA. The liveness for %tileB is455// entirely within the 'hole' in %tileA's live range, so %tileB should get the456// same tile ID as %tileA.457 458// CHECK-LABEL: @fill_holes_in_tile_liveness459func.func @fill_holes_in_tile_liveness(%cond: i1) {460 // CHECK: arm_sme.get_tile {tile_id = [[TILE_ID_A:.*]] : i32}461 %tileA = arm_sme.get_tile : vector<[4]x[4]xf32>462 cf.cond_br %cond, ^bb2, ^bb1463^bb1:464 // CHECK: arm_sme.get_tile {tile_id = [[TILE_ID_A]] : i32}465 %tileB = arm_sme.get_tile : vector<[4]x[4]xf32>466 "test.dummy"(): () -> ()467 "test.some_use"(%tileB) : (vector<[4]x[4]xf32>) -> ()468 cf.br ^bb3469^bb2:470 "test.dummy"(): () -> ()471 "test.dummy"(): () -> ()472 "test.dummy"(): () -> ()473 "test.some_use"(%tileA) : (vector<[4]x[4]xf32>) -> ()474 cf.br ^bb3475^bb3:476 return477}478 479// -----480 481// CHECK-LIVE-RANGE-LABEL: @holes_in_tile_liveness_inactive_overlaps482// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:483// CHECK-LIVE-RANGE: ^bb0:484// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile485// CHECK-LIVE-RANGE-NEXT: E cf.cond_br486// CHECK-LIVE-RANGE-NEXT: ^bb1:487// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile488// CHECK-LIVE-RANGE-NEXT: | test.dummy489// CHECK-LIVE-RANGE-NEXT: | test.some_use490// CHECK-LIVE-RANGE-NEXT: | arm_sme.copy_tile491// CHECK-LIVE-RANGE-NEXT: E cf.br492// CHECK-LIVE-RANGE-NEXT: ^bb2:493// CHECK-LIVE-RANGE-NEXT: | test.dummy494// CHECK-LIVE-RANGE-NEXT: | test.dummy495// CHECK-LIVE-RANGE-NEXT: | test.dummy496// CHECK-LIVE-RANGE-NEXT: |S arm_sme.get_tile497// CHECK-LIVE-RANGE-NEXT: E| test.some_use498// CHECK-LIVE-RANGE-NEXT: | arm_sme.copy_tile499// CHECK-LIVE-RANGE-NEXT: E cf.br500// CHECK-LIVE-RANGE-NEXT: ^bb3:501// CHECK-LIVE-RANGE-NEXT: E test.some_use502// CHECK-LIVE-RANGE-NEXT: func.return503 504// This tests an edge case in inactive live ranges. The first live range is505// inactive at the start of ^bb1. If the tile allocator did not check if the506// second live range overlapped the first it would wrongly re-use tile ID 0507// (as the first live range is inactive so tile ID 0 is free). This would mean508// in ^bb2 two overlapping live ranges would have the same tile ID (bad!).509 510// CHECK-LABEL: @holes_in_tile_liveness_inactive_overlaps511func.func @holes_in_tile_liveness_inactive_overlaps(%cond: i1) {512 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}513 %tileA = arm_sme.get_tile : vector<[4]x[4]xf32>514 cf.cond_br %cond, ^bb2, ^bb1515^bb1:516 // CHECK: arm_sme.get_tile {tile_id = 1 : i32}517 %tileB = arm_sme.get_tile : vector<[4]x[4]xf32>518 "test.dummy"(): () -> ()519 "test.some_use"(%tileB) : (vector<[4]x[4]xf32>) -> ()520 cf.br ^bb3(%tileB: vector<[4]x[4]xf32>)521^bb2:522 "test.dummy"(): () -> ()523 "test.dummy"(): () -> ()524 "test.dummy"(): () -> ()525 // CHECK: arm_sme.get_tile {tile_id = 1 : i32}526 %tileC = arm_sme.get_tile : vector<[4]x[4]xf32>527 "test.some_use"(%tileA) : (vector<[4]x[4]xf32>) -> ()528 cf.br ^bb3(%tileC: vector<[4]x[4]xf32>)529^bb3(%tile: vector<[4]x[4]xf32>):530 "test.some_use"(%tile) : (vector<[4]x[4]xf32>) -> ()531 return532}533 534// -----535 536// This is the same as the previous example, but changes the tile types to537// vector<[16]x[16]xi8>. This means in bb1 the allocator will need to spill the538// first live range (which is inactive).539 540// Note: The live ranges are the same as the previous example (so are not checked).541 542// CHECK-LABEL: @spill_inactive_live_range543func.func @spill_inactive_live_range(%cond: i1) {544 // CHECK: arm_sme.get_tile {tile_id = 16 : i32}545 %tileA = arm_sme.get_tile : vector<[16]x[16]xi8>546 cf.cond_br %cond, ^bb2, ^bb1547^bb1:548 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}549 %tileB = arm_sme.get_tile : vector<[16]x[16]xi8>550 "test.dummy"(): () -> ()551 "test.some_use"(%tileB) : (vector<[16]x[16]xi8>) -> ()552 cf.br ^bb3(%tileB: vector<[16]x[16]xi8>)553^bb2:554 "test.dummy"(): () -> ()555 "test.dummy"(): () -> ()556 "test.dummy"(): () -> ()557 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}558 %tileC = arm_sme.get_tile : vector<[16]x[16]xi8>559 "test.some_use"(%tileA) : (vector<[16]x[16]xi8>) -> ()560 cf.br ^bb3(%tileC: vector<[16]x[16]xi8>)561^bb3(%tile: vector<[16]x[16]xi8>):562 "test.some_use"(%tile) : (vector<[16]x[16]xi8>) -> ()563 return564}565 566// -----567 568// CHECK-LIVE-RANGE-LABEL: @reactivate_inactive_live_range569// CHECK-LIVE-RANGE: ========== Coalesced Live Ranges:570// CHECK-LIVE-RANGE: ^bb0:571// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile572// CHECK-LIVE-RANGE-NEXT: E cf.cond_br573// CHECK-LIVE-RANGE-NEXT: ^bb1:574// CHECK-LIVE-RANGE-NEXT: S arm_sme.get_tile575// CHECK-LIVE-RANGE-NEXT: | test.dummy576// CHECK-LIVE-RANGE-NEXT: E test.some_use577// CHECK-LIVE-RANGE-NEXT: cf.br578// CHECK-LIVE-RANGE-NEXT: ^bb2:579// CHECK-LIVE-RANGE-NEXT: | S arm_sme.get_tile580// CHECK-LIVE-RANGE-NEXT: | | test.dummy581// CHECK-LIVE-RANGE-NEXT: | | test.dummy582// CHECK-LIVE-RANGE-NEXT: | E test.some_use583// CHECK-LIVE-RANGE-NEXT: E test.some_use584// CHECK-LIVE-RANGE-NEXT: cf.br585 586// Here the live range for %tileA becomes inactive in bb1 (so %tileB gets tile587// ID 0 too). Then in bb2 the live range for tileA is reactivated as it overlaps588// with the start of %tileC's live range (which means %tileC gets tile ID 1).589 590func.func @reactivate_inactive_live_range(%cond: i1) {591 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}592 %tileA = arm_sme.get_tile : vector<[4]x[4]xf32>593 cf.cond_br %cond, ^bb2, ^bb1594^bb1:595 // CHECK: arm_sme.get_tile {tile_id = 0 : i32}596 %tileB = arm_sme.get_tile : vector<[16]x[16]xi8>597 "test.dummy"(): () -> ()598 "test.some_use"(%tileB) : (vector<[16]x[16]xi8>) -> ()599 cf.br ^bb3600^bb2:601 // CHECK: arm_sme.get_tile {tile_id = 1 : i32}602 %tileC = arm_sme.get_tile : vector<[4]x[4]xf32>603 "test.dummy"(): () -> ()604 "test.dummy"(): () -> ()605 "test.some_use"(%tileC) : (vector<[4]x[4]xf32>) -> ()606 "test.some_use"(%tileA) : (vector<[4]x[4]xf32>) -> ()607 cf.br ^bb3608^bb3:609 return610}611