This repository was archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Update full-struct references to promoted IBR args #11883
Merged
JosephTremoulet
merged 2 commits into
dotnet:master
from
JosephTremoulet:PromotedStructRefs
May 24, 2017
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17896,19 +17896,32 @@ void Compiler::fgRetypeImplicitByRefArgs() | |
| fieldVarDsc->lvPrefReg = 0; | ||
| } | ||
|
|
||
| if (undoPromotion) | ||
| { | ||
| // Hijack lvFieldLclStart to record the new temp number. | ||
| // It will get fixed up in fgMarkDemotedImplicitByRefArgs. | ||
| varDsc->lvFieldLclStart = newLclNum; | ||
| } | ||
| else | ||
| { | ||
| // Unmark the parameter as promoted (it's a pointer now). | ||
| varDsc->lvPromoted = false; | ||
| varDsc->lvFieldCnt = 0; | ||
| varDsc->lvFieldLclStart = 0; | ||
| } | ||
| // Hijack lvFieldLclStart to record the new temp number. | ||
| // It will get fixed up in fgMarkDemotedImplicitByRefArgs. | ||
| varDsc->lvFieldLclStart = newLclNum; | ||
| // Go ahead and clear lvFieldCnt -- either we're promoting | ||
| // a replacement temp or we're not promoting this arg, and | ||
| // in either case the parameter is now a pointer that doesn't | ||
| // have these fields. | ||
| varDsc->lvFieldCnt = 0; | ||
|
|
||
| // Hijack lvPromoted to communicate to fgMorphImplicitByRefArgs | ||
| // whether references to the struct should be rewritten as | ||
| // indirections off the pointer (not promoted) or references | ||
| // to the new struct local (promoted). | ||
| varDsc->lvPromoted = !undoPromotion; | ||
| } | ||
| else | ||
| { | ||
| // The "undo promotion" path above clears lvPromoted for args that struct | ||
| // promotion wanted to promote but that aren't considered profitable to | ||
| // rewrite. It hijacks lvFieldLclStart to communicate to | ||
| // fgMarkDemotedImplicitByRefArgs that it needs to clean up annotations left | ||
| // on such args for fgMorphImplicitByRefArgs to consult in the interim. | ||
| // Here we have an arg that was simply never promoted, so make sure it doesn't | ||
| // have nonzero lvFieldLclStart, since that would confuse fgMorphImplicitByRefArgs | ||
| // and fgMarkDemotedImplicitByRefArgs. | ||
| assert(varDsc->lvFieldLclStart == 0); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is really applicable to the previous checkin, but I'm wondering why you're not using
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's just the way it gets initialized when the node is created, the ones with 0 for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see. Thanks. |
||
| } | ||
|
|
||
| // Since the parameter in this position is really a pointer, its type is TYP_BYREF. | ||
|
|
@@ -17945,10 +17958,9 @@ void Compiler::fgRetypeImplicitByRefArgs() | |
|
|
||
| //------------------------------------------------------------------------ | ||
| // fgMarkDemotedImplicitByRefArgs: Clear annotations for any implicit byrefs that struct promotion | ||
| // asked to promote but for which fgRetypeImplicitByRefArgs decided | ||
| // to discard the promotion. Appearances of these have now been | ||
| // rewritten (by fgMorphImplicitByRefArgs) using indirections from | ||
| // the pointer parameter. | ||
| // asked to promote. Appearances of these have now been rewritten | ||
| // (by fgMorphImplicitByRefArgs) using indirections from the pointer | ||
| // parameter or references to the promotion temp, as appropriate. | ||
|
|
||
| void Compiler::fgMarkDemotedImplicitByRefArgs() | ||
| { | ||
|
|
@@ -17958,45 +17970,60 @@ void Compiler::fgMarkDemotedImplicitByRefArgs() | |
| { | ||
| LclVarDsc* varDsc = &lvaTable[lclNum]; | ||
|
|
||
| if (lvaIsImplicitByRefLocal(lclNum) && varDsc->lvPromoted) | ||
| { | ||
| // We stashed the pointer to the real promotion temp in lvFieldLclStart | ||
| unsigned structLclNum = varDsc->lvFieldLclStart; | ||
|
|
||
| // Unmark the parameter as promoted. | ||
| varDsc->lvPromoted = false; | ||
| varDsc->lvFieldCnt = 0; | ||
| varDsc->lvFieldLclStart = 0; | ||
| // Clear its ref count; this was set during address-taken analysis so that | ||
| // call morphing could identify single-use implicit byrefs; we're done with | ||
| // that, and want it to be in its default state of zero when we go to set | ||
| // real ref counts for all variables. | ||
| varDsc->lvRefCnt = 0; | ||
|
|
||
| // The temp struct is now unused; set flags appropriately so that we | ||
| // won't allocate space for it on the stack. | ||
| LclVarDsc* structVarDsc = &lvaTable[structLclNum]; | ||
| structVarDsc->lvRefCnt = 0; | ||
| structVarDsc->lvAddrExposed = false; | ||
| if (lvaIsImplicitByRefLocal(lclNum)) | ||
| { | ||
| if (varDsc->lvPromoted) | ||
| { | ||
| // The parameter is simply a pointer now, so clear lvPromoted. It was left set | ||
| // by fgRetypeImplicitByRefArgs to communicate to fgMorphImplicitByRefArgs that | ||
| // appearances of this arg needed to be rewritten to a new promoted struct local. | ||
| varDsc->lvPromoted = false; | ||
|
|
||
| // Clear the lvFieldLclStart value that was set by fgRetypeImplicitByRefArgs | ||
| // to tell fgMorphImplicitByRefArgs which local is the new promoted struct one. | ||
| varDsc->lvFieldLclStart = 0; | ||
| } | ||
| else if (varDsc->lvFieldLclStart != 0) | ||
| { | ||
| // We created new temps to represent a promoted struct corresponding to this | ||
| // parameter, but decided not to go through with the promotion and have | ||
| // rewritten all uses as indirections off the pointer parameter. | ||
| // We stashed the pointer to the new struct temp in lvFieldLclStart; make | ||
| // note of that and clear the annotation. | ||
| unsigned structLclNum = varDsc->lvFieldLclStart; | ||
| varDsc->lvFieldLclStart = 0; | ||
|
|
||
| // Clear the arg's ref count; this was set during address-taken analysis so that | ||
| // call morphing could identify single-use implicit byrefs; we're done with | ||
| // that, and want it to be in its default state of zero when we go to set | ||
| // real ref counts for all variables. | ||
| varDsc->lvRefCnt = 0; | ||
|
|
||
| // The temp struct is now unused; set flags appropriately so that we | ||
| // won't allocate space for it on the stack. | ||
| LclVarDsc* structVarDsc = &lvaTable[structLclNum]; | ||
| structVarDsc->lvRefCnt = 0; | ||
| structVarDsc->lvAddrExposed = false; | ||
| #ifdef DEBUG | ||
| structVarDsc->lvUnusedStruct = true; | ||
| structVarDsc->lvUnusedStruct = true; | ||
| #endif // DEBUG | ||
|
|
||
| unsigned fieldLclStart = structVarDsc->lvFieldLclStart; | ||
| unsigned fieldCount = structVarDsc->lvFieldCnt; | ||
| unsigned fieldLclStop = fieldLclStart + fieldCount; | ||
| unsigned fieldLclStart = structVarDsc->lvFieldLclStart; | ||
| unsigned fieldCount = structVarDsc->lvFieldCnt; | ||
| unsigned fieldLclStop = fieldLclStart + fieldCount; | ||
|
|
||
| for (unsigned fieldLclNum = fieldLclStart; fieldLclNum < fieldLclStop; ++fieldLclNum) | ||
| { | ||
| // Fix the pointer to the parent local. | ||
| LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum]; | ||
| assert(fieldVarDsc->lvParentLcl == lclNum); | ||
| fieldVarDsc->lvParentLcl = structLclNum; | ||
| for (unsigned fieldLclNum = fieldLclStart; fieldLclNum < fieldLclStop; ++fieldLclNum) | ||
| { | ||
| // Fix the pointer to the parent local. | ||
| LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum]; | ||
| assert(fieldVarDsc->lvParentLcl == lclNum); | ||
| fieldVarDsc->lvParentLcl = structLclNum; | ||
|
|
||
| // The field local is now unused; set flags appropriately so that | ||
| // we won't allocate stack space for it. | ||
| fieldVarDsc->lvRefCnt = 0; | ||
| fieldVarDsc->lvAddrExposed = false; | ||
| // The field local is now unused; set flags appropriately so that | ||
| // we won't allocate stack space for it. | ||
| fieldVarDsc->lvRefCnt = 0; | ||
| fieldVarDsc->lvAddrExposed = false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -18074,8 +18101,17 @@ GenTreePtr Compiler::fgMorphImplicitByRefArgs(GenTreePtr tree, bool isAddr) | |
| if (!varTypeIsStruct(lclVarTree)) | ||
| { | ||
| assert(lclVarTree->TypeGet() == TYP_BYREF); | ||
|
|
||
| return nullptr; | ||
| } | ||
| else if (lclVarDsc->lvPromoted) | ||
| { | ||
| // fgRetypeImplicitByRefArgs created a new promoted struct local to represent this | ||
| // arg. Rewrite this to refer to the new local. | ||
| assert(lclVarDsc->lvFieldLclStart != 0); | ||
| lclVarTree->AsLclVarCommon()->SetLclNum(lclVarDsc->lvFieldLclStart); | ||
| return tree; | ||
| } | ||
|
|
||
| fieldHnd = nullptr; | ||
| } | ||
|
|
||
45 changes: 45 additions & 0 deletions
45
tests/src/JIT/Regression/JitBlue/GitHub_11814/GitHub_11814.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // 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. | ||
|
|
||
| // Repro case for a bug involving failure to rewrite all references | ||
| // to a promoted implicit byref struct parameter. | ||
|
|
||
| using System; | ||
| using System.Runtime.CompilerServices; | ||
|
|
||
| class MutateStructArg | ||
| { | ||
| public struct P | ||
| { | ||
| public string S; | ||
| public int X; | ||
| } | ||
|
|
||
| public static int Main() | ||
| { | ||
| P l1 = new P(); | ||
| l1.S = "Hello World"; | ||
| l1.X = 42; | ||
| P l2 = foo(l1); | ||
| Console.WriteLine(l2.S); // Print modified value "Goodbye World" | ||
| if ((l2.S == "Goodbye World") && (l2.X == 100)) | ||
| { | ||
| return 100; // success | ||
| } | ||
| else | ||
| { | ||
| Console.WriteLine("**** Test FAILED ***"); | ||
| return 1; // failure | ||
| } | ||
| } | ||
|
|
||
| [MethodImpl(MethodImplOptions.NoInlining)] | ||
| public static P foo(P a) | ||
| { | ||
| Console.WriteLine(a.S); // Print the incoming value "Hello World" | ||
| a.S = "Goodbye World"; // Mutate the incoming value | ||
| a.X = 100; | ||
| return a; // Copy the modified value to the return value (bug was that this was returning original unmodified arg) | ||
| } | ||
| } |
43 changes: 43 additions & 0 deletions
43
tests/src/JIT/Regression/JitBlue/GitHub_11814/GitHub_11814.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <?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> | ||
| <AssemblyName>$(MSBuildProjectName)</AssemblyName> | ||
| <SchemaVersion>2.0</SchemaVersion> | ||
| <ProjectGuid>{7B521917-193E-48BB-86C6-FE013F3DFF35}</ProjectGuid> | ||
| <OutputType>Exe</OutputType> | ||
| <AppDesignerFolder>Properties</AppDesignerFolder> | ||
| <FileAlignment>512</FileAlignment> | ||
| <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | ||
| <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath> | ||
| <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> | ||
|
|
||
| <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp> | ||
| </PropertyGroup> | ||
| <!-- Default configurations to help VS understand the configurations --> | ||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
| </PropertyGroup> | ||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies"> | ||
| <Visible>False</Visible> | ||
| </CodeAnalysisDependentAssemblyPaths> | ||
| </ItemGroup> | ||
| <PropertyGroup> | ||
| <DebugType></DebugType> | ||
| <Optimize>True</Optimize> | ||
| <AllowUnsafeBlocks>True</AllowUnsafeBlocks> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Compile Include="GitHub_11814.cs" /> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> | ||
| </ItemGroup> | ||
| <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> | ||
| <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "> | ||
| </PropertyGroup> | ||
| </Project> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add comments on these fields in compiler.h, even though these are transient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍