389 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 52; RUN: opt < %s -passes=aggressive-instcombine -S | FileCheck %s3 4; The LIT tests rely on i32, i16 and i8 being valid machine types.5; The bounds checking tests require also i64 and i128.6target datalayout = "n8:16:32:64:128"7 8; This LIT test checks if TruncInstCombine pass correctly recognizes the9; constraints from a signed min-max clamp. The clamp is a sequence of smin and10; smax instructions limiting a variable into a range, smin <= x <= smax.11;12; Each LIT test (except the last ones) has two versions depending on the order13; of smin and smax:14; a) y = smax(smin(x, upper_limit), lower_limit)15; b) y = smin(smax(x, lower_limit), upper_limit)16;17; The clamp is used in TruncInstCombine.cpp pass (as part of aggressive-instcombine)18; to optimize extensions and truncations of lshr. This is what is tested here.19; The pass also optimizes extensions and truncations of other binary operators,20; but in such cases the smin-smax clamp may not be used.21 22define i8 @test_0a(i16 %x) {23; CHECK-LABEL: define i8 @test_0a(24; CHECK-SAME: i16 [[X:%.*]]) {25; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 31)26; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 0)27; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i828; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 229; CHECK-NEXT: ret i8 [[B]]30;31 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 31)32 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 0)33 %a = sext i16 %2 to i3234 %b = lshr i32 %a, 235 %b.trunc = trunc i32 %b to i836 ret i8 %b.trunc37}38 39define i8 @test_0b(i16 %x) {40; CHECK-LABEL: define i8 @test_0b(41; CHECK-SAME: i16 [[X:%.*]]) {42; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smax.i16(i16 [[X]], i16 0)43; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP1]], i16 31)44; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i845; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 246; CHECK-NEXT: ret i8 [[B]]47;48 %1 = tail call i16 @llvm.smax.i16(i16 %x, i16 0)49 %2 = tail call i16 @llvm.smin.i16(i16 %1, i16 31)50 %a = sext i16 %2 to i3251 %b = lshr i32 %a, 252 %b.trunc = trunc i32 %b to i853 ret i8 %b.trunc54}55 56; The following two tests contain add instead of lshr.57; The optimization works here as well.58define i8 @test_1a(i16 %x) {59; CHECK-LABEL: define i8 @test_1a(60; CHECK-SAME: i16 [[X:%.*]]) {61; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 31)62; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 0)63; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i864; CHECK-NEXT: [[B:%.*]] = add i8 [[A]], 265; CHECK-NEXT: ret i8 [[B]]66;67 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 31)68 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 0)69 %a = sext i16 %2 to i3270 %b = add i32 %a, 271 %b.trunc = trunc i32 %b to i872 ret i8 %b.trunc73}74 75define i8 @test_1b(i16 %x) {76; CHECK-LABEL: define i8 @test_1b(77; CHECK-SAME: i16 [[X:%.*]]) {78; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smax.i16(i16 [[X]], i16 0)79; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP1]], i16 31)80; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i881; CHECK-NEXT: [[B:%.*]] = add i8 [[A]], 282; CHECK-NEXT: ret i8 [[B]]83;84 %1 = tail call i16 @llvm.smax.i16(i16 %x, i16 0)85 %2 = tail call i16 @llvm.smin.i16(i16 %1, i16 31)86 %a = sext i16 %2 to i3287 %b = add i32 %a, 288 %b.trunc = trunc i32 %b to i889 ret i8 %b.trunc90}91 92; Tests for clamping with negative min and max.93 94; With sext no optimization occurs.95define i8 @test_2a(i16 %x) {96; CHECK-LABEL: define i8 @test_2a(97; CHECK-SAME: i16 [[X:%.*]]) {98; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 -1)99; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 -31)100; CHECK-NEXT: [[A:%.*]] = sext i16 [[TMP2]] to i32101; CHECK-NEXT: [[B:%.*]] = lshr i32 [[A]], 2102; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8103; CHECK-NEXT: ret i8 [[B_TRUNC]]104;105 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 -1)106 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 -31)107 %a = sext i16 %2 to i32108 %b = lshr i32 %a, 2109 %b.trunc = trunc i32 %b to i8110 ret i8 %b.trunc111}112 113define i8 @test_2b(i16 %x) {114; CHECK-LABEL: define i8 @test_2b(115; CHECK-SAME: i16 [[X:%.*]]) {116; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smax.i16(i16 [[X]], i16 -31)117; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP1]], i16 -1)118; CHECK-NEXT: [[A:%.*]] = sext i16 [[TMP2]] to i32119; CHECK-NEXT: [[B:%.*]] = lshr i32 [[A]], 2120; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8121; CHECK-NEXT: ret i8 [[B_TRUNC]]122;123 %1 = tail call i16 @llvm.smax.i16(i16 %x, i16 -31)124 %2 = tail call i16 @llvm.smin.i16(i16 %1, i16 -1)125 %a = sext i16 %2 to i32126 %b = lshr i32 %a, 2127 %b.trunc = trunc i32 %b to i8128 ret i8 %b.trunc129}130 131; With zext the optimization occurs.132define i8 @test_2c(i16 %x) {133; CHECK-LABEL: define i8 @test_2c(134; CHECK-SAME: i16 [[X:%.*]]) {135; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 -1)136; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 -31)137; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2138; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8139; CHECK-NEXT: ret i8 [[B_TRUNC]]140;141 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 -1)142 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 -31)143 %a = zext i16 %2 to i32144 %b = lshr i32 %a, 2145 %b.trunc = trunc i32 %b to i8146 ret i8 %b.trunc147}148 149define i8 @test_2d(i16 %x) {150; CHECK-LABEL: define i8 @test_2d(151; CHECK-SAME: i16 [[X:%.*]]) {152; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smax.i16(i16 [[X]], i16 -31)153; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP1]], i16 -1)154; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2155; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8156; CHECK-NEXT: ret i8 [[B_TRUNC]]157;158 %1 = tail call i16 @llvm.smax.i16(i16 %x, i16 -31)159 %2 = tail call i16 @llvm.smin.i16(i16 %1, i16 -1)160 %a = zext i16 %2 to i32161 %b = lshr i32 %a, 2162 %b.trunc = trunc i32 %b to i8163 ret i8 %b.trunc164}165 166; Tests for clamping with mixed-signed min and max.167; With zext the optimization occurs.168define i8 @test_3a(i16 %x) {169; CHECK-LABEL: define i8 @test_3a(170; CHECK-SAME: i16 [[X:%.*]]) {171; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 31)172; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 -31)173; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2174; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8175; CHECK-NEXT: ret i8 [[B_TRUNC]]176;177 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 31)178 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 -31)179 %a = zext i16 %2 to i32180 %b = lshr i32 %a, 2181 %b.trunc = trunc i32 %b to i8182 ret i8 %b.trunc183}184 185define i8 @test_3b(i16 %x) {186; CHECK-LABEL: define i8 @test_3b(187; CHECK-SAME: i16 [[X:%.*]]) {188; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smax.i16(i16 [[X]], i16 -31)189; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP1]], i16 31)190; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2191; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8192; CHECK-NEXT: ret i8 [[B_TRUNC]]193;194 %1 = tail call i16 @llvm.smax.i16(i16 %x, i16 -31)195 %2 = tail call i16 @llvm.smin.i16(i16 %1, i16 31)196 %a = zext i16 %2 to i32197 %b = lshr i32 %a, 2198 %b.trunc = trunc i32 %b to i8199 ret i8 %b.trunc200}201 202; Optimizations with vector types.203define <16 x i8> @test_vec_1a(<16 x i16> %x) {204; CHECK-LABEL: define <16 x i8> @test_vec_1a(205; CHECK-SAME: <16 x i16> [[X:%.*]]) {206; CHECK-NEXT: [[TMP1:%.*]] = tail call <16 x i16> @llvm.smin.v16i16(<16 x i16> [[X]], <16 x i16> splat (i16 127))207; CHECK-NEXT: [[TMP2:%.*]] = tail call <16 x i16> @llvm.smax.v16i16(<16 x i16> [[TMP1]], <16 x i16> zeroinitializer)208; CHECK-NEXT: [[A:%.*]] = trunc <16 x i16> [[TMP2]] to <16 x i8>209; CHECK-NEXT: [[B:%.*]] = lshr <16 x i8> [[A]], splat (i8 2)210; CHECK-NEXT: ret <16 x i8> [[B]]211;212 %1 = tail call <16 x i16> @llvm.smin.v16i16(<16 x i16> %x, <16 x i16> splat (i16 127))213 %2 = tail call <16 x i16> @llvm.smax.v16i16(<16 x i16> %1, <16 x i16> zeroinitializer)214 %a = sext <16 x i16> %2 to <16 x i32>215 %b = lshr <16 x i32> %a, splat (i32 2)216 %b.trunc = trunc <16 x i32> %b to <16 x i8>217 ret <16 x i8> %b.trunc218}219 220define <16 x i8> @test_vec_1b(<16 x i16> %x) {221; CHECK-LABEL: define <16 x i8> @test_vec_1b(222; CHECK-SAME: <16 x i16> [[X:%.*]]) {223; CHECK-NEXT: [[TMP1:%.*]] = tail call <16 x i16> @llvm.smax.v16i16(<16 x i16> [[X]], <16 x i16> zeroinitializer)224; CHECK-NEXT: [[TMP2:%.*]] = tail call <16 x i16> @llvm.smin.v16i16(<16 x i16> [[TMP1]], <16 x i16> splat (i16 127))225; CHECK-NEXT: [[A:%.*]] = trunc <16 x i16> [[TMP2]] to <16 x i8>226; CHECK-NEXT: [[B:%.*]] = lshr <16 x i8> [[A]], splat (i8 2)227; CHECK-NEXT: ret <16 x i8> [[B]]228;229 %1 = tail call <16 x i16> @llvm.smax.v16i16(<16 x i16> %x, <16 x i16> zeroinitializer)230 %2 = tail call <16 x i16> @llvm.smin.v16i16(<16 x i16> %1, <16 x i16> splat (i16 127))231 %a = sext <16 x i16> %2 to <16 x i32>232 %b = lshr <16 x i32> %a, splat (i32 2)233 %b.trunc = trunc <16 x i32> %b to <16 x i8>234 ret <16 x i8> %b.trunc235}236 237; A longer test that was the original motivation for the smin-smax clamping.238define i8 @test_final(i16 %x, i16 %y) {239; CHECK-LABEL: define i8 @test_final(240; CHECK-SAME: i16 [[X:%.*]], i16 [[Y:%.*]]) {241; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 127)242; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 0)243; CHECK-NEXT: [[TMP3:%.*]] = tail call i16 @llvm.smax.i16(i16 [[Y]], i16 0)244; CHECK-NEXT: [[TMP4:%.*]] = tail call i16 @llvm.smin.i16(i16 [[TMP3]], i16 127)245; CHECK-NEXT: [[MUL:%.*]] = mul i16 [[TMP2]], [[TMP4]]246; CHECK-NEXT: [[SHR:%.*]] = lshr i16 [[MUL]], 7247; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[SHR]] to i8248; CHECK-NEXT: ret i8 [[TRUNC]]249;250 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 127)251 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 0)252 %x.clamp = zext nneg i16 %2 to i32253 %3 = tail call i16 @llvm.smax.i16(i16 %y, i16 0)254 %4 = tail call i16 @llvm.smin.i16(i16 %3, i16 127)255 %y.clamp = zext nneg i16 %4 to i32256 %mul = mul nuw nsw i32 %x.clamp, %y.clamp257 %shr = lshr i32 %mul, 7258 %trunc= trunc nuw nsw i32 %shr to i8259 ret i8 %trunc260}261 262; Range tests below check if the bounds are dealt with correctly.263 264; This gets optimized.265define i8 @test_bounds_1(i16 %x) {266; CHECK-LABEL: define i8 @test_bounds_1(267; CHECK-SAME: i16 [[X:%.*]]) {268; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 127)269; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 0)270; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i8271; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 7272; CHECK-NEXT: ret i8 [[B]]273;274 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 127)275 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 0)276 %a = sext i16 %2 to i32277 %b = lshr i32 %a, 7278 %b.trunc = trunc i32 %b to i8279 ret i8 %b.trunc280}281 282; While this does not.283define i8 @test_bounds_2(i16 %x) {284; CHECK-LABEL: define i8 @test_bounds_2(285; CHECK-SAME: i16 [[X:%.*]]) {286; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 128)287; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 0)288; CHECK-NEXT: [[A:%.*]] = trunc i16 [[TMP2]] to i8289; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 7290; CHECK-NEXT: ret i8 [[B]]291;292 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 128)293 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 0)294 %a = sext i16 %2 to i32295 %b = lshr i32 %a, 7296 %b.trunc = trunc i32 %b to i8297 ret i8 %b.trunc298}299 300; This should get optimized. We test here if the optimization works correctly301; if the upper limit is signed max int.302define i8 @test_bounds_3(i16 %x) {303; CHECK-LABEL: define i8 @test_bounds_3(304; CHECK-SAME: i16 [[X:%.*]]) {305; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 32767)306; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 32752)307; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2308; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8309; CHECK-NEXT: ret i8 [[B_TRUNC]]310;311 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 32767)312 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 32752)313 %a = sext i16 %2 to i32314 %b = lshr i32 %a, 2315 %b.trunc = trunc i32 %b to i8316 ret i8 %b.trunc317}318 319; Here min = 128 is greater than max = 0.320define i8 @test_bounds_4(i16 %x) {321; CHECK-LABEL: define i8 @test_bounds_4(322; CHECK-SAME: i16 [[X:%.*]]) {323; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 0)324; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 128)325; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2326; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8327; CHECK-NEXT: ret i8 [[B_TRUNC]]328;329 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 0)330 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 128)331 %a = sext i16 %2 to i32332 %b = lshr i32 %a, 2333 %b.trunc = trunc i32 %b to i8334 ret i8 %b.trunc335}336 337; The following 3 tests check the situation where min and max are minimal and338; maximal signed values. No transformations should occur here.339define i8 @test_bounds_5(i16 %x) {340; CHECK-LABEL: define i8 @test_bounds_5(341; CHECK-SAME: i16 [[X:%.*]]) {342; CHECK-NEXT: [[TMP1:%.*]] = tail call i16 @llvm.smin.i16(i16 [[X]], i16 32767)343; CHECK-NEXT: [[TMP2:%.*]] = tail call i16 @llvm.smax.i16(i16 [[TMP1]], i16 -32768)344; CHECK-NEXT: [[B:%.*]] = lshr i16 [[TMP2]], 2345; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8346; CHECK-NEXT: ret i8 [[B_TRUNC]]347;348 %1 = tail call i16 @llvm.smin.i16(i16 %x, i16 32767)349 %2 = tail call i16 @llvm.smax.i16(i16 %1, i16 -32768)350 %a = zext i16 %2 to i32351 %b = lshr i32 %a, 2352 %b.trunc = trunc i32 %b to i8353 ret i8 %b.trunc354}355 356define i8 @test_bounds_6(i32 %x) {357; CHECK-LABEL: define i8 @test_bounds_6(358; CHECK-SAME: i32 [[X:%.*]]) {359; CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.smin.i32(i32 [[X]], i32 2147483647)360; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[TMP1]], i32 -2147483648)361; CHECK-NEXT: [[B:%.*]] = lshr i32 [[TMP2]], 2362; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8363; CHECK-NEXT: ret i8 [[B_TRUNC]]364;365 %1 = tail call i32 @llvm.smin.i32(i32 %x, i32 2147483647)366 %2 = tail call i32 @llvm.smax.i32(i32 %1, i32 -2147483648)367 %a = zext i32 %2 to i64368 %b = lshr i64 %a, 2369 %b.trunc = trunc i64 %b to i8370 ret i8 %b.trunc371}372 373define i8 @test_bounds_7(i64 %x) {374; CHECK-LABEL: define i8 @test_bounds_7(375; CHECK-SAME: i64 [[X:%.*]]) {376; CHECK-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.smin.i64(i64 [[X]], i64 9223372036854775807)377; CHECK-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP1]], i64 -9223372036854775808)378; CHECK-NEXT: [[B:%.*]] = lshr i64 [[TMP2]], 2379; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i64 [[B]] to i8380; CHECK-NEXT: ret i8 [[B_TRUNC]]381;382 %1 = tail call i64 @llvm.smin.i64(i64 %x, i64 9223372036854775807)383 %2 = tail call i64 @llvm.smax.i64(i64 %1, i64 -9223372036854775808)384 %a = zext i64 %2 to i128385 %b = lshr i128 %a, 2386 %b.trunc = trunc i128 %b to i8387 ret i8 %b.trunc388}389