diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp index 3f808298527f8..f4dec5fa12d53 100644 --- a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp +++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp @@ -8,7 +8,9 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/XtensaMCExpr.h" #include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "MCTargetDesc/XtensaTargetStreamer.h" #include "TargetInfo/XtensaTargetInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -22,6 +24,7 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" @@ -35,6 +38,12 @@ class XtensaAsmParser : public MCTargetAsmParser { SMLoc getLoc() const { return getParser().getTok().getLoc(); } + XtensaTargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } + + ParseStatus parseDirective(AsmToken DirectiveID) override; bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override; bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; @@ -45,6 +54,9 @@ class XtensaAsmParser : public MCTargetAsmParser { unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; + bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + // Auto-generated instruction matching functions #define GET_ASSEMBLER_HEADER #include "XtensaGenAsmMatcher.inc" @@ -62,6 +74,7 @@ class XtensaAsmParser : public MCTargetAsmParser { return ParseStatus::NoMatch; } ParseStatus parsePCRelTarget(OperandVector &Operands); + bool parseLiteralDirective(SMLoc L); public: enum XtensaMatchResultTy { @@ -148,7 +161,8 @@ struct XtensaOperand : public MCParsedAsmOperand { bool isImm12() const { return isImm(-2048, 2047); } - bool isImm12m() const { return isImm(-2048, 2047); } + // Convert MOVI to literal load, when immediate is not in range (-2048, 2047) + bool isImm12m() const { return Kind == Immediate; } bool isOffset4m32() const { return isImm(0, 60) && @@ -348,6 +362,69 @@ static SMLoc RefineErrorLoc(const SMLoc Loc, const OperandVector &Operands, return Loc; } +bool XtensaAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + Inst.setLoc(IDLoc); + const unsigned Opcode = Inst.getOpcode(); + switch (Opcode) { + case Xtensa::L32R: { + const MCSymbolRefExpr *OpExpr = + static_cast(Inst.getOperand(1).getExpr()); + XtensaMCExpr::VariantKind Kind = XtensaMCExpr::VK_Xtensa_None; + const MCExpr *NewOpExpr = XtensaMCExpr::create(OpExpr, Kind, getContext()); + Inst.getOperand(1).setExpr(NewOpExpr); + break; + } + case Xtensa::MOVI: { + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + // Expand MOVI operand + if (!Inst.getOperand(1).isExpr()) { + uint64_t ImmOp64 = Inst.getOperand(1).getImm(); + int32_t Imm = ImmOp64; + if (!isInt<12>(Imm)) { + XtensaTargetStreamer &TS = this->getTargetStreamer(); + MCInst TmpInst; + TmpInst.setLoc(IDLoc); + TmpInst.setOpcode(Xtensa::L32R); + const MCExpr *Value = MCConstantExpr::create(ImmOp64, getContext()); + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *Expr = MCSymbolRefExpr::create( + Sym, MCSymbolRefExpr::VK_None, getContext()); + const MCExpr *OpExpr = XtensaMCExpr::create( + Expr, XtensaMCExpr::VK_Xtensa_None, getContext()); + TmpInst.addOperand(Inst.getOperand(0)); + MCOperand Op1 = MCOperand::createExpr(OpExpr); + TmpInst.addOperand(Op1); + TS.emitLiteral(Sym, Value, true, IDLoc); + Inst = TmpInst; + } + } else { + MCInst TmpInst; + TmpInst.setLoc(IDLoc); + TmpInst.setOpcode(Xtensa::L32R); + const MCExpr *Value = Inst.getOperand(1).getExpr(); + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *Expr = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MCExpr *OpExpr = XtensaMCExpr::create( + Expr, XtensaMCExpr::VK_Xtensa_None, getContext()); + TmpInst.addOperand(Inst.getOperand(0)); + MCOperand Op1 = MCOperand::createExpr(OpExpr); + TmpInst.addOperand(Op1); + Inst = TmpInst; + TS.emitLiteral(Sym, Value, true, IDLoc); + } + break; + } + default: + break; + } + + return true; +} + bool XtensaAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, @@ -361,6 +438,7 @@ bool XtensaAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, default: break; case Match_Success: + processInstruction(Inst, IDLoc, Out, STI); Inst.setLoc(IDLoc); Out.emitInstruction(Inst, getSTI()); return false; @@ -686,6 +764,57 @@ bool XtensaAsmParser::ParseInstruction(ParseInstructionInfo &Info, return false; } +bool XtensaAsmParser::parseLiteralDirective(SMLoc L) { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + SMLoc LiteralLoc = getLexer().getLoc(); + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + if (Parser.parseExpression(Value)) + return true; + + const MCSymbolRefExpr *SE = dyn_cast(Value); + + if (!SE) + return Error(LiteralLoc, "literal label must be a symbol"); + + if (Parser.parseComma()) + return true; + + SMLoc OpcodeLoc = getLexer().getLoc(); + if (parseOptionalToken(AsmToken::EndOfStatement)) + return Error(OpcodeLoc, "expected value"); + + if (Parser.parseExpression(Value)) + return true; + + if (parseEOL()) + return true; + + MCSymbol *Sym = getContext().getOrCreateSymbol(SE->getSymbol().getName()); + + TS.emitLiteral(Sym, Value, true, LiteralLoc); + + return false; +} + +ParseStatus XtensaAsmParser::parseDirective(AsmToken DirectiveID) { + StringRef IDVal = DirectiveID.getString(); + SMLoc Loc = getLexer().getLoc(); + + if (IDVal == ".literal_position") { + XtensaTargetStreamer &TS = this->getTargetStreamer(); + TS.emitLiteralPosition(); + return parseEOL(); + } + + if (IDVal == ".literal") { + return parseLiteralDirective(Loc); + } + + return ParseStatus::NoMatch; +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmParser() { RegisterMCAsmParser X(getTheXtensaTarget()); diff --git a/llvm/lib/Target/Xtensa/CMakeLists.txt b/llvm/lib/Target/Xtensa/CMakeLists.txt index 2064511e75b82..726efadc87c0b 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) @@ -15,6 +16,7 @@ add_public_tablegen_target(XtensaCommonTableGen) add_llvm_target(XtensaCodeGen XtensaAsmPrinter.cpp + XtensaConstantPoolValue.cpp XtensaFrameLowering.cpp XtensaInstrInfo.cpp XtensaISelDAGToDAG.cpp @@ -22,6 +24,7 @@ add_llvm_target(XtensaCodeGen XtensaRegisterInfo.cpp XtensaSubtarget.cpp XtensaTargetMachine.cpp + XtensaUtils.cpp LINK_COMPONENTS AsmPrinter diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt index 6841b44f9d569..dc12863394c7a 100644 --- a/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMXtensaDesc XtensaMCCodeEmitter.cpp XtensaMCExpr.cpp XtensaMCTargetDesc.cpp + XtensaTargetStreamer.cpp LINK_COMPONENTS MC diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp index 48674d15bdfbe..87ef66ba742b6 100644 --- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp @@ -8,9 +8,10 @@ // //===----------------------------------------------------------------------===// #include "XtensaMCTargetDesc.h" +#include "TargetInfo/XtensaTargetInfo.h" #include "XtensaInstPrinter.h" #include "XtensaMCAsmInfo.h" -#include "TargetInfo/XtensaTargetInfo.h" +#include "XtensaTargetStreamer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInstrInfo.h" @@ -63,16 +64,29 @@ createXtensaMCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { return createXtensaMCSubtargetInfoImpl(TT, CPU, CPU, FS); } +static MCTargetStreamer * +createXtensaAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, bool isVerboseAsm) { + return new XtensaTargetAsmStreamer(S, OS); +} + +static MCTargetStreamer * +createXtensaObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new XtensaTargetELFStreamer(S); +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaTargetMC() { // Register the MCAsmInfo. - TargetRegistry::RegisterMCAsmInfo(getTheXtensaTarget(), createXtensaMCAsmInfo); + TargetRegistry::RegisterMCAsmInfo(getTheXtensaTarget(), + createXtensaMCAsmInfo); // Register the MCCodeEmitter. TargetRegistry::RegisterMCCodeEmitter(getTheXtensaTarget(), createXtensaMCCodeEmitter); // Register the MCInstrInfo. - TargetRegistry::RegisterMCInstrInfo(getTheXtensaTarget(), createXtensaMCInstrInfo); + TargetRegistry::RegisterMCInstrInfo(getTheXtensaTarget(), + createXtensaMCInstrInfo); // Register the MCInstPrinter. TargetRegistry::RegisterMCInstPrinter(getTheXtensaTarget(), @@ -89,4 +103,12 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaTargetMC() { // Register the MCAsmBackend. TargetRegistry::RegisterMCAsmBackend(getTheXtensaTarget(), createXtensaMCAsmBackend); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(getTheXtensaTarget(), + createXtensaAsmTargetStreamer); + + // Register the ELF target streamer. + TargetRegistry::RegisterObjectTargetStreamer( + getTheXtensaTarget(), createXtensaObjectTargetStreamer); } diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp new file mode 100644 index 0000000000000..0ea70cff4d404 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp @@ -0,0 +1,119 @@ +//===-- XtensaTargetStreamer.cpp - Xtensa Target Streamer Methods ---------===// +// +// 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 provides Xtensa specific target streamer methods. +// +//===----------------------------------------------------------------------===// + +#include "XtensaTargetStreamer.h" +#include "XtensaInstPrinter.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +static std::string getLiteralSectionName(StringRef CSectionName) { + std::size_t Pos = CSectionName.find(".text"); + std::string SectionName; + if (Pos != std::string::npos) { + SectionName = CSectionName.substr(0, Pos); + + if (Pos > 0) + SectionName += ".text"; + + CSectionName = CSectionName.drop_front(Pos); + CSectionName.consume_front(".text"); + + SectionName += ".literal"; + SectionName += CSectionName; + } else { + SectionName = CSectionName; + SectionName += ".literal"; + } + return SectionName; +} + +XtensaTargetStreamer::XtensaTargetStreamer(MCStreamer &S) + : MCTargetStreamer(S) {} + +XtensaTargetAsmStreamer::XtensaTargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : XtensaTargetStreamer(S), OS(OS) {} + +void XtensaTargetAsmStreamer::emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + bool SwitchLiteralSection, SMLoc L) { + SmallString<60> Str; + raw_svector_ostream LiteralStr(Str); + + LiteralStr << "\t.literal " << LblSym->getName() << ", "; + + if (auto CE = dyn_cast(Value)) { + LiteralStr << CE->getValue() << "\n"; + } else if (auto SRE = dyn_cast(Value)) { + const MCSymbol &Sym = SRE->getSymbol(); + LiteralStr << Sym.getName() << "\n"; + } else { + llvm_unreachable("unexpected constant pool entry type"); + } + + OS << LiteralStr.str(); +} + +void XtensaTargetAsmStreamer::emitLiteralPosition() { + OS << "\t.literal_position\n"; +} + +void XtensaTargetAsmStreamer::startLiteralSection(MCSection *BaseSection) { + emitLiteralPosition(); +} + +XtensaTargetELFStreamer::XtensaTargetELFStreamer(MCStreamer &S) + : XtensaTargetStreamer(S) {} + +void XtensaTargetELFStreamer::emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + bool SwitchLiteralSection, SMLoc L) { + MCStreamer &OutStreamer = getStreamer(); + if (SwitchLiteralSection) { + MCContext &Context = OutStreamer.getContext(); + auto *CS = static_cast(OutStreamer.getCurrentSectionOnly()); + std::string SectionName = getLiteralSectionName(CS->getName()); + + MCSection *ConstSection = Context.getELFSection( + SectionName, ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + + OutStreamer.pushSection(); + OutStreamer.switchSection(ConstSection); + } + + OutStreamer.emitLabel(LblSym, L); + OutStreamer.emitValue(Value, 4, L); + + if (SwitchLiteralSection) { + OutStreamer.popSection(); + } +} + +void XtensaTargetELFStreamer::startLiteralSection(MCSection *BaseSection) { + MCContext &Context = getStreamer().getContext(); + + std::string SectionName = getLiteralSectionName(BaseSection->getName()); + + MCSection *ConstSection = Context.getELFSection( + SectionName, ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + + ConstSection->setAlignment(Align(4)); +} + +MCELFStreamer &XtensaTargetELFStreamer::getStreamer() { + return static_cast(Streamer); +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h new file mode 100644 index 0000000000000..817940e880b3c --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h @@ -0,0 +1,58 @@ +//===-- XtensaTargetStreamer.h - Xtensa Target Streamer --------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSATARGETSTREAMER_H +#define LLVM_LIB_TARGET_XTENSA_XTENSATARGETSTREAMER_H + +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/SMLoc.h" + +namespace llvm { +class formatted_raw_ostream; + +class XtensaTargetStreamer : public MCTargetStreamer { +public: + XtensaTargetStreamer(MCStreamer &S); + + // Emit literal label and literal Value to the literal section. If literal + // section is not switched yet (SwitchLiteralSection is true) then switch to + // literal section. + virtual void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + bool SwitchLiteralSection, SMLoc L = SMLoc()) = 0; + + virtual void emitLiteralPosition() = 0; + + // Switch to the literal section. The BaseSection name is used to construct + // literal section name. + virtual void startLiteralSection(MCSection *BaseSection) = 0; +}; + +class XtensaTargetAsmStreamer : public XtensaTargetStreamer { + formatted_raw_ostream &OS; + +public: + XtensaTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + bool SwitchLiteralSection, SMLoc L) override; + void emitLiteralPosition() override; + void startLiteralSection(MCSection *Section) override; +}; + +class XtensaTargetELFStreamer : public XtensaTargetStreamer { +public: + XtensaTargetELFStreamer(MCStreamer &S); + MCELFStreamer &getStreamer(); + void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + bool SwitchLiteralSection, SMLoc L) override; + void emitLiteralPosition() override {} + void startLiteralSection(MCSection *Section) override; +}; +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_XTENSA_XTENSATARGETSTREAMER_H diff --git a/llvm/lib/Target/Xtensa/Xtensa.td b/llvm/lib/Target/Xtensa/Xtensa.td index b953540be94de..460a15e808b3a 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/XtensaAsmPrinter.cpp b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp index 87dbf2eb5166c..e222919b28dc9 100644 --- a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp @@ -12,8 +12,13 @@ //===----------------------------------------------------------------------===// #include "XtensaAsmPrinter.h" +#include "MCTargetDesc/XtensaMCExpr.h" +#include "MCTargetDesc/XtensaTargetStreamer.h" #include "TargetInfo/XtensaTargetInfo.h" +#include "XtensaConstantPoolValue.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/MC/MCExpr.h" @@ -25,12 +30,152 @@ using namespace llvm; +static MCSymbolRefExpr::VariantKind +getModifierVariantKind(XtensaCP::XtensaCPModifier Modifier) { + switch (Modifier) { + case XtensaCP::no_modifier: + return MCSymbolRefExpr::VK_None; + case XtensaCP::TPOFF: + return MCSymbolRefExpr::VK_TPOFF; + } + report_fatal_error("Invalid XtensaCPModifier!"); +} + void XtensaAsmPrinter::emitInstruction(const MachineInstr *MI) { MCInst LoweredMI; lowerToMCInst(MI, LoweredMI); EmitToStreamer(*OutStreamer, LoweredMI); } +void XtensaAsmPrinter::emitMachineConstantPoolValue( + MachineConstantPoolValue *MCPV) { + XtensaConstantPoolValue *ACPV = static_cast(MCPV); + MCSymbol *MCSym; + + assert(ACPV->isExtSymbol() && "unrecognized constant pool value"); + + XtensaConstantPoolSymbol *XtensaSym = cast(ACPV); + const char *Sym = XtensaSym->getSymbol(); + std::string SymName(Sym); + + if (XtensaSym->isPrivateLinkage()) + SymName = ".L" + SymName; + + MCSym = GetExternalSymbolSymbol(StringRef(SymName)); + MCSymbol *LblSym = GetCPISymbol(ACPV->getLabelId()); + auto *TS = + static_cast(OutStreamer->getTargetStreamer()); + MCSymbolRefExpr::VariantKind VK = getModifierVariantKind(ACPV->getModifier()); + + if (ACPV->getModifier() != XtensaCP::no_modifier) { + std::string SymName(MCSym->getName()); + StringRef Modifier = ACPV->getModifierText(); + SymName += Modifier; + MCSym = GetExternalSymbolSymbol(StringRef(SymName)); + } + + const MCExpr *Expr = MCSymbolRefExpr::create(MCSym, VK, OutContext); + TS->emitLiteral(LblSym, Expr, false); +} + +void XtensaAsmPrinter::emitMachineConstantPoolEntry( + const MachineConstantPoolEntry &CPE, int i) { + if (CPE.isMachineConstantPoolEntry()) { + XtensaConstantPoolValue *ACPV = + static_cast(CPE.Val.MachineCPVal); + ACPV->setLabelId(i); + emitMachineConstantPoolValue(CPE.Val.MachineCPVal); + } else { + MCSymbol *LblSym = GetCPISymbol(i); + auto *TS = + static_cast(OutStreamer->getTargetStreamer()); + const Constant *C = CPE.Val.ConstVal; + const MCExpr *Value = nullptr; + + Type *Ty = C->getType(); + if (const auto *CFP = dyn_cast(C)) { + Value = MCConstantExpr::create( + CFP->getValueAPF().bitcastToAPInt().getSExtValue(), OutContext); + } else if (const auto *CI = dyn_cast(C)) { + Value = MCConstantExpr::create(CI->getValue().getSExtValue(), OutContext); + } else if (isa(Ty)) { + Value = lowerConstant(C); + } else { + llvm_unreachable("unexpected constant pool entry type"); + } + + TS->emitLiteral(LblSym, Value, false); + } +} + +// EmitConstantPool - Print to the current output stream assembly +// representations of the constants in the constant pool MCP. This is +// used to print out constants which have been "spilled to memory" by +// the code generator. +void XtensaAsmPrinter::emitConstantPool() { + const Function &F = MF->getFunction(); + const MachineConstantPool *MCP = MF->getConstantPool(); + const std::vector &CP = MCP->getConstants(); + if (CP.empty()) + return; + + OutStreamer->pushSection(); + + auto *TS = + static_cast(OutStreamer->getTargetStreamer()); + MCSection *CS = getObjFileLowering().SectionForGlobal(&F, TM); + TS->startLiteralSection(CS); + + int CPIdx = 0; + for (const MachineConstantPoolEntry &CPE : CP) { + emitMachineConstantPoolEntry(CPE, CPIdx++); + } + + OutStreamer->popSection(); +} + +MCSymbol * +XtensaAsmPrinter::GetConstantPoolIndexSymbol(const MachineOperand &MO) const { + // Create a symbol for the name. + return GetCPISymbol(MO.getIndex()); +} + +MCOperand +XtensaAsmPrinter::LowerSymbolOperand(const MachineOperand &MO, + MachineOperand::MachineOperandType MOTy, + unsigned Offset) const { + const MCSymbol *Symbol; + XtensaMCExpr::VariantKind Kind = XtensaMCExpr::VK_Xtensa_None; + + switch (MOTy) { + case MachineOperand::MO_GlobalAddress: + Symbol = getSymbol(MO.getGlobal()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_ConstantPoolIndex: + Symbol = GetConstantPoolIndexSymbol(MO); + Offset += MO.getOffset(); + break; + default: + report_fatal_error(""); + } + + const MCExpr *ME = + MCSymbolRefExpr::create(Symbol, MCSymbolRefExpr::VK_None, OutContext); + ME = XtensaMCExpr::create(ME, Kind, OutContext); + + if (Offset) { + // Assume offset is never negative. + assert(Offset > 0); + + const MCConstantExpr *OffsetExpr = + MCConstantExpr::create(Offset, OutContext); + ME = MCBinaryExpr::createAdd(ME, OffsetExpr, OutContext); + } + + return MCOperand::createExpr(ME); +} + MCOperand XtensaAsmPrinter::lowerOperand(const MachineOperand &MO, unsigned Offset) const { MachineOperand::MachineOperandType MOTy = MO.getType(); @@ -45,6 +190,9 @@ MCOperand XtensaAsmPrinter::lowerOperand(const MachineOperand &MO, return MCOperand::createImm(MO.getImm() + Offset); case MachineOperand::MO_RegisterMask: break; + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ConstantPoolIndex: + return LowerSymbolOperand(MO, MOTy, Offset); default: report_fatal_error("unknown operand type"); } diff --git a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h index dec2a1ee4954f..f3fec19724aab 100644 --- a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h @@ -15,6 +15,7 @@ #include "XtensaTargetMachine.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/Support/Compiler.h" namespace llvm { @@ -35,6 +36,18 @@ class LLVM_LIBRARY_VISIBILITY XtensaAsmPrinter : public AsmPrinter { StringRef getPassName() const override { return "Xtensa Assembly Printer"; } void emitInstruction(const MachineInstr *MI) override; + void emitConstantPool() override; + + void emitMachineConstantPoolEntry(const MachineConstantPoolEntry &CPE, int i); + + void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override; + + MCSymbol *GetConstantPoolIndexSymbol(const MachineOperand &MO) const; + + MCOperand LowerSymbolOperand(const MachineOperand &MO, + MachineOperand::MachineOperandType MOTy, + unsigned Offset) const; + // Lower MachineInstr MI to MCInst OutMI. void lowerToMCInst(const MachineInstr *MI, MCInst &OutMI) const; diff --git a/llvm/lib/Target/Xtensa/XtensaCallingConv.td b/llvm/lib/Target/Xtensa/XtensaCallingConv.td new file mode 100644 index 0000000000000..a348b4c890b22 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaCallingConv.td @@ -0,0 +1,24 @@ +//===- 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<[ + // First two return values go in a2, a3, a4, a5 + CCIfType<[i32], 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/XtensaConstantPoolValue.cpp b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.cpp new file mode 100644 index 0000000000000..4e53aa5736c72 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.cpp @@ -0,0 +1,207 @@ +//===- XtensaConstantPoolValue.cpp - Xtensa constantpool value ------------===// +// +// 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 implements the Xtensa specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#include "XtensaConstantPoolValue.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/raw_ostream.h" +#include +using namespace llvm; + +XtensaConstantPoolValue::XtensaConstantPoolValue( + Type *Ty, unsigned ID, XtensaCP::XtensaCPKind Kind, + XtensaCP::XtensaCPModifier modifier) + : MachineConstantPoolValue(Ty), LabelId(ID), Kind(Kind), + Modifier(modifier) {} + +XtensaConstantPoolValue::XtensaConstantPoolValue( + LLVMContext &C, unsigned ID, XtensaCP::XtensaCPKind Kind, + XtensaCP::XtensaCPModifier Modifier) + : MachineConstantPoolValue((Type *)Type::getInt32Ty(C)), LabelId(ID), + Kind(Kind), Modifier(Modifier) {} + +XtensaConstantPoolValue::~XtensaConstantPoolValue() {} + +StringRef XtensaConstantPoolValue::getModifierText() const { + switch (Modifier) { + case XtensaCP::no_modifier: + return ""; + case XtensaCP::TPOFF: + return "@TPOFF"; + } + report_fatal_error("Unknown modifier!"); +} + +int XtensaConstantPoolValue::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + report_fatal_error("Shouldn't be calling this directly!"); +} + +void XtensaConstantPoolValue::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddInteger(LabelId); +} + +bool XtensaConstantPoolValue::hasSameValue(XtensaConstantPoolValue *ACPV) { + if (ACPV->Kind == Kind) { + if (ACPV->LabelId == LabelId) + return true; + } + return false; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +void XtensaConstantPoolValue::dump() const { errs() << " " << *this; } +#endif + +void XtensaConstantPoolValue::print(raw_ostream &O) const {} + +//===----------------------------------------------------------------------===// +// XtensaConstantPoolConstant +//===----------------------------------------------------------------------===// + +XtensaConstantPoolConstant::XtensaConstantPoolConstant( + const Constant *C, unsigned ID, XtensaCP::XtensaCPKind Kind) + : XtensaConstantPoolValue(C->getType(), ID, Kind), CVal(C) {} + +XtensaConstantPoolConstant * +XtensaConstantPoolConstant::Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind) { + return new XtensaConstantPoolConstant(C, ID, Kind); +} + +const BlockAddress *XtensaConstantPoolConstant::getBlockAddress() const { + return dyn_cast_or_null(CVal); +} + +int XtensaConstantPoolConstant::getExistingMachineCPValue( + MachineConstantPool *CP, Align Alignment) { + return getExistingMachineCPValueImpl(CP, + Alignment); +} + +bool XtensaConstantPoolConstant::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolConstant *ACPC = + dyn_cast(ACPV); + return ACPC && ACPC->CVal == CVal && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolConstant::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddPointer(CVal); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolConstant::print(raw_ostream &O) const { + O << CVal->getName(); + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolSymbol::XtensaConstantPoolSymbol( + LLVMContext &C, const char *Str, unsigned ID, bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier) + : XtensaConstantPoolValue(C, ID, XtensaCP::CPExtSymbol, Modifier), S(Str), + PrivateLinkage(PrivLinkage) {} + +XtensaConstantPoolSymbol * +XtensaConstantPoolSymbol::Create(LLVMContext &C, const char *Str, unsigned ID, + bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier) + +{ + return new XtensaConstantPoolSymbol(C, Str, ID, PrivLinkage, Modifier); +} + +int XtensaConstantPoolSymbol::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + return getExistingMachineCPValueImpl(CP, Alignment); +} + +bool XtensaConstantPoolSymbol::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolSymbol *ACPS = + dyn_cast(ACPV); + return ACPS && ACPS->S == S && XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolSymbol::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddString(S); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolSymbol::print(raw_ostream &O) const { + O << S; + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolMBB::XtensaConstantPoolMBB(LLVMContext &C, + const MachineBasicBlock *M, + unsigned Id) + : XtensaConstantPoolValue(C, 0, XtensaCP::CPMachineBasicBlock), MBB(M) {} + +XtensaConstantPoolMBB *XtensaConstantPoolMBB::Create(LLVMContext &C, + const MachineBasicBlock *M, + unsigned Idx) { + return new XtensaConstantPoolMBB(C, M, Idx); +} + +int XtensaConstantPoolMBB::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + return getExistingMachineCPValueImpl(CP, Alignment); +} + +bool XtensaConstantPoolMBB::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolMBB *ACPMBB = dyn_cast(ACPV); + return ACPMBB && ACPMBB->MBB == MBB && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolMBB::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddPointer(MBB); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolMBB::print(raw_ostream &O) const { + O << "BB#" << MBB->getNumber(); + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolJumpTable::XtensaConstantPoolJumpTable(LLVMContext &C, + unsigned Index) + : XtensaConstantPoolValue(C, 0, XtensaCP::CPJumpTable), Idx(Index) {} + +XtensaConstantPoolJumpTable *XtensaConstantPoolJumpTable::Create(LLVMContext &C, + unsigned Idx) { + return new XtensaConstantPoolJumpTable(C, Idx); +} + +int XtensaConstantPoolJumpTable::getExistingMachineCPValue( + MachineConstantPool *CP, Align Alignment) { + return getExistingMachineCPValueImpl(CP, + Alignment); +} + +bool XtensaConstantPoolJumpTable::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolJumpTable *ACPJT = + dyn_cast(ACPV); + return ACPJT && ACPJT->Idx == Idx && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolJumpTable::addSelectionDAGCSEId(FoldingSetNodeID &ID) {} + +void XtensaConstantPoolJumpTable::print(raw_ostream &O) const { + O << "JT" << Idx; + XtensaConstantPoolValue::print(O); +} diff --git a/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h new file mode 100644 index 0000000000000..5580de4844746 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h @@ -0,0 +1,263 @@ +//===- XtensaConstantPoolValue.h - Xtensa constantpool value ----*- 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 implements the Xtensa specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H +#define LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H + +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include + +namespace llvm { + +class BlockAddress; +class Constant; +class GlobalValue; +class LLVMContext; +class MachineBasicBlock; + +namespace XtensaCP { +enum XtensaCPKind { + CPExtSymbol, + CPBlockAddress, + CPMachineBasicBlock, + CPJumpTable +}; + +enum XtensaCPModifier { + no_modifier, // None + TPOFF // Thread Pointer Offset +}; +} // namespace XtensaCP + +/// XtensaConstantPoolValue - Xtensa specific constantpool value. This is used +/// to represent PC-relative displacement between the address of the load +/// instruction and the constant being loaded. +class XtensaConstantPoolValue : public MachineConstantPoolValue { + unsigned LabelId; // Label id of the load. + XtensaCP::XtensaCPKind Kind; // Kind of constant. + XtensaCP::XtensaCPModifier Modifier; // Symbol name modifier + //(for example Global Variable name) + +protected: + XtensaConstantPoolValue( + Type *Ty, unsigned ID, XtensaCP::XtensaCPKind Kind, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + XtensaConstantPoolValue( + LLVMContext &C, unsigned id, XtensaCP::XtensaCPKind Kind, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + template + int getExistingMachineCPValueImpl(MachineConstantPool *CP, Align Alignment) { + const std::vector &Constants = CP->getConstants(); + for (unsigned i = 0, e = Constants.size(); i != e; ++i) { + if (Constants[i].isMachineConstantPoolEntry() && + (Constants[i].getAlign() >= Alignment)) { + auto *CPV = static_cast( + Constants[i].Val.MachineCPVal); + if (Derived *APC = dyn_cast(CPV)) + if (cast(this)->equals(APC)) + return i; + } + } + + return -1; + } + +public: + ~XtensaConstantPoolValue() override; + + XtensaCP::XtensaCPModifier getModifier() const { return Modifier; } + bool hasModifier() const { return Modifier != XtensaCP::no_modifier; } + StringRef getModifierText() const; + + unsigned getLabelId() const { return LabelId; } + void setLabelId(unsigned ID) { LabelId = ID; } + + bool isExtSymbol() const { return Kind == XtensaCP::CPExtSymbol; } + bool isBlockAddress() const { return Kind == XtensaCP::CPBlockAddress; } + bool isMachineBasicBlock() const { + return Kind == XtensaCP::CPMachineBasicBlock; + } + bool isJumpTable() const { return Kind == XtensaCP::CPJumpTable; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + virtual bool hasSameValue(XtensaConstantPoolValue *ACPV); + + bool equals(const XtensaConstantPoolValue *A) const { + return this->LabelId == A->LabelId && this->Modifier == A->Modifier; + } + + void print(raw_ostream &O) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const; +#endif +}; + +inline raw_ostream &operator<<(raw_ostream &O, + const XtensaConstantPoolValue &V) { + V.print(O); + return O; +} + +/// XtensaConstantPoolConstant - Xtensa-specific constant pool values for +/// Constants (for example BlockAddresses). +class XtensaConstantPoolConstant : public XtensaConstantPoolValue { + const Constant *CVal; // Constant being loaded. + + XtensaConstantPoolConstant(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind); + +public: + static XtensaConstantPoolConstant *Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind); + + const BlockAddress *getBlockAddress() const; + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + void print(raw_ostream &O) const override; + static bool classof(const XtensaConstantPoolValue *APV) { + return APV->isBlockAddress(); + } + + bool equals(const XtensaConstantPoolConstant *A) const { + return CVal == A->CVal && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolSymbol - Xtensa-specific constantpool values for external +/// symbols. +class XtensaConstantPoolSymbol : public XtensaConstantPoolValue { + const std::string S; // ExtSymbol being loaded. + bool PrivateLinkage; + + XtensaConstantPoolSymbol( + LLVMContext &C, const char *S, unsigned Id, bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + +public: + static XtensaConstantPoolSymbol * + Create(LLVMContext &C, const char *S, unsigned ID, bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + const char *getSymbol() const { return S.c_str(); } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + bool isPrivateLinkage() { return PrivateLinkage; } + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isExtSymbol(); + } + + bool equals(const XtensaConstantPoolSymbol *A) const { + return S == A->S && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolMBB - Xtensa-specific constantpool value of a machine +/// basic block. +class XtensaConstantPoolMBB : public XtensaConstantPoolValue { + const MachineBasicBlock *MBB; // Machine basic block. + + XtensaConstantPoolMBB(LLVMContext &C, const MachineBasicBlock *M, + unsigned ID); + +public: + static XtensaConstantPoolMBB *Create(LLVMContext &C, + const MachineBasicBlock *M, unsigned ID); + + const MachineBasicBlock *getMBB() const { return MBB; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isMachineBasicBlock(); + } + + bool equals(const XtensaConstantPoolMBB *A) const { + return MBB == A->MBB && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolJumpTable - Xtensa-specific constantpool values for Jump +/// Table symbols. +class XtensaConstantPoolJumpTable : public XtensaConstantPoolValue { + unsigned Idx; // Jump Table Index. + + XtensaConstantPoolJumpTable(LLVMContext &C, unsigned Idx); + +public: + static XtensaConstantPoolJumpTable *Create(LLVMContext &C, unsigned Idx); + + unsigned getIndex() const { return Idx; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isJumpTable(); + } + + bool equals(const XtensaConstantPoolJumpTable *A) const { + return Idx == A->Idx && XtensaConstantPoolValue::equals(A); + } +}; + +} // namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp b/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp index 2092a2d947f86..ab37c09bf8bfe 100644 --- a/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp +++ b/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp @@ -37,3 +37,23 @@ void XtensaFrameLowering::emitPrologue(MachineFunction &MF, void XtensaFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const {} + +// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions +MachineBasicBlock::iterator XtensaFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + const XtensaInstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + + if (!hasReservedCallFrame(MF)) { + int64_t Amount = I->getOperand(0).getImm(); + + if (I->getOpcode() == Xtensa::ADJCALLSTACKDOWN) + Amount = -Amount; + + unsigned SP = Xtensa::SP; + TII.adjustStackPtr(SP, Amount, MBB, I); + } + + return MBB.erase(I); +} diff --git a/llvm/lib/Target/Xtensa/XtensaFrameLowering.h b/llvm/lib/Target/Xtensa/XtensaFrameLowering.h index 19e52310a99d9..2da88ab14073a 100644 --- a/llvm/lib/Target/Xtensa/XtensaFrameLowering.h +++ b/llvm/lib/Target/Xtensa/XtensaFrameLowering.h @@ -25,6 +25,10 @@ class XtensaFrameLowering : public TargetFrameLowering { /// the function. void emitPrologue(MachineFunction &, MachineBasicBlock &) const override; void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; }; } // namespace llvm diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp index 3007372754522..5ebedefafc165 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(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(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(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 276fab838d17c..64b996b6a42e5 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "XtensaISelLowering.h" +#include "XtensaConstantPoolValue.h" #include "XtensaSubtarget.h" #include "XtensaTargetMachine.h" #include "llvm/CodeGen/CallingConvLower.h" @@ -22,15 +23,26 @@ #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +#include using namespace llvm; #define DEBUG_TYPE "xtensa-lower" +// Return true if we must use long (in fact, indirect) function call. +// It's simplified version, production implimentation must +// resolve a functions in ROM (usually glibc functions) +static bool isLongCall(const char *str) { + // Currently always use long calls + return true; +} + XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM, const XtensaSubtarget &STI) : TargetLowering(TM), Subtarget(STI) { + MVT PtrVT = MVT::i32; // Set up the register classes. addRegisterClass(MVT::i32, &Xtensa::ARRegClass); @@ -41,18 +53,507 @@ XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM, setMinFunctionAlignment(Align(4)); + setOperationAction(ISD::Constant, MVT::i32, Custom); + setOperationAction(ISD::Constant, MVT::i64, Expand); + + setBooleanContents(ZeroOrOneBooleanContent); + + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + + setOperationAction(ISD::BITCAST, MVT::i32, Expand); + setOperationAction(ISD::BITCAST, MVT::f32, Expand); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Expand); + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Expand); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Expand); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Expand); + + // 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); + } + + setOperationAction(ISD::ConstantPool, PtrVT, Custom); + // 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 Register; + + Align OrigAlign = ArgFlags.getNonZeroOrigAlign(); + bool needs64BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(8)); + bool needs128BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(16)); + + if (ValVT == MVT::i32) { + Register = 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 && (Register == Xtensa::A3 || Register == Xtensa::A5 || + Register == Xtensa::A7)) + Register = State.AllocateReg(IntRegs); + // arguments with 16byte alignment must be passed in the first register or + // passed via stack + if (needs128BitAlign && (Register != Xtensa::A2)) + while ((Register = State.AllocateReg(IntRegs))) + ; + LocVT = MVT::i32; + } else if (ValVT == MVT::f64) { + // Allocate int register and shadow next int register. + Register = State.AllocateReg(IntRegs); + if (Register == Xtensa::A3 || Register == Xtensa::A5 || + Register == Xtensa::A7) + Register = State.AllocateReg(IntRegs); + State.AllocateReg(IntRegs); + LocVT = MVT::i32; + } else { + report_fatal_error("Cannot handle this ValVT."); + } + + if (!Register) { + unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign); + State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); + } else { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Register, LocVT, LocInfo)); + } + + return false; +} + +CCAssignFn *XtensaTargetLowering::CCAssignFnForCall(CallingConv::ID CC, + bool IsVarArg) const { + return CC_Xtensa_Custom; +} + +SDValue XtensaTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + + // Used with vargs to acumulate store chains. + std::vector OutChains; + + if (IsVarArg) + report_fatal_error("Var arg not supported by FormalArguments Lowering"); + + // Assign locations to all of the incoming arguments. + SmallVector 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; +} + +SDValue +XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &DL = CLI.DL; + SmallVector &Outs = CLI.Outs; + SmallVector &OutVals = CLI.OutVals; + SmallVector &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + bool &IsTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); + + // TODO: Support tail call optimization. + IsTailCall = false; + + // Analyze the operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + + CCAssignFn *CC = CCAssignFnForCall(CallConv, IsVarArg); + + CCInfo.AnalyzeCallOperands(Outs, CC); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = CCInfo.getStackSize(); + + Align StackAlignment = TFL->getStackAlign(); + unsigned NextStackOffset = alignTo(NumBytes, StackAlignment); + + Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL); + + // Copy argument values to their designated locations. + std::deque> RegsToPass; + SmallVector MemOpChains; + SDValue StackPtr; + for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) { + CCValAssign &VA = ArgLocs[I]; + SDValue ArgValue = OutVals[I]; + ISD::ArgFlagsTy Flags = Outs[I].Flags; + + if (VA.isRegLoc()) + // Queue up the argument copies and emit them at the end. + RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); + else if (Flags.isByVal()) { + assert(VA.isMemLoc()); + assert(Flags.getByValSize() && + "ByVal args of size 0 should have been ignored by front-end."); + assert(!IsTailCall && + "Do not tail-call optimize if there is a byval argument."); + + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); + unsigned Offset = VA.getLocMemOffset(); + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(Offset, DL)); + SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), DL, MVT::i32); + SDValue Memcpy = DAG.getMemcpy( + Chain, DL, Address, ArgValue, SizeNode, Flags.getNonZeroByValAlign(), + /*isVolatile=*/false, /*AlwaysInline=*/false, + /*isTailCall=*/false, MachinePointerInfo(), MachinePointerInfo()); + MemOpChains.push_back(Memcpy); + } else { + assert(VA.isMemLoc() && "Argument not register or memory"); + + // Work out the address of the stack slot. Unpromoted ints and + // floats are passed as right-justified 8-byte values. + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); + unsigned Offset = VA.getLocMemOffset(); + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(Offset, DL)); + + // Emit the store. + MemOpChains.push_back( + DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); + } + } + + // Join the stores, which are independent of one another. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); + + // Build a sequence of copy-to-reg nodes, chained and glued together. + SDValue Glue; + for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { + unsigned Reg = RegsToPass[I].first; + Chain = DAG.getCopyToReg(Chain, DL, Reg, RegsToPass[I].second, Glue); + Glue = Chain.getValue(1); + } + std::string name; + unsigned char TF = 0; + + // Accept direct calls by converting symbolic call addresses to the + // associated Target* opcodes. + if (ExternalSymbolSDNode *E = dyn_cast(Callee)) { + name = E->getSymbol(); + TF = E->getTargetFlags(); + if (isPositionIndependent()) { + report_fatal_error("PIC relocations is not supported"); + } else + Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, TF); + } else if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + const GlobalValue *GV = G->getGlobal(); + name = GV->getName().str(); + } + + if ((!name.empty()) && isLongCall(name.c_str())) { + // Create a constant pool entry for the callee address + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier; + + XtensaConstantPoolValue *CPV = XtensaConstantPoolSymbol::Create( + *DAG.getContext(), name.c_str(), 0 /* XtensaCLabelIndex */, false, + Modifier); + + // Get the address of the callee into a register + SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4), 0, TF); + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + Callee = CPWrap; + } + + // The first call operand is the chain and the second is the target address. + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add a register mask operand representing the call-preserved registers. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + // Add argument registers to the end of the list so that they are + // known live into the call. + for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { + unsigned Reg = RegsToPass[I].first; + Ops.push_back(DAG.getRegister(Reg, RegsToPass[I].second.getValueType())); + } + + // Glue the call to the argument copies, if any. + if (Glue.getNode()) + Ops.push_back(Glue); + + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + Chain = DAG.getNode(XtensaISD::CALL, DL, NodeTys, Ops); + Glue = Chain.getValue(1); + + // Mark the end of the call, which is glued to the call itself. + Chain = DAG.getCALLSEQ_END(Chain, DAG.getConstant(NumBytes, DL, PtrVT, true), + DAG.getConstant(0, DL, PtrVT, true), Glue, DL); + Glue = Chain.getValue(1); + + // Assign locations to each value returned by this call. + SmallVector RetLocs; + CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext()); + RetCCInfo.AnalyzeCallResult(Ins, RetCC_Xtensa); + + // Copy all of the result registers out of their specified physreg. + for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) { + CCValAssign &VA = RetLocs[I]; + + // Copy the value out, gluing the copy to the end of the call sequence. + unsigned Reg = VA.getLocReg(); + SDValue RetValue = DAG.getCopyFromReg(Chain, DL, Reg, VA.getLocVT(), Glue); + Chain = RetValue.getValue(1); + Glue = RetValue.getValue(2); + + InVals.push_back(RetValue); + } + return Chain; +} + +bool XtensaTargetLowering::CanLowerReturn( + CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl &Outs, LLVMContext &Context) const { + SmallVector 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 &Outs, + const SmallVectorImpl &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 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 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!"); + + // Chain and glue the copies together. + unsigned Register = VA.getLocReg(); + Chain = DAG.getCopyToReg(Chain, DL, Register, RetValue, Glue); + Glue = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(Register, 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::LowerImmediate(SDValue Op, + SelectionDAG &DAG) const { + const ConstantSDNode *CN = cast(Op); + SDLoc DL(CN); + APInt APVal = CN->getAPIntValue(); + int64_t Value = APVal.getSExtValue(); + if (Op.getValueType() == MVT::i32) { + // Check if use node maybe lowered to the MOVI instruction + if (Value > -2048 && Value <= 2047) + return Op; + // Check if use node maybe lowered to the ADDMI instruction + SDNode &OpNode = *Op.getNode(); + if ((OpNode.hasOneUse() && OpNode.use_begin()->getOpcode() == ISD::ADD) && + isShiftedInt<16, 8>(Value)) + return Op; + Type *Ty = Type::getInt32Ty(*DAG.getContext()); + Constant *CV = ConstantInt::get(Ty, Value); + SDValue CP = DAG.getConstantPool(CV, MVT::i32); + return CP; + } + return Op; +} + +SDValue XtensaTargetLowering::getAddrPCRel(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + return DAG.getNode(XtensaISD::PCREL_WRAPPER, DL, Ty, Op); +} + +SDValue XtensaTargetLowering::LowerConstantPool(ConstantPoolSDNode *CP, + SelectionDAG &DAG) const { + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + SDValue Result; + if (!CP->isMachineConstantPoolEntry()) { + Result = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT, CP->getAlign(), + CP->getOffset()); + } else { + report_fatal_error("This constantpool type is not supported yet"); + } + + return getAddrPCRel(Result, DAG); +} + SDValue XtensaTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { switch (Op.getOpcode()) { + case ISD::Constant: + return LowerImmediate(Op, DAG); + case ISD::ConstantPool: + return LowerConstantPool(cast(Op), DAG); default: report_fatal_error("Unexpected node to lower"); } } const char *XtensaTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + case XtensaISD::CALL: + return "XtensaISD::CALL"; + case XtensaISD::PCREL_WRAPPER: + return "XtensaISD::PCREL_WRAPPER"; + 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 8b03712efc9bb..077559e2d6129 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.h +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.h @@ -19,6 +19,23 @@ #include "llvm/CodeGen/TargetLowering.h" namespace llvm { + +namespace XtensaISD { +enum { + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + // Calls a function. Operand 0 is the chain operand and operand 1 + // is the target address. The arguments start at operand 2. + // There is an optional glue operand at the end. + CALL, + + // Wraps a TargetGlobalAddress that should be loaded using PC-relative + // accesses. Operand 0 is the address. + PCREL_WRAPPER, + RET +}; +} + class XtensaSubtarget; class XtensaTargetLowering : public TargetLowering { @@ -30,10 +47,37 @@ class XtensaTargetLowering : public TargetLowering { SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool isVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &DL, + SelectionDAG &DAG) const override; + const XtensaSubtarget &getSubtarget() const { return Subtarget; } private: const XtensaSubtarget &Subtarget; + + SDValue LowerImmediate(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerConstantPool(ConstantPoolSDNode *CP, SelectionDAG &DAG) const; + + SDValue getAddrPCRel(SDValue Op, SelectionDAG &DAG) const; + + 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 41b794d64fdb1..26d8727ce1d3b 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,139 @@ using namespace llvm; +static 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) {} + : XtensaGenInstrInfo(Xtensa::ADJCALLSTACKDOWN, Xtensa::ADJCALLSTACKUP), + RI(STI), STI(STI) {} + +/// Adjust SP by Amount bytes. +void XtensaInstrInfo::adjustStackPtr(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + DebugLoc DL = I != MBB.end() ? I->getDebugLoc() : DebugLoc(); + + if (Amount == 0) + return; + + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Xtensa::ARRegClass; + + // create virtual reg to store immediate + unsigned Reg = RegInfo.createVirtualRegister(RC); + + if (isInt<8>(Amount)) { // addi sp, sp, amount + BuildMI(MBB, I, DL, get(Xtensa::ADDI), Reg).addReg(SP).addImm(Amount); + } else { // Expand immediate that doesn't fit in 8-bit. + unsigned Reg1; + loadImmediate(MBB, I, &Reg1, Amount); + BuildMI(MBB, I, DL, get(Xtensa::ADD), Reg) + .addReg(SP) + .addReg(Reg1, RegState::Kill); + } + + BuildMI(MBB, I, DL, get(Xtensa::OR), SP) + .addReg(Reg, RegState::Kill) + .addReg(Reg, RegState::Kill); +} + +void XtensaInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, MCRegister DestReg, + MCRegister SrcReg, bool KillSrc) const { + // The MOV instruction is not present in core ISA, + // so use OR instruction. + 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); + MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, get(StoreOpcode)) + .addReg(SrcReg, getKillRegState(isKill)); + addFrameReference(MIB, 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 { + assert((RC == &Xtensa::ARRegClass) && + "Unsupported regclass to load or store"); + + LoadOpcode = Xtensa::L32I; + StoreOpcode = Xtensa::S32I; +} + +void XtensaInstrInfo::loadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned *Reg, int64_t Value) const { + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Xtensa::ARRegClass; + + // create virtual reg to store immediate + *Reg = RegInfo.createVirtualRegister(RC); + if (Value >= -2048 && Value <= 2047) { + BuildMI(MBB, MBBI, DL, get(Xtensa::MOVI), *Reg).addImm(Value); + } else if (Value >= -32768 && Value <= 32767) { + int Low = Value & 0xFF; + int High = Value & ~0xFF; + + BuildMI(MBB, MBBI, DL, get(Xtensa::MOVI), *Reg).addImm(Low); + BuildMI(MBB, MBBI, DL, get(Xtensa::ADDMI), *Reg).addReg(*Reg).addImm(High); + } else if (Value >= -4294967296LL && Value <= 4294967295LL) { + // 32 bit arbirary constant + MachineConstantPool *MCP = MBB.getParent()->getConstantPool(); + uint64_t UVal = ((uint64_t)Value) & 0xFFFFFFFFLL; + const Constant *CVal = ConstantInt::get( + Type::getInt32Ty(MBB.getParent()->getFunction().getContext()), UVal, + false); + unsigned Idx = MCP->getConstantPoolIndex(CVal, Align(2U)); + // MCSymbol MSym + BuildMI(MBB, MBBI, DL, get(Xtensa::L32R), *Reg).addConstantPoolIndex(Idx); + } else { + // use L32R to let assembler load immediate best + // TODO replace to L32R + report_fatal_error("Unsupported load immediate value"); + } +} diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.h b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h index 8c73c9bd79408..1acd314e2720a 100644 --- a/llvm/lib/Target/Xtensa/XtensaInstrInfo.h +++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h @@ -35,9 +35,38 @@ class XtensaInstrInfo : public XtensaGenInstrInfo { public: XtensaInstrInfo(const XtensaSubtarget &STI); + void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; + // 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; + + // Emit code before MBBI in MI to move immediate value Value into + // physical register Reg. + void loadImmediate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + unsigned *Reg, int64_t Value) 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 268a9943d8c16..6e9e75257ccf4 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,34 @@ def L32R : RI16_Inst<0x01, (outs AR:$t), (ins L32Rtarget:$label), let imm16 = label; } +// pcrel addr loading using L32R +def : Pat<(Xtensa_pcrel_wrapper tconstpool : $in), (L32R tconstpool : $in)>; + +// FrameIndexes are legalized when they are operands from load/store +// instructions. The same not happens for stack address copies, so an +// add op with mem ComplexPattern is used and the stack address copy +// can be matched. +// Setting of attribute mayLoad is trick to process instruction operands +// in function XtensaRegisterInfo::eliminateFI + +let isCodeGenOnly = 1, mayLoad = 1 in { + + def LEA_ADD : RRI8_Inst<0x02, (outs AR:$t), (ins mem32:$addr), + "addi\t$t, $addr", + [(set AR:$t, addr_ish4:$addr)]> { + bits<12> addr; + + let r = 0x0C; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } +} + +//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 +455,7 @@ let isReturn = 1, isTerminator = 1, isBarrier = 1, Uses = [A0] in { def RET : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins), - "ret", []> { + "ret", [(Xtensa_ret)]> { let m = 0x2; let n = 0x0; let s = 0; @@ -434,6 +463,14 @@ let isReturn = 1, isTerminator = 1, } } +// Call patterns +def : Pat<(Xtensa_call (i32 tglobaladdr:$dst)), + (CALL0 tglobaladdr:$dst)>; +def : Pat<(Xtensa_call (i32 texternalsym:$dst)), + (CALL0 texternalsym:$dst)>; +def : Pat<(Xtensa_call AR:$dst), + (CALLX0 AR:$dst)>; + //===----------------------------------------------------------------------===// // Mem barrier instructions //===----------------------------------------------------------------------===// @@ -506,3 +543,19 @@ def XSR : RSR_Inst<0x00, 0x01, 0x06, (outs AR:$ard, SR:$srd), (ins AR:$t, SR:$sr "xsr\t$t, $sr", []> { let Constraints = "$ard = $t, $srd = $sr"; } + +//===----------------------------------------------------------------------===// +// Stack allocation +//===----------------------------------------------------------------------===// + +// ADJCALLSTACKDOWN/UP implicitly use/def SP because they may be expanded into +// a stack adjustment and the codegen must know that they may modify the stack +// pointer before prolog-epilog rewriting occurs. +let Defs = [SP], Uses = [SP] in { + def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + "#ADJCALLSTACKDOWN", + [(Xtensa_callseq_start timm:$amt1, timm:$amt2)]>; + def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + "#ADJCALLSTACKUP", + [(Xtensa_callseq_end timm:$amt1, timm:$amt2)]>; +} diff --git a/llvm/lib/Target/Xtensa/XtensaOperands.td b/llvm/lib/Target/Xtensa/XtensaOperands.td index 7a1a2e86e8c20..f41081f9bf2f9 100644 --- a/llvm/lib/Target/Xtensa/XtensaOperands.td +++ b/llvm/lib/Target/Xtensa/XtensaOperands.td @@ -195,7 +195,7 @@ def jumptarget : Operand { let ParserMatchClass = XtensaPCRelTargetAsmOperand; } -def L32Rtarget : Operand { +def L32Rtarget : Operand { let PrintMethod = "printL32RTarget"; let EncoderMethod = "getL32RTargetEncoding"; let DecoderMethod = "decodeL32ROperand"; diff --git a/llvm/lib/Target/Xtensa/XtensaOperators.td b/llvm/lib/Target/Xtensa/XtensaOperators.td new file mode 100644 index 0000000000000..cd4d831c85b58 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaOperators.td @@ -0,0 +1,36 @@ +//===- 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Type profiles +//===----------------------------------------------------------------------===// +def SDT_XtensaCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaCall : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; + +def SDT_XtensaWrapPtr : SDTypeProfile<1, 1, + [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; + +//===----------------------------------------------------------------------===// +// Node definitions +//===----------------------------------------------------------------------===// +def Xtensa_call: SDNode<"XtensaISD::CALL", SDT_XtensaCall, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, SDNPVariadic]>; + +def Xtensa_ret: SDNode<"XtensaISD::RET", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def Xtensa_pcrel_wrapper: SDNode<"XtensaISD::PCREL_WRAPPER", SDT_XtensaWrapPtr, []>; + +def Xtensa_callseq_start: SDNode<"ISD::CALLSEQ_START", SDT_XtensaCallSeqStart, + [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; + +def Xtensa_callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_XtensaCallSeqEnd, + [SDNPHasChain, SDNPSideEffect, SDNPOptInGlue, + SDNPOutGlue]>; diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp index f749cc51f96a0..bced2d4ad0095 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 { @@ -60,7 +61,70 @@ BitVector XtensaRegisterInfo::getReservedRegs(const MachineFunction &MF) const { 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); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const std::vector &CSI = MFI.getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + // The following stack frame objects are always referenced relative to $sp: + // 1. Outgoing arguments. + // 2. Pointer to dynamically allocated stack space. + // 3. Locations for callee-saved registers. + // 4. Locations for eh data registers. + // Everything else is referenced relative to whatever register + // getFrameRegister() returns. + unsigned FrameReg; + if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI)) + FrameReg = Xtensa::SP; + else + FrameReg = getFrameRegister(MF); + + // 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(FIOperandNum + 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) { + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc DL = II->getDebugLoc(); + unsigned ADD = Xtensa::ADD; + unsigned Reg; + const XtensaInstrInfo &TII = *static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + + TII.loadImmediate(MBB, II, &Reg, Offset); + BuildMI(MBB, II, DL, TII.get(ADD), Reg) + .addReg(FrameReg) + .addReg(Reg, RegState::Kill); + + FrameReg = Reg; + Offset = 0; + IsKill = true; + } + + MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false, false, IsKill); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset); + + return false; } Register XtensaRegisterInfo::getFrameRegister(const MachineFunction &MF) const { diff --git a/llvm/lib/Target/Xtensa/XtensaUtils.cpp b/llvm/lib/Target/Xtensa/XtensaUtils.cpp new file mode 100644 index 0000000000000..98e424f6ea440 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaUtils.cpp @@ -0,0 +1,59 @@ +//===--- 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 { + +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; +} + +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; + case Xtensa::LEA_ADD: + return (Offset >= -128 && Offset <= 127); + default: + // assume that MI is 32-bit load/store operation + Scale = 4; + break; + } + return isValidAddrOffset(Scale, Offset); +} + +} // namespace llvm diff --git a/llvm/lib/Target/Xtensa/XtensaUtils.h b/llvm/lib/Target/Xtensa/XtensaUtils.h new file mode 100644 index 0000000000000..2b0ac37a6971a --- /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 diff --git a/llvm/test/CodeGen/Xtensa/call.ll b/llvm/test/CodeGen/Xtensa/call.ll new file mode 100644 index 0000000000000..24c7c4f558e13 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/call.ll @@ -0,0 +1,49 @@ +; RUN: llc --mtriple=xtensa < %s | FileCheck %s + +declare i32 @external_function(i32) + +define i32 @test_call_external(i32 %a) nounwind { +; CHECK-LABEL: test_call_external: +; CHECK: # %bb.0: +; CHECK-NEXT: s32i a0, a1, 0 +; CHECK-NEXT: l32r a8, .LCPI0_0 +; CHECK-NEXT: callx0 a8 +; CHECK-NEXT: l32i a0, a1, 0 +; CHECK-NEXT: ret + %1 = call i32 @external_function(i32 %a) + ret i32 %1 +} + +define i32 @defined_function(i32 %a) nounwind { +; CHECK-LABEL: defined_function: +; CHECK: # %bb.0: +; CHECK-NEXT: addi a2, a2, 1 +; CHECK-NEXT: ret + %1 = add i32 %a, 1 + ret i32 %1 +} + +define i32 @test_call_defined(i32 %a) nounwind { +; CHECK-LABEL: test_call_defined: +; CHECK: # %bb.0: +; CHECK-NEXT: s32i a0, a1, 0 +; CHECK-NEXT: l32r a8, .LCPI2_0 +; CHECK-NEXT: callx0 a8 +; CHECK-NEXT: l32i a0, a1, 0 +; CHECK-NEXT: ret + %1 = call i32 @defined_function(i32 %a) nounwind + ret i32 %1 +} + +define i32 @test_call_indirect(ptr %a, i32 %b) nounwind { +; CHECK-LABEL: test_call_indirect: +; CHECK: # %bb.0: +; CHECK-NEXT: s32i a0, a1, 0 +; CHECK-NEXT: or a8, a2, a2 +; CHECK-NEXT: or a2, a3, a3 +; CHECK-NEXT: callx0 a8 +; CHECK-NEXT: l32i a0, a1, 0 +; CHECK-NEXT: ret + %1 = call i32 %a(i32 %b) + ret i32 %1 +} diff --git a/llvm/test/CodeGen/Xtensa/calling-conv.ll b/llvm/test/CodeGen/Xtensa/calling-conv.ll new file mode 100644 index 0000000000000..41ae4220145c2 --- /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/constantpool.ll b/llvm/test/CodeGen/Xtensa/constantpool.ll new file mode 100644 index 0000000000000..9b380d2c37b9e --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/constantpool.ll @@ -0,0 +1,28 @@ +; RUN: llc -mtriple=xtensa -verify-machineinstrs < %s \ +; RUN: | FileCheck %s + +; Test placement of the i32,i64, float and double constants in constantpool + +define dso_local i32 @const_i32() #0 { +; CHECK: .literal_position +; CHECK-NEXT: .literal .LCPI0_0, 74565 +; CHECK-LABEL: const_i32: +; CHECK: l32r a2, .LCPI0_0 + %1 = alloca i32, align 4 + store i32 74565, ptr %1, align 4 + %2 = load i32, ptr %1, align 4 + ret i32 %2 +} + +define dso_local i64 @const_int64() #0 { +; CHECK: .literal_position +; CHECK-NEXT: .literal .LCPI1_0, 305419896 +; CHECK-NEXT: .literal .LCPI1_1, -1859959449 +; CHECK-LABEL: const_int64: +; CHECK: l32r a3, .LCPI1_0 +; CHECK: l32r a2, .LCPI1_1 + %1 = alloca i64, align 8 + store i64 1311768467302729063, ptr %1, align 8 + %2 = load i64, ptr %1, align 8 + ret i64 %2 +} diff --git a/llvm/test/CodeGen/Xtensa/stack-access.ll b/llvm/test/CodeGen/Xtensa/stack-access.ll new file mode 100644 index 0000000000000..1590d24f228f2 --- /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 +} diff --git a/llvm/test/MC/Xtensa/Core/invalid.s b/llvm/test/MC/Xtensa/Core/invalid.s index d3d8fba8169a6..c7473e90c10ba 100644 --- a/llvm/test/MC/Xtensa/Core/invalid.s +++ b/llvm/test/MC/Xtensa/Core/invalid.s @@ -4,10 +4,6 @@ LBL0: # Out of range immediates -# imm12m -movi a1, 3000 -# CHECK: :[[#@LINE-1]]:10: error: expected immediate in range [-2048, 2047] - # imm8 addi a1, a2, 300 # CHECK: :[[#@LINE-1]]:14: error: expected immediate in range [-128, 127] diff --git a/llvm/test/MC/Xtensa/directive-literal.s b/llvm/test/MC/Xtensa/directive-literal.s new file mode 100644 index 0000000000000..269cf20ed45eb --- /dev/null +++ b/llvm/test/MC/Xtensa/directive-literal.s @@ -0,0 +1,42 @@ +# RUN: llvm-mc -triple=xtensa -filetype obj -o - %s \ +# RUN: | llvm-readobj -S --sd - \ +# RUN: | FileCheck -check-prefix=CHECK-LITERAL %s + +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefix=CHECK-INST %s + + .text + .literal_position + .literal .LCPI0_0, 305419896 + .literal .LCPI1_0, ext_var + .global test_literal + .p2align 2 + .type test_literal,@function +test_literal: + l32r a2, .LCPI0_0 + l32r a3, .LCPI1_0 + movi a4, 30000 + movi a5, 1000 + ret + +# CHECK-LITERAL: Section { +# CHECK-LITERAL: Name: .literal +# CHECK-LITERAL: SectionData ( +# CHECK-LITERAL: 0000: 78563412 00000000 30750000 +# CHECK-LITERAL: ) +# CHECK-LITERAL: } + +# CHECK-INST: .literal_position +# CHECK-INST: .literal .LCPI0_0, 305419896 +# CHECK-INST: .literal .LCPI1_0, ext_var +# CHECK-INST: .global test_literal +# CHECK-INST: .p2align 2 +# CHECK-INST: .type test_literal,@function +# CHECK-INST: test_literal: +# CHECK-INST: l32r a2, .LCPI0_0 +# CHECK-INST: l32r a3, .LCPI1_0 +# CHECK-INST: .literal .Ltmp0, 30000 +# CHECK-INST: l32r a4, .Ltmp0 +# CHECK-INST: movi a5, 1000 +# CHECK-INST: ret + diff --git a/llvm/test/MC/Xtensa/invalid-literal.s b/llvm/test/MC/Xtensa/invalid-literal.s new file mode 100644 index 0000000000000..ebb37441059ca --- /dev/null +++ b/llvm/test/MC/Xtensa/invalid-literal.s @@ -0,0 +1,10 @@ +# RUN: not llvm-mc %s -triple=xtensa -filetype=asm 2>&1 | FileCheck %s + +.text +.literal_position +.literal .LCPI0_0 a +# CHECK: [[@LINE-1]]:20: error: expected comma +.literal 123, a +# CHECK: [[@LINE-1]]:10: error: literal label must be a symbol +.literal .LCPI1_0, +# CHECK: [[@LINE-1]]:19: error: expected value