Skip to content

[Xtensa] Add basic support for inline asm constraints. #108986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "XtensaAsmPrinter.h"
#include "MCTargetDesc/XtensaInstPrinter.h"
#include "MCTargetDesc/XtensaMCExpr.h"
#include "MCTargetDesc/XtensaTargetStreamer.h"
#include "TargetInfo/XtensaTargetInfo.h"
Expand Down Expand Up @@ -157,6 +158,57 @@ void XtensaAsmPrinter::emitConstantPool() {
OutStreamer->popSection();
}

void XtensaAsmPrinter::printOperand(const MachineInstr *MI, int OpNo,
raw_ostream &O) {
const MachineOperand &MO = MI->getOperand(OpNo);

switch (MO.getType()) {
case MachineOperand::MO_Register:
case MachineOperand::MO_Immediate: {
MCOperand MC = lowerOperand(MI->getOperand(OpNo));
XtensaInstPrinter::printOperand(MC, O);
break;
}
default:
llvm_unreachable("unknown operand type");
}
}

bool XtensaAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) {
// Print the operand if there is no operand modifier.
if (!ExtraCode || !ExtraCode[0]) {
printOperand(MI, OpNo, O);
return false;
}

// Fallback to the default implementation.
return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, O);
}

bool XtensaAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
unsigned OpNo,
const char *ExtraCode,
raw_ostream &OS) {
if (ExtraCode && ExtraCode[0])
return true; // Unknown modifier.

assert(OpNo + 1 < MI->getNumOperands() && "Insufficient operands");

const MachineOperand &Base = MI->getOperand(OpNo);
const MachineOperand &Offset = MI->getOperand(OpNo + 1);

assert(Base.isReg() &&
"Unexpected base pointer for inline asm memory operand.");
assert(Offset.isImm() && "Unexpected offset for inline asm memory operand.");

OS << XtensaInstPrinter::getRegisterName(Base.getReg());
OS << ", ";
OS << Offset.getImm();

return false;
}

MCSymbol *
XtensaAsmPrinter::GetConstantPoolIndexSymbol(const MachineOperand &MO) const {
// Create a symbol for the name.
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaAsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class LLVM_LIBRARY_VISIBILITY XtensaAsmPrinter : public AsmPrinter {

void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override;

void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O);

bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) override;

bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &OS) override;

MCSymbol *GetConstantPoolIndexSymbol(const MachineOperand &MO) const;

MCSymbol *GetJumpTableSymbol(const MachineOperand &MO) const;
Expand Down
22 changes: 22 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class XtensaDAGToDAGISel : public SelectionDAGISel {

void Select(SDNode *Node) override;

bool SelectInlineAsmMemoryOperand(const SDValue &Op,
InlineAsm::ConstraintCode ConstraintID,
std::vector<SDValue> &OutOps) override;

// For load/store instructions generate (base+offset) pair from
// memory address. The offset must be a multiple of scale argument.
bool selectMemRegAddr(SDValue Addr, SDValue &Base, SDValue &Offset,
Expand Down Expand Up @@ -212,3 +216,21 @@ void XtensaDAGToDAGISel::Select(SDNode *Node) {

SelectCode(Node);
}

bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand(
const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
std::vector<SDValue> &OutOps) {
switch (ConstraintID) {
default:
llvm_unreachable("Unexpected asm memory constraint");
case InlineAsm::ConstraintCode::m: {
SDValue Base, Offset;

selectMemRegAddr(Op, Base, Offset, 4);
OutOps.push_back(Base);
OutOps.push_back(Offset);
return false;
}
}
return false;
}
68 changes: 68 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,74 @@ bool XtensaTargetLowering::isOffsetFoldingLegal(
return false;
}

//===----------------------------------------------------------------------===//
// Inline asm support
//===----------------------------------------------------------------------===//
TargetLowering::ConstraintType
XtensaTargetLowering::getConstraintType(StringRef Constraint) const {
if (Constraint.size() == 1) {
switch (Constraint[0]) {
case 'r':
return C_RegisterClass;
default:
break;
}
}
return TargetLowering::getConstraintType(Constraint);
}

TargetLowering::ConstraintWeight
XtensaTargetLowering::getSingleConstraintMatchWeight(
AsmOperandInfo &Info, const char *Constraint) const {
ConstraintWeight Weight = CW_Invalid;
Value *CallOperandVal = Info.CallOperandVal;
// If we don't have a value, we can't do a match,
// but allow it at the lowest weight.
if (CallOperandVal == NULL)
return CW_Default;

Type *Ty = CallOperandVal->getType();

// Look at the constraint type.
switch (*Constraint) {
default:
Weight = TargetLowering::getSingleConstraintMatchWeight(Info, Constraint);
break;
case 'r':
if (Ty->isIntegerTy())
Weight = CW_Register;
break;
}
return Weight;
}

std::pair<unsigned, const TargetRegisterClass *>
XtensaTargetLowering::getRegForInlineAsmConstraint(
const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
if (Constraint.size() == 1) {
// GCC Constraint Letters
switch (Constraint[0]) {
default:
break;
case 'r': // General-purpose register
return std::make_pair(0U, &Xtensa::ARRegClass);
}
}
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
}

void XtensaTargetLowering::LowerAsmOperandForConstraint(
SDValue Op, StringRef Constraint, std::vector<SDValue> &Ops,
SelectionDAG &DAG) const {
SDLoc DL(Op);

// Only support length 1 constraints for now.
if (Constraint.size() > 1)
return;

TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
}

//===----------------------------------------------------------------------===//
// Calling conventions
//===----------------------------------------------------------------------===//
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ class XtensaTargetLowering : public TargetLowering {

const char *getTargetNodeName(unsigned Opcode) const override;

std::pair<unsigned, const TargetRegisterClass *>
getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
StringRef Constraint, MVT VT) const override;

TargetLowering::ConstraintType
getConstraintType(StringRef Constraint) const override;

TargetLowering::ConstraintWeight
getSingleConstraintMatchWeight(AsmOperandInfo &Info,
const char *Constraint) const override;

void LowerAsmOperandForConstraint(SDValue Op, StringRef Constraint,
std::vector<SDValue> &Ops,
SelectionDAG &DAG) const override;

SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;

SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
Expand Down
23 changes: 23 additions & 0 deletions llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc --mtriple=xtensa --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA32

define i32 @m_offset_0(ptr %p) nounwind {
; LA32-LABEL: m_offset_0:
; LA32: #APP
; LA32-NEXT: l32i a2, a2, 0
; LA32-NEXT: #NO_APP
; LA32-NEXT: ret
%1 = call i32 asm "l32i $0, $1", "=r,*m"(ptr elementtype(i32) %p)
ret i32 %1
}

define i32 @m_offset_1020(ptr %p) nounwind {
; LA32-LABEL: m_offset_1020:
; LA32: #APP
; LA32-NEXT: l32i a2, a2, 1020
; LA32-NEXT: #NO_APP
; LA32-NEXT: ret
%1 = getelementptr inbounds i8, ptr %p, i32 1020
%2 = call i32 asm "l32i $0, $1", "=r,*m"(ptr elementtype(i32) %1)
ret i32 %2
}
29 changes: 29 additions & 0 deletions llvm/test/CodeGen/Xtensa/inline-asm.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc -mtriple=xtensa -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=XTENSA %s

@gi = external global i32

define i32 @constraint_r(i32 %a) {
; XTENSA-LABEL: constraint_r:
; XTENSA: l32r a8, .LCPI0_0
; XTENSA-NEXT: l32i a8, a8, 0
; XTENSA-NEXT: #APP
; XTENSA-NEXT: add a2, a2, a8
; XTENSA-NEXT: #NO_APP
; XTENSA-NEXT: ret
%1 = load i32, i32* @gi
%2 = tail call i32 asm "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %1)
ret i32 %2
}

define i32 @constraint_i(i32 %a) {
; XTENSA-LABEL: constraint_i:
; XTENSA: #APP
; XTENSA-NEXT: addi a2, a2, 113
; XTENSA-NEXT: #NO_APP
; XTENSA-NEXT: ret
%1 = load i32, i32* @gi
%2 = tail call i32 asm "addi $0, $1, $2", "=r,r,i"(i32 %a, i32 113)
ret i32 %2
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also test some cases with physical registers, and different types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for comments. I added more tests.

Loading