160 lines · plain
1// RUN: mlir-opt %s -test-arm-sme-tile-allocation=preprocess-only -split-input-file | FileCheck %s2 3// This file tests the inserting copies for the SME tile allocation. Copies are4// inserted at `cf.br` ops (the predecessors to block arguments). Conditional5// branches are split to prevent conflicts (see cond_br_with_backedge).6 7// CHECK-LABEL: func.func @simple_branch(8// CHECK-SAME: %[[TILE:.*]]: vector<[4]x[4]xf32>)9// %[[COPY:.*]] = arm_sme.copy_tile %[[TILE]] : vector<[4]x[4]xf32>10// cf.br ^bb1(%[[COPY]] : vector<[4]x[4]xf32>)11// ^bb1(%[[BLOCK_ARG:.*]]: vector<[4]x[4]xf32>):12 13func.func @simple_branch(%tile : vector<[4]x[4]xf32>) {14 cf.br ^bb1(%tile: vector<[4]x[4]xf32>)15^bb1(%blockArg: vector<[4]x[4]xf32>):16 return17}18 19// -----20 21// Note: The ^POINTLESS_SHIM_FOR_BB2 block is added as the cond_br splitting does22// not check if it needs to insert a copy or not (there is no harm in the empty23// block though -- it will fold away later).24 25// CHECK-LABEL: func.func @cond_branch(26// CHECK-SAME: %[[COND:.*]]: i1, %[[TILE:.*]]: vector<[4]x[4]xf32>27// CHECK: cf.cond_br %[[COND]], ^[[BB1_COPIES:[[:alnum:]]+]], ^[[POINTLESS_SHIM_FOR_BB2:[[:alnum:]]+]]28// CHECK: ^[[POINTLESS_SHIM_FOR_BB2]]:29// CHECK: cf.br ^[[BB2:.*]]30// CHECK: ^[[BB1_COPIES]]:31// CHECK: arm_sme.copy_tile %[[TILE]] : vector<[4]x[4]xf32>32// CHECK: cf.br ^[[BB1:.*]]33func.func @cond_branch(%cond: i1, %tile: vector<[4]x[4]xf32>) {34 cf.cond_br %cond, ^bb1(%tile: vector<[4]x[4]xf32>), ^bb235^bb1(%blockArg: vector<[4]x[4]xf32>):36 return37^bb2:38 return39}40 41// -----42 43// Reduction of a real world example that shows why we must split conditional branches.44 45// CHECK-LABEL: @cond_branch_with_backedge(46// CHECK-SAME: %[[TILEA:[[:alnum:]]+]]: vector<[4]x[4]xf32>, %[[TILEB:[[:alnum:]]+]]: vector<[4]x[4]xf32>,47// CHECK-SAME: %[[TILEC:[[:alnum:]]+]]: vector<[4]x[4]xf32>, %[[TILED:[[:alnum:]]+]]: vector<[4]x[4]xf32>,48// CHECK: %[[BB1_COPY_0:.*]] = arm_sme.copy_tile %[[TILEA]] : vector<[4]x[4]xf32>49// CHECK: cf.br ^bb1(%{{[[:alnum:]]+}}, %[[BB1_COPY_0]]50// CHECK: ^bb1(%[[CURRENT_INDEX:.*]]: index, %[[ITER_TILE:.*]]: vector<[4]x[4]xf32>):51// CHECK: %[[CONTINUE_LOOP:.*]] = arith.cmpi52// CHECK: cf.cond_br %[[CONTINUE_LOOP]], ^[[BB2_COPIES:[[:alnum:]]+]], ^[[BB3_COPIES:[[:alnum:]]+]]53// CHECK: ^[[BB3_COPIES]]:54// CHECK-NEXT: %[[BB3_COPY_0:.*]] = arm_sme.copy_tile %[[ITER_TILE]] : vector<[4]x[4]xf32>55// CHECK-NEXT: %[[BB3_COPY_1:.*]] = arm_sme.copy_tile %[[TILEB]] : vector<[4]x[4]xf32>56// CHECK-NEXT: %[[BB3_COPY_2:.*]] = arm_sme.copy_tile %[[TILEC]] : vector<[4]x[4]xf32>57// CHECK-NEXT: %[[BB3_COPY_3:.*]] = arm_sme.copy_tile %[[TILED]] : vector<[4]x[4]xf32>58// CHECK-NEXT: cf.br ^[[BB3:[[:alnum:]]+]](%[[BB3_COPY_0]], %[[BB3_COPY_1]], %[[BB3_COPY_2]], %[[BB3_COPY_3]]59// CHECK: ^[[BB2_COPIES]]:60// CHECK-NEXT: cf.br ^[[BB2:[[:alnum:]]+]]61// CHECK: ^[[BB2]]:62// CHECK-NEXT: %[[NEXT_TILE:.*]] = arm_sme.insert_tile_slice %{{.*}}, %[[ITER_TILE]]63// CHECK: %[[BB1_COPY_1:.*]] = arm_sme.copy_tile %[[NEXT_TILE]] : vector<[4]x[4]xf32>64// CHECK: cf.br ^bb1(%{{[[:alnum:]]+}}, %[[BB1_COPY_1]]65// CHECK: ^[[BB3]](%{{.*}}: vector<[4]x[4]xf32>):66// CHECK-NEXT: return67func.func @cond_branch_with_backedge(%tileA: vector<[4]x[4]xf32>, %tileB: vector<[4]x[4]xf32>, %tileC: vector<[4]x[4]xf32>, %tileD: vector<[4]x[4]xf32>, %slice: vector<[4]xf32>) {68 %c0 = arith.constant 0 : index69 %c1 = arith.constant 1 : index70 %c10 = arith.constant 10 : index71 // Live here: %tileA, %tileB, %tileC, %tileD72 cf.br ^bb1(%c0, %tileA : index, vector<[4]x[4]xf32>)73^bb1(%currentIndex: index, %iterTile: vector<[4]x[4]xf32>):74 %continueLoop = arith.cmpi slt, %currentIndex, %c10 : index75 // Live here: %iterTile, %tileB, %tileC, %tileD76 // %iterTile, %tileB, %tileC, %tileD are live out (in the ^bb2 case). If we77 // inserted the (four) `arm_sme.copy_tile` operations here we would run out of tiles.78 // However, note that the copies are only needed if we take the ^bb3 path. So, if we add79 // a new block along that path we can insert the copies without any conflicts.80 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>)81^bb2:82 // Live here: %iterTile, %tileB, %tileC, %tileD83 %nextTile = arm_sme.insert_tile_slice %slice, %iterTile[%currentIndex] : vector<[4]xf32> into vector<[4]x[4]xf32>84 %nextIndex = arith.addi %currentIndex, %c1 : index85 cf.br ^bb1(%nextIndex, %nextTile : index, vector<[4]x[4]xf32>)86^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>):87 // Live here: %finalTileA, %finalTileB, %finalTileC, %finalTileD88 return89}90 91// -----92 93// CHECK-LABEL: @tile_dominance94// CHECK-NOT: arm_sme.copy_tile95func.func @tile_dominance(%arg0: vector<[4]x[4]xf32>) {96 cf.br ^bb197^bb1: // 2 preds: ^bb0, ^bb498 "test.some_use"(%arg0) : (vector<[4]x[4]xf32>) -> ()99 return100^bb2: // no predecessors101 %0 = arm_sme.get_tile : vector<[4]x[4]xf32>102 cf.br ^bb3103^bb3: // pred: ^bb2104 "test.some_use"(%0) : (vector<[4]x[4]xf32>) -> ()105 return106^bb4: // no predecessors107 cf.br ^bb1108^bb5: // no predecessors109 return110}111 112// -----113 114// CHECK-LABEL: func.func @cond_branch_true_and_false_tile_args(115// CHECK-SAME: %[[COND:.*]]: i1, %[[TILE:.*]]: vector<[4]x[4]xf32>116// CHECK-NEXT: cf.cond_br %[[COND]], ^[[BB1_COPIES:[[:alnum:]]+]], ^[[BB2_COPIES:[[:alnum:]]+]]117// CHECK: ^[[BB2_COPIES]]:118// CHECK-NEXT: %[[COPY_0:.*]] = arm_sme.copy_tile %[[TILE]] : vector<[4]x[4]xf32>119// CHECK-NEXT: cf.br ^[[BB2:[[:alnum:]]+]](%[[COPY_0]]120// CHECK: ^[[BB1_COPIES]]:121// CHECK-NEXT: %[[COPY_1:.*]] = arm_sme.copy_tile %[[TILE]] : vector<[4]x[4]xf32>122// CHECK-NEXT: cf.br ^[[BB1:[[:alnum:]]+]](%[[COPY_1]]123// CHECK: ^[[BB1]]{{.*}}:124// CHECK-NEXT: return125// CHECK: ^[[BB2]]{{.*}}:126// CHECK-NEXT: return127func.func @cond_branch_true_and_false_tile_args(%cond: i1, %tile: vector<[4]x[4]xf32>) {128 cf.cond_br %cond, ^bb1(%tile: vector<[4]x[4]xf32>), ^bb2(%tile: vector<[4]x[4]xf32>)129^bb1(%blockArg0: vector<[4]x[4]xf32>):130 return131^bb2(%blockArg1: vector<[4]x[4]xf32>):132 return133}134 135// -----136 137// CHECK-LABEL: @multiple_predecessors138// CHECK: ^bb1:139// CHECK-NEXT: %[[TILE:.*]] = arm_sme.get_tile : vector<[4]x[4]xf32>140// CHECK-NEXT: %[[COPY_0:.*]] = arm_sme.copy_tile %[[TILE]] : vector<[4]x[4]xf32>141// CHECK-NEXT: cf.br ^bb3(%[[COPY_0]] : vector<[4]x[4]xf32>)142// CHECK: ^bb2:143// CHECK-NEXT: %[[ZERO:.*]] = arm_sme.zero : vector<[4]x[4]xf32>144// CHECK-NEXT: %[[COPY_1:.*]] = arm_sme.copy_tile %[[ZERO]] : vector<[4]x[4]xf32>145// CHECK-NEXT: cf.br ^bb3(%[[COPY_1]] : vector<[4]x[4]xf32>)146// CHECK: ^bb3({{.*}}):147// CHECK-NEXT: return148func.func @multiple_predecessors(%cond: i1)149{150 cf.cond_br %cond, ^bb1, ^bb2151^bb1:152 %tile = arm_sme.get_tile : vector<[4]x[4]xf32>153 cf.br ^bb3(%tile : vector<[4]x[4]xf32>)154^bb2:155 %zero = arm_sme.zero : vector<[4]x[4]xf32>156 cf.br ^bb3(%zero : vector<[4]x[4]xf32>)157^bb3(%blockArg: vector<[4]x[4]xf32>): // pred: ^bb1, ^bb2158 return159}160