diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -77,6 +77,11 @@ uint64_t ValSum; }; +struct TruncShiftInfo { + MachineInstr *ShiftMI = nullptr; + LLT NewShiftTy; +}; + using BuildFnTy = std::function; struct MergeTruncStoresInfo { @@ -402,12 +407,9 @@ void applyCombineTruncOfExt(MachineInstr &MI, std::pair &MatchInfo); - /// Transform trunc (shl x, K) to shl (trunc x), - /// K => K < VT.getScalarSizeInBits(). - bool matchCombineTruncOfShl(MachineInstr &MI, - std::pair &MatchInfo); - void applyCombineTruncOfShl(MachineInstr &MI, - std::pair &MatchInfo); + // TODO DOCS + bool matchCombineTruncOfShift(MachineInstr &MI, TruncShiftInfo &MatchInfo); + void applyCombineTruncOfShift(MachineInstr &MI, TruncShiftInfo &MatchInfo); /// Transform G_MUL(x, -1) to G_SUB(0, x) void applyCombineMulByNegativeOne(MachineInstr &MI); diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -618,13 +618,12 @@ (apply [{ Helper.applyCombineTruncOfExt(*${root}, ${matchinfo}); }]) >; -// Fold trunc (shl x, K) -> shl (trunc x), K => K < VT.getScalarSizeInBits(). -def trunc_shl_matchinfo : GIDefMatchData<"std::pair">; -def trunc_shl: GICombineRule < - (defs root:$root, trunc_shl_matchinfo:$matchinfo), +def trunc_shift_matchinfo : GIDefMatchData<"TruncShiftInfo">; +def trunc_shift: GICombineRule < + (defs root:$root, trunc_shift_matchinfo:$matchinfo), (match (wip_match_opcode G_TRUNC):$root, - [{ return Helper.matchCombineTruncOfShl(*${root}, ${matchinfo}); }]), - (apply [{ Helper.applyCombineTruncOfShl(*${root}, ${matchinfo}); }]) + [{ return Helper.matchCombineTruncOfShift(*${root}, ${matchinfo}); }]), + (apply [{ Helper.applyCombineTruncOfShift(*${root}, ${matchinfo}); }]) >; // Transform (mul x, -1) -> (sub 0, x) @@ -1044,7 +1043,7 @@ known_bits_simplifications, ext_ext_fold, not_cmp_fold, opt_brcond_by_inverting_cond, unmerge_merge, fabs_fabs_fold, unmerge_cst, unmerge_dead_to_trunc, - unmerge_zext_to_zext, merge_unmerge, trunc_ext_fold, trunc_shl, + unmerge_zext_to_zext, merge_unmerge, trunc_ext_fold, trunc_shift, const_combines, xor_of_and_with_same_reg, ptr_add_with_zero, shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine, truncstore_merge, div_rem_to_divrem, funnel_shift_combines, diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -2259,44 +2259,89 @@ MI.eraseFromParent(); } -bool CombinerHelper::matchCombineTruncOfShl( - MachineInstr &MI, std::pair &MatchInfo) { +bool CombinerHelper::matchCombineTruncOfShift(MachineInstr &MI, + TruncShiftInfo &MatchInfo) { assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Expected a G_TRUNC"); Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); + + if (!MRI.hasOneNonDBGUse(SrcReg)) + return false; + + LLT SrcTy = MRI.getType(SrcReg); LLT DstTy = MRI.getType(DstReg); - Register ShiftSrc; - Register ShiftAmt; - - if (MRI.hasOneNonDBGUse(SrcReg) && - mi_match(SrcReg, MRI, m_GShl(m_Reg(ShiftSrc), m_Reg(ShiftAmt))) && - isLegalOrBeforeLegalizer( - {TargetOpcode::G_SHL, - {DstTy, getTargetLowering().getPreferredShiftAmountTy(DstTy)}})) { - KnownBits Known = KB->getKnownBits(ShiftAmt); - unsigned Size = DstTy.getSizeInBits(); - if (Known.countMaxActiveBits() <= Log2_32(Size)) { - MatchInfo = std::make_pair(ShiftSrc, ShiftAmt); - return true; - } + + MachineInstr *SrcMI = getDefIgnoringCopies(SrcReg, MRI); + unsigned SrcOpc = SrcMI->getOpcode(); + + if (SrcOpc == TargetOpcode::G_SHL) { + // For left shifts, we always want to do + // (trunc (shl X, K)) -> (shl (trunc X), K) + // when it's legal to do so. + if (!isLegalOrBeforeLegalizer( + {TargetOpcode::G_SHL, + {DstTy, getTargetLowering().getPreferredShiftAmountTy(DstTy)}})) + return false; + + KnownBits Known = KB->getKnownBits(SrcMI->getOperand(2).getReg()); + if (Known.getMaxValue().uge(DstTy.getScalarSizeInBits())) + return false; + + MatchInfo.NewShiftTy = DstTy; + MatchInfo.ShiftMI = SrcMI; + return true; + } + + if (SrcOpc == TargetOpcode::G_ASHR || SrcOpc == TargetOpcode::G_LSHR) { + // Try and reduce the shift when it's profitable according to the target. + LLT NewShiftTy = getTargetLowering().getPreferredShiftAmountTy(SrcTy); + const unsigned NewShiftSize = NewShiftTy.getScalarSizeInBits(); + if (SrcTy.getScalarSizeInBits() <= NewShiftSize || + DstTy.getScalarSizeInBits() >= NewShiftSize) + return false; + + if (!isLegalOrBeforeLegalizer({SrcOpc, {NewShiftTy, NewShiftTy}})) + return false; + + // Make sure we won't lose information by truncating the high bits. + KnownBits Known = KB->getKnownBits(SrcMI->getOperand(2).getReg()); + if (Known.getMaxValue().ugt(NewShiftTy.getScalarSizeInBits() - + DstTy.getScalarSizeInBits())) + return false; + + MatchInfo.NewShiftTy = NewShiftTy; + MatchInfo.ShiftMI = SrcMI; + return true; } + return false; } -void CombinerHelper::applyCombineTruncOfShl( - MachineInstr &MI, std::pair &MatchInfo) { - assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Expected a G_TRUNC"); - Register DstReg = MI.getOperand(0).getReg(); - Register SrcReg = MI.getOperand(1).getReg(); - LLT DstTy = MRI.getType(DstReg); - MachineInstr *SrcMI = MRI.getVRegDef(SrcReg); - - Register ShiftSrc = MatchInfo.first; - Register ShiftAmt = MatchInfo.second; +void CombinerHelper::applyCombineTruncOfShift(MachineInstr &MI, + TruncShiftInfo &MatchInfo) { Builder.setInstrAndDebugLoc(MI); - auto TruncShiftSrc = Builder.buildTrunc(DstTy, ShiftSrc); - Builder.buildShl(DstReg, TruncShiftSrc, ShiftAmt, SrcMI->getFlags()); - MI.eraseFromParent(); + + MachineInstr *ShiftMI = MatchInfo.ShiftMI; + LLT NewShiftTy = MatchInfo.NewShiftTy; + + Register Dst = MI.getOperand(0).getReg(); + LLT DstTy = MRI.getType(Dst); + + Register ShiftAmt = ShiftMI->getOperand(2).getReg(); + Register ShiftSrc = ShiftMI->getOperand(1).getReg(); + ShiftSrc = Builder.buildTrunc(NewShiftTy, ShiftSrc).getReg(0); + + Register NewShift = + Builder + .buildInstr(ShiftMI->getOpcode(), {NewShiftTy}, {ShiftSrc, ShiftAmt}) + .getReg(0); + + if (NewShiftTy == DstTy) + replaceRegWith(MRI, Dst, NewShift); + else + Builder.buildTrunc(Dst, NewShift); + + eraseInst(MI); } bool CombinerHelper::matchAnyExplicitUseIsUndef(MachineInstr &MI) { diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shift.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shift.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shift.mir @@ -0,0 +1,209 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -march=amdgcn -run-pass=amdgpu-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s + +--- +name: trunc_s32_shl_s64_5 +legalized: true +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0_vgpr1 + + ; CHECK-LABEL: name: trunc_s32_shl_s64_5 + ; CHECK: liveins: $vgpr0_vgpr1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $vgpr0_vgpr1 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[COPY]](s64) + ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[TRUNC]], [[C]](s32) + ; CHECK-NEXT: $vgpr0 = COPY [[SHL]](s32) + %0:_(s64) = COPY $vgpr0_vgpr1 + %1:_(s32) = G_CONSTANT i32 1 + %2:_(s64) = G_SHL %0:_, %1 + %3:_(s32) = G_TRUNC %2 + $vgpr0 = COPY %3 +... + +--- +name: trunc_s16_shl_s32_5 +legalized: true +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + + ; CHECK-LABEL: name: trunc_s16_shl_s32_5 + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C]](s32) + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SHL]](s32) + ; CHECK-NEXT: S_ENDPGM 0, implicit [[TRUNC]](s16) + %0:_(s32) = COPY $vgpr0 + %1:_(s32) = G_CONSTANT i32 1 + %2:_(s32) = G_SHL %0:_, %1 + %3:_(s16) = G_TRUNC %2 + S_ENDPGM 0, implicit %3 + +... + +--- +name: trunc_s16_shl_s64_5 +legalized: true +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0_vgpr1 + + ; CHECK-LABEL: name: trunc_s16_shl_s64_5 + ; CHECK: liveins: $vgpr0_vgpr1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $vgpr0_vgpr1 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C]](s32) + ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SHL]](s64) + ; CHECK-NEXT: S_ENDPGM 0, implicit [[TRUNC]](s16) + %0:_(s64) = COPY $vgpr0_vgpr1 + %1:_(s32) = G_CONSTANT i32 1 + %2:_(s64) = G_SHL %0:_, %1 + %3:_(s16) = G_TRUNC %2 + S_ENDPGM 0, implicit %3 + +... + +--- +name: s16_trunc_s64_lshr_16 +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + ; CHECK-LABEL: name: s16_trunc_s64_lshr_16 + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: %amt:_(s32) = G_CONSTANT i32 16 + ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[COPY]], %amt(s32) + ; CHECK-NEXT: %trunc:_(s16) = G_TRUNC [[LSHR]](s32) + ; CHECK-NEXT: %foo:_(s16) = G_CONSTANT i16 55 + ; CHECK-NEXT: %keep:_(s32) = G_MERGE_VALUES %trunc(s16), %foo(s16) + ; CHECK-NEXT: $vgpr0 = COPY %keep(s32) + %0:_(s32) = COPY $vgpr0 + %src:_(s64) = G_ZEXT %0 + %amt:_(s32) = G_CONSTANT i32 16 + %shift:_(s64) = G_LSHR %src, %amt + %trunc:_(s16) = G_TRUNC %shift + %foo:_(s16) = G_CONSTANT i16 55 + %keep:_(s32) = G_MERGE_VALUES %trunc, %foo + $vgpr0 = COPY %keep +... + +--- +name: s16_trunc_s64_ashr_16 +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + ; CHECK-LABEL: name: s16_trunc_s64_ashr_16 + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: %amt:_(s32) = G_CONSTANT i32 16 + ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], %amt(s32) + ; CHECK-NEXT: %trunc:_(s16) = G_TRUNC [[ASHR]](s32) + ; CHECK-NEXT: %foo:_(s16) = G_CONSTANT i16 55 + ; CHECK-NEXT: %keep:_(s32) = G_MERGE_VALUES %trunc(s16), %foo(s16) + ; CHECK-NEXT: $vgpr0 = COPY %keep(s32) + %0:_(s32) = COPY $vgpr0 + %src:_(s64) = G_ZEXT %0 + %amt:_(s32) = G_CONSTANT i32 16 + %shift:_(s64) = G_ASHR %src, %amt + %trunc:_(s16) = G_TRUNC %shift + %foo:_(s16) = G_CONSTANT i16 55 + %keep:_(s32) = G_MERGE_VALUES %trunc, %foo + $vgpr0 = COPY %keep +... + +--- +name: s16_trunc_s64_lshr_17_nofold +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + ; CHECK-LABEL: name: s16_trunc_s64_lshr_17_nofold + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: %src:_(s64) = G_ZEXT [[COPY]](s32) + ; CHECK-NEXT: %amt:_(s32) = G_CONSTANT i32 17 + ; CHECK-NEXT: %shift:_(s64) = G_LSHR %src, %amt(s32) + ; CHECK-NEXT: %trunc:_(s16) = G_TRUNC %shift(s64) + ; CHECK-NEXT: %foo:_(s16) = G_CONSTANT i16 55 + ; CHECK-NEXT: %keep:_(s32) = G_MERGE_VALUES %trunc(s16), %foo(s16) + ; CHECK-NEXT: $vgpr0 = COPY %keep(s32) + %0:_(s32) = COPY $vgpr0 + %src:_(s64) = G_ZEXT %0 + %amt:_(s32) = G_CONSTANT i32 17 + %shift:_(s64) = G_LSHR %src, %amt + %trunc:_(s16) = G_TRUNC %shift + %foo:_(s16) = G_CONSTANT i16 55 + %keep:_(s32) = G_MERGE_VALUES %trunc, %foo + $vgpr0 = COPY %keep +... + +--- +name: s26_trunc_s64_lshr_6 +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + ; CHECK-LABEL: name: s26_trunc_s64_lshr_6 + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: %amt:_(s32) = G_CONSTANT i32 6 + ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[COPY]], %amt(s32) + ; CHECK-NEXT: %trunc:_(s26) = G_TRUNC [[LSHR]](s32) + ; CHECK-NEXT: %foo:_(s26) = G_CONSTANT i26 55 + ; CHECK-NEXT: %keep0:_(s26) = G_ADD %trunc, %foo + ; CHECK-NEXT: %keep1:_(s32) = G_ANYEXT %keep0(s26) + ; CHECK-NEXT: $vgpr0 = COPY %keep1(s32) + %0:_(s32) = COPY $vgpr0 + %src:_(s64) = G_ZEXT %0 + %amt:_(s32) = G_CONSTANT i32 6 + %shift:_(s64) = G_LSHR %src, %amt + %trunc:_(s26) = G_TRUNC %shift + %foo:_(s26) = G_CONSTANT i26 55 + %keep0:_(s26) = G_ADD %trunc, %foo + %keep1:_(s32) = G_ANYEXT %keep0 + $vgpr0 = COPY %keep1 +... + +--- +name: s26_trunc_s64_lshr_7_nofold +tracksRegLiveness: true +body: | + bb.0: + liveins: $vgpr0 + ; CHECK-LABEL: name: s26_trunc_s64_lshr_7_nofold + ; CHECK: liveins: $vgpr0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 + ; CHECK-NEXT: %src:_(s64) = G_ZEXT [[COPY]](s32) + ; CHECK-NEXT: %amt:_(s32) = G_CONSTANT i32 7 + ; CHECK-NEXT: %shift:_(s64) = G_LSHR %src, %amt(s32) + ; CHECK-NEXT: %trunc:_(s26) = G_TRUNC %shift(s64) + ; CHECK-NEXT: %foo:_(s26) = G_CONSTANT i26 55 + ; CHECK-NEXT: %keep0:_(s26) = G_ADD %trunc, %foo + ; CHECK-NEXT: %keep1:_(s32) = G_ANYEXT %keep0(s26) + ; CHECK-NEXT: $vgpr0 = COPY %keep1(s32) + %0:_(s32) = COPY $vgpr0 + %src:_(s64) = G_ZEXT %0 + %amt:_(s32) = G_CONSTANT i32 7 + %shift:_(s64) = G_LSHR %src, %amt + %trunc:_(s26) = G_TRUNC %shift + %foo:_(s26) = G_CONSTANT i26 55 + %keep0:_(s26) = G_ADD %trunc, %foo + %keep1:_(s32) = G_ANYEXT %keep0 + $vgpr0 = COPY %keep1 +... diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shl.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shl.mir deleted file mode 100644 --- a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-trunc-shl.mir +++ /dev/null @@ -1,73 +0,0 @@ -# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py -# RUN: llc -mtriple=amdgcn-amd-amdhsa -run-pass=amdgpu-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s - ---- -name: trunc_s32_shl_s64_5 -legalized: true -tracksRegLiveness: true -body: | - bb.0: - liveins: $vgpr0_vgpr1 - - ; CHECK-LABEL: name: trunc_s32_shl_s64_5 - ; CHECK: liveins: $vgpr0_vgpr1 - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $vgpr0_vgpr1 - ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 - ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[COPY]](s64) - ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[TRUNC]], [[C]](s32) - ; CHECK-NEXT: $vgpr0 = COPY [[SHL]](s32) - %0:_(s64) = COPY $vgpr0_vgpr1 - %1:_(s32) = G_CONSTANT i32 1 - %2:_(s64) = G_SHL %0:_, %1 - %3:_(s32) = G_TRUNC %2 - $vgpr0 = COPY %3 -... - ---- -name: trunc_s16_shl_s32_5 -legalized: true -tracksRegLiveness: true -body: | - bb.0: - liveins: $vgpr0 - - ; CHECK-LABEL: name: trunc_s16_shl_s32_5 - ; CHECK: liveins: $vgpr0 - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0 - ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 - ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C]](s32) - ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SHL]](s32) - ; CHECK-NEXT: S_ENDPGM 0, implicit [[TRUNC]](s16) - %0:_(s32) = COPY $vgpr0 - %1:_(s32) = G_CONSTANT i32 1 - %2:_(s32) = G_SHL %0:_, %1 - %3:_(s16) = G_TRUNC %2 - S_ENDPGM 0, implicit %3 - -... - ---- -name: trunc_s16_shl_s64_5 -legalized: true -tracksRegLiveness: true -body: | - bb.0: - liveins: $vgpr0_vgpr1 - - ; CHECK-LABEL: name: trunc_s16_shl_s64_5 - ; CHECK: liveins: $vgpr0_vgpr1 - ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $vgpr0_vgpr1 - ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 - ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C]](s32) - ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SHL]](s64) - ; CHECK-NEXT: S_ENDPGM 0, implicit [[TRUNC]](s16) - %0:_(s64) = COPY $vgpr0_vgpr1 - %1:_(s32) = G_CONSTANT i32 1 - %2:_(s64) = G_SHL %0:_, %1 - %3:_(s16) = G_TRUNC %2 - S_ENDPGM 0, implicit %3 - -...