Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -916,7 +916,8 @@ return false; // Parse first operand - if (parseOperand(Operands, Name == "call")) + bool ForceImmediate = (Name == "call" || Name == "tailcall"); + if (parseOperand(Operands, ForceImmediate)) return true; // Parse until end of statement, consuming commas between operands Index: lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -97,9 +97,31 @@ const MCSubtargetInfo &STI) const { MCInst TmpInst; MCOperand Func = MI.getOperand(0); - unsigned Ra = RISCV::X1; uint32_t Binary; + // For tail call to an address in a register. + // Emit JALR X0, 0(Reg). + // TODO: We had to custom lower this here since for tail calls we cannot + // allow JALR with any GPR; we need to restrict the register class to only + // caller-saved regs [x5-x7]. Hence we had to create a new regclass GPRTC + // which is a subclass of GPR. However, currently tablegen does not support + // the use of the registers of a subclass of the allowed regclass for an + // instruction. Once this is supported, we can replace the custom lowering + // here with a PseudoInstExpansion rule in RISCVInstrInfo.td. + if (MI.getOpcode() == RISCV::PseudoTCALLri) { + TmpInst = MCInstBuilder(RISCV::JALR) + .addReg(RISCV::X0) + .addReg(Func.getReg()) + .addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::Writer(OS).write(Binary); + return; + } + + unsigned Ra = MI.getOpcode() == RISCV::PseudoTCALLdi ? + RISCV::X6 : + RISCV::X1; + assert(Func.isExpr() && "Expected expression"); const MCExpr *Expr = Func.getExpr(); @@ -128,9 +150,11 @@ // Get byte count of instruction. unsigned Size = Desc.getSize(); - if (MI.getOpcode() == RISCV::PseudoCALL) { + if (MI.getOpcode() == RISCV::PseudoCALL || + MI.getOpcode() == RISCV::PseudoTCALLdi || + MI.getOpcode() == RISCV::PseudoTCALLri) { expandFunctionCall(MI, OS, Fixups, STI); - MCNumEmitted += 2; + MCNumEmitted += (MI.getOpcode() == RISCV::PseudoTCALLri) ? 1 : 2; return; } Index: lib/Target/RISCV/RISCVISelLowering.h =================================================================== --- lib/Target/RISCV/RISCVISelLowering.h +++ lib/Target/RISCV/RISCVISelLowering.h @@ -28,7 +28,8 @@ CALL, SELECT_CC, BuildPairF64, - SplitF64 + SplitF64, + TAILCALL }; } @@ -91,6 +92,11 @@ SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + + bool isEligibleForTailCallOptimization(SDValue Callee, + CallingConv::ID CalleeCC, bool IsVarArg, + bool IsCalleeStructRet, MachineFunction &MF, + const SmallVectorImpl &Outs) const; }; } Index: lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVISelLowering.cpp +++ lib/Target/RISCV/RISCVISelLowering.cpp @@ -18,6 +18,7 @@ #include "RISCVRegisterInfo.h" #include "RISCVSubtarget.h" #include "RISCVTargetMachine.h" +#include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -36,6 +37,8 @@ #define DEBUG_TYPE "riscv-lower" +STATISTIC(NumTailCalls, "Number of tail calls"); + RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, const RISCVSubtarget &STI) : TargetLowering(TM), Subtarget(STI) { @@ -1003,6 +1006,72 @@ return Chain; } +/// IsEligibleForTailCallOptimization - Check whether the call is eligible +/// for tail call optimization. +/// Note: This is modelled after ARM's logic for +// IsEligibleForTailCallOptimization. +bool RISCVTargetLowering::isEligibleForTailCallOptimization( + SDValue Callee, CallingConv::ID CalleeCC, bool IsVarArg, + bool IsCalleeStructRet, MachineFunction &MF, + const SmallVectorImpl &Outs) const { + + const Function &CallerF = MF.getFunction(); + CallingConv::ID CallerCC = CallerF.getCallingConv(); + + // If tail calls are disabled for this function. + if (CallerF.getFnAttribute("disable-tail-calls").getValueAsString() == "true") + return false; + + // Do not do tail call optimization for functions with varargs. + if (IsVarArg) + return false; + + // RISCV has 8 function argument registers: [x10-x11], [x12-x17]. + // If a function requires more argument registers we cannot tail call + // optimize it. + if (Outs.size() >= 8) + return false; + + // Exception-handling functions need a special set of instructions to + // indicate a return to the hardware. Tail-calling another function would + // probably break this. + if (CallerF.hasFnAttribute("interrupt")) + return false; + + // Also avoid tailcall optimization if either caller or callee uses struct + // return semantics. + if (IsCalleeStructRet || CallerF.hasStructRetAttr()) + return false; + + // Externally-defined functions with weak linkage should not be + // tail-called. The behaviour of branch instructions in this situation (as + // used for tail calls) is implementation-defined, so we cannot rely on the + // linker replacing the tail call with a return. + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + const GlobalValue *GV = G->getGlobal(); + if (GV->hasExternalWeakLinkage()) + return false; + } + + // The callee has to preserve all registers the caller needs to preserve. + const RISCVRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC); + if (CalleeCC != CallerCC) { + const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC); + if (!TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved)) + return false; + } + + // Byval parameters hand the function a pointer directly into the stack area + // we want to reuse during a tail call. Working around this *is* possible + // but less efficient and uglier in LowerCall. + for (auto &Arg : Outs) + if (Arg.Flags.isByVal()) + return false; + + return true; +} + // Lower a call to a callseq_start + CALL + callseq_end chain, and add input // and output parameter nodes. SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, @@ -1014,11 +1083,12 @@ SmallVectorImpl &Ins = CLI.Ins; SDValue Chain = CLI.Chain; SDValue Callee = CLI.Callee; - CLI.IsTailCall = false; + bool &IsTailCall = CLI.IsTailCall; CallingConv::ID CallConv = CLI.CallConv; bool IsVarArg = CLI.IsVarArg; EVT PtrVT = getPointerTy(DAG.getDataLayout()); MVT XLenVT = Subtarget.getXLenVT(); + bool IsStructRet = Outs.empty() ? false : Outs[0].Flags.isSRet(); MachineFunction &MF = DAG.getMachineFunction(); @@ -1027,9 +1097,24 @@ CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false, &CLI); + // Check if it's really possible to do a tail call. + if (IsTailCall) + IsTailCall = isEligibleForTailCallOptimization(Callee, CallConv, IsVarArg, + IsStructRet, MF, Outs); + + if (!IsTailCall && CLI.CS && CLI.CS.isMustTailCall()) + report_fatal_error("failed to perform tail call elimination on a call " + "site marked musttail"); + // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = ArgCCInfo.getNextStackOffset(); + if (IsTailCall) { + // For tail calls, memory operands are available in our caller's stack. + NumBytes = 0; + ++NumTailCalls; + } + // Create local copies for byval args SmallVector ByValArgs; for (unsigned i = 0, e = Outs.size(); i != e; ++i) { @@ -1048,17 +1133,20 @@ Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Align, /*IsVolatile=*/false, /*AlwaysInline=*/false, - /*isTailCall=*/false, MachinePointerInfo(), + IsTailCall, MachinePointerInfo(), MachinePointerInfo()); ByValArgs.push_back(FIPtr); } - Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + if (!IsTailCall) + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + + SDValue StackPtr = DAG.getCopyFromReg(Chain, DL, RISCV::X2, + getPointerTy(DAG.getDataLayout())); // Copy argument values to their designated locations. SmallVector, 8> RegsToPass; SmallVector MemOpChains; - SDValue StackPtr; for (unsigned i = 0, j = 0, e = ArgLocs.size(); i != e; ++i) { CCValAssign &VA = ArgLocs[i]; SDValue ArgValue = OutVals[i]; @@ -1144,13 +1232,23 @@ // Work out the address of the stack slot. if (!StackPtr.getNode()) StackPtr = DAG.getCopyFromReg(Chain, DL, RISCV::X2, PtrVT); - SDValue Address = - DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, - DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); - // Emit the store. - MemOpChains.push_back( - DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); + if (!IsTailCall) { + SDValue Address = + DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); + + // Emit the store. + MemOpChains.push_back( + DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); + } else { + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + int FI = MFI.CreateFixedObject(ArgValue.getValueSizeInBits() / 8, + VA.getLocMemOffset(), false); + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + return DAG.getStore(Chain, DL, ArgValue, FIN, MachinePointerInfo(), + /* Alignment = */ 0, MachineMemOperand::MOVolatile); + } } } @@ -1160,12 +1258,27 @@ SDValue Glue; + // Tail call byval lowering might overwrite argument registers so in case of + // tail call optimization the copies to registers are lowered to 'real' stack + // slot. + // Force all the incoming stack arguments to be loaded from the stack + // before any new outgoing arguments are stored to the stack, because the + // outgoing stack slots may alias the incoming argument stack slots, and + // the alias isn't otherwise explicit. This is slightly more conservative + // than necessary, because it means that each store effectively depends + // on every argument instead of just those arguments it would clobber. + if (IsTailCall) + Glue = SDValue(); + // Build a sequence of copy-to-reg nodes, chained and glued together. for (auto &Reg : RegsToPass) { Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, Glue); Glue = Chain.getValue(1); } + if (IsTailCall) + Glue = SDValue(); + // 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. @@ -1185,11 +1298,13 @@ for (auto &Reg : RegsToPass) Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); - // 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)); + if (!IsTailCall) { + // 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)); + } // Glue the call to the argument copies, if any. if (Glue.getNode()) @@ -1197,6 +1312,12 @@ // Emit the call. SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + + if (IsTailCall) { + MF.getFrameInfo().setHasTailCall(); + return DAG.getNode(RISCVISD::TAILCALL, DL, NodeTys, Ops); + } + Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); Glue = Chain.getValue(1); @@ -1352,6 +1473,8 @@ return "RISCVISD::BuildPairF64"; case RISCVISD::SplitF64: return "RISCVISD::SplitF64"; + case RISCVISD::TAILCALL: + return "RISCVISD::TAILCALL"; } return nullptr; } Index: lib/Target/RISCV/RISCVInstrInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.cpp +++ lib/Target/RISCV/RISCVInstrInfo.cpp @@ -387,7 +387,10 @@ case TargetOpcode::KILL: case TargetOpcode::DBG_VALUE: return 0; + case RISCV::PseudoTCALLri: + return 4; case RISCV::PseudoCALL: + case RISCV::PseudoTCALLdi: return 8; case TargetOpcode::INLINEASM: { const MachineFunction &MF = *MI.getParent()->getParent(); Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -38,6 +38,9 @@ [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; def SelectCC : SDNode<"RISCVISD::SELECT_CC", SDT_RISCVSelectCC, [SDNPInGlue]>; +def TailCall : SDNode<"RISCVISD::TAILCALL", SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. @@ -657,6 +660,24 @@ def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>, PseudoInstExpansion<(JALR X0, X1, 0)>; +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, + isCodeGenOnly = 0, Uses = [X2] in { +def PseudoTCALLdi : Pseudo<(outs), (ins bare_symbol:$dst), []> { + let AsmString = "tailcall\t$dst"; +} + +def PseudoTCALLri : Pseudo<(outs), (ins GPRTC:$dst), []> { + let AsmString = "tailcall\t$dst"; +} +} + +def : Pat<(TailCall (iPTR tglobaladdr:$dst)), + (PseudoTCALLdi texternalsym:$dst)>; +def : Pat<(TailCall (iPTR texternalsym:$dst)), + (PseudoTCALLdi texternalsym:$dst)>; +def : Pat<(TailCall GPRTC:$dst), + (PseudoTCALLri GPRTC:$dst)>; + /// Loads multiclass LdPat { Index: lib/Target/RISCV/RISCVRegisterInfo.td =================================================================== --- lib/Target/RISCV/RISCVRegisterInfo.td +++ lib/Target/RISCV/RISCVRegisterInfo.td @@ -128,6 +128,16 @@ [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; } +// For tail calls, we can't use callee-saved registers, as they are restored to +// the saved value before the tail call, which would clobber a call address. +def GPRTC : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 5, 7) + )> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; +} + def SP : RegisterClass<"RISCV", [XLenVT], 32, (add X2)> { let RegInfos = RegInfoByHwMode< [RV32, RV64, DefaultMode], Index: test/CodeGen/RISCV/disable-tail-calls.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/disable-tail-calls.ll @@ -0,0 +1,57 @@ +; RUN: llc < %s -mtriple=riscv32-unknown-linux-gnu \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv32-unknown-linux-gnu -disable-tail-calls \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv32-unknown-linux-gnu -disable-tail-calls=false \ +; RUN: | FileCheck %s --check-prefix=TC + +; RUN: llc < %s -mtriple=riscv32-unknown-elf \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv32-unknown-elf -disable-tail-calls \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv32-unknown-elf -disable-tail-calls=false \ +; RUN: | FileCheck %s --check-prefix=TC + +; RUN: llc < %s -mtriple=riscv64-unknown-linux-gnu \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv64-unknown-linux-gnu -disable-tail-calls \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv64-unknown-linux-gnu -disable-tail-calls=false \ +; RUN: | FileCheck %s --check-prefix=TC + +; RUN: llc < %s -mtriple=riscv64-unknown-elf \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv64-unknown-elf -disable-tail-calls \ +; RUN: | FileCheck %s --check-prefix=NOTC +; RUN: llc < %s -mtriple=riscv64-unknown-elf -disable-tail-calls=false \ +; RUN: | FileCheck %s --check-prefix=TC + +; Check that command line option "-disable-tail-calls" overrides function +; attribute "disable-tail-calls". + +; TC-LABEL: {{\_?}}func_attr +; TC-LABEL: {{\_?}}func_noattr +; TC: tailcall callee +; TC-NOT: call callee + +; NOTC-LABEL: {{\_?}}func_attr +; NOTC-LABEL: {{\_?}}func_noattr +; NOTC-NOT: tailcall callee +; NOTC: call callee + +; Function with attribute #0 = { "disable-tail-calls"="true" } +define i32 @func_attr(i32 %a) #0 { +entry: + %call = tail call i32 @callee(i32 %a) + ret i32 %call +} + +define i32 @func_noattr(i32 %a) { +entry: + %call = tail call i32 @callee(i32 %a) + ret i32 %call +} + +declare i32 @callee(i32) + +attributes #0 = { "disable-tail-calls"="true" } Index: test/CodeGen/RISCV/tail-calls.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/tail-calls.ll @@ -0,0 +1,79 @@ +; RUN: llc -mtriple riscv32-unknown-linux-gnu -o - < %s | FileCheck %s +; RUN: llc -mtriple riscv32-unknown-elf -o - < %s | FileCheck %s +; RUN: llc -mtriple riscv64-unknown-linux-gnu -o - < %s | FileCheck %s +; RUN: llc -mtriple riscv64-unknown-elf -o - < %s | FileCheck %s + +; Perform tail call optimization. +declare i32 @callee(i32 %i) +define i32 @caller(i32 %i) { +; CHECK-LABEL: caller +; CHECK: tailcall callee +entry: + %r = tail call i32 @callee(i32 %i) + ret i32 %r +} + +; Do not do tail call optimization for functions with varargs. +declare i32 @variadic(i32, ...) +define void @v_caller_ints1(i32 %a, i32 %b) { +; CHECK-LABEL: v_caller_ints1: +; CHECK-NOT: tailcall variadic +; CHECK: call variadic +entry: + %call = tail call i32 (i32, ...) @variadic(i32 %a, i32 %b, i32 %b, i32 %a) + ret void +} + +; RISCV has 8 function argument registers: [x10-x11], [x12-x17]. +; If a function requires more argument registers we cannot tail call optimize it. +declare i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i) +define i32 @caller_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i) { +; CHECK-LABEL: caller_args +; CHECK-NOT: tailcall callee_args +; CHECK: call callee_args +entry: + %r = tail call i32 @callee_args(i32 %a, i32 %b, i32 %c, i32 %dd, i32 %e, i32 %ff, i32 %g, i32 %h, i32 %i) + ret i32 %r +} + +; Externally-defined functions with weak linkage should not be tail-called. +; The behaviour of branch instructions in this situation (as used for tail +; calls) is implementation-defined, so we cannot rely on the linker replacing +; the tail call with a return. +declare extern_weak void @callee_weak() +define void @caller_weak() { +; CHECK-LABEL: caller_weak: +; CHECK-NOT: tailcall callee_weak +; CHECK: call callee_weak +entry: + tail call void @callee_weak() + ret void +} + +; Exception-handling functions need a special set of instructions to indicate a +; return to the hardware. Tail-calling another function would probably break +; this. +declare void @callee_irq() +define void @caller_irq() #0 { +; CHECK-LABEL: caller_irq: +; CHECK-NOT: tailcall callee_irq +; CHECK: call callee_irq +entry: + tail call void @callee_irq() + ret void +} +attributes #0 = { "interrupt" } + +; Byval parameters hand the function a pointer directly into the stack area +; we want to reuse during a tail call. Do not tail call optimize functions with +; byval parameters. +declare i32 @callee_byval(i32** byval %a) +define i32 @caller_byval() { +; CHECK-LABEL: caller_byval: +; CHECK-NOT: tailcall callee_byval +; CHECK: call callee_byval +entry: + %a = alloca i32* + %r = tail call i32 @callee_byval(i32** byval %a) + ret i32 %r +} Index: test/MC/RISCV/tail-call-invalid.s =================================================================== --- /dev/null +++ test/MC/RISCV/tail-call-invalid.s @@ -0,0 +1,11 @@ +# RUN: not llvm-mc -triple riscv32 < %s 2>&1 | FileCheck %s + +tailcall 1234 # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %pcrel_hi(1234) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %pcrel_lo(1234) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %pcrel_hi(foo) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %pcrel_lo(foo) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %hi(1234) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %lo(1234) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %hi(foo) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name +tailcall %lo(foo) # CHECK: :[[@LINE]]:10: error: operand must be a bare symbol name Index: test/MC/RISCV/tail-call.s =================================================================== --- /dev/null +++ test/MC/RISCV/tail-call.s @@ -0,0 +1,40 @@ +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=INSTR %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s +# RUN: llvm-mc -triple riscv32 < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=FIXUP %s + +.long foo + +tailcall foo +# RELOC: R_RISCV_CALL foo 0x0 +# INSTR: auipc t1, 0 +# INSTR: jalr t1 +# FIXUP: fixup A - offset: 0, value: foo, kind: +tailcall bar +# RELOC: R_RISCV_CALL bar 0x0 +# INSTR: auipc t1, 0 +# INSTR: jalr t1 +# FIXUP: fixup A - offset: 0, value: bar, kind: + +# Ensure that tail calls to functions whose names coincide with register names +# work. + +tailcall zero +# RELOC: R_RISCV_CALL zero 0x0 +# INSTR: auipc t1, 0 +# INSTR: jalr t1 +# FIXUP: fixup A - offset: 0, value: zero, kind: + +tailcall f1 +# RELOC: R_RISCV_CALL f1 0x0 +# INSTR: auipc t1, 0 +# INSTR: jalr t1 +# FIXUP: fixup A - offset: 0, value: f1, kind: + +tailcall ra +# RELOC: R_RISCV_CALL ra 0x0 +# INSTR: auipc t1, 0 +# INSTR: jalr t1 +# FIXUP: fixup A - offset: 0, value: ra, kind: