Skip to content

Commit 54a9f0e

Browse files
[RISCV][GISEL] Legalize, regbankselect, and instruction-select G_VSCALE (#85967)
G_VSCALE should be lowered using VLENB. If the type is not sXLen it should be lowered using a G_VSCALE on the narrow type and a G_MUL. regbank select and instruction select are straightforward so we really only need to add tests to show it works.
1 parent 3324f4d commit 54a9f0e

File tree

12 files changed

+953
-1
lines changed

12 files changed

+953
-1
lines changed

llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,17 @@ class MachineIRBuilder {
11651165
/// \return a MachineInstrBuilder for the newly created instruction.
11661166
MachineInstrBuilder buildVScale(const DstOp &Res, const ConstantInt &MinElts);
11671167

1168+
/// Build and insert \p Res = G_VSCALE \p MinElts
1169+
///
1170+
/// G_VSCALE puts the value of the runtime vscale multiplied by \p MinElts
1171+
/// into \p Res.
1172+
///
1173+
/// \pre setBasicBlock or setMI must have been called.
1174+
/// \pre \p Res must be a generic virtual register with scalar type.
1175+
///
1176+
/// \return a MachineInstrBuilder for the newly created instruction.
1177+
MachineInstrBuilder buildVScale(const DstOp &Res, const APInt &MinElts);
1178+
11681179
/// Build and insert a G_INTRINSIC instruction.
11691180
///
11701181
/// There are four different opcodes based on combinations of whether the

llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,20 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
16991699
case TargetOpcode::G_FLDEXP:
17001700
case TargetOpcode::G_STRICT_FLDEXP:
17011701
return narrowScalarFLDEXP(MI, TypeIdx, NarrowTy);
1702+
case TargetOpcode::G_VSCALE: {
1703+
Register Dst = MI.getOperand(0).getReg();
1704+
LLT Ty = MRI.getType(Dst);
1705+
1706+
// Assume VSCALE(1) fits into a legal integer
1707+
const APInt One(NarrowTy.getSizeInBits(), 1);
1708+
auto VScaleBase = MIRBuilder.buildVScale(NarrowTy, One);
1709+
auto ZExt = MIRBuilder.buildZExt(Ty, VScaleBase);
1710+
auto C = MIRBuilder.buildConstant(Ty, *MI.getOperand(1).getCImm());
1711+
MIRBuilder.buildMul(Dst, ZExt, C);
1712+
1713+
MI.eraseFromParent();
1714+
return Legalized;
1715+
}
17021716
}
17031717
}
17041718

@@ -2966,7 +2980,7 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) {
29662980
case TargetOpcode::G_VECREDUCE_FMIN:
29672981
case TargetOpcode::G_VECREDUCE_FMAX:
29682982
case TargetOpcode::G_VECREDUCE_FMINIMUM:
2969-
case TargetOpcode::G_VECREDUCE_FMAXIMUM:
2983+
case TargetOpcode::G_VECREDUCE_FMAXIMUM: {
29702984
if (TypeIdx != 0)
29712985
return UnableToLegalize;
29722986
Observer.changingInstr(MI);
@@ -2980,6 +2994,19 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) {
29802994
Observer.changedInstr(MI);
29812995
return Legalized;
29822996
}
2997+
case TargetOpcode::G_VSCALE: {
2998+
MachineOperand &SrcMO = MI.getOperand(1);
2999+
LLVMContext &Ctx = MIRBuilder.getMF().getFunction().getContext();
3000+
const APInt &SrcVal = SrcMO.getCImm()->getValue();
3001+
// The CImm is always a signed value
3002+
const APInt Val = SrcVal.sext(WideTy.getSizeInBits());
3003+
Observer.changingInstr(MI);
3004+
SrcMO.setCImm(ConstantInt::get(Ctx, Val));
3005+
widenScalarDst(MI, WideTy);
3006+
Observer.changedInstr(MI);
3007+
return Legalized;
3008+
}
3009+
}
29833010
}
29843011

29853012
static void getUnmergePieces(SmallVectorImpl<Register> &Pieces,

llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,13 @@ MachineInstrBuilder MachineIRBuilder::buildVScale(const DstOp &Res,
811811
return VScale;
812812
}
813813

814+
MachineInstrBuilder MachineIRBuilder::buildVScale(const DstOp &Res,
815+
const APInt &MinElts) {
816+
ConstantInt *CI =
817+
ConstantInt::get(getMF().getFunction().getContext(), MinElts);
818+
return buildVScale(Res, *CI);
819+
}
820+
814821
static unsigned getIntrinsicOpcode(bool HasSideEffects, bool IsConvergent) {
815822
if (HasSideEffects && IsConvergent)
816823
return TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS;

llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,10 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
408408
.clampScalar(0, s32, sXLen)
409409
.lowerForCartesianProduct({s32, sXLen, p0}, {p0});
410410

411+
getActionDefinitionsBuilder(G_VSCALE)
412+
.clampScalar(0, sXLen, sXLen)
413+
.customFor({sXLen});
414+
411415
getLegacyLegalizerInfo().computeTables();
412416
}
413417

@@ -529,6 +533,48 @@ bool RISCVLegalizerInfo::shouldBeInConstantPool(APInt APImm,
529533
return !(!SeqLo.empty() && (SeqLo.size() + 2) <= STI.getMaxBuildIntsCost());
530534
}
531535

536+
bool RISCVLegalizerInfo::legalizeVScale(MachineInstr &MI,
537+
MachineIRBuilder &MIB) const {
538+
const LLT XLenTy(STI.getXLenVT());
539+
Register Dst = MI.getOperand(0).getReg();
540+
541+
// We define our scalable vector types for lmul=1 to use a 64 bit known
542+
// minimum size. e.g. <vscale x 2 x i32>. VLENB is in bytes so we calculate
543+
// vscale as VLENB / 8.
544+
static_assert(RISCV::RVVBitsPerBlock == 64, "Unexpected bits per block!");
545+
if (STI.getRealMinVLen() < RISCV::RVVBitsPerBlock)
546+
// Support for VLEN==32 is incomplete.
547+
return false;
548+
549+
// We assume VLENB is a multiple of 8. We manually choose the best shift
550+
// here because SimplifyDemandedBits isn't always able to simplify it.
551+
uint64_t Val = MI.getOperand(1).getCImm()->getZExtValue();
552+
if (isPowerOf2_64(Val)) {
553+
uint64_t Log2 = Log2_64(Val);
554+
if (Log2 < 3) {
555+
auto VLENB = MIB.buildInstr(RISCV::G_READ_VLENB, {XLenTy}, {});
556+
MIB.buildLShr(Dst, VLENB, MIB.buildConstant(XLenTy, 3 - Log2));
557+
} else if (Log2 > 3) {
558+
auto VLENB = MIB.buildInstr(RISCV::G_READ_VLENB, {XLenTy}, {});
559+
MIB.buildShl(Dst, VLENB, MIB.buildConstant(XLenTy, Log2 - 3));
560+
} else {
561+
MIB.buildInstr(RISCV::G_READ_VLENB, {Dst}, {});
562+
}
563+
} else if ((Val % 8) == 0) {
564+
// If the multiplier is a multiple of 8, scale it down to avoid needing
565+
// to shift the VLENB value.
566+
auto VLENB = MIB.buildInstr(RISCV::G_READ_VLENB, {XLenTy}, {});
567+
MIB.buildMul(Dst, VLENB, MIB.buildConstant(XLenTy, Val / 8));
568+
} else {
569+
auto VLENB = MIB.buildInstr(RISCV::G_READ_VLENB, {XLenTy}, {});
570+
auto VScale = MIB.buildLShr(XLenTy, VLENB, MIB.buildConstant(XLenTy, 3));
571+
MIB.buildMul(Dst, VScale, MIB.buildConstant(XLenTy, Val));
572+
}
573+
574+
MI.eraseFromParent();
575+
return true;
576+
}
577+
532578
bool RISCVLegalizerInfo::legalizeCustom(
533579
LegalizerHelper &Helper, MachineInstr &MI,
534580
LostDebugLocObserver &LocObserver) const {
@@ -586,6 +632,8 @@ bool RISCVLegalizerInfo::legalizeCustom(
586632
}
587633
case TargetOpcode::G_VASTART:
588634
return legalizeVAStart(MI, MIRBuilder);
635+
case TargetOpcode::G_VSCALE:
636+
return legalizeVScale(MI, MIRBuilder);
589637
}
590638

591639
llvm_unreachable("expected switch to return");

llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class RISCVLegalizerInfo : public LegalizerInfo {
4242
GISelChangeObserver &Observer) const;
4343

4444
bool legalizeVAStart(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
45+
bool legalizeVScale(MachineInstr &MI, MachineIRBuilder &MIB) const;
4546
};
4647
} // end namespace llvm
4748
#endif

llvm/lib/Target/RISCV/RISCVInstrGISel.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,11 @@ def G_FCLASS : RISCVGenericInstruction {
2424
let hasSideEffects = false;
2525
}
2626
def : GINodeEquiv<G_FCLASS, riscv_fclass>;
27+
28+
// Pseudo equivalent to a RISCVISD::READ_VLENB.
29+
def G_READ_VLENB : RISCVGenericInstruction {
30+
let OutOperandList = (outs type0:$dst);
31+
let InOperandList = (ins);
32+
let hasSideEffects = false;
33+
}
34+
def : GINodeEquiv<G_READ_VLENB, riscv_read_vlenb>;

0 commit comments

Comments
 (0)