Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: import entire method for OSR, prune unneeded parts later #83910

Merged
merged 3 commits into from
Mar 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4420,7 +4420,9 @@ class Compiler
DomTreeNode* fgSsaDomTree;

bool fgBBVarSetsInited;
bool fgOSROriginalEntryBBProtected;

// Track how many artificial ref counts we've added to fgEntryBB (for OSR)
unsigned fgEntryBBExtraRefs;

// Allocate array like T* a = new T[fgBBNumMax + 1];
// Using helper so we don't keep forgetting +1.
Expand Down
12 changes: 6 additions & 6 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ void Compiler::fgInit()

/* Initialize the basic block list */

fgFirstBB = nullptr;
fgLastBB = nullptr;
fgFirstColdBlock = nullptr;
fgEntryBB = nullptr;
fgOSREntryBB = nullptr;
fgOSROriginalEntryBBProtected = false;
fgFirstBB = nullptr;
fgLastBB = nullptr;
fgFirstColdBlock = nullptr;
fgEntryBB = nullptr;
fgOSREntryBB = nullptr;
fgEntryBBExtraRefs = 0;

#if defined(FEATURE_EH_FUNCLETS)
fgFirstFuncletBB = nullptr;
Expand Down
9 changes: 5 additions & 4 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2888,11 +2888,12 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
blockRefs += 1;
}

// Under OSR, if we also are keeping the original method entry around,
// mark that as implicitly referenced as well.
if (opts.IsOSR() && (block == fgEntryBB) && fgOSROriginalEntryBBProtected)
// Under OSR, if we also are keeping the original method entry around
// via artifical ref counts, account for those.
//
if (opts.IsOSR() && (block == fgEntryBB))
{
blockRefs += 1;
blockRefs += fgEntryBBExtraRefs;
}

/* Check the bbRefs */
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12624,6 +12624,17 @@ void Compiler::impImport()
// which we should verify over when we find jump targets.
impImportBlockPending(entryBlock);

if (opts.IsOSR())
{
// We now import all the IR and keep it around so we can
// analyze address exposure more robustly.
//
JITDUMP("OSR: protecting original method entry " FMT_BB "\n", fgEntryBB->bbNum);
impImportBlockPending(fgEntryBB);
fgEntryBB->bbRefs++;
fgEntryBBExtraRefs++;
}

/* Import blocks in the worker-list until there are no more */

while (impPendingList)
Expand Down
28 changes: 0 additions & 28 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1295,34 +1295,6 @@ var_types Compiler::impImportCall(OPCODE opcode,
successor->bbFlags |= BBF_TAILCALL_SUCCESSOR;
optMethodFlags |= OMF_HAS_TAILCALL_SUCCESSOR;
}

// If this call might eventually turn into a loop back to method entry, make sure we
// import the method entry.
//
assert(call->IsCall());
GenTreeCall* const actualCall = call->AsCall();
const bool mustImportEntryBlock = gtIsRecursiveCall(methHnd) || actualCall->IsInlineCandidate() ||
actualCall->IsGuardedDevirtualizationCandidate();

// Only schedule importation if we're not currently importing the entry BB.
//
if (opts.IsOSR() && mustImportEntryBlock && (compCurBB != fgEntryBB))
{
JITDUMP("\nOSR: inlineable or recursive tail call [%06u] in the method, so scheduling " FMT_BB
" for importation\n",
dspTreeID(call), fgEntryBB->bbNum);
impImportBlockPending(fgEntryBB);

if (!fgOSROriginalEntryBBProtected && (fgEntryBB != fgFirstBB))
{
// Protect fgEntryBB from deletion, since it may not have any
// explicit flow references until morph.
//
fgEntryBB->bbRefs += 1;
fgOSROriginalEntryBBProtected = true;
JITDUMP(" also protecting original method entry " FMT_BB "\n", fgEntryBB->bbNum);
}
}
}
}

Expand Down
15 changes: 1 addition & 14 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ void Compiler::lvaInitTypeRef()
}
}

// If this is an OSR method, mark all the OSR locals and model OSR exposure.
// If this is an OSR method, mark all the OSR locals.
//
// Do this before we add the GS Cookie Dummy or Outgoing args to the locals
// so we don't have to do special checks to exclude them.
Expand All @@ -334,19 +334,6 @@ void Compiler::lvaInitTypeRef()
{
LclVarDsc* const varDsc = lvaGetDesc(lclNum);
varDsc->lvIsOSRLocal = true;

if (info.compPatchpointInfo->IsExposed(lclNum))
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to communicate the exposure information in the patchpoint information?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. However, I'll leave this as is for now so older jits can still do somewhat reasonable things with newer SPMI data.

JITDUMP("-- V%02u is OSR exposed\n", lclNum);
varDsc->lvHasLdAddrOp = 1;

// todo: Why does it apply only to non-structs?
//
if (!varTypeIsStruct(varDsc) && !varTypeIsSIMD(varDsc))
{
lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::OSR_EXPOSED));
}
}
}
}

Expand Down
13 changes: 6 additions & 7 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13840,14 +13840,13 @@ void Compiler::fgMorphBlocks()
//
if (opts.IsOSR() && (fgEntryBB != nullptr))
{
if (fgOSROriginalEntryBBProtected)
{
JITDUMP("OSR: un-protecting original method entry " FMT_BB "\n", fgEntryBB->bbNum);
assert(fgEntryBB->bbRefs > 0);
fgEntryBB->bbRefs--;
}
JITDUMP("OSR: un-protecting original method entry " FMT_BB "\n", fgEntryBB->bbNum);
assert(fgEntryBBExtraRefs == 1);
assert(fgEntryBB->bbRefs >= 1);
fgEntryBB->bbRefs--;
fgEntryBBExtraRefs = 0;

// And we don't need to remember this block anymore.
// We don't need to remember this block anymore.
fgEntryBB = nullptr;
}

Expand Down
47 changes: 47 additions & 0 deletions src/tests/JIT/opt/OSR/exposure1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

// Runtime 83738: need to ensure that 's' in 'Foo'
// is marked as address exposed during OSR compiles.

public class Exposure1
{
[MethodImpl(MethodImplOptions.NoInlining)]
static void Bar()
{
}

[MethodImpl(MethodImplOptions.NoInlining)]
static bool Foo(int n)
{
S s = new S { F = 1234 };
ref int foo = ref s.F;

for (int i = 0; i < n; i++)
{
Bar();
}

int abc = s.F * 3 + 4;
foo = 25;
int def = s.F * 3 + 4;

int eabc = 1234 * 3 + 4;
int edef = 25 * 3 + 4;
Console.WriteLine("abc = {0} (expected {1}), def = {2} (expected {3})", abc, eabc, def, edef);
return (abc == eabc && def == edef);
}

public static int Main()
{
return Foo(50000) ? 100 : -1;
}

public struct S
{
public int F;
}
}
13 changes: 13 additions & 0 deletions src/tests/JIT/opt/OSR/exposure1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType />
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_QuickJitForLoops" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_OnStackReplacement" Value="1" />
</ItemGroup>
</Project>
47 changes: 47 additions & 0 deletions src/tests/JIT/opt/OSR/exposure2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

// Runtime 83738: need to ensure that 's' in 'Foo'
// is marked as address exposed during OSR compiles.

public class Exposure2
{
[MethodImpl(MethodImplOptions.NoInlining)]
static void Bar()
{
}

[MethodImpl(MethodImplOptions.NoInlining)]
static bool Foo(int n)
{
S s = new S { F = 1234 };
ref S foo = ref s;

for (int i = 0; i < n; i++)
{
Bar();
}

int abc = s.F * 3 + 4;
foo.F = 25;
int def = s.F * 3 + 4;

int eabc = 1234 * 3 + 4;
int edef = 25 * 3 + 4;
Console.WriteLine("abc = {0} (expected {1}), def = {2} (expected {3})", abc, eabc, def, edef);
return (abc == eabc && def == edef);
}

public static int Main()
{
return Foo(50000) ? 100 : -1;
}

public struct S
{
public int F;
}
}
13 changes: 13 additions & 0 deletions src/tests/JIT/opt/OSR/exposure2.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType />
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_QuickJitForLoops" Value="1" />
<CLRTestEnvironmentVariable Include="DOTNET_TC_OnStackReplacement" Value="1" />
</ItemGroup>
</Project>