Skip to content

Commit dc2d0d5

Browse files
authored
[Xtensa] Add basic support for inline asm constraints. (#108986)
1 parent 4f95150 commit dc2d0d5

8 files changed

+266
-0
lines changed

llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "XtensaAsmPrinter.h"
15+
#include "MCTargetDesc/XtensaInstPrinter.h"
1516
#include "MCTargetDesc/XtensaMCExpr.h"
1617
#include "MCTargetDesc/XtensaTargetStreamer.h"
1718
#include "TargetInfo/XtensaTargetInfo.h"
@@ -157,6 +158,57 @@ void XtensaAsmPrinter::emitConstantPool() {
157158
OutStreamer->popSection();
158159
}
159160

161+
void XtensaAsmPrinter::printOperand(const MachineInstr *MI, int OpNo,
162+
raw_ostream &O) {
163+
const MachineOperand &MO = MI->getOperand(OpNo);
164+
165+
switch (MO.getType()) {
166+
case MachineOperand::MO_Register:
167+
case MachineOperand::MO_Immediate: {
168+
MCOperand MC = lowerOperand(MI->getOperand(OpNo));
169+
XtensaInstPrinter::printOperand(MC, O);
170+
break;
171+
}
172+
default:
173+
llvm_unreachable("unknown operand type");
174+
}
175+
}
176+
177+
bool XtensaAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
178+
const char *ExtraCode, raw_ostream &O) {
179+
// Print the operand if there is no operand modifier.
180+
if (!ExtraCode || !ExtraCode[0]) {
181+
printOperand(MI, OpNo, O);
182+
return false;
183+
}
184+
185+
// Fallback to the default implementation.
186+
return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O);
187+
}
188+
189+
bool XtensaAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
190+
unsigned OpNo,
191+
const char *ExtraCode,
192+
raw_ostream &OS) {
193+
if (ExtraCode && ExtraCode[0])
194+
return true; // Unknown modifier.
195+
196+
assert(OpNo + 1 < MI->getNumOperands() && "Insufficient operands");
197+
198+
const MachineOperand &Base = MI->getOperand(OpNo);
199+
const MachineOperand &Offset = MI->getOperand(OpNo + 1);
200+
201+
assert(Base.isReg() &&
202+
"Unexpected base pointer for inline asm memory operand.");
203+
assert(Offset.isImm() && "Unexpected offset for inline asm memory operand.");
204+
205+
OS << XtensaInstPrinter::getRegisterName(Base.getReg());
206+
OS << ", ";
207+
OS << Offset.getImm();
208+
209+
return false;
210+
}
211+
160212
MCSymbol *
161213
XtensaAsmPrinter::GetConstantPoolIndexSymbol(const MachineOperand &MO) const {
162214
// Create a symbol for the name.

llvm/lib/Target/Xtensa/XtensaAsmPrinter.h

+8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ class LLVM_LIBRARY_VISIBILITY XtensaAsmPrinter : public AsmPrinter {
4242

4343
void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override;
4444

45+
void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O);
46+
47+
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
48+
const char *ExtraCode, raw_ostream &O) override;
49+
50+
bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
51+
const char *ExtraCode, raw_ostream &OS) override;
52+
4553
MCSymbol *GetConstantPoolIndexSymbol(const MachineOperand &MO) const;
4654

4755
MCSymbol *GetJumpTableSymbol(const MachineOperand &MO) const;

llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class XtensaDAGToDAGISel : public SelectionDAGISel {
3333

3434
void Select(SDNode *Node) override;
3535

36+
bool SelectInlineAsmMemoryOperand(const SDValue &Op,
37+
InlineAsm::ConstraintCode ConstraintID,
38+
std::vector<SDValue> &OutOps) override;
39+
3640
// For load/store instructions generate (base+offset) pair from
3741
// memory address. The offset must be a multiple of scale argument.
3842
bool selectMemRegAddr(SDValue Addr, SDValue &Base, SDValue &Offset,
@@ -212,3 +216,22 @@ void XtensaDAGToDAGISel::Select(SDNode *Node) {
212216

213217
SelectCode(Node);
214218
}
219+
220+
bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand(
221+
const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
222+
std::vector<SDValue> &OutOps) {
223+
switch (ConstraintID) {
224+
default:
225+
llvm_unreachable("Unexpected asm memory constraint");
226+
case InlineAsm::ConstraintCode::m: {
227+
SDValue Base, Offset;
228+
229+
selectMemRegAddr(Op, Base, Offset, 4);
230+
OutOps.push_back(Base);
231+
OutOps.push_back(Offset);
232+
233+
return false;
234+
}
235+
}
236+
return false;
237+
}

llvm/lib/Target/Xtensa/XtensaISelLowering.cpp

+68
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,74 @@ bool XtensaTargetLowering::isOffsetFoldingLegal(
142142
return false;
143143
}
144144

145+
//===----------------------------------------------------------------------===//
146+
// Inline asm support
147+
//===----------------------------------------------------------------------===//
148+
TargetLowering::ConstraintType
149+
XtensaTargetLowering::getConstraintType(StringRef Constraint) const {
150+
if (Constraint.size() == 1) {
151+
switch (Constraint[0]) {
152+
case 'r':
153+
return C_RegisterClass;
154+
default:
155+
break;
156+
}
157+
}
158+
return TargetLowering::getConstraintType(Constraint);
159+
}
160+
161+
TargetLowering::ConstraintWeight
162+
XtensaTargetLowering::getSingleConstraintMatchWeight(
163+
AsmOperandInfo &Info, const char *Constraint) const {
164+
ConstraintWeight Weight = CW_Invalid;
165+
Value *CallOperandVal = Info.CallOperandVal;
166+
// If we don't have a value, we can't do a match,
167+
// but allow it at the lowest weight.
168+
if (!CallOperandVal)
169+
return CW_Default;
170+
171+
Type *Ty = CallOperandVal->getType();
172+
173+
// Look at the constraint type.
174+
switch (*Constraint) {
175+
default:
176+
Weight = TargetLowering::getSingleConstraintMatchWeight(Info, Constraint);
177+
break;
178+
case 'r':
179+
if (Ty->isIntegerTy())
180+
Weight = CW_Register;
181+
break;
182+
}
183+
return Weight;
184+
}
185+
186+
std::pair<unsigned, const TargetRegisterClass *>
187+
XtensaTargetLowering::getRegForInlineAsmConstraint(
188+
const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
189+
if (Constraint.size() == 1) {
190+
// GCC Constraint Letters
191+
switch (Constraint[0]) {
192+
default:
193+
break;
194+
case 'r': // General-purpose register
195+
return std::make_pair(0U, &Xtensa::ARRegClass);
196+
}
197+
}
198+
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
199+
}
200+
201+
void XtensaTargetLowering::LowerAsmOperandForConstraint(
202+
SDValue Op, StringRef Constraint, std::vector<SDValue> &Ops,
203+
SelectionDAG &DAG) const {
204+
SDLoc DL(Op);
205+
206+
// Only support length 1 constraints for now.
207+
if (Constraint.size() > 1)
208+
return;
209+
210+
TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
211+
}
212+
145213
//===----------------------------------------------------------------------===//
146214
// Calling conventions
147215
//===----------------------------------------------------------------------===//

llvm/lib/Target/Xtensa/XtensaISelLowering.h

+15
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ class XtensaTargetLowering : public TargetLowering {
7676

7777
const char *getTargetNodeName(unsigned Opcode) const override;
7878

79+
std::pair<unsigned, const TargetRegisterClass *>
80+
getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
81+
StringRef Constraint, MVT VT) const override;
82+
83+
TargetLowering::ConstraintType
84+
getConstraintType(StringRef Constraint) const override;
85+
86+
TargetLowering::ConstraintWeight
87+
getSingleConstraintMatchWeight(AsmOperandInfo &Info,
88+
const char *Constraint) const override;
89+
90+
void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
91+
std::vector<SDValue> &Ops,
92+
SelectionDAG &DAG) const override;
93+
7994
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
8095

8196
SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; RUN: not llc --mtriple=xtensa < %s 2>&1 | FileCheck %s
3+
4+
define void @constraint_f() nounwind {
5+
; CHECK: error: unknown asm constraint 'f'
6+
tail call void asm "addi a1, a1, $0", "f"(i32 1)
7+
ret void
8+
}
9+
10+
define i32 @register_a100(i32 %a) nounwind {
11+
; CHECK: error: couldn't allocate input reg for constraint '{$a100}'
12+
%1 = tail call i32 asm "addi $0, $1, 1", "=r,{$a100}"(i32 %a)
13+
ret i32 %1
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc --mtriple=xtensa < %s | FileCheck %s --check-prefix=XTENSA
3+
4+
define i32 @m_offset_0(ptr %p) nounwind {
5+
; XTENSA-LABEL: m_offset_0:
6+
; XTENSA: #APP
7+
; XTENSA-NEXT: l32i a2, a2, 0
8+
; XTENSA-NEXT: #NO_APP
9+
; XTENSA-NEXT: ret
10+
%1 = call i32 asm "l32i $0, $1", "=r,*m"(ptr elementtype(i32) %p)
11+
ret i32 %1
12+
}
13+
14+
define i32 @m_offset_1020(ptr %p) nounwind {
15+
; XTENSA-LABEL: m_offset_1020:
16+
; XTENSA: #APP
17+
; XTENSA-NEXT: l32i a2, a2, 1020
18+
; XTENSA-NEXT: #NO_APP
19+
; XTENSA-NEXT: ret
20+
%1 = getelementptr inbounds i8, ptr %p, i32 1020
21+
%2 = call i32 asm "l32i $0, $1", "=r,*m"(ptr elementtype(i32) %1)
22+
ret i32 %2
23+
}
24+
25+
define i8 @m_i8_offset_7(ptr %p) nounwind {
26+
; XTENSA-LABEL: m_i8_offset_7:
27+
; XTENSA: addi a8, a2, 7
28+
; XTENSA-NEXT: #APP
29+
; XTENSA-NEXT: l8ui a2, a8, 0
30+
; XTENSA-NEXT: #NO_APP
31+
; XTENSA-NEXT: ret
32+
%1 = getelementptr inbounds i8, ptr %p, i32 7
33+
%2 = call i8 asm "l8ui $0, $1", "=r,*m"(ptr elementtype(i8) %1)
34+
ret i8 %2
35+
}
36+
37+
define i16 @m_i16_offset_10(ptr %p) nounwind {
38+
; XTENSA-LABEL: m_i16_offset_10:
39+
; XTENSA: #APP
40+
; XTENSA-NEXT: l16si a2, a2, 20
41+
; XTENSA-NEXT: #NO_APP
42+
; XTENSA-NEXT: ret
43+
%1 = getelementptr inbounds i16, ptr %p, i32 10
44+
%2 = call i16 asm "l16si $0, $1", "=r,*m"(ptr elementtype(i16) %1)
45+
ret i16 %2
46+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
2+
; RUN: llc -mtriple=xtensa < %s \
3+
; RUN: | FileCheck -check-prefix=XTENSA %s
4+
5+
@gi = external global i32
6+
7+
define i32 @constraint_r(i32 %a) {
8+
; XTENSA-LABEL: constraint_r:
9+
; XTENSA: l32r a8, .LCPI0_0
10+
; XTENSA-NEXT: l32i a8, a8, 0
11+
; XTENSA-NEXT: #APP
12+
; XTENSA-NEXT: add a2, a2, a8
13+
; XTENSA-NEXT: #NO_APP
14+
; XTENSA-NEXT: ret
15+
%1 = load i32, ptr @gi
16+
%2 = tail call i32 asm "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %1)
17+
ret i32 %2
18+
}
19+
20+
define i32 @constraint_i(i32 %a) {
21+
; XTENSA-LABEL: constraint_i:
22+
; XTENSA: #APP
23+
; XTENSA-NEXT: addi a2, a2, 113
24+
; XTENSA-NEXT: #NO_APP
25+
; XTENSA-NEXT: ret
26+
%1 = load i32, ptr @gi
27+
%2 = tail call i32 asm "addi $0, $1, $2", "=r,r,i"(i32 %a, i32 113)
28+
ret i32 %2
29+
}
30+
31+
define i32 @explicit_register_a3(i32 %a) nounwind {
32+
; XTENSA-LABEL: explicit_register_a3:
33+
; XTENSA: or a3, a2, a2
34+
; XTENSA-NEXT: #APP
35+
; XTENSA-NEXT: addi a2, a3, 1
36+
; XTENSA-NEXT: #NO_APP
37+
; XTENSA-NEXT: ret
38+
%1 = tail call i32 asm "addi $0, $1, 1", "=r,{a3}"(i32 %a)
39+
ret i32 %1
40+
}

0 commit comments

Comments
 (0)