regNumber getCallIndirectionCellReg(GenTreeCall* call);
void genCall(GenTreeCall* call);
void genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackArgBytes));
+ void genDefinePendingCallLabel(GenTreeCall* call);
void genJmpMethod(GenTree* jmp);
BasicBlock* genCallFinally(BasicBlock* block);
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
genCallInstruction(call);
- // for pinvoke/intrinsic/tailcalls we may have needed to get the address of
- // a label. In case it is indirect with CFG enabled make sure we do not get
- // the address after the validation but only after the actual call that
- // comes after.
- if (genPendingCallLabel && !call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL))
- {
- genDefineInlineTempLabel(genPendingCallLabel);
- genPendingCallLabel = nullptr;
- }
+ genDefinePendingCallLabel(call);
#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
return result;
}
+//------------------------------------------------------------------------
+// genDefinePendingLabel - If necessary, define the pending call label after a
+// call instruction was emitted.
+//
+// Arguments:
+// call - the call node
+//
+void CodeGen::genDefinePendingCallLabel(GenTreeCall* call)
+{
+ // for pinvoke/intrinsic/tailcalls we may have needed to get the address of
+ // a label.
+ if (!genPendingCallLabel)
+ {
+ return;
+ }
+
+ // For certain indirect calls we may introduce helper calls before that we need to skip:
+ // - CFG may introduce a call to the validator first
+ // - Generic virtual methods may compute the target dynamically through a separate helper call
+ if (call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL) ||
+ call->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR))
+ {
+ return;
+ }
+
+ genDefineInlineTempLabel(genPendingCallLabel);
+ genPendingCallLabel = nullptr;
+}
+
/*****************************************************************************
*
* Generates code for all the function and funclet prologs and epilogs.
genCallInstruction(call);
- // for pinvoke/intrinsic/tailcalls we may have needed to get the address of
- // a label. In case it is indirect with CFG enabled make sure we do not get
- // the address after the validation but only after the actual call that
- // comes after.
- if (genPendingCallLabel && !call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL))
- {
- genDefineInlineTempLabel(genPendingCallLabel);
- genPendingCallLabel = nullptr;
- }
+ genDefinePendingCallLabel(call);
#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
genCallInstruction(call);
- // for pinvoke/intrinsic/tailcalls we may have needed to get the address of
- // a label. In case it is indirect with CFG enabled make sure we do not get
- // the address after the validation but only after the actual call that
- // comes after.
- if (genPendingCallLabel && !call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL))
- {
- genDefineInlineTempLabel(genPendingCallLabel);
- genPendingCallLabel = nullptr;
- }
+ genDefinePendingCallLabel(call);
#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
genCallInstruction(call X86_ARG(stackArgBytes));
- // for pinvoke/intrinsic/tailcalls we may have needed to get the address of
- // a label. In case it is indirect with CFG enabled make sure we do not get
- // the address after the validation but only after the actual call that
- // comes after.
- if (genPendingCallLabel && !call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL))
- {
- genDefineInlineTempLabel(genPendingCallLabel);
- genPendingCallLabel = nullptr;
- }
+ genDefinePendingCallLabel(call);
#ifdef DEBUG
// We should not have GC pointers in killed registers live around the call.
LOG((LF_STUBS, LL_INFO1000, "TAILCALLHELP: Incoming sig %s\n", incSig.GetCString()));
#endif
- *storeArgsNeedsTarget = pCalleeMD == NULL || pCalleeMD->IsSharedByGenericInstantiations();
+ // GVMs are not strictly "needs target" for the unshared case, but the JIT
+ // is going to resolve the target anyway so we may as well take it in the
+ // stub to avoid computing it in both places.
+ *storeArgsNeedsTarget = pCalleeMD == NULL || pCalleeMD->IsSharedByGenericInstantiations() ||
+ (pCalleeMD->IsVirtual() && !pCalleeMD->IsStatic() && pCalleeMD->HasMethodInstantiation());
// The tailcall helper stubs are always allocated together with the caller.
// If we ever wish to share these stubs they should be allocated with the
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Runtime_87393
+
+open System.Runtime.CompilerServices
+
+[<AbstractClass>]
+type Foo() =
+ abstract M<'a> : 'a -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int -> int
+
+type Bar() as this =
+ inherit Foo()
+
+ [<DefaultValue>]
+ static val mutable private _f : Foo
+
+ do
+ Bar._f <- this
+
+ override this.M<'a> (a0 : 'a) num acc _ _ _ _ _ _ _ _ _ _ _ _ _ =
+ if num <= 0 then
+ acc
+ else
+ Bar.M2 a0 (num - 1) (acc + num) 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+ [<MethodImpl(MethodImplOptions.NoInlining)>]
+ static member M2 (a0 : 'a) (num : int) (acc : int) a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 =
+ Bar._f.M a0 num acc a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
+
+module Main =
+
+ [<EntryPoint>]
+ let main _argv =
+ let f : Foo = Bar()
+ let v = f.M 0 65000 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ if v = 2112532500 then
+ printfn "PASS"
+ 100
+ else
+ printfn "FAIL: Result was %A" v
+ -1
+
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <!-- Needed for GCStressIncompatible -->
+ <RequiresProcessIsolation>true</RequiresProcessIsolation>
+ <NoStandardLib>True</NoStandardLib>
+ <Noconfig>True</Noconfig>
+ <Optimize>True</Optimize>
+ <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
+ <GCStressIncompatible>True</GCStressIncompatible>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildProjectName).fs" />
+ </ItemGroup>
+</Project>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/tailcall/more_tailcalls/*">
<Issue>https://github.com/dotnet/runtimelab/issues/155: Tailcalls</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/Runtime_87393/Runtime_87393/*">
+ <Issue>https://github.com/dotnet/runtimelab/issues/155: Tailcalls</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/throwbox/fault_throwbox/*">
<Issue>https://github.com/dotnet/runtimelab/issues/155: Non-exception throws</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/tailcall/more_tailcalls/**">
<Issue>needs triage</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/Runtime_87393/**">
+ <Issue>FSharp Test</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/tailcall/mutual_recursion/**">
<Issue>FSharp Test</Issue>
</ExcludeList>