summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Rozenfeld <erozen@microsoft.com>2018-11-09 16:53:49 -0800
committerEugene Rozenfeld <erozen@microsoft.com>2018-11-13 19:05:42 -0800
commit73f84776b9db118f855672724388cb1def3f2906 (patch)
treec8a543d0b9f2a496934a64290f1a2b4567e8ece4
parent2e7d443bb9b43b39b59da8d7c66b206a6c1f2ee6 (diff)
downloadcoreclr-73f84776b9db118f855672724388cb1def3f2906.tar.gz
coreclr-73f84776b9db118f855672724388cb1def3f2906.tar.bz2
coreclr-73f84776b9db118f855672724388cb1def3f2906.zip
Fix for bug 20499.
When the jit is copying a struct-typed return to the return local in a synchronous method on arm64, it ends up invoking an importer utility outside the importer, where impTreeLast is not set. The call sequence is fgMorphBlocks --> gtNewTempAssign --> impAssignStruct --> impAssignStructPtr --> impAppendTree When impAssignStructPtr sees GT_COMMA src nodes, it unwraps them and inserts additional statements. The fix is to pass an insertion point statement through this call chain to prevent impAssignStruct and impAssignStructPtr from calling impAppendTree outside of the importer.
-rw-r--r--src/jit/compiler.h8
-rw-r--r--src/jit/gentree.cpp13
-rw-r--r--src/jit/importer.cpp76
-rw-r--r--src/jit/morph.cpp20
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.cs52
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.csproj33
6 files changed, 175 insertions, 27 deletions
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index ad555b812f..3754ae375e 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -2438,7 +2438,11 @@ public:
GenTree* gtNewAssignNode(GenTree* dst, GenTree* src);
- GenTree* gtNewTempAssign(unsigned tmp, GenTree* val);
+ GenTree* gtNewTempAssign(unsigned tmp,
+ GenTree* val,
+ GenTree** pAfterStmt = nullptr,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ BasicBlock* block = nullptr);
GenTree* gtNewRefCOMfield(GenTree* objPtr,
CORINFO_RESOLVED_TOKEN* pResolvedToken,
@@ -3525,12 +3529,14 @@ public:
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
GenTree** pAfterStmt = nullptr,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
BasicBlock* block = nullptr);
GenTree* impAssignStructPtr(GenTree* dest,
GenTree* src,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
GenTree** pAfterStmt = nullptr,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
BasicBlock* block = nullptr);
GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref);
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index b685bf2479..f69ec59546 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -14235,8 +14235,11 @@ DONE:
// gtNewTempAssign: Create an assignment of the given value to a temp.
//
// Arguments:
-// tmp - local number for a compiler temp
-// val - value to assign to the temp
+// tmp - local number for a compiler temp
+// val - value to assign to the temp
+// pAfterStmt - statement to insert any additional statements after
+// ilOffset - il offset for new statements
+// block - block to insert any additional statements in
//
// Return Value:
// Normally a new assignment node.
@@ -14248,9 +14251,9 @@ DONE:
// May update the type of the temp, if it was previously unknown.
//
// May set compFloatingPointUsed.
-//
-GenTree* Compiler::gtNewTempAssign(unsigned tmp, GenTree* val)
+GenTree* Compiler::gtNewTempAssign(
+ unsigned tmp, GenTree* val, GenTree** pAfterStmt, IL_OFFSETX ilOffset, BasicBlock* block)
{
// Self-assignment is a nop.
if (val->OperGet() == GT_LCL_VAR && val->gtLclVarCommon.gtLclNum == tmp)
@@ -14346,7 +14349,7 @@ GenTree* Compiler::gtNewTempAssign(unsigned tmp, GenTree* val)
}
dest->gtFlags |= GTF_DONT_CSE;
valx->gtFlags |= GTF_DONT_CSE;
- asg = impAssignStruct(dest, val, structHnd, (unsigned)CHECK_SPILL_NONE);
+ asg = impAssignStruct(dest, val, structHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, ilOffset, block);
}
else
{
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 68071e451a..26aafb72b2 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -785,7 +785,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum,
val->gtType = lvaTable[tmpNum].lvType;
GenTree* dst = gtNewLclvNode(tmpNum, val->gtType);
- asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, block);
+ asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, ilOffset, block);
}
else
{
@@ -1013,24 +1013,40 @@ GenTreeArgList* Compiler::impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, u
}
}
-/*****************************************************************************
- Assign (copy) the structure from 'src' to 'dest'. The structure is a value
- class of type 'clsHnd'. It returns the tree that should be appended to the
- statement list that represents the assignment.
- Temp assignments may be appended to impTreeList if spilling is necessary.
- curLevel is the stack level for which a spill may be being done.
- */
+//------------------------------------------------------------------------
+// impAssignStruct: Assign (copy) the structure from 'src' to 'dest'.
+//
+// Arguments:
+// dest - destination of the assignment
+// src - source of the assignment
+// structHnd - handle representing the struct type
+// curLevel - stack level for which a spill may be being done
+// pAfterStmt - statement to insert any additional statements after
+// ilOffset - il offset for new statements
+// block - block to insert any additional statements in
+//
+// Return Value:
+// The tree that should be appended to the statement list that represents the assignment.
+//
+// Notes:
+// Temp assignments may be appended to impTreeList if spilling is necessary.
GenTree* Compiler::impAssignStruct(GenTree* dest,
GenTree* src,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
- GenTree** pAfterStmt, /* = NULL */
- BasicBlock* block /* = NULL */
+ GenTree** pAfterStmt, /* = nullptr */
+ IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
+ BasicBlock* block /* = nullptr */
)
{
assert(varTypeIsStruct(dest));
+ if (ilOffset == BAD_IL_OFFSET)
+ {
+ ilOffset = impCurStmtOffs;
+ }
+
while (dest->gtOper == GT_COMMA)
{
assert(varTypeIsStruct(dest->gtOp.gtOp2)); // Second thing is the struct
@@ -1038,11 +1054,11 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
// Append all the op1 of GT_COMMA trees before we evaluate op2 of the GT_COMMA tree.
if (pAfterStmt)
{
- *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(dest->gtOp.gtOp1, impCurStmtOffs));
+ *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(dest->gtOp.gtOp1, ilOffset));
}
else
{
- impAppendTree(dest->gtOp.gtOp1, curLevel, impCurStmtOffs); // do the side effect
+ impAppendTree(dest->gtOp.gtOp1, curLevel, ilOffset); // do the side effect
}
// set dest to the second thing
@@ -1072,16 +1088,33 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest);
}
- return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, block));
+ return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, ilOffset, block));
}
-/*****************************************************************************/
+//------------------------------------------------------------------------
+// impAssignStructPtr: Assign (copy) the structure from 'src' to 'destAddr'.
+//
+// Arguments:
+// destAddr - address of the destination of the assignment
+// src - source of the assignment
+// structHnd - handle representing the struct type
+// curLevel - stack level for which a spill may be being done
+// pAfterStmt - statement to insert any additional statements after
+// ilOffset - il offset for new statements
+// block - block to insert any additional statements in
+//
+// Return Value:
+// The tree that should be appended to the statement list that represents the assignment.
+//
+// Notes:
+// Temp assignments may be appended to impTreeList if spilling is necessary.
GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
GenTree* src,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
GenTree** pAfterStmt, /* = NULL */
+ IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
BasicBlock* block /* = NULL */
)
{
@@ -1089,6 +1122,11 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
GenTree* dest = nullptr;
unsigned destFlags = 0;
+ if (ilOffset == BAD_IL_OFFSET)
+ {
+ ilOffset = impCurStmtOffs;
+ }
+
#if defined(UNIX_AMD64_ABI)
assert(varTypeIsStruct(src) || (src->gtOper == GT_ADDR && src->TypeGet() == TYP_BYREF));
// TODO-ARM-BUG: Does ARM need this?
@@ -1281,11 +1319,11 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
GenTree* asg = gtNewAssignNode(ptrSlot, src->gtOp.gtOp1);
if (pAfterStmt)
{
- *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(asg, impCurStmtOffs));
+ *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(asg, ilOffset));
}
else
{
- impAppendTree(asg, curLevel, impCurStmtOffs);
+ impAppendTree(asg, curLevel, ilOffset);
}
// return the assign of the type value, to be appended
@@ -1297,15 +1335,15 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
assert(varTypeIsStruct(src->gtOp.gtOp2) || src->gtOp.gtOp2->gtType == TYP_BYREF);
if (pAfterStmt)
{
- *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(src->gtOp.gtOp1, impCurStmtOffs));
+ *pAfterStmt = fgInsertStmtAfter(block, *pAfterStmt, gtNewStmt(src->gtOp.gtOp1, ilOffset));
}
else
{
- impAppendTree(src->gtOp.gtOp1, curLevel, impCurStmtOffs); // do the side effect
+ impAppendTree(src->gtOp.gtOp1, curLevel, ilOffset); // do the side effect
}
// Evaluate the second thing using recursion.
- return impAssignStructPtr(destAddr, src->gtOp.gtOp2, structHnd, curLevel, pAfterStmt, block);
+ return impAssignStructPtr(destAddr, src->gtOp.gtOp2, structHnd, curLevel, pAfterStmt, ilOffset, block);
}
else if (src->IsLocal())
{
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 6a98df9ea4..cca6c35605 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -15850,9 +15850,25 @@ void Compiler::fgMorphBlocks()
noway_assert(ret->OperGet() == GT_RETURN);
noway_assert(ret->gtGetOp1() != nullptr);
- GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1());
+ GenTree* pAfterStatement = last;
+ IL_OFFSETX offset = last->AsStmt()->gtStmtILoffsx;
+ GenTree* tree =
+ gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block);
+ if (tree->OperIsCopyBlkOp())
+ {
+ tree = fgMorphCopyBlock(tree);
+ }
- last->gtStmt.gtStmtExpr = (tree->OperIsCopyBlkOp()) ? fgMorphCopyBlock(tree) : tree;
+ if (pAfterStatement == last)
+ {
+ last->gtStmt.gtStmtExpr = tree;
+ }
+ else
+ {
+ // gtNewTempAssign inserted additional statements after last
+ fgRemoveStmt(block, last);
+ last = fgInsertStmtAfter(block, pAfterStatement, gtNewStmt(tree, offset));
+ }
// make sure that copy-prop ignores this assignment.
last->gtStmt.gtStmtExpr->gtFlags |= GTF_DONT_CSE;
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.cs b/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.cs
new file mode 100644
index 0000000000..6548c36c5b
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+
+struct S
+{
+ public long y;
+ public int x;
+}
+
+class Z
+{
+ virtual public S F()
+ {
+ S s = new S();
+ s.x = 100;
+ s.y = -1;
+ return s;
+ }
+
+}
+
+class X
+{
+ Z z;
+
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Synchronized)]
+ public S G() => z.F();
+
+ public static int Main()
+ {
+ int result = Test();
+ if (result == 100) {
+ Console.WriteLine("SUCCESS");
+ }
+ else {
+ Console.WriteLine("FAILURE");
+ }
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static int Test()
+ {
+ var x = new X();
+ x.z = new Z();
+ return x.G().x;
+ }
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.csproj
new file mode 100644
index 0000000000..42f8a01f39
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_20499/GitHub_20499.csproj
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{2649FAFE-07BF-4F93-8120-BA9A69285ABB}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+ <PropertyGroup>
+ <DebugType>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildProjectName).cs" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project> \ No newline at end of file