================ @@ -0,0 +1,1116 @@ +//=- PISACombine.td - Define PISA Combine Rules --------------*- tablegen -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines GlobalISel combine rules for the PISA target. +/// +//===----------------------------------------------------------------------===// + +include "llvm/Target/GlobalISel/Combine.td" + +def extra_commute_iconstant_to_rhs : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_SMIN, G_SMAX, G_UMIN, G_UMAX, G_UADDO, G_SADDO, + G_UMULO, G_SMULO, G_UMULH, G_SMULH, G_UADDSAT, + G_SADDSAT, G_SMULFIX, G_UMULFIX, G_SMULFIXSAT, + G_UMULFIXSAT):$root, [{ + return getIConstantVRegVal(${root}->getOperand(1).getReg(), MRI).has_value() && + !getIConstantVRegVal(${root}->getOperand(2).getReg(), MRI).has_value(); + }]), + (apply [{ + Observer.changingInstr(*${root}); + Register LHSReg = ${root}->getOperand(1).getReg(); + Register RHSReg = ${root}->getOperand(2).getReg(); + ${root}->getOperand(1).setReg(RHSReg); + ${root}->getOperand(2).setReg(LHSReg); + Observer.changedInstr(*${root}); + }]) +>; + +def extra_commute_fconstant_to_rhs : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_FMINNUM, G_FMAXNUM, G_FMINNUM_IEEE, + G_FMAXNUM_IEEE, G_FMINIMUM, G_FMAXIMUM, + G_FADD, G_FMUL):$root, [{ + return getFConstantVRegValWithLookThrough(${root}->getOperand(1).getReg(), MRI).has_value() && + !getFConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI).has_value(); + }]), + (apply [{ + Observer.changingInstr(*${root}); + Register LHSReg = ${root}->getOperand(1).getReg(); + Register RHSReg = ${root}->getOperand(2).getReg(); + ${root}->getOperand(1).setReg(RHSReg); + ${root}->getOperand(2).setReg(LHSReg); + Observer.changedInstr(*${root}); + }]) +>; + +def extra_commute_intrinsic_iconstant_to_rhs : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_INTRINSIC):$root, [{ + Intrinsic::ID IntrinsicID = cast<GIntrinsic>(*${root}).getIntrinsicID(); + bool IsFloatArg = false; + switch (IntrinsicID) { + default: + return false; + case Intrinsic::pisa_fadd: + case Intrinsic::pisa_fmul: + IsFloatArg = true; + break; + } + return IsFloatArg ? + (getFConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI).has_value() && + !getFConstantVRegValWithLookThrough(${root}->getOperand(3).getReg(), MRI).has_value()) : + (getIConstantVRegVal(${root}->getOperand(2).getReg(), MRI).has_value() && + !getIConstantVRegVal(${root}->getOperand(3).getReg(), MRI).has_value()); + }]), + (apply [{ + Observer.changingInstr(*${root}); + Register LHSReg = ${root}->getOperand(2).getReg(); + Register RHSReg = ${root}->getOperand(3).getReg(); + ${root}->getOperand(2).setReg(RHSReg); + ${root}->getOperand(3).setReg(LHSReg); + Observer.changedInstr(*${root}); + }]) +>; + +// AND rd1, rs1, C1 +// AND rd2, rd1, C1 - remove copy +def extra_remove_redundant_constant : GICombineRule< + (defs root:$root, register_matchinfo:$matchinfo), + (match (wip_match_opcode G_AND, G_OR):$root, + [{ + std::optional<ValueAndVReg> C1 = + getIConstantVRegValWithLookThrough( + ${root}->getOperand(2).getReg(), MRI); + if (C1.has_value()) { + MachineInstr *DefMI = + getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI); + if (DefMI && (${root}->getOpcode() == DefMI->getOpcode())) { + std::optional<ValueAndVReg> C2 = + getIConstantVRegValWithLookThrough( + DefMI->getOperand(2).getReg(), MRI); + if (C2.has_value()) { + ${matchinfo} = DefMI->getOperand(0).getReg(); + return C1->Value == C2->Value; + } + } + } + return false; + }]), + (apply + [{ + Helper.replaceSingleDefInstWithReg(*${root}, ${matchinfo}); + }]) +>; + +// G_[F]CONSTANT i16 x +// G_[F]CONSTANT i16 y +// G_[F]CONSTANT i16 z +// G_[F]CONSTANT i16 w +// G_BUILD_VECTOR x, y, z, w +// => G_CONSTANT i64 [wzyx] +// => G_BITCAST +def build_vector_with_constants : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_BUILD_VECTOR):$root, + [{ + LLT Type = MRI.getType(${root}->getOperand(0).getReg()); + if (Type.isPointerVector()) + return false; + TypeSize Size = Type.getSizeInBits(); + if ((Size <= 64) && isPowerOf2_32(Size)) { + for (unsigned I=1; I < ${root}->getNumOperands(); I++) { + std::optional<ValueAndVReg> C = + getAnyConstantVRegValWithLookThrough( + ${root}->getOperand(I).getReg(), MRI); + if (!C.has_value()) { + return false; + } + } + return true; + } + return false; + }]), + (apply + [{ + applyBuildVectorWithConstants(*${root}); + }]) +>; + +// i56 = G_LOAD x +// i32 = G_TRUNC i56 +// => i32 = G_LOAD x +def truncated_load : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_TRUNC):$root, + [{ + MachineInstr *DefMI = + getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI); + LLT Type = MRI.getType(DefMI->getOperand(0).getReg()); + TypeSize Size = Type.getSizeInBits(); + bool IsVector = Type.isVector(); + return !isPowerOf2_32(Size) && !IsVector && (DefMI->getOpcode() == TargetOpcode::G_LOAD); + }]), + (apply + [{ + applyTruncatedLoad(*${root}); + }]) +>; + +// i24 x = G_TRUNC i32 +// G_STORE i24 x +// => <4 x i8> y = BITCAST x +// <3 x i8> z = SHUFFLE_VECTOR y, <0,1,2> +// G_STORE <3 x i8> z +def truncated_store : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_STORE):$root, + [{ + return matchTruncatedStore(*${root}); + }]), + (apply + [{ + applyTruncatedStore(*${root}); + }]) +>; + +// i24 = G_LOAD x +// i32 = G_ZEXT i24 +// => +// i32 loadRes = G_LOAD x +// i32 = AND loadRes, 0x00FFFFFF +def extended_load : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_ZEXT):$root, + [{ + return matchExtendedLoad(*${root}); + }]), + (apply + [{ + applyExtendedLoad(*${root}); + }]) +>; + +// i24 = G_LOAD x +// => +// %3:_(s24) = G_ZEXTLOAD %0:reg32b(p0) +// %7:_(s32) = G_CONSTANT i32 2 +// %6:reg32b(p0) = G_PTR_ADD %0:reg32b, %7:_(s32) +// %9:_(s24) = G_ZEXTLOAD %6:reg32b(p0) +// %11:_(s24) = G_CONSTANT i24 16 +// %10:_(s24) = G_SHL %9:_, %11:_(s24) +// %12:_(s24) = G_OR %3:_, %10:_ +// Similar transformation is done for G_STORE. +def expand_nonpowerof2_load_store : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_LOAD, G_STORE):$root, + [{ + return matchExpandNonPowerOf2LoadStore(*${root}); + }]), + (apply + [{ + applyExpandNonPowerOf2LoadStore(*${root}); + }]) +>; + +// If the value we store resulted from a G_LOAD that the rule above expanded, +// then we can use the individual load values directly instead of merging into +// one integer, and then splitting it again. Size modifications between the +// loads and store (i.e. SEXT/ZEXT/TRUNC) are also supported by +// truncating/extending the relevant load results (or, in the case of +// truncation, ignoring some loads entirely) +// +// Below is a simple example where we simply load and store an i56: +// +// bb.1.entry: +// %0:reg32b(p0) = functionParameter_32b 0 +// -> %3:_(s56) = G_ZEXTLOAD %0:reg32b(p0) :: +// (load (s32) from %ir.dst) +// %4:_(s56) = G_CONSTANT i56 0 +// %7:_(s32) = G_CONSTANT i32 4 +// %6:reg32b(p0) = G_PTR_ADD %0:reg32b, %7:_(s32) +// -> %9:_(s56) = G_ZEXTLOAD %6:reg32b(p0) :: +// (load (s16) from %ir.dst + 4, align 4) +// %11:_(s56) = G_CONSTANT i56 32 +// %10:_(s56) = G_SHL %9:_, %11:_(s56) +// %12:_(s56) = G_OR %3:_, %10:_ +// %14:_(s32) = G_CONSTANT i32 6 +// %13:reg32b(p0) = G_PTR_ADD %0:reg32b, %14:_(s32) +// -> %16:_(s56) = G_ZEXTLOAD %13:reg32b(p0) :: +// (load (s8) from %ir.dst + 6, align 2, basealign 4) +// %18:_(s56) = G_CONSTANT i56 48 +// %17:_(s56) = G_SHL %16:_, %18:_(s56) +// %19:_(s56) = G_OR %12:_, %17:_ +// G_STORE %19:_(s56), %0:reg32b(p0) :: +// (store (s56) into %ir.dst, align 4) +// ret +def loadVector_matchinfo : GIDefMatchData<"SmallVector<std::pair<MachineInstr *, unsigned>, 8>">; +def MI_matchinfo : GIDefMatchData<"MachineInstr *">; +def simplify_nonpowerof2_load_store_chain : GICombineRule< + (defs root:$root, loadVector_matchinfo:$matchinfo, MI_matchinfo:$MI_matchinfo), + (match (wip_match_opcode G_STORE):$root, + [{ + return matchSimplifyNonPowerOf2LoadStoreChain(*${root}, ${matchinfo}, ${MI_matchinfo}); + }]), + (apply + [{ + applySimplifyNonPowerOf2LoadStoreChain(*${root}, ${matchinfo}, ${MI_matchinfo}); + }]) +>; + +// i1 C = G_CMP ne i? A, 0 +// i? S = G_SELECT i1 C, i? LHS, i? RHS +// => S = sel.? LHS, RHS, A +// ... or ... +// i1 C = G_CMP eq i? A, 0 +// i? S = G_SELECT i1 C, i? LHS, i? RHS +// => S = sel.? RHS, LHS, A +def compare_select_reg : GICombineRule< + (defs root:$SelectMI), + (match (wip_match_opcode G_SELECT):$SelectMI, + [{ + return matchCompareSelect(${SelectMI}); + }]), + (apply + [{ + applyCompareSelect(${SelectMI}); + }]) +>; + +// arcp G_FDIV %0, %1 +// => +// %rcp = arcp G_INTRINSIC intrinsic(@llvm.pisa.frcp), %1 +// %result = arcp G_FMUL %0, %rcp +def fdiv_to_rcp_fmul : GICombineRule< + (defs root:$root, build_fn_matchinfo:$matchinfo), + (match (wip_match_opcode G_FDIV):$root, + [{ return matchFDivToRcpFMul(*${root}, ${matchinfo}); }]), + (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }]) +>; + +//%2:registers(s32) = G_SHL %0:reg32b, 2 +//%4:registers(s32) = G_ADD %2:registers, %3:registers +// => %4:registers(s32) = G_PISA_SMAD %0:reg32b, 4, %3:registers +def shl_add_to_mad : GICombineRule< + (defs root:$root, build_fn_matchinfo:$matchinfo), + (match (wip_match_opcode G_ADD):$root, + [{ return matchShlAddToMad(*${root}, ${matchinfo}); }]), + (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>; + + +// i32 %lo = G_TRUNC i32 a 16 +// i32 %shift = G_LSHR i32 a 16 +// %hi = G_TRUNC i32 %shift to i16 +// => %1 = G_BITCAST i32 a to <2 x 16> +// => %hi = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 1 +// => %lo = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 0 +def trunc_shift_to_vector_extract : GICombineRule< + (defs root:$TruncMI), + (match (wip_match_opcode G_TRUNC):$TruncMI, + [{ return matchTruncatedShift(*${TruncMI}); }]), + (apply + [{ applyTruncatedShift(*${TruncMI}); }]) +>; + +// %5 = G_CONSTANT i16 x +// %6 = G_CONSTANT i16 y +// %7 = G_AND %5:_, %6:_ +// %2 = G_TRUNC %7 to i8 +// => %2 = COPY (i8)(x & y) +def const_and_trunc_to_copy : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_TRUNC):$root, + [{ + MachineInstr *AndMI = + getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI); + if (AndMI->getOpcode() == TargetOpcode::G_AND) { + std::optional<ValueAndVReg> C1 = + getIConstantVRegValWithLookThrough( + AndMI->getOperand(1).getReg(), MRI); + std::optional<ValueAndVReg> C2 = + getIConstantVRegValWithLookThrough( + AndMI->getOperand(2).getReg(), MRI); + if (C1.has_value() && C2.has_value()) { + APInt Value = C1->Value & C2->Value; + return (Value.getZExtValue() >> MRI.getType(${root}->getOperand(0).getReg()).getSizeInBits()) == 0; + } + } + return false; + }]), + (apply + [{ + MachineInstr *AndMI = + getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI); + std::optional<ValueAndVReg> C1 = + getIConstantVRegValWithLookThrough( + AndMI->getOperand(1).getReg(), MRI); + std::optional<ValueAndVReg> C2 = + getIConstantVRegValWithLookThrough( + AndMI->getOperand(2).getReg(), MRI); + APInt Value = (C1->Value & C2->Value) + .trunc(MRI.getType(${root}->getOperand(0).getReg()) + .getSizeInBits()); + Helper.replaceInstWithConstant(*${root}, Value); + }]) +>; + +// G_AND (G_ZEXT x), C -> G_ZEXT (G_AND x, trunc(C)). +// CodeGenPrepare can form the widened mask, but keeping the mask narrow lets +// address folding see the original arithmetic shape. +def zext_and_to_and_zext : GICombineRule< + (defs root:$root, build_fn_matchinfo:$matchinfo), + (match (wip_match_opcode G_AND):$root, + [{ return matchZExtAndToAndZExt(*${root}, ${matchinfo}); }]), + (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>; + +// %114:_(<5 x s32>) = G_EXTRACT_SUBVECTOR %113:_(<8 x s32>), 0 +// %65:_(s32) = G_EXTRACT_VECTOR_ELT %114:_(<5 x s32>), %66:_(s32) +// => %65:_(s32) = G_EXTRACT_VECTOR_ELT %113:_(<8 x s32>), %66:_(s32) + +def extract_subvector_to_extract_elt : GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_EXTRACT_VECTOR_ELT):$root, + [{ + MachineInstr *SubvectorMI = + getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI); + if (!SubvectorMI) { + return false; + } + if (SubvectorMI->getOpcode() != TargetOpcode::G_EXTRACT_SUBVECTOR) { + return false; + } + if (SubvectorMI->getOperand(2).isReg()){ + // Skip to avoid needing to generate add instruction for index adjustment. + return false; + } + std::optional<ValueAndVReg> C = + getIConstantVRegValWithLookThrough( + ${root}->getOperand(2).getReg(), MRI); + if (!C.has_value()){ + if (SubvectorMI->getOperand(2).getImm() != 0){ + // Skip to avoid needing to generate add instruction for index adjustment. + return false; + } ---------------- topperc wrote:
Identation is off here https://github.com/llvm/llvm-project/pull/214107 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
