Skip to content

Fix NeedsAlignedBaseOffset for composite R2R#35231

Merged
davidwrighton merged 14 commits into
dotnet:masterfrom
davidwrighton:fix_cg2_composite
Apr 29, 2020
Merged

Fix NeedsAlignedBaseOffset for composite R2R#35231
davidwrighton merged 14 commits into
dotnet:masterfrom
davidwrighton:fix_cg2_composite

Conversation

@davidwrighton
Copy link
Copy Markdown
Member

Synchronize and correct NeedsAlignedBaseOffset

  • Crossgen2 and the runtime had different models for when the base type size needed to be aligned up
  • This only caused issues for composite crossgen2 builds as the logic was heavily module focussed
  • Also, the IsInSameVersionBubble logic was non-functional for composite build

All of the following conditions must hold, or there shall be alignment inserted between a base and derived type

  • The derived type must declare that the base type is in the same version bubble as the derived type
  • The base type must declare that it was able to derive from its base type without needing alignment
  • The base type must not be instantiated over a valuetype where the valuetype instantiation contributes to the instance field layout of the base type, and that valuetype is not wholly defined in one bubble

Also add a test for various mixed combinations

@davidwrighton
Copy link
Copy Markdown
Member Author

/azp list

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Exactly the same logic is now duplicated here and in the MethodTableBuilder::CheckLayoutDependsOnOtherModules. So it seems that this whole block can be replaced by

        return pParentMT->GetClass()->HasLayoutDependsOnOtherModules();

trylek
trylek previously approved these changes Apr 21, 2020
Copy link
Copy Markdown
Member

@trylek trylek left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for fixing these inconsistencies!

@davidwrighton
Copy link
Copy Markdown
Member Author

Add a multitude of tests and fix issues related to composite, input bubble and regular r2r crossgen build. The tests will need some refactoring once @nattress finishes his test harness work, but for now there is a batch script.

@davidwrighton davidwrighton marked this pull request as ready for review April 24, 2020 00:42
- Crossgen2 and the runtime had different models for when the base type size needed to be aligned up
- This only caused issues for composite crossgen2 builds as the logic was heavily module focussed
- Also, the IsInSameVersionBubble logic was non-functional for composite build

All of the following conditions must hold, or there shall be alignment inserted between a base and derived type
- The derived type must declare that the base type is in the same version bubble as the derived type
- The base type must declare that it was able to derive from its base type without needing alignment
- The base type must not be instantiated over a valuetype where the valuetype instantiation contributes to the instance field layout of the base type, and that valuetype is not wholly defined in one bubble
crossgen, crossgen2, and crossgen2 with input bubble enable
@davidwrighton
Copy link
Copy Markdown
Member Author

@dotnet/crossgen-contrib

@jkotas This changes the field layout for non-R2R to follow rules similar to R2R in some somewhat rare cases. I'd like your opinion on that.

@davidwrighton davidwrighton dismissed trylek’s stale review April 24, 2020 18:05

It was a review of an earlier change

Comment thread src/coreclr/src/vm/class.h Outdated
m_VMFlags |= VMFLAG_LAYOUT_DEPENDS_ON_OTHER_MODULES;
}

inline BOOL HasLayoutDependsOnOtherVersionBubbles()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we really need both HasLayoutDependsOnOtherModules and HasLayoutDependsOnOtherVersionBubbles ?

I would expect to have just the version bubble one.

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Apr 26, 2020

I am having hard time figuring out the reasons behind the rules. I would expect the rules to be:

All of the following conditions must hold, or there shall be alignment inserted between a base and derived type

  • The derived type must declare that the base type is in the same version bubble as the derived type
  • The base type must declare that its layout does not depend on types outside its version bubble

What am I missing?

@davidwrighton
Copy link
Copy Markdown
Member Author

There are several issues fixed in this change, and the change description does not currently describe them well.

  1. Many of the primitives that the calculation used returned subtly different values in the runtime and in crossgen2. As well as syncrhonizing the overall layout algorithm with the runtime. Fixing these issues is what fixed the composite build.

  2. The second issue is what drove the push to a new algorithm and is actually not related to composite builds. The design of the existing algorithm relied on the concept that the various version bubbles were the same at both crossgen time and at runtime. Unfortunately, that assumption isn't true with input bubble style compilations in the style that we want to build for technologies such as layered containers.

Layer 1 - All assemblies AOT compiled here will be compiled with /inputbubble enabled.
System.Private.CoreLib.dll
System.*.dll

Layer 2
Asp.NET Dlls, or other content layered on top of the assemblies produced in layer 1. Also compiled with /inputbubble.

Layer 3
Application dlls, compiled with whatever policy the developer chooses (input bubble, normal R2R, no AOT, etc)

The concern is for the layout of a class Derived<SomeStruct>

Defined in Layer 1
class Base<T>
{
    public T _t;
}
class Derived<U> : Base<T>
{
    public U _u;
}

Defined in Layer 2
struct SomeStruct
{
    public byte _someByte;
}

For Derived<SomeStruct>, what is the offset of _u? An algorithm exclusively built on the version bubble rules would have difficulty in this situation.

The type clearly cannot exist during compilation of Layer1, as SomeStruct is within the set of assemblies defined in Layer1.

When Layer2 is being compiled, it will consider all components of Derived<SomeStruct> to be within the same version bubble, so it will generate code without extra alignment.

However, when running the application, the version bubble algorithm will declare that Base<SomeStruct> has cross version bubble dependent layout, and layout the type Derived<SomeStruct with extra alignment.

The new algorithm as written uses a stricter definition for version bubble when considering layout that is caused by a generic instantiation. In particular, the version bubble rule retreats to consider the indivisible unit of the module which will cause both runtime and crossgen time compilation of Derived<SomeStruct> to have the same layout.

The practical effect is that the /inputbubble switch becomes reliable as long as the assemblies statically depended on by the assembly under compilation are also compiled with the /inputbubble switch, or not compiled at all. The system no longer requires that ALL assemblies that might be compiled with the inputbubble switch be compiled knowing exactly the same set of references.

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Apr 27, 2020

Ok, your example makes sense.

Would it make sense to have the R2R layout to be based exclusively on module boundaries, and never consider version bubbles for it to keep the rules simple?

@davidwrighton
Copy link
Copy Markdown
Member Author

I believe we would have to force ALL type layout to follow the module local rules, not just R2R layout. As you say, its much simpler, and less complex to explain, and probably less risky going forward. I'm not convinced this new algorithm worth the risk, as I don't have good data either way how much we win from the non-aligned layout optimization.

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Apr 27, 2020

I believe we would have to force ALL type layout to follow the module local rules, not just R2R layout.

What would be the example that demonstrates we need to force ALL type layout to follow the module local rules?

@davidwrighton
Copy link
Copy Markdown
Member Author

Its the same example as above, except for some reason we were unable to use the R2R image for the assembly which defined Base<T> or Derived<T>. In that case, the code in layer 2 will assume a type layout for Derived<T> that isn't the same as the runtime will compute.

@davidwrighton
Copy link
Copy Markdown
Member Author

@mangod9 I've confirmed that this fix addresses the failures seen in running asp.net with composite enabled.

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Apr 28, 2020

for some reason we were unable to use the R2R image for the assembly which defined Base or Derived

We may be unable to use code from the R2R image, but we should be still able to see that this is R2R image and it should follow the R2R layout rules.

@davidwrighton
Copy link
Copy Markdown
Member Author

That's a detail I don't actually believe is certain, and I'm somewhat uncomfortable with relying on every dependent image to be R2R'd. It could happen, but we have historically had discussion about crossgening only some files, etc.

To be honest, I'm somewhat uncomfortable with the algorithm I wrote up as well, as it assumes that all crossgen used on a dependent must be compiled with inputbubble if some other module with inputbubble exists which depends on it. I feel this is somewhat more likely to be reliable, but again, its not something I'm convinced is a safe model.

@trylek
Copy link
Copy Markdown
Member

trylek commented Apr 28, 2020

I wonder whether part of the trouble with --inputbubble may be in that it was designed with this "all-or-nothing" policy in mind. Now we're trying to improve on composability, would it help to just ditch the switch and instead use a new type of reference to define "those modules that won't be compiled into the R2R executable currently being produced but logically form a part of its version bubble", something like -rb (reference-bubble)?

@davidwrighton
Copy link
Copy Markdown
Member Author

@trylek you're right, but @jkotas talked after the meeting today and came up with an approach that doesn't require that complexity.

  1. Require that all assemblies dependend on by an inputbubble assembly to be run through the crossgen2 compiler in some way. (To support the legitimate case where we may not wish to compile any methods in a binary, we will build a new switch for crossgen2 that will put an R2R header on an image, but not use it for anything.

  2. Revert the algorithm to the model built for R2R in the past, except that the "module" that must be identical would allow for all of the contents of a single composite image to be considered as a single module.

  3. Remove the existing version bubble handling in favor of the strict "module" concept above.

  4. When we add module validation to the R2R product, if the image was compiled with inputbubble enabled, validate that the image is an R2R image.

  5. If an image has R2R data, even if it isn't useable (due to an instruction set mismatch or something, or version number problem), the image shall be considered to be an R2R image for the purposes of this type layout algorithm.

…putbubble compiled assembly must be r2r compiled

Fix R2RDump for composite images
while (true)
{
Thread.Sleep(1000);
s_testFailObj = o;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: indent

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, that was temporary logic in the test to make it easier to debug. I'll delete the new code.

</data>
<data name="CompileNoMethodsOption" xml:space="preserve">
<value>True to not compile any methods into the R2R image (default = false)</value>
</data>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe: True to skip compiling methods into the R2R image?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Again, I should never be allowed to write customer visible text. :) I like your wording better.

Copy link
Copy Markdown
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

Looks much better :-)

Copy link
Copy Markdown
Member

@trylek trylek left a comment

Choose a reason for hiding this comment

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

Looks awesome, thanks David for driving this to successful resolution!

}
}

// Indicate
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does this comment mean?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I fell asleep before finishing writing it... update on the way along with use of an enum instead of an int.

{
EcmaModule module = (EcmaModule)nonEcmaModule;
if (IsModuleInCompilationGroup(module))
return 2;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be nice to have enum or named constants for these 2 and 3.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed. I'm putting an enum together which should make this code somewhat easier to parse.

}
}
#endif // FEATURE_READYTORUN_COMPILER
GetHalfBakedClass()->SetHasLayoutDependsOnOtherModules();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the case when this bit was not already set by the CheckLayoutDependsOnOtherModules?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hmm, it appears to be legacy from the other algorithm I wrote up. I'll remove it, and verify that the tests all pass.


if "%COMPOSITENAME%"=="" goto done

set BUILDCMD=%TESTBATCHROOT%\..\..\..\..\..\..\.dotnet\dotnet %CORE_ROOT%\crossgen2\crossgen2.dll -r %CORE_ROOT%\* -r %TESTINITIALBINPATH%\*.dll -o %TESTINITIALBINPATH%\%TESTTARGET_DIR%\%COMPOSITENAME%Composite.dll --composite %COMPILEARG%
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am surprised that "-r %CORE_ROOT%*" doesn't cause crossgen2 to fail for files that are not managed dlls.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Crossgen2 filters out native dlls automatically; the only reason we're using more explicit reference file groups in CLRTest.Crossgen.targets and build-test.cmd/sh is to make it a bit faster and get rid of the annoying warning messages.


if "%COMPOSITENAME%"=="" goto done

set BUILDCMD=%TESTBATCHROOT%\..\..\..\..\..\..\.dotnet\dotnet %CORE_ROOT%\crossgen2\crossgen2.dll -r %CORE_ROOT%\* -r %TESTINITIALBINPATH%\*.dll -o %TESTINITIALBINPATH%\%TESTTARGET_DIR%\%COMPOSITENAME%Composite.dll --composite %COMPILEARG%
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A nit - it would be nice to extract the dotnet path to a variable. I also think that it is not guaranteed that the .dotnet folder exists. I believe that Viktor told me some time ago that it is not created in case you have the same version of dotnet installed on your machine. I think the right way is to use the dotnet.cmd/sh script in the root of the repo that takes care of locating the dotnet exe.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This entire test harness of ugly batch files is temporary, to be replaced with whatever @nattress comes up with. The reason I use the exact path to the dotnet exe is that it allows for copying/pasting the printed build command and passing it as a launch argument to the debugger which saved me quite a lot of time during development. I don't intend to fix this before checkin.

@@ -0,0 +1,322 @@
echo off
setlocal
set TESTDIR=%~dp0\..\..\..\..\..\..\artifacts\tests\coreclr\Windows_NT.x64.Debug\readytorun\crossboundarylayout\crossboundarytest\crossboundarytest
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this script get the arch / build flavor as arguments?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In theory yes, but in practice, as this logic is entirely cross arch/os it only needs testing in one environment, and the goal is to delete these scripts as soon as @nattress produces a way to write these tests that is actually reasonable.

@@ -0,0 +1,322 @@
echo off
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if this script (and the other ones) should be located in src/coreclr/tests/scripts instead.

Copy link
Copy Markdown
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

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

LGTM, just one tiny grammar nit.

…/ReadyToRunCompilationModuleGroupBase.cs

Co-Authored-By: Jan Vorlicek <janvorli@microsoft.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants