Index: llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def =================================================================== --- llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -58,3 +58,10 @@ ELF_RELOC(R_RISCV_SET32, 56) ELF_RELOC(R_RISCV_32_PCREL, 57) ELF_RELOC(R_RISCV_IRELATIVE, 58) +/* Overlay extension - awaiting real numbers as part of RISC-V psABI */ +ELF_RELOC(R_RISCV_OVL_HI20, 220) +ELF_RELOC(R_RISCV_OVL_LO12_I, 221) +ELF_RELOC(R_RISCV_OVL32, 222) +ELF_RELOC(R_RISCV_OVLPLT_HI20, 223) +ELF_RELOC(R_RISCV_OVLPLT_LO12_I, 224) +ELF_RELOC(R_RISCV_OVLPLT32, 225) Index: llvm/include/llvm/MC/MCExpr.h =================================================================== --- llvm/include/llvm/MC/MCExpr.h +++ llvm/include/llvm/MC/MCExpr.h @@ -352,6 +352,10 @@ VK_VE_TPOFF_HI32, // symbol@tpoff_hi VK_VE_TPOFF_LO32, // symbol@tpoff_lo + // A SymbolRef VK is needed in order to parse e.g. `.word foo@overlay_plt` + VK_RISCV_OVLPLT, // overlay_plt + VK_RISCV_OVL, // overlay + VK_TPREL, VK_DTPREL }; Index: llvm/lib/Analysis/InlineCost.cpp =================================================================== --- llvm/lib/Analysis/InlineCost.cpp +++ llvm/lib/Analysis/InlineCost.cpp @@ -2813,6 +2813,13 @@ if (Callee->isPresplitCoroutine()) return InlineResult::failure("unsplited coroutine call"); + // Don't inline overlay functions, or inline into overlay functions + Function *Caller = Call.getCaller(); + if (Caller->hasFnAttribute("overlay-call")) + return InlineResult::failure("caller is overlaycall"); + if (Callee->hasFnAttribute("overlay-call")) + return InlineResult::failure("callee is overlaycall"); + // Never inline calls with byval arguments that does not have the alloca // address space. Since byval arguments can be replaced with a copy to an // alloca, the inlined code would need to be adjusted to handle that the @@ -2838,7 +2845,6 @@ // Never inline functions with conflicting attributes (unless callee has // always-inline attribute). - Function *Caller = Call.getCaller(); if (!functionsHaveCompatibleAttributes(Caller, Callee, CalleeTTI, GetTLI)) return InlineResult::failure("conflicting attributes"); Index: llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -624,6 +624,19 @@ Name = getSectionPrefixForGlobal(Kind); } + // For overlay functions, replace the default section name with ".ovlinput" + if (isa(GO) && + cast(GO)->hasFnAttribute("overlay-call")) + Name = ".ovlinput"; + + // For overlay data, also change the section prefix to ".ovlinput", since + // the overlay system mixes the two + if (isa(GO) && + cast(GO)->hasAttribute("overlay-data")) { + assert(Name == ".rodata" && "non-constant ovldata?"); + Name = ".ovlinput"; + } + bool HasPrefix = false; if (const auto *F = dyn_cast(GO)) { if (Optional Prefix = F->getSectionPrefix()) { @@ -885,6 +898,14 @@ EmitUniqueSection = TM.getDataSections(); } EmitUniqueSection |= GO->hasComdat(); + + if (Kind.isText() && isa(GO) && + cast(GO)->hasFnAttribute("overlay-call")) + EmitUniqueSection = true; + if (isa(GO) && + cast(GO)->hasAttribute("overlay-data")) + EmitUniqueSection = true; + return selectELFSectionForGlobal(getContext(), GO, Kind, getMangler(), TM, Used.count(GO), EmitUniqueSection, Flags, &NextUniqueID); Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -1119,7 +1119,11 @@ continue; GV->setUnnamedAddr(R.second.UnnamedAddr ? GlobalValue::UnnamedAddr::Global : GlobalValue::UnnamedAddr::None); - if (EnableLTOInternalization && R.second.Partition == 0) + bool isOverlayCall = false; + if (isa(GV) && + cast(GV)->hasFnAttribute("overlay-call")) + isOverlayCall = true; + if (EnableLTOInternalization && R.second.Partition == 0 && !isOverlayCall) GV->setLinkage(GlobalValue::InternalLinkage); } Index: llvm/lib/MC/MCExpr.cpp =================================================================== --- llvm/lib/MC/MCExpr.cpp +++ llvm/lib/MC/MCExpr.cpp @@ -379,6 +379,8 @@ case VK_VE_TLS_GD_LO32: return "tls_gd_lo"; case VK_VE_TPOFF_HI32: return "tpoff_hi"; case VK_VE_TPOFF_LO32: return "tpoff_lo"; + case VK_RISCV_OVLPLT: return "overlay_plt"; + case VK_RISCV_OVL: return "overlay"; } llvm_unreachable("Invalid variant kind"); } @@ -520,6 +522,8 @@ .Case("tls_gd_lo", VK_VE_TLS_GD_LO32) .Case("tpoff_hi", VK_VE_TPOFF_HI32) .Case("tpoff_lo", VK_VE_TPOFF_LO32) + .Case("overlay_plt", VK_RISCV_OVLPLT) + .Case("overlay", VK_RISCV_OVL) .Default(VK_Invalid); } Index: llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -59,6 +59,7 @@ class RISCVAsmParser : public MCTargetAsmParser { SmallVector FeatureBitStack; + bool WarnOnReservedReg = true; SmallVector ParserOptionsStack; ParserOptionsSet ParserOptions; @@ -389,7 +390,9 @@ return false; return RISCVAsmParser::classifySymbolRef(getImm(), VK) && (VK == RISCVMCExpr::VK_RISCV_CALL || - VK == RISCVMCExpr::VK_RISCV_CALL_PLT); + VK == RISCVMCExpr::VK_RISCV_CALL_PLT || + VK == RISCVMCExpr::VK_RISCV_OVLCALL || + VK == RISCVMCExpr::VK_RISCV_OVL2RESCALL); } bool isPseudoJumpSymbol() const { @@ -618,7 +621,9 @@ return IsValid && ((IsConstantImm && VK == RISCVMCExpr::VK_RISCV_None) || VK == RISCVMCExpr::VK_RISCV_LO || VK == RISCVMCExpr::VK_RISCV_PCREL_LO || - VK == RISCVMCExpr::VK_RISCV_TPREL_LO); + VK == RISCVMCExpr::VK_RISCV_TPREL_LO || + VK == RISCVMCExpr::VK_RISCV_OVLPLT_LO || + VK == RISCVMCExpr::VK_RISCV_OVL_LO); } bool isSImm12Lsb0() const { return isBareSimmNLsb0<12>(); } @@ -645,11 +650,15 @@ if (!IsConstantImm) { IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK); return IsValid && (VK == RISCVMCExpr::VK_RISCV_HI || - VK == RISCVMCExpr::VK_RISCV_TPREL_HI); + VK == RISCVMCExpr::VK_RISCV_TPREL_HI || + VK == RISCVMCExpr::VK_RISCV_OVLPLT_HI || + VK == RISCVMCExpr::VK_RISCV_OVL_HI); } else { return isUInt<20>(Imm) && (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_HI || - VK == RISCVMCExpr::VK_RISCV_TPREL_HI); + VK == RISCVMCExpr::VK_RISCV_TPREL_HI || + VK == RISCVMCExpr::VK_RISCV_OVLPLT_HI || + VK == RISCVMCExpr::VK_RISCV_OVL_HI); } } @@ -1484,6 +1493,12 @@ if (Identifier.consume_back("@plt")) Kind = RISCVMCExpr::VK_RISCV_CALL_PLT; + if (Identifier.consume_back("@overlay")) + Kind = RISCVMCExpr::VK_RISCV_OVLCALL; + + if (Identifier.consume_back("@resident")) + Kind = RISCVMCExpr::VK_RISCV_OVL2RESCALL; + MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); Res = RISCVMCExpr::create(Res, Kind, getContext()); @@ -1951,10 +1966,34 @@ return false; } + if (Option == "warnreservedreg") { + getTargetStreamer().emitDirectiveOptionWarnReservedReg(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + WarnOnReservedReg = true; + return false; + } + + if (Option == "nowarnreservedreg") { + getTargetStreamer().emitDirectiveOptionNoWarnReservedReg(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + WarnOnReservedReg = false; + return false; + } + // Unknown option. Warning(Parser.getTok().getLoc(), - "unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or " - "'norelax'"); + "unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax', " + "'norelax', 'warnreservedreg' or 'nowarnreservedreg'"); Parser.eatToEndOfStatement(); return false; } @@ -2200,7 +2239,53 @@ return false; } +// Since only a RISCVGenSubtargetInfo is available here, extract register +// reservation directly from the STI bitfields. +static bool isRegisterReserved(MCRegister Reg, const MCSubtargetInfo &STI) { + switch (Reg) { + default: return false; + case RISCV::X1: return STI.getFeatureBits()[RISCV::FeatureReserveX1]; + case RISCV::X2: return STI.getFeatureBits()[RISCV::FeatureReserveX2]; + case RISCV::X3: return STI.getFeatureBits()[RISCV::FeatureReserveX3]; + case RISCV::X4: return STI.getFeatureBits()[RISCV::FeatureReserveX4]; + case RISCV::X5: return STI.getFeatureBits()[RISCV::FeatureReserveX5]; + case RISCV::X6: return STI.getFeatureBits()[RISCV::FeatureReserveX6]; + case RISCV::X7: return STI.getFeatureBits()[RISCV::FeatureReserveX7]; + case RISCV::X8: return STI.getFeatureBits()[RISCV::FeatureReserveX8]; + case RISCV::X9: return STI.getFeatureBits()[RISCV::FeatureReserveX9]; + case RISCV::X10: return STI.getFeatureBits()[RISCV::FeatureReserveX10]; + case RISCV::X11: return STI.getFeatureBits()[RISCV::FeatureReserveX11]; + case RISCV::X12: return STI.getFeatureBits()[RISCV::FeatureReserveX12]; + case RISCV::X13: return STI.getFeatureBits()[RISCV::FeatureReserveX13]; + case RISCV::X14: return STI.getFeatureBits()[RISCV::FeatureReserveX14]; + case RISCV::X15: return STI.getFeatureBits()[RISCV::FeatureReserveX15]; + case RISCV::X16: return STI.getFeatureBits()[RISCV::FeatureReserveX16]; + case RISCV::X17: return STI.getFeatureBits()[RISCV::FeatureReserveX17]; + case RISCV::X18: return STI.getFeatureBits()[RISCV::FeatureReserveX18]; + case RISCV::X19: return STI.getFeatureBits()[RISCV::FeatureReserveX19]; + case RISCV::X20: return STI.getFeatureBits()[RISCV::FeatureReserveX20]; + case RISCV::X21: return STI.getFeatureBits()[RISCV::FeatureReserveX21]; + case RISCV::X22: return STI.getFeatureBits()[RISCV::FeatureReserveX22]; + case RISCV::X23: return STI.getFeatureBits()[RISCV::FeatureReserveX23]; + case RISCV::X24: return STI.getFeatureBits()[RISCV::FeatureReserveX24]; + case RISCV::X25: return STI.getFeatureBits()[RISCV::FeatureReserveX25]; + case RISCV::X26: return STI.getFeatureBits()[RISCV::FeatureReserveX26]; + case RISCV::X27: return STI.getFeatureBits()[RISCV::FeatureReserveX27]; + case RISCV::X28: return STI.getFeatureBits()[RISCV::FeatureReserveX28]; + case RISCV::X29: return STI.getFeatureBits()[RISCV::FeatureReserveX29]; + case RISCV::X30: return STI.getFeatureBits()[RISCV::FeatureReserveX30]; + case RISCV::X31: return STI.getFeatureBits()[RISCV::FeatureReserveX31]; + } +} + void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { + // Provide a warning about defs of reserved registers. + auto &Desc = MII.get(Inst.getOpcode()); + for (unsigned i = 0; i < Desc.getNumDefs(); i++) { + if (Inst.getOperand(i).isReg() && isRegisterReserved(Inst.getOperand(i).getReg(), getSTI()) && WarnOnReservedReg) + Warning(Inst.getLoc(), "Instruction modifies reserved register"); + } + MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); if (Res) Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -93,6 +93,13 @@ {"fixup_riscv_set_6b", 2, 6, 0}, {"fixup_riscv_sub_6b", 2, 6, 0}, + + {"fixup_riscv_ovl_hi20", 12, 20, 0}, + {"fixup_riscv_ovl_lo12_i", 20, 12, 0}, + {"fixup_riscv_ovl32", 0, 32, 0}, + {"fixup_riscv_ovlplt_hi20", 12, 20, 0}, + {"fixup_riscv_ovlplt_lo12_i", 20, 12, 0}, + {"fixup_riscv_ovlplt32", 0, 32, 0}, }; static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, "Not all fixup kinds added to Infos array"); Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -157,11 +157,17 @@ MO_TPREL_ADD = 10, MO_TLS_GOT_HI = 11, MO_TLS_GD_HI = 12, + MO_OVLCALL = 13, + MO_OVL2RESCALL = 14, + MO_OVL_LO = 15, + MO_OVL_HI = 16, + MO_OVLPLT_LO = 17, + MO_OVLPLT_HI = 18, // Used to differentiate between target-specific "direct" flags and "bitmask" // flags. A machine operand can only have one "direct" flag, but can have // multiple "bitmask" flags. - MO_DIRECT_FLAG_MASK = 15 + MO_DIRECT_FLAG_MASK = 31 }; } // namespace RISCVII Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -119,6 +119,14 @@ if (Expr->getKind() == MCExpr::Target && cast(Expr)->getKind() == RISCVMCExpr::VK_RISCV_32_PCREL) return ELF::R_RISCV_32_PCREL; + if (Expr->getKind() == MCExpr::SymbolRef && + cast(Expr)->getKind() == + MCSymbolRefExpr::VK_RISCV_OVLPLT) + return ELF::R_RISCV_OVLPLT32; + if (Expr->getKind() == MCExpr::SymbolRef && + cast(Expr)->getKind() == + MCSymbolRefExpr::VK_RISCV_OVL) + return ELF::R_RISCV_OVL32; return ELF::R_RISCV_32; case FK_Data_8: return ELF::R_RISCV_64; @@ -166,6 +174,14 @@ return ELF::R_RISCV_ADD64; case RISCV::fixup_riscv_sub_64: return ELF::R_RISCV_SUB64; + case RISCV::fixup_riscv_ovl_hi20: + return ELF::R_RISCV_OVL_HI20; + case RISCV::fixup_riscv_ovl_lo12_i: + return ELF::R_RISCV_OVL_LO12_I; + case RISCV::fixup_riscv_ovlplt_hi20: + return ELF::R_RISCV_OVLPLT_HI20; + case RISCV::fixup_riscv_ovlplt_lo12_i: + return ELF::R_RISCV_OVLPLT_LO12_I; } } Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h @@ -103,6 +103,8 @@ void emitDirectiveOptionNoRVC() override; void emitDirectiveOptionRelax() override; void emitDirectiveOptionNoRelax() override; + void emitDirectiveOptionWarnReservedReg() override; + void emitDirectiveOptionNoWarnReservedReg() override; }; MCELFStreamer *createRISCVELFStreamer(MCContext &C, Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp @@ -76,6 +76,8 @@ void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {} void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {} void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {} +void RISCVTargetELFStreamer::emitDirectiveOptionWarnReservedReg() {} +void RISCVTargetELFStreamer::emitDirectiveOptionNoWarnReservedReg() {} void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) { setAttributeItem(Attribute, Value, /*OverwriteExisting=*/true); Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -105,6 +105,13 @@ // DWARF CFA. fixup_riscv_sub_6b, + fixup_riscv_ovl_hi20, + fixup_riscv_ovl_lo12_i, + fixup_riscv_ovl32, + fixup_riscv_ovlplt_hi20, + fixup_riscv_ovlplt_lo12_i, + fixup_riscv_ovlplt32, + // Used as a sentinel, must be the last fixup_riscv_invalid, NumTargetFixupKinds = fixup_riscv_invalid - FirstTargetFixupKind Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -56,6 +56,10 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + unsigned expandFunctionCallOverlay(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + void expandAddTPRel(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; @@ -149,6 +153,82 @@ support::endian::write(OS, Binary, support::little); } +unsigned +RISCVMCCodeEmitter::expandFunctionCallOverlay(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + MCInst TmpInst; + uint32_t Binary; + bool Relax = STI.getFeatureBits()[RISCV::FeatureRelax]; + + unsigned Opcode = MI.getOpcode(); + unsigned NumInstrs = 0; + if (Opcode == RISCV::PseudoOVLCALL || + Opcode == RISCV::PseudoOVLCALLIndirect) { + // OVLCALL -> LUI ; ADDI ; JALR + // Note: Fixups are added manually since they are needed at non-zero offsets + MCRegister Ra = RISCV::X1; + MCRegister Token = RISCV::X30; + MCRegister EntryPoint = RISCV::X31; + + if (Opcode == RISCV::PseudoOVLCALL) { + // Extract the call symbol that should be targetted by this call. + assert(MI.getOperand(0).isExpr() && "Something has gone wrong?"); + const RISCVMCExpr *RCallExpr = cast(MI.getOperand(0).getExpr()); + assert(RCallExpr->getSubExpr()->getKind() == MCExpr::SymbolRef); + const MCSymbolRefExpr *Sym = cast(RCallExpr->getSubExpr()); + bool isOVL = RCallExpr->getKind() == RISCVMCExpr::VK_RISCV_OVLCALL; + if (!isOVL && (RCallExpr->getKind() != RISCVMCExpr::VK_RISCV_OVL2RESCALL)) + report_fatal_error("symbol used with 'ovlcall' must be marked with " + "either @resident or @overlay"); + + // LUI + TmpInst = MCInstBuilder(RISCV::LUI).addReg(Token).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + NumInstrs++; + Fixups.push_back(MCFixup::create(0, Sym, + isOVL ? MCFixupKind(RISCV::fixup_riscv_ovl_hi20) + : MCFixupKind(RISCV::fixup_riscv_hi20), + MI.getLoc())); + if (Relax) + Fixups.push_back(MCFixup::create(0, MCConstantExpr::create(0, Ctx), + MCFixupKind(RISCV::fixup_riscv_relax), + MI.getLoc())); + // ADDI + TmpInst = MCInstBuilder(RISCV::ADDI).addReg(Token).addReg(Token).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + NumInstrs++; + Fixups.push_back(MCFixup::create(4, Sym, + isOVL ? MCFixupKind(RISCV::fixup_riscv_ovl_lo12_i) + : MCFixupKind(RISCV::fixup_riscv_lo12_i), + MI.getLoc())); + if (Relax) + Fixups.push_back(MCFixup::create(4, MCConstantExpr::create(0, Ctx), + MCFixupKind(RISCV::fixup_riscv_relax), + MI.getLoc())); + } else { + assert(Opcode == RISCV::PseudoOVLCALLIndirect); + MCRegister TargetReg = MI.getOperand(0).getReg(); + // MV + TmpInst = MCInstBuilder(RISCV::ADDI).addReg(Token).addReg(TargetReg).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + NumInstrs++; + } + + // JALR + TmpInst = MCInstBuilder(RISCV::JALR).addReg(Ra).addReg(EntryPoint).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + NumInstrs++; + } else + llvm_unreachable("Unexpected opcode!"); + + return NumInstrs; +} + // Expand PseudoAddTPRel to a simple ADD with the correct relocation. void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, @@ -209,6 +289,12 @@ return; } + if (MI.getOpcode() == RISCV::PseudoOVLCALL || + MI.getOpcode() == RISCV::PseudoOVLCALLIndirect) { + MCNumEmitted += expandFunctionCallOverlay(MI, OS, Fixups, STI); + return; + } + if (MI.getOpcode() == RISCV::PseudoAddTPRel) { expandAddTPRel(MI, OS, Fixups, STI); MCNumEmitted += 1; @@ -290,6 +376,12 @@ case RISCVMCExpr::VK_RISCV_Invalid: case RISCVMCExpr::VK_RISCV_32_PCREL: llvm_unreachable("Unhandled fixup kind!"); + case RISCVMCExpr::VK_RISCV_OVLCALL: + case RISCVMCExpr::VK_RISCV_OVL2RESCALL: + // Overlay calls are expanded into multiple instructions with different + // fixups depending on the instruction sequence, so they aren't + // handled here. + return 0; case RISCVMCExpr::VK_RISCV_TPREL_ADD: // tprel_add is only used to indicate that a relocation should be emitted // for an add instruction used in TP-relative addressing. It should not be @@ -355,6 +447,18 @@ FixupKind = RISCV::fixup_riscv_call_plt; RelaxCandidate = true; break; + case RISCVMCExpr::VK_RISCV_OVL_LO: + FixupKind = RISCV::fixup_riscv_ovl_lo12_i; + break; + case RISCVMCExpr::VK_RISCV_OVL_HI: + FixupKind = RISCV::fixup_riscv_ovl_hi20; + break; + case RISCVMCExpr::VK_RISCV_OVLPLT_LO: + FixupKind = RISCV::fixup_riscv_ovlplt_lo12_i; + break; + case RISCVMCExpr::VK_RISCV_OVLPLT_HI: + FixupKind = RISCV::fixup_riscv_ovlplt_hi20; + break; } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -36,6 +36,12 @@ VK_RISCV_TLS_GD_HI, VK_RISCV_CALL, VK_RISCV_CALL_PLT, + VK_RISCV_OVLCALL, + VK_RISCV_OVL2RESCALL, + VK_RISCV_OVL_LO, + VK_RISCV_OVL_HI, + VK_RISCV_OVLPLT_LO, + VK_RISCV_OVLPLT_HI, VK_RISCV_32_PCREL, VK_RISCV_Invalid // Must be the last item }; Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -35,13 +35,18 @@ void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { VariantKind Kind = getKind(); bool HasVariant = ((Kind != VK_RISCV_None) && (Kind != VK_RISCV_CALL) && - (Kind != VK_RISCV_CALL_PLT)); + (Kind != VK_RISCV_CALL_PLT) && (Kind != VK_RISCV_OVLCALL) && + (Kind != VK_RISCV_OVL2RESCALL)); if (HasVariant) OS << '%' << getVariantKindName(getKind()) << '('; Expr->print(OS, MAI); if (Kind == VK_RISCV_CALL_PLT) OS << "@plt"; + if (Kind == VK_RISCV_OVLCALL) + OS << "@overlay"; + if (Kind == VK_RISCV_OVL2RESCALL) + OS << "@resident"; if (HasVariant) OS << ')'; } @@ -120,6 +125,10 @@ .Case("tprel_add", VK_RISCV_TPREL_ADD) .Case("tls_ie_pcrel_hi", VK_RISCV_TLS_GOT_HI) .Case("tls_gd_pcrel_hi", VK_RISCV_TLS_GD_HI) + .Case("overlay_lo", VK_RISCV_OVL_LO) + .Case("overlay_hi", VK_RISCV_OVL_HI) + .Case("overlay_pltlo", VK_RISCV_OVLPLT_LO) + .Case("overlay_plthi", VK_RISCV_OVLPLT_HI) .Default(VK_RISCV_Invalid); } @@ -154,6 +163,14 @@ return "call_plt"; case VK_RISCV_32_PCREL: return "32_pcrel"; + case VK_RISCV_OVL_LO: + return "overlay_lo"; + case VK_RISCV_OVL_HI: + return "overlay_hi"; + case VK_RISCV_OVLPLT_LO: + return "overlay_pltlo"; + case VK_RISCV_OVLPLT_HI: + return "overlay_plthi"; } llvm_unreachable("Invalid ELF symbol kind"); } @@ -207,7 +224,8 @@ Kind == VK_RISCV_GOT_HI || Kind == VK_RISCV_TPREL_HI || Kind == VK_RISCV_TPREL_LO || Kind == VK_RISCV_TPREL_ADD || Kind == VK_RISCV_TLS_GOT_HI || Kind == VK_RISCV_TLS_GD_HI || - Kind == VK_RISCV_CALL || Kind == VK_RISCV_CALL_PLT) + Kind == VK_RISCV_CALL || Kind == VK_RISCV_CALL_PLT || + Kind == VK_RISCV_OVLCALL || Kind == VK_RISCV_OVL2RESCALL) return false; if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h @@ -29,6 +29,8 @@ virtual void emitDirectiveOptionNoRVC(); virtual void emitDirectiveOptionRelax(); virtual void emitDirectiveOptionNoRelax(); + virtual void emitDirectiveOptionWarnReservedReg(); + virtual void emitDirectiveOptionNoWarnReservedReg(); virtual void emitAttribute(unsigned Attribute, unsigned Value); virtual void finishAttributeSection(); virtual void emitTextAttribute(unsigned Attribute, StringRef String); @@ -59,6 +61,8 @@ void emitDirectiveOptionNoRVC() override; void emitDirectiveOptionRelax() override; void emitDirectiveOptionNoRelax() override; + void emitDirectiveOptionWarnReservedReg() override; + void emitDirectiveOptionNoWarnReservedReg() override; }; } Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -36,6 +36,8 @@ void RISCVTargetStreamer::emitIntTextAttribute(unsigned Attribute, unsigned IntValue, StringRef StringValue) {} +void RISCVTargetStreamer::emitDirectiveOptionWarnReservedReg() {} +void RISCVTargetStreamer::emitDirectiveOptionNoWarnReservedReg() {} void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) { if (STI.hasFeature(RISCV::FeatureRV32E)) @@ -147,3 +149,11 @@ StringRef StringValue) {} void RISCVTargetAsmStreamer::finishAttributeSection() {} + +void RISCVTargetAsmStreamer::emitDirectiveOptionWarnReservedReg() { + OS << "\t.option\twarnreservedreg\n"; +} + +void RISCVTargetAsmStreamer::emitDirectiveOptionNoWarnReservedReg() { + OS << "\t.option\tnowarnreservedreg\n"; +} Index: llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -49,6 +49,9 @@ bool runOnMachineFunction(MachineFunction &MF) override; + const MCExpr *lowerConstant(const Constant *CV) override; + void SetupMachineFunction(MachineFunction &MF) override; + void emitInstruction(const MachineInstr *MI) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, @@ -196,6 +199,36 @@ RTS.emitTargetAttributes(*STI); } +const MCExpr *RISCVAsmPrinter::lowerConstant(const Constant *CV) { + if (auto *Fn = dyn_cast(CV)) { + if (Fn->hasFnAttribute("overlay-call")) { + const MCSymbolRefExpr *Expr = + MCSymbolRefExpr::create(getSymbol(cast(CV)), + MCSymbolRefExpr::VK_RISCV_OVLPLT, OutContext); + return Expr; + } + } + if (auto *GV = dyn_cast(CV)) { + if (GV->hasAttribute("overlay-data")) { + const MCSymbolRefExpr *Expr = + MCSymbolRefExpr::create(getSymbol(cast(CV)), + MCSymbolRefExpr::VK_RISCV_OVL, OutContext); + return Expr; + } + } + return AsmPrinter::lowerConstant(CV); +} +void RISCVAsmPrinter::SetupMachineFunction(MachineFunction &MF) { + // Set the current MCSubtargetInfo to a copy which has the correct + // feature bits for the current MachineFunction + MCSubtargetInfo &NewSTI = + OutStreamer->getContext().getSubtargetCopy(*TM.getMCSubtargetInfo()); + NewSTI.setFeatureBits(MF.getSubtarget().getFeatureBits()); + STI = &NewSTI; + + return AsmPrinter::SetupMachineFunction(MF); +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVAsmPrinter() { RegisterAsmPrinter X(getTheRISCV32Target()); Index: llvm/lib/Target/RISCV/RISCVISelLowering.h =================================================================== --- llvm/lib/Target/RISCV/RISCVISelLowering.h +++ llvm/lib/Target/RISCV/RISCVISelLowering.h @@ -43,6 +43,9 @@ TAIL, // Multiply high for signedxunsigned. MULHSU, + // CALL equivalent nodes for overlay calls + OVLCALL, + OVLCALL_INDIRECT, // RV64I shifts, directly matching the semantics of the named RISC-V // instructions. SLLW, @@ -529,7 +532,8 @@ RISCVCCAssignFn Fn) const; template - SDValue getAddr(NodeTy *N, SelectionDAG &DAG, bool IsLocal = true) const; + SDValue getAddr(NodeTy *N, SelectionDAG &DAG, bool IsLocal = true, + unsigned SpecialMOLo = 0, unsigned SpecialMOHi = 0) const; SDValue getStaticTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG, bool UseGOT) const; Index: llvm/lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -2810,10 +2810,21 @@ template SDValue RISCVTargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG, - bool IsLocal) const { + bool IsLocal, unsigned SpecialMOLo, + unsigned SpecialMOHi) const { SDLoc DL(N); EVT Ty = getPointerTy(DAG.getDataLayout()); + // Overlay calls are special. They are unaffected by -fpic or the code + // model, so they're handled first here. + if (SpecialMOLo || SpecialMOHi) { + assert(SpecialMOLo && SpecialMOHi && "MOFlags only on one half?"); + SDValue AddrLo = getTargetNode(N, DL, Ty, DAG, SpecialMOLo); + SDValue AddrHi = getTargetNode(N, DL, Ty, DAG, SpecialMOHi); + SDValue LoadHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, AddrHi), 0); + return SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, LoadHi, AddrLo), 0); + } + if (isPositionIndependent()) { SDValue Addr = getTargetNode(N, DL, Ty, DAG, 0); if (IsLocal) @@ -2859,7 +2870,30 @@ const GlobalValue *GV = N->getGlobal(); bool IsLocal = getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV); - SDValue Addr = getAddr(N, DAG, IsLocal); + + // If we're loading a function address with the overlay-call attribute or + // referring to a GlobalVariable with the overlay-data attribute then the + // GlobalAddress must be modifier to refer to where the function resides + // in its overlay group. + unsigned LoFlags = 0, HiFlags = 0; + if (auto *F = dyn_cast(GV)) { + if (F->hasFnAttribute("overlay-call")) { + if (Offset != 0) + report_fatal_error ("Arithmetic on overlay tokens is not supported"); + LoFlags = RISCVII::MO_OVLPLT_LO; + HiFlags = RISCVII::MO_OVLPLT_HI; + } + } + else if (auto *GVar = dyn_cast(GV)) { + if (GVar->hasAttribute("overlay-data")) { + if (Offset != 0) + report_fatal_error ("Arithmetic on overlay tokens is not supported"); + LoFlags = RISCVII::MO_OVL_LO; + LoFlags = RISCVII::MO_OVL_HI; + } + } + + SDValue Addr = getAddr(N, DAG, IsLocal, LoFlags, HiFlags); // In order to maximise the opportunity for common subexpression elimination, // emit a separate ADD node for the global address offset instead of folding @@ -7721,10 +7755,13 @@ Optional FirstMaskArgument) { // X5 and X6 might be used for save-restore libcall. + // ComRV: Do not use X28, X29, X30 nor X31 for FastCC, since these will be + // reserved. FIXME: Add support for removing registers that are reserved + // from this list. static const MCPhysReg GPRList[] = { RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13, RISCV::X14, - RISCV::X15, RISCV::X16, RISCV::X17, RISCV::X7, RISCV::X28, - RISCV::X29, RISCV::X30, RISCV::X31}; + RISCV::X15, RISCV::X16, RISCV::X17, RISCV::X7/*, RISCV::X28, + RISCV::X29, RISCV::X30, RISCV::X31*/}; if (LocVT == MVT::i32 || LocVT == MVT::i64) { if (unsigned Reg = State.AllocateReg(GPRList)) { @@ -8035,6 +8072,15 @@ if (Caller.hasFnAttribute("interrupt")) return false; + // Overlay function can never tailcall, nor can be tail called + if (Caller.hasFnAttribute("overlay-call")) + return false; + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + if (auto *CalleeF = dyn_cast(G->getGlobal())) + if (CalleeF->hasFnAttribute("overlay-call")) + return false; + } + // Do not tail call opt if the stack is used to pass parameters. if (CCInfo.getNextStackOffset() != 0) return false; @@ -8303,22 +8349,40 @@ // If the callee is a GlobalAddress/ExternalSymbol node, turn it into a // TargetGlobalAddress/TargetExternalSymbol node so that legalize won't // split it and then direct call can be matched by PseudoCALL. + bool isOVLCC = false; + bool isIndirect = false; if (GlobalAddressSDNode *S = dyn_cast(Callee)) { const GlobalValue *GV = S->getGlobal(); unsigned OpFlags = RISCVII::MO_CALL; - if (!getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) + auto *F = dyn_cast(GV); + if ((F && F->hasFnAttribute("overlay-call")) || + MF.getFunction().hasFnAttribute("overlay-call")) { + isOVLCC = true; + if (F && F->hasFnAttribute("overlay-call")) + OpFlags = RISCVII::MO_OVLCALL; /*overlay*/ + else + OpFlags = RISCVII::MO_OVL2RESCALL; /*overlay to resident call*/ + } + else if (!getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) OpFlags = RISCVII::MO_PLT; Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, OpFlags); } else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { unsigned OpFlags = RISCVII::MO_CALL; - - if (!getTargetMachine().shouldAssumeDSOLocal(*MF.getFunction().getParent(), + // In the case of the caller being an overlay function, make function calls + // use ovlcall (resident mode) + if (MF.getFunction().hasFnAttribute("overlay-call")) { + OpFlags = RISCVII::MO_OVL2RESCALL; + isOVLCC = true; + } + else if (!getTargetMachine().shouldAssumeDSOLocal(*MF.getFunction().getParent(), nullptr)) OpFlags = RISCVII::MO_PLT; Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT, OpFlags); + } else { + isIndirect = true; } // The first call operand is the chain and the second is the target address. @@ -8351,7 +8415,18 @@ return DAG.getNode(RISCVISD::TAIL, DL, NodeTys, Ops); } - Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); + // If this is an indirect function call and the caller is an overlaycall, then + // use an ovlcall_indirect node to indicate the call must go via the manager + // regardless of whether the callee is an ovlcall or not (will be either + // resident function or PLT stub) + if (isIndirect && + MF.getFunction().hasFnAttribute("overlay-call")) { + Chain = DAG.getNode(RISCVISD::OVLCALL_INDIRECT, DL, NodeTys, Ops); + } else if (isOVLCC) { + Chain = DAG.getNode(RISCVISD::OVLCALL, DL, NodeTys, Ops); + } else { + Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); + } DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge); Glue = Chain.getValue(1); @@ -8674,6 +8749,8 @@ NODE_NAME_CASE(READ_CSR) NODE_NAME_CASE(WRITE_CSR) NODE_NAME_CASE(SWAP_CSR) + NODE_NAME_CASE(OVLCALL) + NODE_NAME_CASE(OVLCALL_INDIRECT) } // clang-format on return nullptr; Index: llvm/lib/Target/RISCV/RISCVInstrInfo.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -788,6 +788,7 @@ case RISCV::PseudoLA: case RISCV::PseudoLA_TLS_IE: case RISCV::PseudoLA_TLS_GD: + case RISCV::PseudoOVLCALLIndirect: return 8; case RISCV::PseudoAtomicLoadNand32: case RISCV::PseudoAtomicLoadNand64: @@ -809,6 +810,8 @@ return 16; case RISCV::PseudoMaskedCmpXchg32: return 32; + case RISCV::PseudoOVLCALL: + return 12; case TargetOpcode::INLINEASM: case TargetOpcode::INLINEASM_BR: { const MachineFunction &MF = *MI.getParent()->getParent(); Index: llvm/lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -83,6 +83,15 @@ SDT_RISCVReadCycleWide, [SDNPHasChain, SDNPSideEffect]>; +// Nodes for other Calling Conventions +def riscv_ovlcall : SDNode<"RISCVISD::OVLCALL", SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; +def riscv_ovlcall_indirect : SDNode<"RISCVISD::OVLCALL_INDIRECT", + SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, + SDNPOutGlue, SDNPVariadic]>; + //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. //===----------------------------------------------------------------------===// @@ -1050,6 +1059,30 @@ def : Pat<(riscv_call tglobaladdr:$func), (PseudoCALL tglobaladdr:$func)>; def : Pat<(riscv_call texternalsym:$func), (PseudoCALL texternalsym:$func)>; +// Overlay call handling +let isCall = 1, Defs = [X1], isCodeGenOnly = 0, mayStore = 0, mayLoad = 0, + hasSideEffects = 0 in { + def PseudoOVLCALL : Pseudo<(outs), (ins call_symbol:$func), []> { + let AsmString = "ovlcall\t$func"; + } + // It is not possible to do an indirect call with a plain "ovlcall" because + // the expansion depends on knowing whether or not the caller is in an + // overlay. These pseudo instructions are used instead and take a register + // operand instead of an immediate. + def PseudoOVLCALLIndirect : Pseudo<(outs), (ins GPR:$rs1), []> { + let AsmString = "ovlcall_indirect\t$rs1"; + } +} + +def : Pat<(riscv_ovlcall tglobaladdr:$func), (PseudoOVLCALL tglobaladdr:$func)>; +def : Pat<(riscv_ovlcall texternalsym:$func), (PseudoOVLCALL texternalsym:$func)>; + +// Indirect calls from overlay functions will explicitly use +// RISCVISD::OVLCALL_INDIRECT +def : Pat<(riscv_ovlcall_indirect GPR:$rs1), + (PseudoOVLCALLIndirect GPR:$rs1)>; + + def : Pat<(riscv_uret_flag), (URET X0, X0)>; def : Pat<(riscv_sret_flag), (SRET X0, X0)>; def : Pat<(riscv_mret_flag), (MRET X0, X0)>; Index: llvm/lib/Target/RISCV/RISCVMCInstLower.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVMCInstLower.cpp +++ llvm/lib/Target/RISCV/RISCVMCInstLower.cpp @@ -73,6 +73,24 @@ case RISCVII::MO_TLS_GD_HI: Kind = RISCVMCExpr::VK_RISCV_TLS_GD_HI; break; + case RISCVII::MO_OVLCALL: + Kind = RISCVMCExpr::VK_RISCV_OVLCALL; + break; + case RISCVII::MO_OVL2RESCALL: + Kind = RISCVMCExpr::VK_RISCV_OVL2RESCALL; + break; + case RISCVII::MO_OVL_LO: + Kind = RISCVMCExpr::VK_RISCV_OVL_LO; + break; + case RISCVII::MO_OVL_HI: + Kind = RISCVMCExpr::VK_RISCV_OVL_HI; + break; + case RISCVII::MO_OVLPLT_LO: + Kind = RISCVMCExpr::VK_RISCV_OVLPLT_LO; + break; + case RISCVII::MO_OVLPLT_HI: + Kind = RISCVMCExpr::VK_RISCV_OVLPLT_HI; + break; } const MCExpr *ME = Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2529,6 +2529,10 @@ // of the calling conventions is compatible with C calling convention // this call must be unreachable, as the call is undefined. if ((CalleeF->getCallingConv() != Call.getCallingConv() && + !(CalleeF->getCallingConv() == llvm::CallingConv::C && + Call.getFunction()->hasFnAttribute("overlay-call")) && + !(Call.getCallingConv() == llvm::CallingConv::C && + CalleeF->hasFnAttribute("overlay-call")) && !(CalleeF->getCallingConv() == llvm::CallingConv::C && TargetLibraryInfoImpl::isCallingConvCCompatible(&Call)) && !(Call.getCallingConv() == llvm::CallingConv::C && Index: llvm/test/CodeGen/RISCV/ovlcall-fncall.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/ovlcall-fncall.ll @@ -0,0 +1,125 @@ +; RUN: llc -mtriple riscv32-unknown-elf -filetype=obj < %s\ +; RUN: | llvm-objdump -d -r -M no-aliases -M numeric - | FileCheck %s + +; Test for overlay system function call instructions/relocations + +@indirect_foo = dso_local global i32 (...)* bitcast (i32 ()* @foo to i32 (...)*), align 4 +@indirect_bar = dso_local global i32 (...)* bitcast (i32 ()* @bar to i32 (...)*), align 4 + +define dso_local i32 @foo() #0 { +entry: + ret i32 0 +} + +define dso_local i32 @bar() #1 align 4 { +entry: + ret i32 0 +} + +define dso_local i32 @normal_to_normal() #0 { +entry: +; CHECK-LABEL: : +; CHECK: auipc +; (no reloc check because assembler will have resolved this) + %call = call i32 @foo() + ret i32 0 +} + +define dso_local i32 @normal_to_ovl() #0 { +entry: +; CHECK-LABEL: : +; CHECK: lui x30, 0 +; CHECK-NEXT: R_RISCV_OVL_HI20 +; CHECK-NEXT: addi x30, x30 +; CHECK-NEXT: R_RISCV_OVL_LO12_I +; CHECK-NEXT: jalr x1, 0(x31) + %call = call i32 @bar() + ret i32 0 +} + +define dso_local i32 @normal_to_normal_indirect() #0 { +entry: +; CHECK-LABEL: : +; CHECK: lui x[[REG:[0-9]+]], 0 +; CHECK-NEXT: R_RISCV_HI20 indirect_foo +; CHECK-NEXT: lw x[[REG]], 0(x[[REG]]) +; CHECK-NEXT: R_RISCV_LO12_I indirect_foo +; CHECK-NEXT: jalr x1, 0(x[[REG]]) + %0 = load i32 (...)*, i32 (...)** @indirect_foo, align 4 + %callee.knr.cast = bitcast i32 (...)* %0 to i32 ()* + %call = call i32 %callee.knr.cast() + ret i32 0 +} + +define dso_local i32 @normal_to_ovl_indirect() #0 { +entry: +; CHECK-LABEL: : +; CHECK: lui x[[REG:[0-9]+]], 0 +; CHECK-NEXT: R_RISCV_HI20 indirect_bar +; CHECK-NEXT: lw x[[REG]], 0(x[[REG]]) +; CHECK-NEXT: R_RISCV_LO12_I indirect_bar +; CHECK-NEXT: jalr x1, 0(x[[REG]]) + %0 = load i32 (...)*, i32 (...)** @indirect_bar, align 4 + %callee.knr.cast = bitcast i32 (...)* %0 to i32 ()* + %call = call i32 %callee.knr.cast() + ret i32 0 +} + +define dso_local i32 @ovl_to_normal() #1 align 4 { +entry: +; CHECK-LABEL: : +; CHECK: lui x30, 0 +; CHECK-NEXT: R_RISCV_HI20 foo +; CHECK-NEXT: addi x30, x30, 0 +; CHECK-NEXT: R_RISCV_LO12_I foo +; CHECK-NEXT: jalr x1, 0(x31) + %call = call i32 @foo() + ret i32 0 +} + +define dso_local i32 @ovl_to_ovl() #1 align 4 { +entry: +; CHECK-LABEL: : +; CHECK: lui x30, 0 +; CHECK-NEXT: R_RISCV_OVL_HI20 bar +; CHECK-NEXT: addi x30, x30, 0 +; CHECK-NEXT: R_RISCV_OVL_LO12_I bar +; CHECK-NEXT: jalr x1, 0(x31) + %call = call i32 @bar() + ret i32 0 +} + +define dso_local i32 @ovl_to_normal_indirect() #1 align 4 { +entry: +; NOTE: Since x30 is reserved, assumes that the value is materialised then moved +; CHECK-LABEL: : +; CHECK: lui x[[REG:[0-9]+]], 0 +; CHECK-NEXT: R_RISCV_HI20 indirect_foo +; CHECK-NEXT: lw x[[REG]], 0(x[[REG]]) +; CHECK-NEXT: R_RISCV_LO12_I indirect_foo +; CHECK-NEXT: addi x30, x[[REG]], 0 +; CHECK-NEXT: jalr x1, 0(x31) + %0 = load i32 (...)*, i32 (...)** @indirect_foo, align 4 + %callee.knr.cast = bitcast i32 (...)* %0 to i32 ()* + %call = call i32 %callee.knr.cast() + ret i32 0 +} + +define dso_local i32 @ovl_to_ovl_indirect() #1 align 4 { +entry: +; NOTE: Since x30 is reserved, assumes that the value is materialised then moved +; CHECK-LABEL: : +; CHECK: lui x[[REG:[0-9]+]], 0 +; CHECK-NEXT: R_RISCV_HI20 indirect_bar +; CHECK-NEXT: lw x[[REG]], 0(x[[REG]]) +; CHECK-NEXT: R_RISCV_LO12_I indirect_bar +; CHECK-NEXT: addi x30, x[[REG]], 0 +; CHECK-NEXT: jalr x1, 0(x31) + %0 = load i32 (...)*, i32 (...)** @indirect_bar, align 4 + %callee.knr.cast = bitcast i32 (...)* %0 to i32 ()* + %call = call i32 %callee.knr.cast() + ret i32 0 +} + +attributes #0 = { noinline nounwind optnone "target-features"="+reserve-x28,+reserve-x29,+reserve-x30,+reserve-x31,-relax" } +attributes #1 = { noinline nounwind optnone "target-features"="+reserve-x28,+reserve-x29,+reserve-x30,+reserve-x31,-relax" "overlay-call" } Index: llvm/test/CodeGen/RISCV/ovlcall-memcpy.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/ovlcall-memcpy.ll @@ -0,0 +1,23 @@ +; RUN: llc -mtriple riscv32-unknown-elf < %s | FileCheck %s + +; Check that memcpy lowers to an ovlcall@resident call +define void @foo(i8* %a, i8* %b, i32 %c) #0 align 4 { +entry: + %a.addr = alloca i8*, align 4 + %b.addr = alloca i8*, align 4 + %c.addr = alloca i32, align 4 + store i8* %a, i8** %a.addr, align 4 + store i8* %b, i8** %b.addr, align 4 + store i32 %c, i32* %c.addr, align 4 + %0 = load i8*, i8** %a.addr, align 4 + %1 = load i8*, i8** %b.addr, align 4 + %2 = load i32, i32* %c.addr, align 4 + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %0, i8* align 1 %1, i32 %2, i1 false) +; CHECK: ovlcall memcpy@resident + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #1 + +attributes #0 = { noinline nounwind optsize "target-features"="+a,+c,+m,+relax,+reserve-x28,+reserve-x29,+reserve-x30,+reserve-x31" "overlay-call" } +attributes #1 = { argmemonly nounwind willreturn } Index: llvm/test/CodeGen/RISCV/ovlcall-sections.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/ovlcall-sections.ll @@ -0,0 +1,25 @@ +; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s + +; Test for overlay functions being placed into the correct sections + +; foo is an overlay function so should be placed in .ovlinput.foo +define dso_local i32 @foo() #0 { +entry: +; CHECK: .section .ovlinput.foo +; CHECK-NEXT: .globl foo +; CHECK: foo: +; CHECK: .size foo + ret i32 0 +} + +; bar is not an overlay function so should be placed in .text +define dso_local i32 @bar() { +entry: +; CHECK: .text +; CHECK-NEXT: .globl bar +; CHECK: bar: +; CHECK: .size bar + ret i32 1 +} + +attributes #0 = { "overlay-call" } Index: llvm/test/MC/RISCV/option-invalid.s =================================================================== --- llvm/test/MC/RISCV/option-invalid.s +++ llvm/test/MC/RISCV/option-invalid.s @@ -13,7 +13,7 @@ # CHECK: error: unexpected token, expected end of statement .option rvc foo -# CHECK: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or 'norelax' +# CHECK: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax', 'norelax', 'warnreservedreg' or 'nowarnreservedreg' .option bar # CHECK: error: .option pop with no .option push Index: llvm/test/MC/RISCV/ovlcall-pseudos.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/ovlcall-pseudos.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv64 < %s \ +# RUN: | llvm-objdump -M no-aliases -d -r - \ +# RUN: | FileCheck %s + +# CHECK: lui t5, 0 +# CHECK-NEXT: R_RISCV_OVL_HI20 a_symbol +# CHECK-NEXT: addi t5, t5, 0 +# CHECK-NEXT: R_RISCV_OVL_LO12_I a_symbol +# CHECK-NEXT: jalr ra, 0(t6) +ovlcall a_symbol@overlay + +# CHECK: lui t5, 0 +# CHECK-NEXT: R_RISCV_HI20 b_symbol +# CHECK-NEXT: addi t5, t5, 0 +# CHECK-NEXT: R_RISCV_LO12_I b_symbol +# CHECK-NEXT: jalr ra, 0(t6) +ovlcall b_symbol@resident + +# CHECK: addi t5, s0, 0 +# CHECK-NEXT: jalr ra, 0(t6) +ovlcall_indirect s0 Index: llvm/test/MC/RISCV/ovlplt-hi-lo.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/ovlplt-hi-lo.s @@ -0,0 +1,12 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv64 < %s \ +# RUN: | llvm-objdump -M no-aliases -d -r - \ +# RUN: | FileCheck %s + +# CHECK: lui a4, 0 +# CHECK-NEXT: R_RISCV_OVLPLT_HI20 foo +lui a4, %overlay_plthi(foo) + +# CHECK: addi a4, a5, 0 +# CHECK-NEXT: R_RISCV_OVLPLT_LO12_I foo +addi a4, a5, %overlay_pltlo(foo) + Index: llvm/test/Transforms/Inline/RISCV/lit.local.cfg =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/RISCV/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'RISCV' in config.root.targets: + config.unsupported = True Index: llvm/test/Transforms/Inline/RISCV/riscv-overlaycall.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/RISCV/riscv-overlaycall.ll @@ -0,0 +1,19 @@ +; RUN: opt -inline -S %s | FileCheck %s +; RUN: opt -passes='cgscc(inline)' -S %s | FileCheck %s + +define void @f() #0 { +entry: + tail call void @g() + unreachable + +; CHECK-LABEL: @f +; CHECK: call +} + +define void @g() { +entry: +; CHECK-LABEL: @g + unreachable +} + +attributes #0 = { "overlay-call" } Index: llvm/test/Transforms/Inline/RISCV/riscv-overlaycallee.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/RISCV/riscv-overlaycallee.ll @@ -0,0 +1,19 @@ +; RUN: opt -inline -S %s | FileCheck %s +; RUN: opt -passes='cgscc(inline)' -S %s | FileCheck %s + +define void @f() { +entry: + tail call void @g() + unreachable + +; CHECK-LABEL: @f +; CHECK: call +} + +define void @g() #0 { +entry: +; CHECK-LABEL: @g + unreachable +} + +attributes #0 = { "overlay-call" }