From 9ee86ede127e9ed51d6a2dace2e3221eaaa97b83 Mon Sep 17 00:00:00 2001 From: Andrei Safronov Date: Tue, 17 Sep 2024 17:51:31 +0300 Subject: [PATCH 1/2] [Xtensa] Add basic support for inline asm constraints. --- llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp | 52 ++++++++++++++ llvm/lib/Target/Xtensa/XtensaAsmPrinter.h | 8 +++ llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp | 22 ++++++ llvm/lib/Target/Xtensa/XtensaISelLowering.cpp | 68 +++++++++++++++++++ llvm/lib/Target/Xtensa/XtensaISelLowering.h | 15 ++++ .../Xtensa/inline-asm-mem-constraint.ll | 23 +++++++ llvm/test/CodeGen/Xtensa/inline-asm.ll | 29 ++++++++ 7 files changed, 217 insertions(+) create mode 100644 llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll create mode 100644 llvm/test/CodeGen/Xtensa/inline-asm.ll diff --git a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp index 3f99387f759d9..db86637ecf83f 100644 --- a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "XtensaAsmPrinter.h" +#include "MCTargetDesc/XtensaInstPrinter.h" #include "MCTargetDesc/XtensaMCExpr.h" #include "MCTargetDesc/XtensaTargetStreamer.h" #include "TargetInfo/XtensaTargetInfo.h" @@ -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. diff --git a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h index f9cf5ae8c9f65..1137309cd9a45 100644 --- a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h @@ -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; diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp index 6f6d3342fcd7f..6b56016063d49 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp @@ -33,6 +33,10 @@ class XtensaDAGToDAGISel : public SelectionDAGISel { void Select(SDNode *Node) override; + bool SelectInlineAsmMemoryOperand(const SDValue &Op, + InlineAsm::ConstraintCode ConstraintID, + std::vector &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, @@ -212,3 +216,21 @@ void XtensaDAGToDAGISel::Select(SDNode *Node) { SelectCode(Node); } + +bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, + std::vector &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; +} diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp index bc1360e212307..3e7064be4096f 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp @@ -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 +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 &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 //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.h b/llvm/lib/Target/Xtensa/XtensaISelLowering.h index 2a878e45047d2..f1cd00c41437a 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.h +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.h @@ -76,6 +76,21 @@ class XtensaTargetLowering : public TargetLowering { const char *getTargetNodeName(unsigned Opcode) const override; + std::pair + 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 &Ops, + SelectionDAG &DAG) const override; + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, diff --git a/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll b/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll new file mode 100644 index 0000000000000..73c731f3acd12 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll @@ -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 +} diff --git a/llvm/test/CodeGen/Xtensa/inline-asm.ll b/llvm/test/CodeGen/Xtensa/inline-asm.ll new file mode 100644 index 0000000000000..e080d01197843 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm.ll @@ -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 +} From eff6c2653d4c497ec8c80f5afc9f5434e3897a9e Mon Sep 17 00:00:00 2001 From: Andrei Safronov Date: Wed, 25 Sep 2024 01:31:15 +0300 Subject: [PATCH 2/2] [Xtensa] Added asm inline tests. --- llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp | 1 + llvm/lib/Target/Xtensa/XtensaISelLowering.cpp | 2 +- .../test/CodeGen/Xtensa/inline-asm-invalid.ll | 14 ++++++ .../Xtensa/inline-asm-mem-constraint.ll | 45 ++++++++++++++----- llvm/test/CodeGen/Xtensa/inline-asm.ll | 17 +++++-- 5 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp index 6b56016063d49..af1110487b427 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp @@ -229,6 +229,7 @@ bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand( selectMemRegAddr(Op, Base, Offset, 4); OutOps.push_back(Base); OutOps.push_back(Offset); + return false; } } diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp index 3e7064be4096f..670930e99334f 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp @@ -165,7 +165,7 @@ XtensaTargetLowering::getSingleConstraintMatchWeight( 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) + if (!CallOperandVal) return CW_Default; Type *Ty = CallOperandVal->getType(); diff --git a/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll b/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll new file mode 100644 index 0000000000000..2a436dd156dd7 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll @@ -0,0 +1,14 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: not llc --mtriple=xtensa < %s 2>&1 | FileCheck %s + +define void @constraint_f() nounwind { +; CHECK: error: unknown asm constraint 'f' + tail call void asm "addi a1, a1, $0", "f"(i32 1) + ret void +} + +define i32 @register_a100(i32 %a) nounwind { +; CHECK: error: couldn't allocate input reg for constraint '{$a100}' + %1 = tail call i32 asm "addi $0, $1, 1", "=r,{$a100}"(i32 %a) + ret i32 %1 +} diff --git a/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll b/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll index 73c731f3acd12..4b27ba9337f88 100644 --- a/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll +++ b/llvm/test/CodeGen/Xtensa/inline-asm-mem-constraint.ll @@ -1,23 +1,46 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc --mtriple=xtensa --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA32 +; RUN: llc --mtriple=xtensa < %s | FileCheck %s --check-prefix=XTENSA 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 +; XTENSA-LABEL: m_offset_0: +; XTENSA: #APP +; XTENSA-NEXT: l32i a2, a2, 0 +; XTENSA-NEXT: #NO_APP +; XTENSA-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 +; XTENSA-LABEL: m_offset_1020: +; XTENSA: #APP +; XTENSA-NEXT: l32i a2, a2, 1020 +; XTENSA-NEXT: #NO_APP +; XTENSA-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 } + +define i8 @m_i8_offset_7(ptr %p) nounwind { +; XTENSA-LABEL: m_i8_offset_7: +; XTENSA: addi a8, a2, 7 +; XTENSA-NEXT: #APP +; XTENSA-NEXT: l8ui a2, a8, 0 +; XTENSA-NEXT: #NO_APP +; XTENSA-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 7 + %2 = call i8 asm "l8ui $0, $1", "=r,*m"(ptr elementtype(i8) %1) + ret i8 %2 +} + +define i16 @m_i16_offset_10(ptr %p) nounwind { +; XTENSA-LABEL: m_i16_offset_10: +; XTENSA: #APP +; XTENSA-NEXT: l16si a2, a2, 20 +; XTENSA-NEXT: #NO_APP +; XTENSA-NEXT: ret + %1 = getelementptr inbounds i16, ptr %p, i32 10 + %2 = call i16 asm "l16si $0, $1", "=r,*m"(ptr elementtype(i16) %1) + ret i16 %2 +} diff --git a/llvm/test/CodeGen/Xtensa/inline-asm.ll b/llvm/test/CodeGen/Xtensa/inline-asm.ll index e080d01197843..748f5f857acfd 100644 --- a/llvm/test/CodeGen/Xtensa/inline-asm.ll +++ b/llvm/test/CodeGen/Xtensa/inline-asm.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 -; RUN: llc -mtriple=xtensa -verify-machineinstrs < %s \ +; RUN: llc -mtriple=xtensa < %s \ ; RUN: | FileCheck -check-prefix=XTENSA %s @gi = external global i32 @@ -12,7 +12,7 @@ define i32 @constraint_r(i32 %a) { ; XTENSA-NEXT: add a2, a2, a8 ; XTENSA-NEXT: #NO_APP ; XTENSA-NEXT: ret - %1 = load i32, i32* @gi + %1 = load i32, ptr @gi %2 = tail call i32 asm "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %1) ret i32 %2 } @@ -23,7 +23,18 @@ define i32 @constraint_i(i32 %a) { ; XTENSA-NEXT: addi a2, a2, 113 ; XTENSA-NEXT: #NO_APP ; XTENSA-NEXT: ret - %1 = load i32, i32* @gi + %1 = load i32, ptr @gi %2 = tail call i32 asm "addi $0, $1, $2", "=r,r,i"(i32 %a, i32 113) ret i32 %2 } + +define i32 @explicit_register_a3(i32 %a) nounwind { +; XTENSA-LABEL: explicit_register_a3: +; XTENSA: or a3, a2, a2 +; XTENSA-NEXT: #APP +; XTENSA-NEXT: addi a2, a3, 1 +; XTENSA-NEXT: #NO_APP +; XTENSA-NEXT: ret + %1 = tail call i32 asm "addi $0, $1, 1", "=r,{a3}"(i32 %a) + ret i32 %1 +}