1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 2; RUN: opt < %s -passes=aggressive-instcombine -S | FileCheck %s 3 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 the 9; constraints from a signed min-max clamp. The clamp is a sequence of smin and 10; 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 order 13; 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 i8 28; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 2 29; 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 i32 34 %b = lshr i32 %a, 2 35 %b.trunc = trunc i32 %b to i8 36 ret i8 %b.trunc 37} 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 i8 45; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 2 46; 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 i32 51 %b = lshr i32 %a, 2 52 %b.trunc = trunc i32 %b to i8 53 ret i8 %b.trunc 54} 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 i8 64; CHECK-NEXT: [[B:%.*]] = add i8 [[A]], 2 65; 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 i32 70 %b = add i32 %a, 2 71 %b.trunc = trunc i32 %b to i8 72 ret i8 %b.trunc 73} 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 i8 81; CHECK-NEXT: [[B:%.*]] = add i8 [[A]], 2 82; 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 i32 87 %b = add i32 %a, 2 88 %b.trunc = trunc i32 %b to i8 89 ret i8 %b.trunc 90} 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 i32 101; CHECK-NEXT: [[B:%.*]] = lshr i32 [[A]], 2 102; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8 103; 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 i32 108 %b = lshr i32 %a, 2 109 %b.trunc = trunc i32 %b to i8 110 ret i8 %b.trunc 111} 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 i32 119; CHECK-NEXT: [[B:%.*]] = lshr i32 [[A]], 2 120; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8 121; 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 i32 126 %b = lshr i32 %a, 2 127 %b.trunc = trunc i32 %b to i8 128 ret i8 %b.trunc 129} 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]], 2 138; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 139; 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 i32 144 %b = lshr i32 %a, 2 145 %b.trunc = trunc i32 %b to i8 146 ret i8 %b.trunc 147} 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]], 2 155; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 156; 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 i32 161 %b = lshr i32 %a, 2 162 %b.trunc = trunc i32 %b to i8 163 ret i8 %b.trunc 164} 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]], 2 174; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 175; 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 i32 180 %b = lshr i32 %a, 2 181 %b.trunc = trunc i32 %b to i8 182 ret i8 %b.trunc 183} 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]], 2 191; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 192; 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 i32 197 %b = lshr i32 %a, 2 198 %b.trunc = trunc i32 %b to i8 199 ret i8 %b.trunc 200} 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.trunc 218} 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.trunc 235} 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]], 7 247; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[SHR]] to i8 248; 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 i32 253 %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 i32 256 %mul = mul nuw nsw i32 %x.clamp, %y.clamp 257 %shr = lshr i32 %mul, 7 258 %trunc= trunc nuw nsw i32 %shr to i8 259 ret i8 %trunc 260} 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 i8 271; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 7 272; 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 i32 277 %b = lshr i32 %a, 7 278 %b.trunc = trunc i32 %b to i8 279 ret i8 %b.trunc 280} 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 i8 289; CHECK-NEXT: [[B:%.*]] = lshr i8 [[A]], 7 290; 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 i32 295 %b = lshr i32 %a, 7 296 %b.trunc = trunc i32 %b to i8 297 ret i8 %b.trunc 298} 299 300; This should get optimized. We test here if the optimization works correctly 301; 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]], 2 308; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 309; 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 i32 314 %b = lshr i32 %a, 2 315 %b.trunc = trunc i32 %b to i8 316 ret i8 %b.trunc 317} 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]], 2 326; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 327; 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 i32 332 %b = lshr i32 %a, 2 333 %b.trunc = trunc i32 %b to i8 334 ret i8 %b.trunc 335} 336 337; The following 3 tests check the situation where min and max are minimal and 338; 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]], 2 345; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i16 [[B]] to i8 346; 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 i32 351 %b = lshr i32 %a, 2 352 %b.trunc = trunc i32 %b to i8 353 ret i8 %b.trunc 354} 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]], 2 362; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i32 [[B]] to i8 363; 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 i64 368 %b = lshr i64 %a, 2 369 %b.trunc = trunc i64 %b to i8 370 ret i8 %b.trunc 371} 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]], 2 379; CHECK-NEXT: [[B_TRUNC:%.*]] = trunc i64 [[B]] to i8 380; 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 i128 385 %b = lshr i128 %a, 2 386 %b.trunc = trunc i128 %b to i8 387 ret i8 %b.trunc 388} 389