[llvm] [Xtensa] Implement base CallConvention. (PR #83280)
Andrei Safronov via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 28 07:45:32 PST 2024
https://github.com/andreisfr created https://github.com/llvm/llvm-project/pull/83280
Implement base Calling Convention functionality. Implement stack load/store register operations.
>From d56762f710d4cb25303a589447d6a63c6b450cb8 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Wed, 28 Feb 2024 18:07:34 +0300
Subject: [PATCH] [Xtensa] Implement base CallConvention.
Implement base Calling Convention functionality. Implement stack
load/store register operations.
---
llvm/lib/Target/Xtensa/CMakeLists.txt | 2 +
llvm/lib/Target/Xtensa/Xtensa.td | 6 +
llvm/lib/Target/Xtensa/XtensaCallingConv.td | 28 ++
llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp | 52 +++-
llvm/lib/Target/Xtensa/XtensaISelLowering.cpp | 267 ++++++++++++++++++
llvm/lib/Target/Xtensa/XtensaISelLowering.h | 23 ++
llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp | 69 +++++
llvm/lib/Target/Xtensa/XtensaInstrInfo.h | 21 ++
llvm/lib/Target/Xtensa/XtensaInstrInfo.td | 8 +-
llvm/lib/Target/Xtensa/XtensaOperators.td | 13 +
llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp | 48 +++-
llvm/lib/Target/Xtensa/XtensaRegisterInfo.h | 4 +
llvm/lib/Target/Xtensa/XtensaUtils.cpp | 60 ++++
llvm/lib/Target/Xtensa/XtensaUtils.h | 27 ++
llvm/test/CodeGen/Xtensa/calling-conv.ll | 78 +++++
llvm/test/CodeGen/Xtensa/stack-access.ll | 35 +++
16 files changed, 734 insertions(+), 7 deletions(-)
create mode 100644 llvm/lib/Target/Xtensa/XtensaCallingConv.td
create mode 100644 llvm/lib/Target/Xtensa/XtensaOperators.td
create mode 100644 llvm/lib/Target/Xtensa/XtensaUtils.cpp
create mode 100644 llvm/lib/Target/Xtensa/XtensaUtils.h
create mode 100644 llvm/test/CodeGen/Xtensa/calling-conv.ll
create mode 100644 llvm/test/CodeGen/Xtensa/stack-access.ll
diff --git a/llvm/lib/Target/Xtensa/CMakeLists.txt b/llvm/lib/Target/Xtensa/CMakeLists.txt
index 2064511e75b82e..432b76d4fd576a 100644
--- a/llvm/lib/Target/Xtensa/CMakeLists.txt
+++ b/llvm/lib/Target/Xtensa/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_TARGET_DEFINITIONS Xtensa.td)
tablegen(LLVM XtensaGenAsmMatcher.inc -gen-asm-matcher)
tablegen(LLVM XtensaGenAsmWriter.inc -gen-asm-writer)
+tablegen(LLVM XtensaGenCallingConv.inc -gen-callingconv)
tablegen(LLVM XtensaGenDAGISel.inc -gen-dag-isel)
tablegen(LLVM XtensaGenDisassemblerTables.inc -gen-disassembler)
tablegen(LLVM XtensaGenInstrInfo.inc -gen-instr-info)
@@ -22,6 +23,7 @@ add_llvm_target(XtensaCodeGen
XtensaRegisterInfo.cpp
XtensaSubtarget.cpp
XtensaTargetMachine.cpp
+ XtensaUtils.cpp
LINK_COMPONENTS
AsmPrinter
diff --git a/llvm/lib/Target/Xtensa/Xtensa.td b/llvm/lib/Target/Xtensa/Xtensa.td
index b953540be94de0..460a15e808b3a4 100644
--- a/llvm/lib/Target/Xtensa/Xtensa.td
+++ b/llvm/lib/Target/Xtensa/Xtensa.td
@@ -35,6 +35,12 @@ def : Proc<"generic", []>;
include "XtensaRegisterInfo.td"
+//===----------------------------------------------------------------------===//
+// Calling Convention Description
+//===----------------------------------------------------------------------===//
+
+include "XtensaCallingConv.td"
+
//===----------------------------------------------------------------------===//
// Instruction Descriptions
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/Xtensa/XtensaCallingConv.td b/llvm/lib/Target/Xtensa/XtensaCallingConv.td
new file mode 100644
index 00000000000000..9d60c38ae73c4a
--- /dev/null
+++ b/llvm/lib/Target/Xtensa/XtensaCallingConv.td
@@ -0,0 +1,28 @@
+//===- XtensaCallingConv.td - Xtensa Calling Conventions -*- tablegen ---*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This describes the calling conventions for the Xtensa ABI.
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Xtensa return value calling convention
+//===----------------------------------------------------------------------===//
+def RetCC_Xtensa : CallingConv<[
+ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,
+ CCIfType<[f32], CCBitConvertToType<i32>>,
+
+ // First two return values go in a2, a3, a4, a5
+ CCIfType<[i32], CCAssignToReg<[A2, A3, A4, A5]>>,
+ CCIfType<[f32], CCAssignToReg<[A2, A3, A4, A5]>>,
+ CCIfType<[i64], CCAssignToRegWithShadow<[A2, A4], [A3, A5]>>
+]>;
+
+//===----------------------------------------------------------------------===//
+// Callee-saved register lists.
+//===----------------------------------------------------------------------===//
+
+def CSR_Xtensa : CalleeSavedRegs<(add A0, A12, A13, A14, A15)>;
diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
index 30073727545228..5ebedefafc165e 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
@@ -12,9 +12,11 @@
#include "Xtensa.h"
#include "XtensaTargetMachine.h"
+#include "XtensaUtils.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -37,9 +39,57 @@ class XtensaDAGToDAGISel : public SelectionDAGISel {
void Select(SDNode *Node) 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,
int Scale) {
- report_fatal_error("MemReg address is not implemented yet");
+ EVT ValTy = Addr.getValueType();
+
+ // if Address is FI, get the TargetFrameIndex.
+ if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
+ Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
+ Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), ValTy);
+
+ return true;
+ }
+
+ if (TM.isPositionIndependent()) {
+ DiagnosticInfoUnsupported Diag(CurDAG->getMachineFunction().getFunction(),
+ "PIC relocations are not supported ",
+ Addr.getDebugLoc());
+ CurDAG->getContext()->diagnose(Diag);
+ }
+
+ if ((Addr.getOpcode() == ISD::TargetExternalSymbol ||
+ Addr.getOpcode() == ISD::TargetGlobalAddress))
+ return false;
+
+ // Addresses of the form FI+const
+ bool Valid = false;
+ if (CurDAG->isBaseWithConstantOffset(Addr)) {
+ ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1));
+ int64_t OffsetVal = CN->getSExtValue();
+
+ Valid = isValidAddrOffset(Scale, OffsetVal);
+
+ if (Valid) {
+ // If the first operand is a FI, get the TargetFI Node
+ if (FrameIndexSDNode *FIN =
+ dyn_cast<FrameIndexSDNode>(Addr.getOperand(0)))
+ Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
+ else
+ Base = Addr.getOperand(0);
+
+ Offset =
+ CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), ValTy);
+ return true;
+ }
+ }
+
+ // Last case
+ Base = Addr;
+ Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), Addr.getValueType());
+ return true;
}
bool selectMemRegAddrISH1(SDValue Addr, SDValue &Base, SDValue &Offset) {
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
index 276fab838d17c0..e8cbd26ffed113 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
@@ -41,10 +41,273 @@ XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM,
setMinFunctionAlignment(Align(4));
+ setBooleanContents(ZeroOrOneBooleanContent);
+
+ // No sign extend instructions for i1
+ for (MVT VT : MVT::integer_valuetypes()) {
+ setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote);
+ setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote);
+ setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote);
+ }
+
// Compute derived properties from the register classes
computeRegisterProperties(STI.getRegisterInfo());
}
+//===----------------------------------------------------------------------===//
+// Calling conventions
+//===----------------------------------------------------------------------===//
+
+#include "XtensaGenCallingConv.inc"
+
+static bool CC_Xtensa_Custom(unsigned ValNo, MVT ValVT, MVT LocVT,
+ CCValAssign::LocInfo LocInfo,
+ ISD::ArgFlagsTy ArgFlags, CCState &State) {
+ static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4,
+ Xtensa::A5, Xtensa::A6, Xtensa::A7};
+
+ if (ArgFlags.isByVal()) {
+ Align ByValAlign = ArgFlags.getNonZeroByValAlign();
+ unsigned ByValSize = ArgFlags.getByValSize();
+ if (ByValSize < 4) {
+ ByValSize = 4;
+ }
+ if (ByValAlign < Align(4)) {
+ ByValAlign = Align(4);
+ }
+ unsigned Offset = State.AllocateStack(ByValSize, ByValAlign);
+ State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
+ // Mark all unused registers as allocated to avoid misuse
+ // of such registers.
+ while (State.AllocateReg(IntRegs))
+ ;
+ return false;
+ }
+
+ // Promote i8 and i16
+ if (LocVT == MVT::i8 || LocVT == MVT::i16) {
+ LocVT = MVT::i32;
+ if (ArgFlags.isSExt())
+ LocInfo = CCValAssign::SExt;
+ else if (ArgFlags.isZExt())
+ LocInfo = CCValAssign::ZExt;
+ else
+ LocInfo = CCValAssign::AExt;
+ }
+
+ unsigned Reg;
+
+ Align OrigAlign = ArgFlags.getNonZeroOrigAlign();
+ bool needs64BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(8));
+ bool needs128BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(16));
+
+ if (ValVT == MVT::i32 || ValVT == MVT::f32) {
+ Reg = State.AllocateReg(IntRegs);
+ // If this is the first part of an i64 arg,
+ // the allocated register must be either A2, A4 or A6.
+ if (needs64BitAlign &&
+ (Reg == Xtensa::A3 || Reg == Xtensa::A5 || Reg == Xtensa::A7))
+ Reg = State.AllocateReg(IntRegs);
+ // arguments with 16byte alignment must be passed in the first register or
+ // passed via stack
+ if (needs128BitAlign && Reg != Xtensa::A2)
+ while ((Reg = State.AllocateReg(IntRegs)))
+ ;
+ LocVT = MVT::i32;
+ } else if (ValVT == MVT::f64) {
+ // Allocate int register and shadow next int register.
+ Reg = State.AllocateReg(IntRegs);
+ if (Reg == Xtensa::A3 || Reg == Xtensa::A5 || Reg == Xtensa::A7)
+ Reg = State.AllocateReg(IntRegs);
+ State.AllocateReg(IntRegs);
+ LocVT = MVT::i32;
+ } else {
+ report_fatal_error("Cannot handle this ValVT.");
+ }
+
+ if (!Reg) {
+ unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign);
+ State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo));
+ } else {
+ State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+ }
+
+ return false;
+}
+
+CCAssignFn *XtensaTargetLowering::CCAssignFnForCall(CallingConv::ID CC,
+ bool IsVarArg) const {
+ return CC_Xtensa_Custom;
+}
+
+// Value is a value of type VA.getValVT() that we need to copy into
+// the location described by VA. Return a copy of Value converted to
+// VA.getValVT(). The caller is responsible for handling indirect values.
+static SDValue convertValVTToLocVT(SelectionDAG &DAG, SDLoc DL, CCValAssign &VA,
+ SDValue Value) {
+ switch (VA.getLocInfo()) {
+ case CCValAssign::SExt:
+ return DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Value);
+ case CCValAssign::ZExt:
+ return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value);
+ case CCValAssign::AExt:
+ return DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Value);
+ case CCValAssign::BCvt:
+ return DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Value);
+ case CCValAssign::Full:
+ return Value;
+ default:
+ report_fatal_error("Unhandled getLocInfo()");
+ }
+}
+
+SDValue XtensaTargetLowering::LowerFormalArguments(
+ SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
+ SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
+ MachineFunction &MF = DAG.getMachineFunction();
+ MachineFrameInfo &MFI = MF.getFrameInfo();
+
+ // Used with vargs to acumulate store chains.
+ std::vector<SDValue> OutChains;
+
+ if (IsVarArg)
+ report_fatal_error("Var arg not supported by FormalArguments Lowering");
+
+ // Assign locations to all of the incoming arguments.
+ SmallVector<CCValAssign, 16> ArgLocs;
+ CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs,
+ *DAG.getContext());
+
+ CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForCall(CallConv, IsVarArg));
+
+ for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
+ CCValAssign &VA = ArgLocs[i];
+ // Arguments stored on registers
+ if (VA.isRegLoc()) {
+ EVT RegVT = VA.getLocVT();
+ const TargetRegisterClass *RC;
+
+ if (RegVT == MVT::i32)
+ RC = &Xtensa::ARRegClass;
+ else
+ report_fatal_error("RegVT not supported by FormalArguments Lowering");
+
+ // Transform the arguments stored on
+ // physical registers into virtual ones
+ unsigned Register = MF.addLiveIn(VA.getLocReg(), RC);
+ SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Register, RegVT);
+
+ // If this is an 8 or 16-bit value, it has been passed promoted
+ // to 32 bits. Insert an assert[sz]ext to capture this, then
+ // truncate to the right size.
+ if (VA.getLocInfo() != CCValAssign::Full) {
+ unsigned Opcode = 0;
+ if (VA.getLocInfo() == CCValAssign::SExt)
+ Opcode = ISD::AssertSext;
+ else if (VA.getLocInfo() == CCValAssign::ZExt)
+ Opcode = ISD::AssertZext;
+ if (Opcode)
+ ArgValue = DAG.getNode(Opcode, DL, RegVT, ArgValue,
+ DAG.getValueType(VA.getValVT()));
+ ArgValue = DAG.getNode((VA.getValVT() == MVT::f32) ? ISD::BITCAST
+ : ISD::TRUNCATE,
+ DL, VA.getValVT(), ArgValue);
+ }
+
+ InVals.push_back(ArgValue);
+
+ } else {
+ assert(VA.isMemLoc());
+
+ EVT ValVT = VA.getValVT();
+
+ // The stack pointer offset is relative to the caller stack frame.
+ int FI = MFI.CreateFixedObject(ValVT.getStoreSize(), VA.getLocMemOffset(),
+ true);
+
+ if (Ins[VA.getValNo()].Flags.isByVal()) {
+ // Assume that in this case load operation is created
+ SDValue FIN = DAG.getFrameIndex(FI, MVT::i32);
+ InVals.push_back(FIN);
+ } else {
+ // Create load nodes to retrieve arguments from the stack
+ SDValue FIN =
+ DAG.getFrameIndex(FI, getFrameIndexTy(DAG.getDataLayout()));
+ InVals.push_back(DAG.getLoad(
+ ValVT, DL, Chain, FIN,
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)));
+ }
+ }
+ }
+
+ // All stores are grouped in one node to allow the matching between
+ // the size of Ins and InVals. This only happens when on varg functions
+ if (!OutChains.empty()) {
+ OutChains.push_back(Chain);
+ Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains);
+ }
+
+ return Chain;
+}
+
+bool XtensaTargetLowering::CanLowerReturn(
+ CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const {
+ SmallVector<CCValAssign, 16> RVLocs;
+ CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context);
+ return CCInfo.CheckReturn(Outs, RetCC_Xtensa);
+}
+
+SDValue
+XtensaTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
+ bool IsVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs,
+ const SmallVectorImpl<SDValue> &OutVals,
+ const SDLoc &DL, SelectionDAG &DAG) const {
+ if (IsVarArg)
+ report_fatal_error("VarArg not supported");
+
+ MachineFunction &MF = DAG.getMachineFunction();
+
+ // Assign locations to each returned value.
+ SmallVector<CCValAssign, 16> RetLocs;
+ CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext());
+ RetCCInfo.AnalyzeReturn(Outs, RetCC_Xtensa);
+
+ SDValue Glue;
+ // Quick exit for void returns
+ if (RetLocs.empty())
+ return DAG.getNode(XtensaISD::RET, DL, MVT::Other, Chain);
+
+ // Copy the result values into the output registers.
+ SmallVector<SDValue, 4> RetOps;
+ RetOps.push_back(Chain);
+ for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) {
+ CCValAssign &VA = RetLocs[I];
+ SDValue RetValue = OutVals[I];
+
+ // Make the return register live on exit.
+ assert(VA.isRegLoc() && "Can only return in registers!");
+
+ // Promote the value as required.
+ RetValue = convertValVTToLocVT(DAG, DL, VA, RetValue);
+
+ // Chain and glue the copies together.
+ unsigned Reg = VA.getLocReg();
+ Chain = DAG.getCopyToReg(Chain, DL, Reg, RetValue, Glue);
+ Glue = Chain.getValue(1);
+ RetOps.push_back(DAG.getRegister(Reg, VA.getLocVT()));
+ }
+
+ // Update chain and glue.
+ RetOps[0] = Chain;
+ if (Glue.getNode())
+ RetOps.push_back(Glue);
+
+ return DAG.getNode(XtensaISD::RET, DL, MVT::Other, RetOps);
+}
+
SDValue XtensaTargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
switch (Op.getOpcode()) {
@@ -54,5 +317,9 @@ SDValue XtensaTargetLowering::LowerOperation(SDValue Op,
}
const char *XtensaTargetLowering::getTargetNodeName(unsigned Opcode) const {
+ switch (Opcode) {
+ case XtensaISD::RET:
+ return "XtensaISD::RET";
+ }
return nullptr;
}
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.h b/llvm/lib/Target/Xtensa/XtensaISelLowering.h
index 8b03712efc9bb5..599d0b9441980e 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelLowering.h
+++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.h
@@ -19,6 +19,11 @@
#include "llvm/CodeGen/TargetLowering.h"
namespace llvm {
+
+namespace XtensaISD {
+enum { FIRST_NUMBER = ISD::BUILTIN_OP_END, RET };
+}
+
class XtensaSubtarget;
class XtensaTargetLowering : public TargetLowering {
@@ -30,10 +35,28 @@ class XtensaTargetLowering : public TargetLowering {
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
+ SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
+ bool isVarArg,
+ const SmallVectorImpl<ISD::InputArg> &Ins,
+ const SDLoc &DL, SelectionDAG &DAG,
+ SmallVectorImpl<SDValue> &InVals) const override;
+
+ bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
+ bool isVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs,
+ LLVMContext &Context) const override;
+
+ SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs,
+ const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
+ SelectionDAG &DAG) const override;
+
const XtensaSubtarget &getSubtarget() const { return Subtarget; }
private:
const XtensaSubtarget &Subtarget;
+
+ CCAssignFn *CCAssignFnForCall(CallingConv::ID CC, bool IsVarArg) const;
};
} // end namespace llvm
diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp b/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp
index 41b794d64fdb1f..a826c8511a7354 100644
--- a/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp
@@ -15,6 +15,7 @@
#include "XtensaInstrInfo.h"
#include "XtensaTargetMachine.h"
#include "llvm/CodeGen/MachineConstantPool.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
@@ -23,5 +24,73 @@
using namespace llvm;
+static inline const MachineInstrBuilder &
+addFrameReference(const MachineInstrBuilder &MIB, int FI) {
+ MachineInstr *MI = MIB;
+ MachineFunction &MF = *MI->getParent()->getParent();
+ MachineFrameInfo &MFFrame = MF.getFrameInfo();
+ const MCInstrDesc &MCID = MI->getDesc();
+ MachineMemOperand::Flags Flags = MachineMemOperand::MONone;
+ if (MCID.mayLoad())
+ Flags |= MachineMemOperand::MOLoad;
+ if (MCID.mayStore())
+ Flags |= MachineMemOperand::MOStore;
+ int64_t Offset = 0;
+ Align Alignment = MFFrame.getObjectAlign(FI);
+
+ MachineMemOperand *MMO =
+ MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI, Offset),
+ Flags, MFFrame.getObjectSize(FI), Alignment);
+ return MIB.addFrameIndex(FI).addImm(Offset).addMemOperand(MMO);
+}
+
XtensaInstrInfo::XtensaInstrInfo(const XtensaSubtarget &STI)
: XtensaGenInstrInfo(), RI(STI), STI(STI) {}
+
+void XtensaInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, MCRegister DestReg,
+ MCRegister SrcReg, bool KillSrc) const {
+ // when we are copying a phys reg we want the bits for fp
+ if (Xtensa::ARRegClass.contains(DestReg, SrcReg))
+ BuildMI(MBB, MBBI, DL, get(Xtensa::OR), DestReg)
+ .addReg(SrcReg, getKillRegState(KillSrc))
+ .addReg(SrcReg, getKillRegState(KillSrc));
+ else
+ report_fatal_error("Impossible reg-to-reg copy");
+}
+
+void XtensaInstrInfo::storeRegToStackSlot(
+ MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg,
+ bool isKill, int FrameIdx, const TargetRegisterClass *RC,
+ const TargetRegisterInfo *TRI, Register VReg) const {
+ DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
+ unsigned LoadOpcode, StoreOpcode;
+ getLoadStoreOpcodes(RC, LoadOpcode, StoreOpcode, FrameIdx);
+ addFrameReference(BuildMI(MBB, MBBI, DL, get(StoreOpcode))
+ .addReg(SrcReg, getKillRegState(isKill)),
+ FrameIdx);
+}
+
+void XtensaInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ Register DestReg, int FrameIdx,
+ const TargetRegisterClass *RC,
+ const TargetRegisterInfo *TRI,
+ Register VReg) const {
+ DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
+ unsigned LoadOpcode, StoreOpcode;
+ getLoadStoreOpcodes(RC, LoadOpcode, StoreOpcode, FrameIdx);
+ addFrameReference(BuildMI(MBB, MBBI, DL, get(LoadOpcode), DestReg), FrameIdx);
+}
+
+void XtensaInstrInfo::getLoadStoreOpcodes(const TargetRegisterClass *RC,
+ unsigned &LoadOpcode,
+ unsigned &StoreOpcode,
+ int64_t offset) const {
+ if (RC == &Xtensa::ARRegClass) {
+ LoadOpcode = Xtensa::L32I;
+ StoreOpcode = Xtensa::S32I;
+ } else
+ report_fatal_error("Unsupported regclass to load or store");
+}
diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.h b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h
index 8c73c9bd794081..efd62ff7fa16c8 100644
--- a/llvm/lib/Target/Xtensa/XtensaInstrInfo.h
+++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h
@@ -38,6 +38,27 @@ class XtensaInstrInfo : public XtensaGenInstrInfo {
// Return the XtensaRegisterInfo, which this class owns.
const XtensaRegisterInfo &getRegisterInfo() const { return RI; }
+ void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg,
+ bool KillSrc) const override;
+
+ void storeRegToStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI, Register SrcReg,
+ bool isKill, int FrameIndex,
+ const TargetRegisterClass *RC,
+ const TargetRegisterInfo *TRI,
+ Register VReg) const override;
+
+ void loadRegFromStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI, Register DestReg,
+ int FrameIdx, const TargetRegisterClass *RC,
+ const TargetRegisterInfo *TRI,
+ Register VReg) const override;
+
+ // Get the load and store opcodes for a given register class and offset.
+ void getLoadStoreOpcodes(const TargetRegisterClass *RC, unsigned &LoadOpcode,
+ unsigned &StoreOpcode, int64_t offset) const;
+
const XtensaSubtarget &getSubtarget() const { return STI; }
};
} // end namespace llvm
diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.td b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
index 268a9943d8c160..e9887601bd72a7 100644
--- a/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
+++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
@@ -14,6 +14,7 @@
include "XtensaInstrFormats.td"
include "XtensaOperands.td"
+include "XtensaOperators.td"
//===----------------------------------------------------------------------===//
// Arithmetic & Logical instructions
@@ -238,6 +239,11 @@ def L32R : RI16_Inst<0x01, (outs AR:$t), (ins L32Rtarget:$label),
let imm16 = label;
}
+//extending loads
+def : Pat<(i32 (extloadi1 addr_ish1:$addr)), (L8UI addr_ish1:$addr)>;
+def : Pat<(i32 (extloadi8 addr_ish1:$addr)), (L8UI addr_ish1:$addr)>;
+def : Pat<(i32 (extloadi16 addr_ish2:$addr)), (L16UI addr_ish2:$addr)>;
+
//===----------------------------------------------------------------------===//
// Conditional branch instructions
//===----------------------------------------------------------------------===//
@@ -426,7 +432,7 @@ let isReturn = 1, isTerminator = 1,
isBarrier = 1, Uses = [A0] in {
def RET : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins),
- "ret", []> {
+ "ret", [(Xtensa_retflag)]> {
let m = 0x2;
let n = 0x0;
let s = 0;
diff --git a/llvm/lib/Target/Xtensa/XtensaOperators.td b/llvm/lib/Target/Xtensa/XtensaOperators.td
new file mode 100644
index 00000000000000..894337f622c46d
--- /dev/null
+++ b/llvm/lib/Target/Xtensa/XtensaOperators.td
@@ -0,0 +1,13 @@
+//===- XtensaOperators.td - Xtensa-specific operators ---------*- tblgen-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Node definitions
+//===----------------------------------------------------------------------===//
+def Xtensa_retflag: SDNode<"XtensaISD::RET", SDTNone,
+ [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp
index f749cc51f96a04..09692d25115f57 100644
--- a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp
@@ -13,6 +13,9 @@
#include "XtensaRegisterInfo.h"
#include "XtensaInstrInfo.h"
#include "XtensaSubtarget.h"
+#include "XtensaUtils.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Debug.h"
@@ -31,15 +34,13 @@ XtensaRegisterInfo::XtensaRegisterInfo(const XtensaSubtarget &STI)
const uint16_t *
XtensaRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
- // Calling convention is not implemented yet
- return nullptr;
+ return CSR_Xtensa_SaveList;
}
const uint32_t *
XtensaRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const {
- // Calling convention is not implemented yet
- return nullptr;
+ return CSR_Xtensa_RegMask;
}
BitVector XtensaRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
@@ -57,10 +58,47 @@ BitVector XtensaRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
return Reserved;
}
+bool XtensaRegisterInfo::eliminateFI(MachineBasicBlock::iterator II,
+ unsigned OpNo, int FrameIndex,
+ uint64_t StackSize,
+ int64_t SPOffset) const {
+ MachineInstr &MI = *II;
+ unsigned FrameReg = Xtensa::SP;
+
+ // Calculate final offset.
+ // - There is no need to change the offset if the frame object is one of the
+ // following: an outgoing argument, pointer to a dynamically allocated
+ // stack space or a $gp restore location,
+ // - If the frame object is any of the following, its offset must be adjusted
+ // by adding the size of the stack:
+ // incoming argument, callee-saved register location or local variable.
+ bool IsKill = false;
+ int64_t Offset =
+ SPOffset + (int64_t)StackSize + MI.getOperand(OpNo + 1).getImm();
+
+ bool Valid = isValidAddrOffset(MI, Offset);
+
+ // If MI is not a debug value, make sure Offset fits in the 16-bit immediate
+ // field.
+ if (!MI.isDebugValue() && !Valid)
+ report_fatal_error("Load immediate not supported yet");
+
+ MI.getOperand(OpNo).ChangeToRegister(FrameReg, false, false, IsKill);
+ MI.getOperand(OpNo + 1).ChangeToImmediate(Offset);
+
+ return false;
+}
+
bool XtensaRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
int SPAdj, unsigned FIOperandNum,
RegScavenger *RS) const {
- report_fatal_error("Eliminate frame index not supported yet");
+ MachineInstr &MI = *II;
+ MachineFunction &MF = *MI.getParent()->getParent();
+ int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
+ uint64_t stackSize = MF.getFrameInfo().getStackSize();
+ int64_t spOffset = MF.getFrameInfo().getObjectOffset(FrameIndex);
+
+ return eliminateFI(MI, FIOperandNum, FrameIndex, stackSize, spOffset);
}
Register XtensaRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h
index a4eda87f4c8b74..9bed39335dde84 100644
--- a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h
+++ b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h
@@ -49,6 +49,10 @@ struct XtensaRegisterInfo : public XtensaGenRegisterInfo {
RegScavenger *RS = nullptr) const override;
Register getFrameRegister(const MachineFunction &MF) const override;
+
+private:
+ bool eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo,
+ int FrameIndex, uint64_t StackSize, int64_t SPOffset) const;
};
} // end namespace llvm
diff --git a/llvm/lib/Target/Xtensa/XtensaUtils.cpp b/llvm/lib/Target/Xtensa/XtensaUtils.cpp
new file mode 100644
index 00000000000000..81d6b32201e16d
--- /dev/null
+++ b/llvm/lib/Target/Xtensa/XtensaUtils.cpp
@@ -0,0 +1,60 @@
+//===--- XtensaUtils.cpp ---- Xtensa Utility Functions ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains miscellaneous utility functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "XtensaUtils.h"
+
+namespace llvm {
+
+// Check address offset for load/store instructions
+// The offset should be multiple of scale
+bool isValidAddrOffset(int Scale, int64_t OffsetVal) {
+ bool Valid = false;
+
+ switch (Scale) {
+ case 1:
+ Valid = (OffsetVal >= 0 && OffsetVal <= 255);
+ break;
+ case 2:
+ Valid = (OffsetVal >= 0 && OffsetVal <= 510) && ((OffsetVal & 0x1) == 0);
+ break;
+ case 4:
+ Valid = (OffsetVal >= 0 && OffsetVal <= 1020) && ((OffsetVal & 0x3) == 0);
+ break;
+ default:
+ break;
+ }
+ return Valid;
+}
+
+// Check address offset for load/store instructions
+bool isValidAddrOffset(MachineInstr &MI, int64_t Offset) {
+ int Scale = 0;
+
+ switch (MI.getOpcode()) {
+ case Xtensa::L8UI:
+ case Xtensa::S8I:
+ Scale = 1;
+ break;
+ case Xtensa::L16SI:
+ case Xtensa::L16UI:
+ case Xtensa::S16I:
+ Scale = 2;
+ break;
+ default:
+ // assume that MI is 32-bit load/store operation
+ Scale = 4;
+ break;
+ }
+ return isValidAddrOffset(Scale, Offset);
+}
+
+} // namespace llvm
\ No newline at end of file
diff --git a/llvm/lib/Target/Xtensa/XtensaUtils.h b/llvm/lib/Target/Xtensa/XtensaUtils.h
new file mode 100644
index 00000000000000..2dd733a5e3d294
--- /dev/null
+++ b/llvm/lib/Target/Xtensa/XtensaUtils.h
@@ -0,0 +1,27 @@
+//===--- XtensaUtils.h ---- Xtensa Utility Functions ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains miscellaneous utility functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAUTILS_H
+#define LLVM_LIB_TARGET_XTENSA_XTENSAUTILS_H
+
+#include "XtensaInstrInfo.h"
+#include "llvm/CodeGen/MachineInstr.h"
+
+namespace llvm {
+// Check address offset for load/store instructions
+// The offset should be multiple of scale
+bool isValidAddrOffset(int Scale, int64_t OffsetVal);
+
+// Check address offset for load/store instructions
+bool isValidAddrOffset(MachineInstr &MI, int64_t Offset);
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_XTENSA_XTENSAUTILS_H
\ No newline at end of file
diff --git a/llvm/test/CodeGen/Xtensa/calling-conv.ll b/llvm/test/CodeGen/Xtensa/calling-conv.ll
new file mode 100644
index 00000000000000..41ae4220145c27
--- /dev/null
+++ b/llvm/test/CodeGen/Xtensa/calling-conv.ll
@@ -0,0 +1,78 @@
+; RUN: llc -mtriple=xtensa -O1 -verify-machineinstrs < %s \
+; RUN: | FileCheck %s -check-prefix=XTENSA
+
+; Check placement of first 6 arguments in registers and 7th argument on stack
+define dso_local i32 @test1(i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3, i32 noundef %4, i32 noundef %5, ptr nocapture noundef readonly byval(i32) align 4 %6) {
+; XTENSA-LABEL: @test1
+; XTENSA: add a8, a7, a2
+; XTENSA: l32i a9, a1, 0
+; XTENSA: add a2, a8, a9
+; XTENSA: ret
+ %8 = load i32, ptr %6, align 4
+ %9 = add nsw i32 %5, %0
+ %10 = add nsw i32 %9, %8
+ ret i32 %10
+}
+
+; Check placement of second i64 argument in registers
+define dso_local i32 @test2(i32 noundef %0, i64 noundef %1, i32 noundef %2) {
+; XTENSA-LABEL: @test2
+; XTENSA: add a8, a6, a2
+; XTENSA: add a2, a8, a4
+; XTENSA: ret
+ %4 = trunc i64 %1 to i32
+ %5 = add nsw i32 %2, %0
+ %6 = add nsw i32 %5, %4
+ ret i32 %6
+}
+
+; Check placement of first argument typeof i8 in register
+define dso_local i32 @test3(i8 noundef signext %0, i64 noundef %1, i32 noundef %2) {
+; XTENSA-LABEL: @test3
+; XTENSA: add a8, a2, a6
+; XTENSA: add a2, a8, a4
+; XTENSA: ret
+ %4 = trunc i64 %1 to i32
+ %5 = sext i8 %0 to i32
+ %6 = add nsw i32 %5, %2
+ %7 = add nsw i32 %6, %4
+ ret i32 %7
+}
+
+; Check placement of 4th argument typeof i64 on stack
+define dso_local i32 @test4(i8 noundef signext %0, i64 noundef %1, i32 noundef %2, ptr nocapture noundef readonly byval(i64) align 8 %3) {
+; XTENSA-LABEL: @test4
+; XTENSA: add a8, a2, a6
+; XTENSA: add a8, a8, a4
+; XTENSA: l32i a9, a1, 0
+; XTENSA: add a2, a8, a9
+; XTENSA: ret
+ %5 = load i64, ptr %3, align 8
+ %6 = trunc i64 %1 to i32
+ %7 = trunc i64 %5 to i32
+ %8 = sext i8 %0 to i32
+ %9 = add nsw i32 %8, %2
+ %10 = add nsw i32 %9, %6
+ %11 = add nsw i32 %10, %7
+ ret i32 %11
+}
+
+; Check placement of 128 bit structure on registers
+define dso_local i32 @test5([4 x i32] %0, i32 noundef %1) {
+; XTENSA-LABEL: @test5
+; XTENSA: add a2, a2, a6
+; XTENSA: ret
+ %3 = extractvalue [4 x i32] %0, 0
+ %4 = add nsw i32 %3, %1
+ ret i32 %4
+}
+
+; Check placement of 128 bit structure on stack
+define dso_local i32 @test6(i32 noundef %0, [4 x i32] %1) {
+; XTENSA-LABEL: @test6
+; XTENSA: add a2, a3, a2
+; XTENSA: ret
+ %3 = extractvalue [4 x i32] %1, 0
+ %4 = add nsw i32 %3, %0
+ ret i32 %4
+}
diff --git a/llvm/test/CodeGen/Xtensa/stack-access.ll b/llvm/test/CodeGen/Xtensa/stack-access.ll
new file mode 100644
index 00000000000000..1590d24f228f2e
--- /dev/null
+++ b/llvm/test/CodeGen/Xtensa/stack-access.ll
@@ -0,0 +1,35 @@
+; RUN: llc -mtriple=xtensa -O0 -verify-machineinstrs < %s \
+; RUN: | FileCheck %s -check-prefix=XTENSA
+
+define i8 @loadi8(i8 %a) {
+; XTENSA-LABEL: loadi8:
+; XTENSA: s8i a2, a1, 3
+; XTENSA: l8ui a2, a1, 3
+; XTENSA: ret
+ %b = alloca i8, align 1
+ store i8 %a, ptr %b, align 1
+ %1 = load i8, ptr %b, align 1
+ ret i8 %1
+}
+
+define i16 @loadi16(i16 %a) {
+; XTENSA-LABEL: loadi16:
+; XTENSA: s16i a2, a1, 2
+; XTENSA: l16ui a2, a1, 2
+; XTENSA: ret
+ %b = alloca i16, align 2
+ store i16 %a, ptr %b, align 2
+ %1 = load i16, ptr %b, align 2
+ ret i16 %1
+}
+
+define i32 @loadi32(i32 %a) {
+; XTENSA-LABEL: loadi32:
+; XTENSA: s32i a2, a1, 0
+; XTENSA: l32i a2, a1, 0
+; XTENSA: ret
+ %b = alloca i32, align 4
+ store i32 %a, ptr %b, align 4
+ %1 = load i32, ptr %b, align 4
+ ret i32 %1
+}
More information about the llvm-commits
mailing list