dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.21k stars 4.72k forks source link

Mono interpreter has a general limit of 64k for offsets and indexes #46622

Open BrzVlad opened 3 years ago

BrzVlad commented 3 years ago

The interpreter instruction stream is an array of ushort. This data type is used to store most of the common data, from opcode, offsets, static data indexes etc. In some uncommon scenarios, for large methods, we can hit this limit.

ghost commented 3 years ago

Tagging subscribers to this area: @brzvlad See info in area-owners.md if you want to be subscribed.

Issue Details
When encountering a method with a large enough local space, the compiler will throw `Unable to run method: locals size too big.`
Author: BrzVlad
Assignees: BrzVlad
Labels: `area-Codegen-Interpreter-mono`
Milestone: Future
Eddie-Hartman commented 12 months ago

@BrzVlad @javiercn I believe that I am currently running into this issue. The odd thing is that it doesn't occur when running locally on Chrome or Edge in Debug or Release mode, but occurs when the app is deployed. It also occurs locally and when deployed in Firefox.

I'm currently looking for a workaround I can use and not to get this fixed in the runtime itself as I need a fix soon. My situation is that I'm running a Blazor WASM app with EntityFrameworkCore using this library. My migration is seeding information for U.S. counties for a lookup table, and therefore has thousands of insert statements like so:

            migrationBuilder.InsertData(
                table: "CountyLookup",
                columns: new[] { "CountyCode", "StateCode", "CountyName" },
                values: new object[,]
                {
                    { 1, 1, "Autauga County" },
                    { 3, 1, "Baldwin County" },
                    { 5, 1, "Barbour County" },

I would prefer to keep this generated rather than manually providing an insert into script so that this data may be easily updated with future migrations (it's being built from another file).

I've tried breaking it up into inserts of say 50 at a time rather than all at once, hoping that would reduce the amount of locals, but that didn't work.

  1. Do you have any other suggestions?
  2. Is this potentially fixed in .net 8?
  3. Do you know why it works fine running locally, but fails when deployed?
BrzVlad commented 12 months ago

What is the exact error that you are hitting ? Locals size too big ?

Eddie-Hartman commented 12 months ago
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Unable to run method 'void DataProject.Migrations.BridgeDbContextNS.Addingcountylookuptable:Up (Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)': locals size too big.
System.InvalidProgramException: Unable to run method 'void DataProject.Migrations.BridgeDbContextNS.Addingcountylookuptable:Up (Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)': locals size too big.
   at Microsoft.EntityFrameworkCore.Migrations.Migration.BuildOperations(Action`1 buildAction)
   at Microsoft.EntityFrameworkCore.Migrations.Migration.get_UpOperations()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration, MigrationsSqlGenerationOptions options)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass16_2.<GetMigrationCommandLists>b__2()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateAsync(String targetMigration, CancellationToken cancellationToken)
   at SqliteWasmHelper.SqliteWasmDbContextFactory`1.<CreateDbContextAsync>d__15[[DataProject.Data.BridgeDbContext, DataProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at SNBI_Collector.Pages.Index.FetchInitialData()
   at SNBI_Collector.Pages.Index.OnInitializedAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task , ComponentState )

If it would help at all, you can see it live here: snbicollector.com

I'm happy to turn on verbose logging in the runtime if you could tell me how to do that. I can see in transform.c I could potentially get more info once that level of logging is enabled.

BrzVlad commented 12 months ago

Is there any chance I could get an isolated project for reproducing this ?

Eddie-Hartman commented 12 months ago

Creating an isolated case is certainly a possibility, but would take some time to spin up. If you are potentially open to it, I'm fine with setting up a call and potentially sending over what I currently have to expedite the process. I could send you a meeting invite however you prefer (google meet, teams, etc), but I could send a teams invite to your gmail if that's good for you.

If you'd prefer I send a different more open source repo for reproducibility, that would take some time and I'd have to focus on fixing my problem at hand first.

BrzVlad commented 12 months ago

Could you try adding in your wasm project <WasmDebugLevel>-1</WasmDebugLevel>. Not sure if this has the effect I'm hoping but worth giving it a try. Feel free to mail me directly and we can figure something out.

Eddie-Hartman commented 12 months ago

@BrzVlad I kind of surprised myself and was able to set up a repo for reproducing the issue pretty quickly: https://github.com/Eddie-Hartman/LocalsSize Clone, run, then open the URL in Firefox because again, the error does NOT appear locally for whatever reason when using Chrome or Edge locally. Let me know if there is anything else I can do to help. I'll try the WasmDebugLevel above in the meantime and see if I can find anything.

Eddie-Hartman commented 12 months ago

I walked away from this for a while, but came back and tried to debug a bit more. I really don't understand the above. Adding that to my project file does not add additional logging.

I also tried some other search results such as:

<WasmNativeDebugSymbols>true</WasmNativeDebugSymbols>
<WasmNativeStrip>false</WasmNativeStrip>

which didn't work either and: https://github.com/dotnet/runtime/blob/main/src/mono/wasm/debugger/debugger.md which seems like a rabbit hole I don't want to go down unless I know that's the correct way to debug this.

Please let me know how to proceed.

BrzVlad commented 12 months ago

The point of WasmDebugLevel was not to add verbose logging for investigation but rather I expected it to disable all optimizations on the interpreter, which is the configuration when running from VS. My understanding is that it didn't fix anything ? I somewhat suspect tiering to be at the root cause of this issue.

I can't run your sample because it fails with:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: TypeInitialization_Type, Microsoft.Data.Sqlite.SqliteConnection
System.TypeInitializationException: TypeInitialization_Type, Microsoft.Data.Sqlite.SqliteConnection
 ---> System.Reflection.TargetInvocationException: Arg_TargetInvocationException
 ---> System.DllNotFoundException: e_sqlite3

Could you share a folder publish of the sample ?

Eddie-Hartman commented 12 months ago

Yeah when I added that it didn't seem to have any effect. It still failed. I emailed you an invite to a call if you'd like to join. I'm debugging the repo for reproducing the issue now to see if there is anything I can do to get that working for you. Weird that it runs just fine on my machine.

Eddie-Hartman commented 12 months ago

Here is a zip of the publish: https://drive.google.com/file/d/1Hh2DiXgDGDOvpEPrvUJg2l5a7uCJW2h3/view?usp=sharing

Eddie-Hartman commented 12 months ago

I was able to get a workaround working thanks to the devs in the discord in the webassembley channel. I had it broken up into different methods calls, but I needed to specifically do it like so:

InsertCountyData1(migrationBuilder);
InsertCountyData2(migrationBuilder);

Rather than having a method where I was passing the data in as arguments.

Thanks to @vargaz @BrzVlad @lambdageek and @kg for your help!

BrzVlad commented 11 months ago

The culprit for the above scenario was indeed tiering, with untiered version not being able to properly compact the space for the locals. This scenario will be handled in the future by https://github.com/dotnet/runtime/pull/94381.