aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp')
-rw-r--r--llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp439
1 files changed, 434 insertions, 5 deletions
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 3f829cc2e677..8f03a7ac41d3 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -11,23 +11,452 @@
//===----------------------------------------------------------------------===//
#include "RISCVLegalizerInfo.h"
+#include "RISCVMachineFunctionInfo.h"
#include "RISCVSubtarget.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Type.h"
using namespace llvm;
+using namespace LegalityPredicates;
+using namespace LegalizeMutations;
-RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST) {
- const unsigned XLen = ST.getXLen();
- const LLT XLenLLT = LLT::scalar(XLen);
+// Is this type supported by scalar FP arithmetic operations given the current
+// subtarget.
+static LegalityPredicate typeIsScalarFPArith(unsigned TypeIdx,
+ const RISCVSubtarget &ST) {
+ return [=, &ST](const LegalityQuery &Query) {
+ return Query.Types[TypeIdx].isScalar() &&
+ ((ST.hasStdExtF() && Query.Types[TypeIdx].getSizeInBits() == 32) ||
+ (ST.hasStdExtD() && Query.Types[TypeIdx].getSizeInBits() == 64));
+ };
+}
+
+RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
+ : STI(ST), XLen(STI.getXLen()), sXLen(LLT::scalar(XLen)) {
+ const LLT sDoubleXLen = LLT::scalar(2 * XLen);
+ const LLT p0 = LLT::pointer(0, XLen);
+ const LLT s1 = LLT::scalar(1);
+ const LLT s8 = LLT::scalar(8);
+ const LLT s16 = LLT::scalar(16);
+ const LLT s32 = LLT::scalar(32);
+ const LLT s64 = LLT::scalar(64);
using namespace TargetOpcode;
getActionDefinitionsBuilder({G_ADD, G_SUB, G_AND, G_OR, G_XOR})
- .legalFor({XLenLLT})
- .clampScalar(0, XLenLLT, XLenLLT);
+ .legalFor({s32, sXLen})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, s32, sXLen);
+
+ getActionDefinitionsBuilder(
+ {G_UADDE, G_UADDO, G_USUBE, G_USUBO}).lower();
+
+ getActionDefinitionsBuilder({G_SADDO, G_SSUBO}).minScalar(0, sXLen).lower();
+
+ auto &ShiftActions = getActionDefinitionsBuilder({G_ASHR, G_LSHR, G_SHL});
+ if (ST.is64Bit())
+ ShiftActions.customFor({{s32, s32}});
+ ShiftActions.legalFor({{s32, s32}, {s32, sXLen}, {sXLen, sXLen}})
+ .widenScalarToNextPow2(0)
+ .clampScalar(1, s32, sXLen)
+ .clampScalar(0, s32, sXLen)
+ .minScalarSameAs(1, 0);
+
+ if (ST.is64Bit()) {
+ getActionDefinitionsBuilder({G_ZEXT, G_SEXT, G_ANYEXT})
+ .legalFor({{sXLen, s32}})
+ .maxScalar(0, sXLen);
+
+ getActionDefinitionsBuilder(G_SEXT_INREG)
+ .customFor({sXLen})
+ .maxScalar(0, sXLen)
+ .lower();
+ } else {
+ getActionDefinitionsBuilder({G_ZEXT, G_SEXT, G_ANYEXT}).maxScalar(0, sXLen);
+
+ getActionDefinitionsBuilder(G_SEXT_INREG).maxScalar(0, sXLen).lower();
+ }
+
+ // Merge/Unmerge
+ for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
+ unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
+ unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
+ auto &MergeUnmergeActions = getActionDefinitionsBuilder(Op);
+ if (XLen == 32 && ST.hasStdExtD()) {
+ LLT IdxZeroTy = G_MERGE_VALUES ? s64 : s32;
+ LLT IdxOneTy = G_MERGE_VALUES ? s32 : s64;
+ MergeUnmergeActions.legalFor({IdxZeroTy, IdxOneTy});
+ }
+ MergeUnmergeActions.widenScalarToNextPow2(LitTyIdx, XLen)
+ .widenScalarToNextPow2(BigTyIdx, XLen)
+ .clampScalar(LitTyIdx, sXLen, sXLen)
+ .clampScalar(BigTyIdx, sXLen, sXLen);
+ }
+
+ getActionDefinitionsBuilder({G_FSHL, G_FSHR}).lower();
+
+ auto &RotateActions = getActionDefinitionsBuilder({G_ROTL, G_ROTR});
+ if (ST.hasStdExtZbb()) {
+ RotateActions.legalFor({{s32, sXLen}, {sXLen, sXLen}});
+ // Widen s32 rotate amount to s64 so SDAG patterns will match.
+ if (ST.is64Bit())
+ RotateActions.widenScalarIf(all(typeIs(0, s32), typeIs(1, s32)),
+ changeTo(1, sXLen));
+ }
+ RotateActions.lower();
+
+ getActionDefinitionsBuilder(G_BITREVERSE).maxScalar(0, sXLen).lower();
+
+ auto &BSWAPActions = getActionDefinitionsBuilder(G_BSWAP);
+ if (ST.hasStdExtZbb())
+ BSWAPActions.legalFor({sXLen}).clampScalar(0, sXLen, sXLen);
+ else
+ BSWAPActions.maxScalar(0, sXLen).lower();
+
+ auto &CountZerosActions = getActionDefinitionsBuilder({G_CTLZ, G_CTTZ});
+ auto &CountZerosUndefActions =
+ getActionDefinitionsBuilder({G_CTLZ_ZERO_UNDEF, G_CTTZ_ZERO_UNDEF});
+ if (ST.hasStdExtZbb()) {
+ CountZerosActions.legalFor({{s32, s32}, {sXLen, sXLen}})
+ .clampScalar(0, s32, sXLen)
+ .widenScalarToNextPow2(0)
+ .scalarSameSizeAs(1, 0);
+ } else {
+ CountZerosActions.maxScalar(0, sXLen).scalarSameSizeAs(1, 0).lower();
+ CountZerosUndefActions.maxScalar(0, sXLen).scalarSameSizeAs(1, 0);
+ }
+ CountZerosUndefActions.lower();
+
+ auto &CTPOPActions = getActionDefinitionsBuilder(G_CTPOP);
+ if (ST.hasStdExtZbb()) {
+ CTPOPActions.legalFor({{s32, s32}, {sXLen, sXLen}})
+ .clampScalar(0, s32, sXLen)
+ .widenScalarToNextPow2(0)
+ .scalarSameSizeAs(1, 0);
+ } else {
+ CTPOPActions.maxScalar(0, sXLen).scalarSameSizeAs(1, 0).lower();
+ }
+
+ getActionDefinitionsBuilder({G_CONSTANT, G_IMPLICIT_DEF})
+ .legalFor({s32, sXLen, p0})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, s32, sXLen);
+
+ getActionDefinitionsBuilder(G_ICMP)
+ .legalFor({{sXLen, sXLen}, {sXLen, p0}})
+ .widenScalarToNextPow2(1)
+ .clampScalar(1, sXLen, sXLen)
+ .clampScalar(0, sXLen, sXLen);
+
+ auto &SelectActions = getActionDefinitionsBuilder(G_SELECT).legalFor(
+ {{s32, sXLen}, {p0, sXLen}});
+ if (XLen == 64 || ST.hasStdExtD())
+ SelectActions.legalFor({{s64, sXLen}});
+ SelectActions.widenScalarToNextPow2(0)
+ .clampScalar(0, s32, (XLen == 64 || ST.hasStdExtD()) ? s64 : s32)
+ .clampScalar(1, sXLen, sXLen);
+
+ auto &LoadStoreActions =
+ getActionDefinitionsBuilder({G_LOAD, G_STORE})
+ .legalForTypesWithMemDesc({{s32, p0, s8, 8},
+ {s32, p0, s16, 16},
+ {s32, p0, s32, 32},
+ {p0, p0, sXLen, XLen}});
+ auto &ExtLoadActions =
+ getActionDefinitionsBuilder({G_SEXTLOAD, G_ZEXTLOAD})
+ .legalForTypesWithMemDesc({{s32, p0, s8, 8}, {s32, p0, s16, 16}});
+ if (XLen == 64) {
+ LoadStoreActions.legalForTypesWithMemDesc({{s64, p0, s8, 8},
+ {s64, p0, s16, 16},
+ {s64, p0, s32, 32},
+ {s64, p0, s64, 64}});
+ ExtLoadActions.legalForTypesWithMemDesc(
+ {{s64, p0, s8, 8}, {s64, p0, s16, 16}, {s64, p0, s32, 32}});
+ } else if (ST.hasStdExtD()) {
+ LoadStoreActions.legalForTypesWithMemDesc({{s64, p0, s64, 64}});
+ }
+ LoadStoreActions.clampScalar(0, s32, sXLen).lower();
+ ExtLoadActions.widenScalarToNextPow2(0).clampScalar(0, s32, sXLen).lower();
+
+ getActionDefinitionsBuilder({G_PTR_ADD, G_PTRMASK}).legalFor({{p0, sXLen}});
+
+ getActionDefinitionsBuilder(G_PTRTOINT)
+ .legalFor({{sXLen, p0}})
+ .clampScalar(0, sXLen, sXLen);
+
+ getActionDefinitionsBuilder(G_INTTOPTR)
+ .legalFor({{p0, sXLen}})
+ .clampScalar(1, sXLen, sXLen);
+
+ getActionDefinitionsBuilder(G_BRCOND).legalFor({sXLen}).minScalar(0, sXLen);
+
+ getActionDefinitionsBuilder(G_BRJT).legalFor({{p0, sXLen}});
+
+ getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0});
+
+ getActionDefinitionsBuilder(G_PHI)
+ .legalFor({p0, sXLen})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, sXLen, sXLen);
+
+ getActionDefinitionsBuilder({G_GLOBAL_VALUE, G_JUMP_TABLE, G_CONSTANT_POOL})
+ .legalFor({p0});
+
+ if (ST.hasStdExtM() || ST.hasStdExtZmmul()) {
+ getActionDefinitionsBuilder(G_MUL)
+ .legalFor({s32, sXLen})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, s32, sXLen);
+
+ // clang-format off
+ getActionDefinitionsBuilder({G_SMULH, G_UMULH})
+ .legalFor({sXLen})
+ .lower();
+ // clang-format on
+
+ getActionDefinitionsBuilder({G_SMULO, G_UMULO}).minScalar(0, sXLen).lower();
+ } else {
+ getActionDefinitionsBuilder(G_MUL)
+ .libcallFor({sXLen, sDoubleXLen})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, sXLen, sDoubleXLen);
+
+ getActionDefinitionsBuilder({G_SMULH, G_UMULH}).lowerFor({sXLen});
+
+ getActionDefinitionsBuilder({G_SMULO, G_UMULO})
+ .minScalar(0, sXLen)
+ // Widen sXLen to sDoubleXLen so we can use a single libcall to get
+ // the low bits for the mul result and high bits to do the overflow
+ // check.
+ .widenScalarIf(typeIs(0, sXLen),
+ LegalizeMutations::changeTo(0, sDoubleXLen))
+ .lower();
+ }
+
+ if (ST.hasStdExtM()) {
+ getActionDefinitionsBuilder({G_UDIV, G_SDIV, G_UREM, G_SREM})
+ .legalFor({s32, sXLen})
+ .libcallFor({sDoubleXLen})
+ .clampScalar(0, s32, sDoubleXLen)
+ .widenScalarToNextPow2(0);
+ } else {
+ getActionDefinitionsBuilder({G_UDIV, G_SDIV, G_UREM, G_SREM})
+ .libcallFor({sXLen, sDoubleXLen})
+ .clampScalar(0, sXLen, sDoubleXLen)
+ .widenScalarToNextPow2(0);
+ }
+
+ auto &AbsActions = getActionDefinitionsBuilder(G_ABS);
+ if (ST.hasStdExtZbb())
+ AbsActions.customFor({s32, sXLen}).minScalar(0, sXLen);
+ AbsActions.lower();
+
+ auto &MinMaxActions =
+ getActionDefinitionsBuilder({G_UMAX, G_UMIN, G_SMAX, G_SMIN});
+ if (ST.hasStdExtZbb())
+ MinMaxActions.legalFor({sXLen}).minScalar(0, sXLen);
+ MinMaxActions.lower();
+
+ getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
+
+ getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE, G_MEMSET}).libcall();
+
+ getActionDefinitionsBuilder(G_DYN_STACKALLOC).lower();
+
+ // FP Operations
+
+ getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FMA, G_FNEG,
+ G_FABS, G_FSQRT, G_FMAXNUM, G_FMINNUM})
+ .legalIf(typeIsScalarFPArith(0, ST));
+
+ getActionDefinitionsBuilder(G_FCOPYSIGN)
+ .legalIf(all(typeIsScalarFPArith(0, ST), typeIsScalarFPArith(1, ST)));
+
+ getActionDefinitionsBuilder(G_FPTRUNC).legalIf(
+ [=, &ST](const LegalityQuery &Query) -> bool {
+ return (ST.hasStdExtD() && typeIs(0, s32)(Query) &&
+ typeIs(1, s64)(Query));
+ });
+ getActionDefinitionsBuilder(G_FPEXT).legalIf(
+ [=, &ST](const LegalityQuery &Query) -> bool {
+ return (ST.hasStdExtD() && typeIs(0, s64)(Query) &&
+ typeIs(1, s32)(Query));
+ });
+
+ getActionDefinitionsBuilder(G_FCMP)
+ .legalIf(all(typeIs(0, sXLen), typeIsScalarFPArith(1, ST)))
+ .clampScalar(0, sXLen, sXLen);
+
+ // TODO: Support vector version of G_IS_FPCLASS.
+ getActionDefinitionsBuilder(G_IS_FPCLASS)
+ .customIf(all(typeIs(0, s1), typeIsScalarFPArith(1, ST)));
+
+ getActionDefinitionsBuilder(G_FCONSTANT)
+ .legalIf(typeIsScalarFPArith(0, ST))
+ .lowerFor({s32, s64});
+
+ getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI})
+ .legalIf(all(typeInSet(0, {s32, sXLen}), typeIsScalarFPArith(1, ST)))
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, s32, sXLen);
+
+ getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
+ .legalIf(all(typeIsScalarFPArith(0, ST), typeInSet(1, {s32, sXLen})))
+ .widenScalarToNextPow2(1)
+ .clampScalar(1, s32, sXLen);
+
+ // FIXME: We can do custom inline expansion like SelectionDAG.
+ // FIXME: Legal with Zfa.
+ getActionDefinitionsBuilder({G_FCEIL, G_FFLOOR})
+ .libcallFor({s32, s64});
+
+ getActionDefinitionsBuilder(G_VASTART).customFor({p0});
+
+ // va_list must be a pointer, but most sized types are pretty easy to handle
+ // as the destination.
+ getActionDefinitionsBuilder(G_VAARG)
+ // TODO: Implement narrowScalar and widenScalar for G_VAARG for types
+ // outside the [s32, sXLen] range.
+ .clampScalar(0, s32, sXLen)
+ .lowerForCartesianProduct({s32, sXLen, p0}, {p0});
getLegacyLegalizerInfo().computeTables();
}
+
+static Type *getTypeForLLT(LLT Ty, LLVMContext &C) {
+ if (Ty.isVector())
+ return FixedVectorType::get(IntegerType::get(C, Ty.getScalarSizeInBits()),
+ Ty.getNumElements());
+ return IntegerType::get(C, Ty.getSizeInBits());
+}
+
+bool RISCVLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &MI) const {
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ switch (IntrinsicID) {
+ default:
+ return false;
+ case Intrinsic::vacopy: {
+ // vacopy arguments must be legal because of the intrinsic signature.
+ // No need to check here.
+
+ MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
+ MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+ MachineFunction &MF = *MI.getMF();
+ const DataLayout &DL = MIRBuilder.getDataLayout();
+ LLVMContext &Ctx = MF.getFunction().getContext();
+
+ Register DstLst = MI.getOperand(1).getReg();
+ LLT PtrTy = MRI.getType(DstLst);
+
+ // Load the source va_list
+ Align Alignment = DL.getABITypeAlign(getTypeForLLT(PtrTy, Ctx));
+ MachineMemOperand *LoadMMO = MF.getMachineMemOperand(
+ MachinePointerInfo(), MachineMemOperand::MOLoad, PtrTy, Alignment);
+ auto Tmp = MIRBuilder.buildLoad(PtrTy, MI.getOperand(2), *LoadMMO);
+
+ // Store the result in the destination va_list
+ MachineMemOperand *StoreMMO = MF.getMachineMemOperand(
+ MachinePointerInfo(), MachineMemOperand::MOStore, PtrTy, Alignment);
+ MIRBuilder.buildStore(DstLst, Tmp, *StoreMMO);
+
+ MI.eraseFromParent();
+ return true;
+ }
+ }
+}
+
+bool RISCVLegalizerInfo::legalizeShlAshrLshr(
+ MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+ GISelChangeObserver &Observer) const {
+ assert(MI.getOpcode() == TargetOpcode::G_ASHR ||
+ MI.getOpcode() == TargetOpcode::G_LSHR ||
+ MI.getOpcode() == TargetOpcode::G_SHL);
+ MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+ // If the shift amount is a G_CONSTANT, promote it to a 64 bit type so the
+ // imported patterns can select it later. Either way, it will be legal.
+ Register AmtReg = MI.getOperand(2).getReg();
+ auto VRegAndVal = getIConstantVRegValWithLookThrough(AmtReg, MRI);
+ if (!VRegAndVal)
+ return true;
+ // Check the shift amount is in range for an immediate form.
+ uint64_t Amount = VRegAndVal->Value.getZExtValue();
+ if (Amount > 31)
+ return true; // This will have to remain a register variant.
+ auto ExtCst = MIRBuilder.buildConstant(LLT::scalar(64), Amount);
+ Observer.changingInstr(MI);
+ MI.getOperand(2).setReg(ExtCst.getReg(0));
+ Observer.changedInstr(MI);
+ return true;
+}
+
+bool RISCVLegalizerInfo::legalizeVAStart(MachineInstr &MI,
+ MachineIRBuilder &MIRBuilder) const {
+ // Stores the address of the VarArgsFrameIndex slot into the memory location
+ assert(MI.getOpcode() == TargetOpcode::G_VASTART);
+ MachineFunction *MF = MI.getParent()->getParent();
+ RISCVMachineFunctionInfo *FuncInfo = MF->getInfo<RISCVMachineFunctionInfo>();
+ int FI = FuncInfo->getVarArgsFrameIndex();
+ LLT AddrTy = MIRBuilder.getMRI()->getType(MI.getOperand(0).getReg());
+ auto FINAddr = MIRBuilder.buildFrameIndex(AddrTy, FI);
+ assert(MI.hasOneMemOperand());
+ MIRBuilder.buildStore(FINAddr, MI.getOperand(0).getReg(),
+ *MI.memoperands()[0]);
+ MI.eraseFromParent();
+ return true;
+}
+
+bool RISCVLegalizerInfo::legalizeCustom(LegalizerHelper &Helper,
+ MachineInstr &MI) const {
+ MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
+ GISelChangeObserver &Observer = Helper.Observer;
+ switch (MI.getOpcode()) {
+ default:
+ // No idea what to do.
+ return false;
+ case TargetOpcode::G_ABS:
+ return Helper.lowerAbsToMaxNeg(MI);
+ case TargetOpcode::G_SHL:
+ case TargetOpcode::G_ASHR:
+ case TargetOpcode::G_LSHR:
+ return legalizeShlAshrLshr(MI, MIRBuilder, Observer);
+ case TargetOpcode::G_SEXT_INREG: {
+ // Source size of 32 is sext.w.
+ int64_t SizeInBits = MI.getOperand(2).getImm();
+ if (SizeInBits == 32)
+ return true;
+
+ return Helper.lower(MI, 0, /* Unused hint type */ LLT()) ==
+ LegalizerHelper::Legalized;
+ }
+ case TargetOpcode::G_IS_FPCLASS: {
+ Register GISFPCLASS = MI.getOperand(0).getReg();
+ Register Src = MI.getOperand(1).getReg();
+ const MachineOperand &ImmOp = MI.getOperand(2);
+ MachineIRBuilder MIB(MI);
+
+ // Turn LLVM IR's floating point classes to that in RISC-V,
+ // by simply rotating the 10-bit immediate right by two bits.
+ APInt GFpClassImm(10, static_cast<uint64_t>(ImmOp.getImm()));
+ auto FClassMask = MIB.buildConstant(sXLen, GFpClassImm.rotr(2).zext(XLen));
+ auto ConstZero = MIB.buildConstant(sXLen, 0);
+
+ auto GFClass = MIB.buildInstr(RISCV::G_FCLASS, {sXLen}, {Src});
+ auto And = MIB.buildAnd(sXLen, GFClass, FClassMask);
+ MIB.buildICmp(CmpInst::ICMP_NE, GISFPCLASS, And, ConstZero);
+
+ MI.eraseFromParent();
+ return true;
+ }
+ case TargetOpcode::G_VASTART:
+ return legalizeVAStart(MI, MIRBuilder);
+ }
+
+ llvm_unreachable("expected switch to return");
+}