[llvm] [Xtensa] Add basic support for inline asm constraints. (PR #108986)

Andrei Safronov via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 24 15:32:51 PDT 2024


https://github.com/andreisfr updated https://github.com/llvm/llvm-project/pull/108986

>From 9ee86ede127e9ed51d6a2dace2e3221eaaa97b83 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
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 3f99387f759d93..db86637ecf83f3 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 f9cf5ae8c9f656..1137309cd9a450 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 6f6d3342fcd7f2..6b56016063d49d 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<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,
@@ -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;
+}
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
index bc1360e2123075..3e7064be4096f8 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<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
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.h b/llvm/lib/Target/Xtensa/XtensaISelLowering.h
index 2a878e45047d21..f1cd00c41437a4 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<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,
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 00000000000000..73c731f3acd123
--- /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 00000000000000..e080d01197843c
--- /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
+
+ at 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 <safronov at espressif.com>
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 6b56016063d49d..af1110487b4274 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 3e7064be4096f8..670930e99334f2 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 00000000000000..2a436dd156dd77
--- /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 73c731f3acd123..4b27ba9337f880 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 e080d01197843c..748f5f857acfd8 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
+}



More information about the llvm-commits mailing list