Skip to content

Commit

Permalink
JIT: don't inline methods with small stackallocs if the call site is … (
Browse files Browse the repository at this point in the history
#43516)

The logic in `fgInlinePrependStatements` that zero-initializes locals doesn't
kick in for jit temps introduced when small stackallocs are optimized. So if we
inline a method with a small stackalloc into a loop, the memory for the
stackalloc doesn't get properly re-zeroed on each iteration.

Fix by disallowing such inlines by adding an extra check: the call site must
not be in a loop.

Closes #43391.
  • Loading branch information
AndyAyersMS authored Oct 19, 2020
1 parent 466e4b7 commit f8fb84c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4182,6 +4182,7 @@ class Compiler

BasicBlock* impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_HANDLE clsHnd, bool isSingleBlockFilter);

bool impBlockIsInALoop(BasicBlock* block);
void impImportBlockCode(BasicBlock* block);

void impReimportMarkBlock(BasicBlock* block);
Expand Down
27 changes: 23 additions & 4 deletions src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10824,6 +10824,25 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
} while (0)
#endif // DEBUG

//------------------------------------------------------------------------
// impBlockIsInALoop: check if a block might be in a loop
//
// Arguments:
// block - block to check
//
// Returns:
// true if the block might be in a loop.
//
// Notes:
// Conservatively correct; may return true for some blocks that are
// not actually in loops.
//
bool Compiler::impBlockIsInALoop(BasicBlock* block)
{
return (compIsForInlining() && ((impInlineInfo->iciBlock->bbFlags & BBF_BACKWARD_JUMP) != 0)) ||
((block->bbFlags & BBF_BACKWARD_JUMP) != 0);
}

#ifdef _PREFAST_
#pragma warning(push)
#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
Expand Down Expand Up @@ -14031,9 +14050,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
lvaSetStruct(lclNum, resolvedToken.hClass, true /* unsafe value cls check */);
}

bool bbInALoop =
(compIsForInlining() && ((impInlineInfo->iciBlock->bbFlags & BBF_BACKWARD_JUMP) != 0)) ||
((block->bbFlags & BBF_BACKWARD_JUMP) != 0);
bool bbInALoop = impBlockIsInALoop(block);
bool bbIsReturn = (block->bbJumpKind == BBJ_RETURN) &&
(!compIsForInlining() || (impInlineInfo->iciBlock->bbJumpKind == BBJ_RETURN));
LclVarDsc* const lclDsc = lvaGetDesc(lclNum);
Expand Down Expand Up @@ -15146,14 +15163,16 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
const ssize_t allocSize = op2->AsIntCon()->IconValue();

bool bbInALoop = impBlockIsInALoop(block);

if (allocSize == 0)
{
// Result is nullptr
JITDUMP("Converting stackalloc of 0 bytes to push null unmanaged pointer\n");
op1 = gtNewIconNode(0, TYP_I_IMPL);
convertedToLocal = true;
}
else if ((allocSize > 0) && ((compCurBB->bbFlags & BBF_BACKWARD_JUMP) == 0))
else if ((allocSize > 0) && !bbInALoop)
{
// Get the size threshold for local conversion
ssize_t maxSize = DEFAULT_MAX_LOCALLOC_TO_LOCAL_SIZE;
Expand Down
26 changes: 26 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_43391/Runtime_43391.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Block inlining of small localloc callee if call site is in a loop.

using System;

class Runtime_43391
{
static int Main()
{
int r = 58;
for (int i = 1; i >= 0; i--)
{
r += Test(i);
}
return r;
}

public static unsafe byte Test(int i)
{
byte* p = stackalloc byte[8];
p[i] = 42;
return p[1];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<CLRTestPriority>0</CLRTestPriority>
</PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="Runtime_43391.cs" />
</ItemGroup>
</Project>

0 comments on commit f8fb84c

Please sign in to comment.