Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Throw statement triggers: "has ldstr VM restriction" #53726

Closed
redknightlois opened this issue Jun 4, 2021 · 7 comments · Fixed by #64521
Closed

JIT: Throw statement triggers: "has ldstr VM restriction" #53726

redknightlois opened this issue Jun 4, 2021 · 7 comments · Fixed by #64521
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI question Answer questions and provide assistance, not an issue with source code or documentation. tenet-performance Performance related issue
Milestone

Comments

@redknightlois
Copy link

Description

The JIT is not able to inline this method because of the throw new FormatException("Bad variable size int"); statement with the following error:

generic !!0  (value class System.ReadOnlySpan`1<unsigned int8>,int32,int32&)
Fail Reason: has ldstr VM restriction

A cleanup version of the offending code:

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static T ReadVariableSizeInt<T>(ReadOnlySpan<byte> buffer, int pos, out int offset) where T : unmanaged
        {
            if (typeof(T) == typeof(int) || typeof(T) == typeof(uint))
            {
                int ui = 0;

                offset = 0;

                byte b = buffer[pos + 0];
                ui |= (b & 0x7F);
                offset++;
                if ((b & 0x80) == 0)
                    goto End;

                b = buffer[pos + 1];
                ui |= (b & 0x7F) << 7;
                offset++;
                if ((b & 0x80) == 0)
                    goto End;

                b = buffer[pos + 2];
                ui |= (b & 0x7F) << 14;
                offset++;
                if ((b & 0x80) == 0)
                    goto End;

                b = buffer[pos + 3];
                ui |= (b & 0x7F) << 21;
                offset++;
                if ((b & 0x80) == 0)
                    goto End;

                b = buffer[pos + 4];
                ui |= (b & 0x7F) << 28;
                offset++;
                if ((b & 0x80) != 0)
                    throw new FormatException("Bad variable size int");

                End: return (T)(object)ui;
            }

            throw new NotSupportedException($"The type {nameof(T)} is not supported to be written.");
        }

Configuration

.Net 5.0

Analysis

It sounds to me that because the offending line is a throw statement and is to be marked as cold, that restriction should not apply.

@redknightlois redknightlois added the tenet-performance Performance related issue label Jun 4, 2021
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Jun 4, 2021
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@redknightlois redknightlois changed the title Throw statement triggers: "has ldstr VM restriction" JIT: Throw statement triggers: "has ldstr VM restriction" Jun 4, 2021
@EgorBo
Copy link
Member

EgorBo commented Jun 4, 2021

From my understanding that is intentional: we don't inline methods with string literals inside from other modules, e.g.:

ClassLibrary1:

public class Class1
{
    public string GetString() => "Hello";
}

ConsoleApp1 (references ClassLibrary1 lib):

string Test() => Class1.GetString(); // GetString won't be inlined -- has ldstr VM restriction

because the way interning works, we only can inline it if ClassLibrary doesn't use that literal anywhere. However, I am not sure I fully understand this restriction (perhaps, AOT only?)

@redknightlois
Copy link
Author

Yes, I dont know really why it cannot be inlined. But in this case where there is no such construct and also it is essentially a throw it doesn't make much sense that it behaves like that. Or, no method that throws should be able to be inlined.

@jeffschwMSFT jeffschwMSFT added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 2021
@JulieLeeMSFT JulieLeeMSFT added question Answer questions and provide assistance, not an issue with source code or documentation. and removed untriaged New issue has not been triaged by the area owner labels Jun 7, 2021
@JulieLeeMSFT
Copy link
Member

CC @AndyAyersMS.

Yes, I dont know really why it cannot be inlined.

@AndyAyersMS
Copy link
Member

AndyAyersMS commented Jun 7, 2021

As @EgorBo says, this is a precaution migrating strings cross-module, in case string interning is active in the caller or callee's module. But seemingly we could relax this, when jitting, most of the time:

// TODO: We can probably be smarter here if the caller is jitted, as we will
// know for sure if the inlinee has really no string interning active (currently
// it's only on in the ngen case (besides requiring the attribute)), but this is getting
// too subtle. Will only do if somebody screams about it, as bugs here are going to
// be tough to find
if ((pOrigCallerModule != pCalleeModule) && pCalleeModule->IsNoStringInterning())
{
dwRestrictions |= INLINE_NO_CALLEE_LDSTR;
}

@redknightlois
Copy link
Author

@AndyAyersMS ROFL, you got the scream.

@JulieLeeMSFT JulieLeeMSFT added this to the 7.0.0 milestone Jun 15, 2021
@JulieLeeMSFT
Copy link
Member

Pushing out to .NET 7 since it is not likely to be handled by Preview 7.

MichalStrehovsky added a commit to MichalStrehovsky/runtime that referenced this issue Jan 31, 2022
The code that was dealing with this relaxation was deleted in dotnet#57693 last year (ceeload.cpp, line 4006). We still had code that was telling RyuJIT not to inline string literals across modules if `NoStringInterning` is active because it might be observable, but since fragile NGen got deleted, I don't believe it's actually observable anymore and we could have deleted this together with fragile NGen support.

Closes dotnet#53726 - we no longer need to worry about loosening the restriction - we just drop it all.
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 31, 2022
jkotas pushed a commit that referenced this issue Jan 31, 2022
* Delete code related to CompilationRelaxations.NoStringInterning

The code that was dealing with this relaxation was deleted in #57693 last year (ceeload.cpp, line 4006). We still had code that was telling RyuJIT not to inline string literals across modules if `NoStringInterning` is active because it might be observable, but since fragile NGen got deleted, I don't believe it's actually observable anymore and we could have deleted this together with fragile NGen support.

Closes #53726 - we no longer need to worry about loosening the restriction - we just drop it all.
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 31, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Mar 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI question Answer questions and provide assistance, not an issue with source code or documentation. tenet-performance Performance related issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants