350 lines · plain
1// RUN: mlir-opt --arith-int-range-narrowing="int-bitwidths-supported=1,8,16,24,32" %s | FileCheck %s2 3//===----------------------------------------------------------------------===//4// Some basic tests5//===----------------------------------------------------------------------===//6 7// Truncate possibly-negative values in a signed way8// CHECK-LABEL: func @test_addi_neg9// CHECK: %[[POS:.*]] = test.with_bounds {smax = 1 : index, smin = 0 : index, umax = 1 : index, umin = 0 : index} : index10// CHECK: %[[NEG:.*]] = test.with_bounds {smax = 0 : index, smin = -1 : index, umax = -1 : index, umin = 0 : index} : index11// CHECK: %[[POS_I8:.*]] = arith.index_castui %[[POS]] : index to i812// CHECK: %[[NEG_I8:.*]] = arith.index_cast %[[NEG]] : index to i813// CHECK: %[[RES_I8:.*]] = arith.addi %[[POS_I8]], %[[NEG_I8]] : i814// CHECK: %[[RES:.*]] = arith.index_cast %[[RES_I8]] : i8 to index15// CHECK: return %[[RES]] : index16func.func @test_addi_neg() -> index {17 %0 = test.with_bounds { umin = 0 : index, umax = 1 : index, smin = 0 : index, smax = 1 : index } : index18 %1 = test.with_bounds { umin = 0 : index, umax = -1 : index, smin = -1 : index, smax = 0 : index } : index19 %2 = arith.addi %0, %1 : index20 return %2 : index21}22 23// CHECK-LABEL: func @test_addi24// CHECK: %[[A:.*]] = test.with_bounds {smax = 5 : index, smin = 4 : index, umax = 5 : index, umin = 4 : index} : index25// CHECK: %[[B:.*]] = test.with_bounds {smax = 7 : index, smin = 6 : index, umax = 7 : index, umin = 6 : index} : index26// CHECK: %[[A_CASTED:.*]] = arith.index_castui %[[A]] : index to i827// CHECK: %[[B_CASTED:.*]] = arith.index_castui %[[B]] : index to i828// CHECK: %[[RES:.*]] = arith.addi %[[A_CASTED]], %[[B_CASTED]] : i829// CHECK: %[[RES_CASTED:.*]] = arith.index_castui %[[RES]] : i8 to index30// CHECK: return %[[RES_CASTED]] : index31func.func @test_addi() -> index {32 %0 = test.with_bounds { umin = 4 : index, umax = 5 : index, smin = 4 : index, smax = 5 : index } : index33 %1 = test.with_bounds { umin = 6 : index, umax = 7 : index, smin = 6 : index, smax = 7 : index } : index34 %2 = arith.addi %0, %1 : index35 return %2 : index36}37 38// CHECK-LABEL: func @test_addi_vec39// CHECK: %[[A:.*]] = test.with_bounds {smax = 5 : index, smin = 4 : index, umax = 5 : index, umin = 4 : index} : vector<4xindex>40// CHECK: %[[B:.*]] = test.with_bounds {smax = 7 : index, smin = 6 : index, umax = 7 : index, umin = 6 : index} : vector<4xindex>41// CHECK: %[[A_CASTED:.*]] = arith.index_castui %[[A]] : vector<4xindex> to vector<4xi8>42// CHECK: %[[B_CASTED:.*]] = arith.index_castui %[[B]] : vector<4xindex> to vector<4xi8>43// CHECK: %[[RES:.*]] = arith.addi %[[A_CASTED]], %[[B_CASTED]] : vector<4xi8>44// CHECK: %[[RES_CASTED:.*]] = arith.index_castui %[[RES]] : vector<4xi8> to vector<4xindex>45// CHECK: return %[[RES_CASTED]] : vector<4xindex>46func.func @test_addi_vec() -> vector<4xindex> {47 %0 = test.with_bounds { umin = 4 : index, umax = 5 : index, smin = 4 : index, smax = 5 : index } : vector<4xindex>48 %1 = test.with_bounds { umin = 6 : index, umax = 7 : index, smin = 6 : index, smax = 7 : index } : vector<4xindex>49 %2 = arith.addi %0, %1 : vector<4xindex>50 return %2 : vector<4xindex>51}52 53// CHECK-LABEL: func @test_addi_i6454// CHECK: %[[A:.*]] = test.with_bounds {smax = 5 : i64, smin = 4 : i64, umax = 5 : i64, umin = 4 : i64} : i6455// CHECK: %[[B:.*]] = test.with_bounds {smax = 7 : i64, smin = 6 : i64, umax = 7 : i64, umin = 6 : i64} : i6456// CHECK: %[[A_CASTED:.*]] = arith.trunci %[[A]] : i64 to i857// CHECK: %[[B_CASTED:.*]] = arith.trunci %[[B]] : i64 to i858// CHECK: %[[RES:.*]] = arith.addi %[[A_CASTED]], %[[B_CASTED]] : i859// CHECK: %[[RES_CASTED:.*]] = arith.extui %[[RES]] : i8 to i6460// CHECK: return %[[RES_CASTED]] : i6461func.func @test_addi_i64() -> i64 {62 %0 = test.with_bounds { umin = 4 : i64, umax = 5 : i64, smin = 4 : i64, smax = 5 : i64 } : i6463 %1 = test.with_bounds { umin = 6 : i64, umax = 7 : i64, smin = 6 : i64, smax = 7 : i64 } : i6464 %2 = arith.addi %0, %1 : i6465 return %2 : i6466}67 68// CHECK-LABEL: func @test_cmpi69// CHECK: %[[A:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : index70// CHECK: %[[B:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : index71// CHECK: %[[A_CASTED:.*]] = arith.index_castui %[[A]] : index to i872// CHECK: %[[B_CASTED:.*]] = arith.index_castui %[[B]] : index to i873// CHECK: %[[RES:.*]] = arith.cmpi slt, %[[A_CASTED]], %[[B_CASTED]] : i874// CHECK: return %[[RES]] : i175func.func @test_cmpi() -> i1 {76 %0 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : index77 %1 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : index78 %2 = arith.cmpi slt, %0, %1 : index79 return %2 : i180}81 82// CHECK-LABEL: func @test_cmpi_vec83// CHECK: %[[A:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : vector<4xindex>84// CHECK: %[[B:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : vector<4xindex>85// CHECK: %[[A_CASTED:.*]] = arith.index_castui %[[A]] : vector<4xindex> to vector<4xi8>86// CHECK: %[[B_CASTED:.*]] = arith.index_castui %[[B]] : vector<4xindex> to vector<4xi8>87// CHECK: %[[RES:.*]] = arith.cmpi slt, %[[A_CASTED]], %[[B_CASTED]] : vector<4xi8>88// CHECK: return %[[RES]] : vector<4xi1>89func.func @test_cmpi_vec() -> vector<4xi1> {90 %0 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : vector<4xindex>91 %1 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : vector<4xindex>92 %2 = arith.cmpi slt, %0, %1 : vector<4xindex>93 return %2 : vector<4xi1>94}95 96// CHECK-LABEL: func @test_add_cmpi97// CHECK: %[[A:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : index98// CHECK: %[[B:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : index99// CHECK: %[[C:.*]] = test.with_bounds {smax = 10 : index, smin = 0 : index, umax = 10 : index, umin = 0 : index} : index100// CHECK: %[[A_CASTED:.*]] = arith.index_castui %[[A]] : index to i8101// CHECK: %[[B_CASTED:.*]] = arith.index_castui %[[B]] : index to i8102// CHECK: %[[RES1:.*]] = arith.addi %[[A_CASTED]], %[[B_CASTED]] : i8103// CHECK: %[[C_CASTED:.*]] = arith.index_castui %[[C]] : index to i8104// CHECK: %[[RES2:.*]] = arith.cmpi slt, %[[C_CASTED]], %[[RES1]] : i8105// CHECK: return %[[RES2]] : i1106func.func @test_add_cmpi() -> i1 {107 %0 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : index108 %1 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : index109 %3 = test.with_bounds { umin = 0 : index, umax = 10 : index, smin = 0 : index, smax = 10 : index } : index110 %4 = arith.addi %0, %1 : index111 %5 = arith.cmpi slt, %3, %4 : index112 return %5 : i1113}114 115// CHECK-LABEL: func @test_add_cmpi_i64116// CHECK: %[[A:.*]] = test.with_bounds {smax = 10 : i64, smin = 0 : i64, umax = 10 : i64, umin = 0 : i64} : i64117// CHECK: %[[B:.*]] = test.with_bounds {smax = 10 : i64, smin = 0 : i64, umax = 10 : i64, umin = 0 : i64} : i64118// CHECK: %[[C:.*]] = test.with_bounds {smax = 10 : i64, smin = 0 : i64, umax = 10 : i64, umin = 0 : i64} : i64119// CHECK: %[[A_CASTED:.*]] = arith.trunci %[[A]] : i64 to i8120// CHECK: %[[B_CASTED:.*]] = arith.trunci %[[B]] : i64 to i8121// CHECK: %[[RES1:.*]] = arith.addi %[[A_CASTED]], %[[B_CASTED]] : i8122// CHECK: %[[C_CASTED:.*]] = arith.trunci %[[C]] : i64 to i8123// CHECK: %[[RES2:.*]] = arith.cmpi slt, %[[C_CASTED]], %[[RES1]] : i8124// CHECK: return %[[RES2]] : i1125func.func @test_add_cmpi_i64() -> i1 {126 %0 = test.with_bounds { umin = 0 : i64, umax = 10 : i64, smin = 0 : i64, smax = 10 : i64 } : i64127 %1 = test.with_bounds { umin = 0 : i64, umax = 10 : i64, smin = 0 : i64, smax = 10 : i64 } : i64128 %3 = test.with_bounds { umin = 0 : i64, umax = 10 : i64, smin = 0 : i64, smax = 10 : i64 } : i64129 %4 = arith.addi %0, %1 : i64130 %5 = arith.cmpi slt, %3, %4 : i64131 return %5 : i1132}133 134//===----------------------------------------------------------------------===//135// arith.addi136//===----------------------------------------------------------------------===//137 138// CHECK-LABEL: func.func @addi_extui_i8139// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)140// CHECK-NEXT: %[[EXT0:.+]] = arith.extui %[[ARG0]] : i8 to i32141// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32142// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16143// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16144// CHECK-NEXT: %[[ADD:.+]] = arith.addi %[[LHS]], %[[RHS]] : i16145// CHECK-NEXT: %[[RET:.+]] = arith.extui %[[ADD]] : i16 to i32146// CHECK-NEXT: return %[[RET]] : i32147func.func @addi_extui_i8(%lhs: i8, %rhs: i8) -> i32 {148 %a = arith.extui %lhs : i8 to i32149 %b = arith.extui %rhs : i8 to i32150 %r = arith.addi %a, %b : i32151 return %r : i32152}153 154// This can be optimized to i16 since we're dealing in [-128, 127] + [0, 255],155// which is [-128, 382]156//157// CHECK-LABEL: func.func @addi_mixed_ext_i8158// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)159// CHECK-NEXT: %[[EXT0:.+]] = arith.extsi %[[ARG0]] : i8 to i32160// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32161// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16162// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16163// CHECK-NEXT: %[[ADD:.+]] = arith.addi %[[LHS]], %[[RHS]] : i16164// CHECK-NEXT: %[[RET:.+]] = arith.extsi %[[ADD]] : i16 to i32165// CHECK-NEXT: return %[[RET]] : i32166func.func @addi_mixed_ext_i8(%lhs: i8, %rhs: i8) -> i32 {167 %a = arith.extsi %lhs : i8 to i32168 %b = arith.extui %rhs : i8 to i32169 %r = arith.addi %a, %b : i32170 return %r : i32171}172 173// This case should not get optimized because we cannot reduce the bitwidth174// below i16, given the pass options set.175//176// CHECK-LABEL: func.func @addi_extsi_i16177// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)178// CHECK-NEXT: %[[EXT0:.+]] = arith.extsi %[[ARG0]] : i8 to i16179// CHECK-NEXT: %[[EXT1:.+]] = arith.extsi %[[ARG1]] : i8 to i16180// CHECK-NEXT: %[[ADD:.+]] = arith.addi %[[EXT0]], %[[EXT1]] : i16181// CHECK-NEXT: return %[[ADD]] : i16182func.func @addi_extsi_i16(%lhs: i8, %rhs: i8) -> i16 {183 %a = arith.extsi %lhs : i8 to i16184 %b = arith.extsi %rhs : i8 to i16185 %r = arith.addi %a, %b : i16186 return %r : i16187}188 189//===----------------------------------------------------------------------===//190// arith.subi191//===----------------------------------------------------------------------===//192 193// CHECK-LABEL: func.func @subi_extui_i8194// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)195// CHECK-NEXT: %[[EXT0:.+]] = arith.extui %[[ARG0]] : i8 to i32196// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32197// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16198// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16199// CHECK-NEXT: %[[SUB:.+]] = arith.subi %[[LHS]], %[[RHS]] : i16200// CHECK-NEXT: %[[RET:.+]] = arith.extsi %[[SUB]] : i16 to i32201// CHECK-NEXT: return %[[RET]] : i32202func.func @subi_extui_i8(%lhs: i8, %rhs: i8) -> i32 {203 %a = arith.extui %lhs : i8 to i32204 %b = arith.extui %rhs : i8 to i32205 %r = arith.subi %a, %b : i32206 return %r : i32207}208 209// Despite the mixed sign and zero extensions, we can optimize here210//211// CHECK-LABEL: func.func @subi_mixed_ext_i8212// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)213// CHECK-NEXT: %[[EXT0:.+]] = arith.extsi %[[ARG0]] : i8 to i32214// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32215// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16216// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16217// CHECK-NEXT: %[[ADD:.+]] = arith.subi %[[LHS]], %[[RHS]] : i16218// CHECK-NEXT: %[[RET:.+]] = arith.extsi %[[ADD]] : i16 to i32219// CHECK-NEXT: return %[[RET]] : i32220func.func @subi_mixed_ext_i8(%lhs: i8, %rhs: i8) -> i32 {221 %a = arith.extsi %lhs : i8 to i32222 %b = arith.extui %rhs : i8 to i32223 %r = arith.subi %a, %b : i32224 return %r : i32225}226 227//===----------------------------------------------------------------------===//228// arith.muli229//===----------------------------------------------------------------------===//230 231// CHECK-LABEL: func.func @muli_extui_i8232// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)233// CHECK-NEXT: %[[EXT0:.+]] = arith.extui %[[ARG0]] : i8 to i32234// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32235// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16236// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16237// CHECK-NEXT: %[[MUL:.+]] = arith.muli %[[LHS]], %[[RHS]] : i16238// CHECK-NEXT: %[[RET:.+]] = arith.extui %[[MUL]] : i16 to i32239// CHECK-NEXT: return %[[RET]] : i32240func.func @muli_extui_i8(%lhs: i8, %rhs: i8) -> i32 {241 %a = arith.extui %lhs : i8 to i32242 %b = arith.extui %rhs : i8 to i32243 %r = arith.muli %a, %b : i32244 return %r : i32245}246 247// We do not expect this case to be optimized because given n-bit operands,248// arith.muli produces 2n bits of result.249//250// CHECK-LABEL: func.func @muli_extsi_i32251// CHECK-SAME: (%[[ARG0:.+]]: i16, %[[ARG1:.+]]: i16)252// CHECK-NEXT: %[[LHS:.+]] = arith.extsi %[[ARG0]] : i16 to i32253// CHECK-NEXT: %[[RHS:.+]] = arith.extsi %[[ARG1]] : i16 to i32254// CHECK-NEXT: %[[RET:.+]] = arith.muli %[[LHS]], %[[RHS]] : i32255// CHECK-NEXT: return %[[RET]] : i32256func.func @muli_extsi_i32(%lhs: i16, %rhs: i16) -> i32 {257 %a = arith.extsi %lhs : i16 to i32258 %b = arith.extsi %rhs : i16 to i32259 %r = arith.muli %a, %b : i32260 return %r : i32261}262 263// The mixed extensions mean that we have [-128, 127] * [0, 255], which can264// be computed exactly in i16.265//266// CHECK-LABEL: func.func @muli_mixed_ext_i8267// CHECK-SAME: (%[[ARG0:.+]]: i8, %[[ARG1:.+]]: i8)268// CHECK-NEXT: %[[EXT0:.+]] = arith.extsi %[[ARG0]] : i8 to i32269// CHECK-NEXT: %[[EXT1:.+]] = arith.extui %[[ARG1]] : i8 to i32270// CHECK-NEXT: %[[LHS:.+]] = arith.trunci %[[EXT0]] : i32 to i16271// CHECK-NEXT: %[[RHS:.+]] = arith.trunci %[[EXT1]] : i32 to i16272// CHECK-NEXT: %[[MUL:.+]] = arith.muli %[[LHS]], %[[RHS]] : i16273// CHECK-NEXT: %[[RET:.+]] = arith.extsi %[[MUL]] : i16 to i32274// CHECK-NEXT: return %[[RET]] : i32275func.func @muli_mixed_ext_i8(%lhs: i8, %rhs: i8) -> i32 {276 %a = arith.extsi %lhs : i8 to i32277 %b = arith.extui %rhs : i8 to i32278 %r = arith.muli %a, %b : i32279 return %r : i32280}281 282// Can't reduce width here since we need the extra bits283// CHECK-LABEL: func.func @i32_overflows_to_index284// CHECK-SAME: (%[[ARG0:.+]]: i32)285// CHECK: %[[CLAMPED:.+]] = arith.maxsi %[[ARG0]], %{{.*}} : i32286// CHECK: %[[CAST:.+]] = arith.index_castui %[[CLAMPED]] : i32 to index287// CHECK: %[[MUL:.+]] = arith.muli %[[CAST]], %{{.*}} : index288// CHECK: return %[[MUL]] : index289func.func @i32_overflows_to_index(%arg0: i32) -> index {290 %c0_i32 = arith.constant 0 : i32291 %c4 = arith.constant 4 : index292 %clamped = arith.maxsi %arg0, %c0_i32 : i32293 %cast = arith.index_castui %clamped : i32 to index294 %mul = arith.muli %cast, %c4 : index295 return %mul : index296}297 298// Can't reduce width here since we need the extra bits299// CHECK-LABEL: func.func @i32_overflows_to_i64300// CHECK-SAME: (%[[ARG0:.+]]: i32)301// CHECK: %[[CLAMPED:.+]] = arith.maxsi %[[ARG0]], %{{.*}} : i32302// CHECK: %[[CAST:.+]] = arith.extui %[[CLAMPED]] : i32 to i64303// CHECK: %[[MUL:.+]] = arith.muli %[[CAST]], %{{.*}} : i64304// CHECK: return %[[MUL]] : i64305func.func @i32_overflows_to_i64(%arg0: i32) -> i64 {306 %c0_i32 = arith.constant 0 : i32307 %c4_i64 = arith.constant 4 : i64308 %clamped = arith.maxsi %arg0, %c0_i32 : i32309 %cast = arith.extui %clamped : i32 to i64310 %mul = arith.muli %cast, %c4_i64 : i64311 return %mul : i64312}313 314// Motivating example for negative number support, added as a test case315// and simplified316// CHECK-LABEL: func.func @clamp_to_loop_bound_and_id()317// CHECK: %[[TID:.+]] = test.with_bounds318// CHECK-SAME: umax = 63319// CHECK: %[[BOUND:.+]] = test.with_bounds320// CHECK-SAME: umax = 112321// CHECK: scf.for %[[ARG0:.+]] = %{{.*}} to %[[BOUND]] step %{{.*}}322// CHECK-DAG: %[[BOUND_I8:.+]] = arith.index_castui %[[BOUND]] : index to i8323// CHECK-DAG: %[[ARG0_I8:.+]] = arith.index_castui %[[ARG0]] : index to i8324// CHECK: %[[V0_I8:.+]] = arith.subi %[[BOUND_I8]], %[[ARG0_I8]] : i8325// CHECK: %[[V1_I8:.+]] = arith.minsi %[[V0_I8]], %{{.*}} : i8326// CHECK: %[[V1_INDEX:.+]] = arith.index_cast %[[V1_I8]] : i8 to index327// CHECK: %[[V1_I16:.+]] = arith.index_cast %[[V1_INDEX]] : index to i16328// CHECK: %[[TID_I16:.+]] = arith.index_castui %[[TID]] : index to i16329// CHECK: %[[V2_I16:.+]] = arith.subi %[[V1_I16]], %[[TID_I16]] : i16330// CHECK: %[[V3:.+]] = arith.cmpi slt, %[[V2_I16]], %{{.*}} : i16331// CHECK: scf.if %[[V3]]332func.func @clamp_to_loop_bound_and_id() {333 %c0 = arith.constant 0 : index334 %c16 = arith.constant 16 : index335 %c64 = arith.constant 64 : index336 337 %tid = test.with_bounds {smin = 0 : index, smax = 63 : index, umin = 0 : index, umax = 63 : index} : index338 %bound = test.with_bounds {smin = 16 : index, smax = 112 : index, umin = 16 : index, umax = 112 : index} : index339 scf.for %arg0 = %c16 to %bound step %c64 {340 %0 = arith.subi %bound, %arg0 : index341 %1 = arith.minsi %0, %c64 : index342 %2 = arith.subi %1, %tid : index343 %3 = arith.cmpi slt, %2, %c0 : index344 scf.if %3 {345 vector.print str "sideeffect"346 }347 }348 return349}350