Skip to content

Commit a190fcd

Browse files
committed
[CSKY] Add inline asm constraints and related codegen support
There are kinds of inline asm constraints and corresponding register class or register as following. 'b': mGPRRegClass 'v': sGPRRegClass 'w': sFPR32RegClass or sFPR64RegClass 'c': C register 'z': R14 register 'h': HI register 'l': LO register 'y': HI or LO register It also adds codegen test for inline-asm including constraints, clobbers and abi names.
1 parent 289b8e0 commit a190fcd

14 files changed

+1969
-0
lines changed

llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class CSKYAsmParser : public MCTargetAsmParser {
5252

5353
const MCRegisterInfo *MRI;
5454

55+
unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
56+
unsigned Kind) override;
57+
5558
bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo,
5659
int64_t Lower, int64_t Upper, Twine Msg);
5760

@@ -612,6 +615,11 @@ struct CSKYOperand : public MCParsedAsmOperand {
612615
#define GET_MNEMONIC_SPELL_CHECKER
613616
#include "CSKYGenAsmMatcher.inc"
614617

618+
static MCRegister convertFPR32ToFPR64(MCRegister Reg) {
619+
assert(Reg >= CSKY::F0_32 && Reg <= CSKY::F31_32 && "Invalid register");
620+
return Reg - CSKY::F0_32 + CSKY::F0_64;
621+
}
622+
615623
static std::string CSKYMnemonicSpellCheck(StringRef S, const FeatureBitset &FBS,
616624
unsigned VariantID = 0);
617625

@@ -1473,6 +1481,40 @@ OperandMatchResultTy CSKYAsmParser::tryParseRegister(unsigned &RegNo,
14731481

14741482
bool CSKYAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
14751483

1484+
unsigned CSKYAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
1485+
unsigned Kind) {
1486+
CSKYOperand &Op = static_cast<CSKYOperand &>(AsmOp);
1487+
1488+
if (!Op.isReg())
1489+
return Match_InvalidOperand;
1490+
1491+
MCRegister Reg = Op.getReg();
1492+
1493+
if (CSKYMCRegisterClasses[CSKY::FPR32RegClassID].contains(Reg)) {
1494+
// As the parser couldn't differentiate an FPR64 from an FPR32, coerce the
1495+
// register from FPR32 to FPR64 if necessary.
1496+
if (Kind == MCK_FPR64 || Kind == MCK_sFPR64_V) {
1497+
Op.Reg.RegNum = convertFPR32ToFPR64(Reg);
1498+
if (Kind == MCK_sFPR64_V &&
1499+
(Op.Reg.RegNum < CSKY::F0_64 || Op.Reg.RegNum > CSKY::F15_64))
1500+
return Match_InvalidRegOutOfRange;
1501+
if (Kind == MCK_FPR64 &&
1502+
(Op.Reg.RegNum < CSKY::F0_64 || Op.Reg.RegNum > CSKY::F31_64))
1503+
return Match_InvalidRegOutOfRange;
1504+
return Match_Success;
1505+
}
1506+
}
1507+
1508+
if (CSKYMCRegisterClasses[CSKY::GPRRegClassID].contains(Reg)) {
1509+
if (Kind == MCK_GPRPair) {
1510+
Op.Reg.RegNum = MRI->getEncodingValue(Reg) + CSKY::R0_R1;
1511+
return Match_Success;
1512+
}
1513+
}
1514+
1515+
return Match_InvalidOperand;
1516+
}
1517+
14761518
void CSKYAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) {
14771519
MCInst CInst;
14781520
bool Res = false;

llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,69 @@ void CSKYAsmPrinter::emitMachineConstantPoolValue(
218218
OutStreamer->emitValue(Expr, Size);
219219
}
220220

221+
bool CSKYAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
222+
const char *ExtraCode, raw_ostream &OS) {
223+
// First try the generic code, which knows about modifiers like 'c' and 'n'.
224+
if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
225+
return false;
226+
227+
const MachineOperand &MO = MI->getOperand(OpNo);
228+
if (ExtraCode && ExtraCode[0]) {
229+
if (ExtraCode[1] != 0)
230+
return true; // Unknown modifier.
231+
232+
switch (ExtraCode[0]) {
233+
default:
234+
return true; // Unknown modifier.
235+
case 'R':
236+
if (MO.getType() == MachineOperand::MO_Register) {
237+
OS << CSKYInstPrinter::getRegisterName(MO.getReg() + 1);
238+
return false;
239+
}
240+
}
241+
}
242+
243+
switch (MO.getType()) {
244+
case MachineOperand::MO_Immediate:
245+
OS << MO.getImm();
246+
return false;
247+
case MachineOperand::MO_Register:
248+
if (MO.getReg() == CSKY::C)
249+
return false;
250+
OS << CSKYInstPrinter::getRegisterName(MO.getReg());
251+
return false;
252+
case MachineOperand::MO_GlobalAddress:
253+
PrintSymbolOperand(MO, OS);
254+
return false;
255+
case MachineOperand::MO_BlockAddress: {
256+
MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress());
257+
Sym->print(OS, MAI);
258+
return false;
259+
}
260+
default:
261+
break;
262+
}
263+
264+
return true;
265+
}
266+
267+
bool CSKYAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
268+
unsigned OpNo, const char *ExtraCode,
269+
raw_ostream &OS) {
270+
if (!ExtraCode) {
271+
const MachineOperand &MO = MI->getOperand(OpNo);
272+
// For now, we only support register memory operands in registers and
273+
// assume there is no addend
274+
if (!MO.isReg())
275+
return true;
276+
277+
OS << "(" << CSKYInstPrinter::getRegisterName(MO.getReg()) << ", 0)";
278+
return false;
279+
}
280+
281+
return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS);
282+
}
283+
221284
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeCSKYAsmPrinter() {
222285
RegisterAsmPrinter<CSKYAsmPrinter> X(getTheCSKYTarget());
223286
}

llvm/lib/Target/CSKY/CSKYAsmPrinter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class LLVM_LIBRARY_VISIBILITY CSKYAsmPrinter : public AsmPrinter {
5252

5353
// we emit constant pools customly!
5454
void emitConstantPool() override{};
55+
56+
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
57+
const char *ExtraCode, raw_ostream &OS) override;
58+
59+
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
60+
const char *ExtraCode, raw_ostream &OS) override;
5561
};
5662
} // end namespace llvm
5763

llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ class CSKYDAGToDAGISel : public SelectionDAGISel {
4242
void Select(SDNode *N) override;
4343
bool selectAddCarry(SDNode *N);
4444
bool selectSubCarry(SDNode *N);
45+
bool selectInlineAsm(SDNode *N);
46+
47+
SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);
48+
49+
bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
50+
std::vector<SDValue> &OutOps) override;
4551

4652
#include "CSKYGenDAGISel.inc"
4753
};
@@ -86,6 +92,10 @@ void CSKYDAGToDAGISel::Select(SDNode *N) {
8692
IsSelected = true;
8793
break;
8894
}
95+
case ISD::INLINEASM:
96+
case ISD::INLINEASM_BR:
97+
IsSelected = selectInlineAsm(N);
98+
break;
8999
}
90100

91101
if (IsSelected)
@@ -95,6 +105,167 @@ void CSKYDAGToDAGISel::Select(SDNode *N) {
95105
SelectCode(N);
96106
}
97107

108+
bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
109+
std::vector<SDValue> AsmNodeOperands;
110+
unsigned Flag, Kind;
111+
bool Changed = false;
112+
unsigned NumOps = N->getNumOperands();
113+
114+
// Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
115+
// However, some instructions (e.g. mula.s32) require GPR pair.
116+
// Since there is no constraint to explicitly specify a
117+
// reg pair, we use GPRPair reg class for "%r" for 64-bit data.
118+
119+
SDLoc dl(N);
120+
SDValue Glue =
121+
N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
122+
123+
SmallVector<bool, 8> OpChanged;
124+
// Glue node will be appended late.
125+
for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
126+
++i) {
127+
SDValue op = N->getOperand(i);
128+
AsmNodeOperands.push_back(op);
129+
130+
if (i < InlineAsm::Op_FirstOperand)
131+
continue;
132+
133+
if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(i))) {
134+
Flag = C->getZExtValue();
135+
Kind = InlineAsm::getKind(Flag);
136+
} else
137+
continue;
138+
139+
// Immediate operands to inline asm in the SelectionDAG are modeled with
140+
// two operands. The first is a constant of value InlineAsm::Kind_Imm, and
141+
// the second is a constant with the value of the immediate. If we get here
142+
// and we have a Kind_Imm, skip the next operand, and continue.
143+
if (Kind == InlineAsm::Kind_Imm) {
144+
SDValue op = N->getOperand(++i);
145+
AsmNodeOperands.push_back(op);
146+
continue;
147+
}
148+
149+
unsigned NumRegs = InlineAsm::getNumOperandRegisters(Flag);
150+
if (NumRegs)
151+
OpChanged.push_back(false);
152+
153+
unsigned DefIdx = 0;
154+
bool IsTiedToChangedOp = false;
155+
// If it's a use that is tied with a previous def, it has no
156+
// reg class constraint.
157+
if (Changed && InlineAsm::isUseOperandTiedToDef(Flag, DefIdx))
158+
IsTiedToChangedOp = OpChanged[DefIdx];
159+
160+
// Memory operands to inline asm in the SelectionDAG are modeled with two
161+
// operands: a constant of value InlineAsm::Kind_Mem followed by the input
162+
// operand. If we get here and we have a Kind_Mem, skip the next operand (so
163+
// it doesn't get misinterpreted), and continue. We do this here because
164+
// it's important to update the OpChanged array correctly before moving on.
165+
if (Kind == InlineAsm::Kind_Mem) {
166+
SDValue op = N->getOperand(++i);
167+
AsmNodeOperands.push_back(op);
168+
continue;
169+
}
170+
171+
if (Kind != InlineAsm::Kind_RegUse && Kind != InlineAsm::Kind_RegDef &&
172+
Kind != InlineAsm::Kind_RegDefEarlyClobber)
173+
continue;
174+
175+
unsigned RC;
176+
bool HasRC = InlineAsm::hasRegClassConstraint(Flag, RC);
177+
if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||
178+
NumRegs != 2)
179+
continue;
180+
181+
assert((i + 2 < NumOps) && "Invalid number of operands in inline asm");
182+
SDValue V0 = N->getOperand(i + 1);
183+
SDValue V1 = N->getOperand(i + 2);
184+
unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();
185+
unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg();
186+
SDValue PairedReg;
187+
MachineRegisterInfo &MRI = MF->getRegInfo();
188+
189+
if (Kind == InlineAsm::Kind_RegDef ||
190+
Kind == InlineAsm::Kind_RegDefEarlyClobber) {
191+
// Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
192+
// the original GPRs.
193+
194+
Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
195+
PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
196+
SDValue Chain = SDValue(N, 0);
197+
198+
SDNode *GU = N->getGluedUser();
199+
SDValue RegCopy =
200+
CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
201+
202+
// Extract values from a GPRPair reg and copy to the original GPR reg.
203+
SDValue Sub0 =
204+
CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
205+
SDValue Sub1 =
206+
CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
207+
SDValue T0 =
208+
CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
209+
SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
210+
211+
// Update the original glue user.
212+
std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
213+
Ops.push_back(T1.getValue(1));
214+
CurDAG->UpdateNodeOperands(GU, Ops);
215+
} else {
216+
// For Kind == InlineAsm::Kind_RegUse, we first copy two GPRs into a
217+
// GPRPair and then pass the GPRPair to the inline asm.
218+
SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
219+
220+
// As REG_SEQ doesn't take RegisterSDNode, we copy them first.
221+
SDValue T0 =
222+
CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
223+
SDValue T1 =
224+
CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
225+
SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
226+
227+
// Copy REG_SEQ into a GPRPair-typed VR and replace the original two
228+
// i32 VRs of inline asm with it.
229+
Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
230+
PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
231+
Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
232+
233+
AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
234+
Glue = Chain.getValue(1);
235+
}
236+
237+
Changed = true;
238+
239+
if (PairedReg.getNode()) {
240+
OpChanged[OpChanged.size() - 1] = true;
241+
Flag = InlineAsm::getFlagWord(Kind, 1 /* RegNum*/);
242+
if (IsTiedToChangedOp)
243+
Flag = InlineAsm::getFlagWordForMatchingOp(Flag, DefIdx);
244+
else
245+
Flag = InlineAsm::getFlagWordForRegClass(Flag, CSKY::GPRPairRegClassID);
246+
// Replace the current flag.
247+
AsmNodeOperands[AsmNodeOperands.size() - 1] =
248+
CurDAG->getTargetConstant(Flag, dl, MVT::i32);
249+
// Add the new register node and skip the original two GPRs.
250+
AsmNodeOperands.push_back(PairedReg);
251+
// Skip the next two GPRs.
252+
i += 2;
253+
}
254+
}
255+
256+
if (Glue.getNode())
257+
AsmNodeOperands.push_back(Glue);
258+
if (!Changed)
259+
return false;
260+
261+
SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),
262+
CurDAG->getVTList(MVT::Other, MVT::Glue),
263+
AsmNodeOperands);
264+
New->setNodeId(-1);
265+
ReplaceNode(N, New.getNode());
266+
return true;
267+
}
268+
98269
bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {
99270
MachineSDNode *NewNode = nullptr;
100271
auto Type0 = N->getValueType(0);
@@ -175,6 +346,31 @@ bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {
175346
return true;
176347
}
177348

349+
SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) {
350+
SDLoc dl(V0.getNode());
351+
SDValue RegClass =
352+
CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32);
353+
SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32);
354+
SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32);
355+
const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1};
356+
return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);
357+
}
358+
359+
bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(
360+
const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
361+
switch (ConstraintID) {
362+
case InlineAsm::Constraint_m:
363+
// We just support simple memory operands that have a single address
364+
// operand and need no special handling.
365+
OutOps.push_back(Op);
366+
return false;
367+
default:
368+
break;
369+
}
370+
371+
return true;
372+
}
373+
178374
FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM) {
179375
return new CSKYDAGToDAGISel(TM);
180376
}

0 commit comments

Comments
 (0)