[llvm-branch-commits] [llvm] [3/7][PISA] Add PISA register file, instruction set, and MC layer (PR #214107)
Craig Topper via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Oct 22 09:53:04 PDT 2026
================
@@ -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;
+ }
+ }
+ LLVM_DEBUG(dbgs() << " Success: extract_subvector_to_extract_elt rule matches!\n");
+ return true;
+ }]),
+ (apply
+ [{
+ MachineInstr *SubvectorMI =
+ getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ Register SrcReg = SubvectorMI->getOperand(1).getReg();
+ Register IdxReg = ${root}->getOperand(2).getReg();
+ Register DstReg = ${root}->getOperand(0).getReg();
+ std::optional<ValueAndVReg> C =
+ getIConstantVRegValWithLookThrough(
+ ${root}->getOperand(2).getReg(), MRI);
+ if (C.has_value() && SubvectorMI->getOperand(2).isImm()){
+ uint64_t NewIdx = C->Value.getZExtValue() + SubvectorMI->getOperand(2).getImm();
+ B.buildExtractVectorElementConstant(DstReg, SrcReg, NewIdx);
+ }
+ else {
----------------
topperc wrote:
else should be on the same line as the previous closing brace
https://github.com/llvm/llvm-project/pull/214107
More information about the llvm-branch-commits
mailing list