Skip to content

Commit a63ead4

Browse files
committed
[RISCV] GPR Pairs for Inline Asm using Pr
This patch adds support for getting even-odd general purpose register pairs into and out of inline assembly using the `Pr` constraint as proposed in riscv-non-isa/riscv-c-api-doc#92 There are a few different pieces to this patch, each of which need their own explanation. Target-Independent Changes: - This adds two new Machine Value Types (MVTs), which represent pairs for each xlen. Two are needed because MVTs usually have a fixed length. This change unfortunately increases the size of SelectionDAG tables indexed by MVT by a small percentage. - When an inline assembly block returns multiple values, it returns them in a struct, rather than as a single value. This fixes TargetLowering so that `getAsmOperandValueType` is called on the types in that struct, so that targets have the opportunity to propose their own MVT for an inline assembly operand where this wouldn't match conventional arguments/return values. This matches what happens when a single value is returned. RISC-V Changes: - Renames the Register Class used for f64 values on rv32i_zdinx from `GPRPair*` to `GPRF64Pair*`. These register classes are kept broadly unmodified, as their primary value type is used for type inference over selection patterns. This rename affects quite a lot of files. I reordered the definitions in RISCVRegisterInfo.td and added headings to make it easier to browse. - Adds new `GPRPair*` register classes which will be used for `Pr` constraints and for instructions that need an even-odd GPR pair. This new type is used for `amocas.d.*`(rv32) and `amocas.q.*`(rv64) in Zacas, instead of the `GPRF64Pair` class being used before. - Marks the new `GPRPair` class legal as for holding a `MVT::riscv_i<xlen>_pair`. Two new RISCVISD node types are added for creating and destructing a pair - `BuildGPRPair` and `SplitGPRPair`, and are introduced when bitcasting to/from the pair type and the `i<2*xlen>` type. - This adds an override for `getNumRegisters` to ensure that `i<2*xlen>` values, when going to/from inline assembly, only allocate one (pair) register (they would otherwise allocate two). - Ensures that the DAGCombiner doesn't merge the `bitcast` between `i<2*xlen>` types and the pair type into a load/store, as we want to legalise these 2*xlen-wide loads/stores as before - by splitting them into two xlen-wide loads/stores, which will happen with `i<2*xlen>` types. - Ensures that Clang understands that `Pr` is a valid inline assembly constraint.
1 parent 6d91d7c commit a63ead4

File tree

14 files changed

+459
-114
lines changed

14 files changed

+459
-114
lines changed

clang/lib/Basic/Targets/RISCV.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ bool RISCVTargetInfo::validateAsmConstraint(
108108
return true;
109109
}
110110
return false;
111+
case 'P':
112+
// An even-odd register pair - GPR
113+
if (Name[1] == 'r') {
114+
Info.setAllowsRegister();
115+
Name += 1;
116+
return true;
117+
}
118+
return false;
111119
case 'v':
112120
// A vector register.
113121
if (Name[1] == 'r' || Name[1] == 'd' || Name[1] == 'm') {
@@ -122,8 +130,9 @@ bool RISCVTargetInfo::validateAsmConstraint(
122130
std::string RISCVTargetInfo::convertConstraint(const char *&Constraint) const {
123131
std::string R;
124132
switch (*Constraint) {
125-
// c* and v* are two-letter constraints on RISC-V.
133+
// c*, P*, and v* are all two-letter constraints on RISC-V.
126134
case 'c':
135+
case 'P':
127136
case 'v':
128137
R = std::string("^") + std::string(Constraint, 2);
129138
Constraint += 1;

clang/test/CodeGen/RISCV/riscv-inline-asm.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ void test_cf(float f, double d) {
3333
asm volatile("" : "=cf"(cd) : "cf"(d));
3434
}
3535

36+
#if __riscv_xlen == 32
37+
typedef long long double_xlen_t;
38+
#elif __riscv_xlen == 64
39+
typedef __int128_t double_xlen_t;
40+
#endif
41+
double_xlen_t test_Pr_wide_scalar(double_xlen_t p) {
42+
// CHECK-LABEL: define{{.*}} {{i128|i64}} @test_Pr_wide_scalar(
43+
// CHECK: call {{i128|i64}} asm sideeffect "", "=^Pr,^Pr"({{i128|i64}} %{{.*}})
44+
double_xlen_t ret;
45+
asm volatile("" : "=Pr"(ret) : "Pr"(p));
46+
return ret;
47+
}
48+
3649
void test_I(void) {
3750
// CHECK-LABEL: define{{.*}} void @test_I()
3851
// CHECK: call void asm sideeffect "", "I"(i32 2047)

llvm/include/llvm/CodeGen/ValueTypes.td

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -317,20 +317,23 @@ def riscv_nxv16i8x3 : VTVecTup<384, 3, i8, 220>; // RISCV vector tuple(min_num_
317317
def riscv_nxv16i8x4 : VTVecTup<512, 4, i8, 221>; // RISCV vector tuple(min_num_elts=16, nf=4)
318318
def riscv_nxv32i8x2 : VTVecTup<512, 2, i8, 222>; // RISCV vector tuple(min_num_elts=32, nf=2)
319319

320-
def x86mmx : ValueType<64, 223>; // X86 MMX value
321-
def Glue : ValueType<0, 224>; // Pre-RA sched glue
322-
def isVoid : ValueType<0, 225>; // Produces no value
323-
def untyped : ValueType<8, 226> { // Produces an untyped value
320+
def riscv_i32_pair : ValueType<64, 223>; // RISCV pair of RV32 GPRs
321+
def riscv_i64_pair : ValueType<128, 224>; // RISCV pair of RV64 GPRs
322+
323+
def x86mmx : ValueType<64, 225>; // X86 MMX value
324+
def Glue : ValueType<0, 226>; // Pre-RA sched glue
325+
def isVoid : ValueType<0, 227>; // Produces no value
326+
def untyped : ValueType<8, 228> { // Produces an untyped value
324327
let LLVMName = "Untyped";
325328
}
326-
def funcref : ValueType<0, 227>; // WebAssembly's funcref type
327-
def externref : ValueType<0, 228>; // WebAssembly's externref type
328-
def exnref : ValueType<0, 229>; // WebAssembly's exnref type
329-
def x86amx : ValueType<8192, 230>; // X86 AMX value
330-
def i64x8 : ValueType<512, 231>; // 8 Consecutive GPRs (AArch64)
329+
def funcref : ValueType<0, 229>; // WebAssembly's funcref type
330+
def externref : ValueType<0, 230>; // WebAssembly's externref type
331+
def exnref : ValueType<0, 231>; // WebAssembly's exnref type
332+
def x86amx : ValueType<8192, 232>; // X86 AMX value
333+
def i64x8 : ValueType<512, 233>; // 8 Consecutive GPRs (AArch64)
331334
def aarch64svcount
332-
: ValueType<16, 232>; // AArch64 predicate-as-counter
333-
def spirvbuiltin : ValueType<0, 233>; // SPIR-V's builtin type
335+
: ValueType<16, 234>; // AArch64 predicate-as-counter
336+
def spirvbuiltin : ValueType<0, 235>; // SPIR-V's builtin type
334337

335338
let isNormalValueType = false in {
336339
def token : ValueType<0, 504>; // TokenTy

llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5753,7 +5753,8 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
57535753
assert(!Call.getType()->isVoidTy() && "Bad inline asm!");
57545754
if (auto *STy = dyn_cast<StructType>(Call.getType())) {
57555755
OpInfo.ConstraintVT =
5756-
getSimpleValueType(DL, STy->getElementType(ResNo));
5756+
getAsmOperandValueType(DL, STy->getElementType(ResNo))
5757+
.getSimpleVT();
57575758
} else {
57585759
assert(ResNo == 0 && "Asm only has one result!");
57595760
OpInfo.ConstraintVT =

llvm/lib/CodeGen/ValueTypes.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ std::string EVT::getEVTString() const {
177177
if (isFloatingPoint())
178178
return "f" + utostr(getSizeInBits());
179179
llvm_unreachable("Invalid EVT!");
180+
case MVT::riscv_i32_pair:
181+
return "riscv_i32_pair";
182+
case MVT::riscv_i64_pair:
183+
return "riscv_i64_pair";
180184
case MVT::bf16: return "bf16";
181185
case MVT::ppcf128: return "ppcf128";
182186
case MVT::isVoid: return "isVoid";
@@ -214,6 +218,8 @@ Type *EVT::getTypeForEVT(LLVMContext &Context) const {
214218
assert(isExtended() && "Type is not extended!");
215219
return LLVMTy;
216220
case MVT::isVoid: return Type::getVoidTy(Context);
221+
case MVT::riscv_i32_pair: return IntegerType::get(Context, 64);
222+
case MVT::riscv_i64_pair: return IntegerType::get(Context, 128);
217223
case MVT::x86mmx: return llvm::FixedVectorType::get(llvm::IntegerType::get(Context, 64), 1);
218224
case MVT::aarch64svcount:
219225
return TargetExtType::get(Context, "aarch64.svcount");

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,12 @@ struct RISCVOperand final : public MCParsedAsmOperand {
481481
RISCVMCRegisterClasses[RISCV::GPRRegClassID].contains(Reg.RegNum);
482482
}
483483

484+
bool isGPRPair() const {
485+
return Kind == KindTy::Register &&
486+
RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(
487+
Reg.RegNum);
488+
}
489+
484490
bool isGPRF16() const {
485491
return Kind == KindTy::Register &&
486492
RISCVMCRegisterClasses[RISCV::GPRF16RegClassID].contains(Reg.RegNum);
@@ -491,17 +497,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
491497
RISCVMCRegisterClasses[RISCV::GPRF32RegClassID].contains(Reg.RegNum);
492498
}
493499

494-
bool isGPRAsFPR() const { return isGPR() && Reg.IsGPRAsFPR; }
495-
bool isGPRAsFPR16() const { return isGPRF16() && Reg.IsGPRAsFPR; }
496-
bool isGPRAsFPR32() const { return isGPRF32() && Reg.IsGPRAsFPR; }
497-
bool isGPRPairAsFPR() const { return isGPRPair() && Reg.IsGPRAsFPR; }
498-
499-
bool isGPRPair() const {
500+
bool isGPRF64Pair() const {
500501
return Kind == KindTy::Register &&
501-
RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(
502+
RISCVMCRegisterClasses[RISCV::GPRF64PairRegClassID].contains(
502503
Reg.RegNum);
503504
}
504505

506+
bool isGPRAsFPR() const { return isGPR() && Reg.IsGPRAsFPR; }
507+
bool isGPRAsFPR16() const { return isGPRF16() && Reg.IsGPRAsFPR; }
508+
bool isGPRAsFPR32() const { return isGPRF32() && Reg.IsGPRAsFPR; }
509+
bool isGPRPairAsFPR64() const { return isGPRF64Pair() && Reg.IsGPRAsFPR; }
510+
505511
static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
506512
RISCVMCExpr::VariantKind &VK) {
507513
if (auto *RE = dyn_cast<RISCVMCExpr>(Expr)) {
@@ -2399,7 +2405,7 @@ ParseStatus RISCVAsmParser::parseGPRPairAsFPR64(OperandVector &Operands) {
23992405
const MCRegisterInfo *RI = getContext().getRegisterInfo();
24002406
MCRegister Pair = RI->getMatchingSuperReg(
24012407
Reg, RISCV::sub_gpr_even,
2402-
&RISCVMCRegisterClasses[RISCV::GPRPairRegClassID]);
2408+
&RISCVMCRegisterClasses[RISCV::GPRF64PairRegClassID]);
24032409
Operands.push_back(RISCVOperand::createReg(Pair, S, E, /*isGPRAsFPR=*/true));
24042410
return ParseStatus::Success;
24052411
}

llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,14 +952,43 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
952952
ReplaceNode(Node, Res);
953953
return;
954954
}
955+
case RISCVISD::BuildGPRPair: {
956+
SDValue Ops[] = {
957+
CurDAG->getTargetConstant(RISCV::GPRPairRegClassID, DL, MVT::i32),
958+
Node->getOperand(0),
959+
CurDAG->getTargetConstant(RISCV::sub_gpr_even, DL, MVT::i32),
960+
Node->getOperand(1),
961+
CurDAG->getTargetConstant(RISCV::sub_gpr_odd, DL, MVT::i32)};
962+
963+
SDNode *N = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
964+
Subtarget->getXLenPairVT(), Ops);
965+
ReplaceNode(Node, N);
966+
return;
967+
}
968+
case RISCVISD::SplitGPRPair: {
969+
if (!SDValue(Node, 0).use_empty()) {
970+
SDValue Lo = CurDAG->getTargetExtractSubreg(RISCV::sub_gpr_even, DL, VT,
971+
Node->getOperand(0));
972+
ReplaceUses(SDValue(Node, 0), Lo);
973+
}
974+
975+
if (!SDValue(Node, 1).use_empty()) {
976+
SDValue Hi = CurDAG->getTargetExtractSubreg(RISCV::sub_gpr_odd, DL, VT,
977+
Node->getOperand(0));
978+
ReplaceUses(SDValue(Node, 1), Hi);
979+
}
980+
981+
CurDAG->RemoveDeadNode(Node);
982+
return;
983+
}
955984
case RISCVISD::BuildPairF64: {
956985
if (!Subtarget->hasStdExtZdinx())
957986
break;
958987

959988
assert(!Subtarget->is64Bit() && "Unexpected subtarget");
960989

961990
SDValue Ops[] = {
962-
CurDAG->getTargetConstant(RISCV::GPRPairRegClassID, DL, MVT::i32),
991+
CurDAG->getTargetConstant(RISCV::GPRF64PairRegClassID, DL, MVT::i32),
963992
Node->getOperand(0),
964993
CurDAG->getTargetConstant(RISCV::sub_gpr_even, DL, MVT::i32),
965994
Node->getOperand(1),

0 commit comments

Comments
 (0)