Skip to content

Inlining FAILED: has ldstr VM restriction #12492

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

Closed
benaadams opened this issue Apr 13, 2019 · 8 comments
Closed

Inlining FAILED: has ldstr VM restriction #12492

benaadams opened this issue Apr 13, 2019 · 8 comments

Comments

@benaadams
Copy link
Member

If one assembly has a static property as follows:

public static class HeaderNames
{
    public static string Date => "Date";
}

Using it in another assembly

if (ReferenceEquals(HeaderNames.Date, key))

Fails to inline the property at Jit time with

[FAILED: has ldstr VM restriction] HeaderNames:get_Date():ref

Is this by design? /cc @AndyAyersMS @jkotas

@benaadams
Copy link
Member Author

Workaround is to lift the string load to a readonly static

public static class HeaderNames
{
    private readonly static string s_date = "Date";

    public static string Date => s_date;
}

@AndyAyersMS
Copy link
Member

I don't know the specifics here, so will defer to Jan.

@benaadams
Copy link
Member Author

Note, the behaviour I'm trying to capture is reference equality which direct string consts cross-assembly don't have (as they embed in the call site assembly); so using properties to achieve this.

@jkotas
Copy link
Member

jkotas commented Apr 14, 2019

This check is there to ensure that the inlining won't change the string instance returned for constant string.

It looks like a fragile NGen optimization. I think this restriction can be relaxed without giving up much since we are not optimizing for fragile NGen anymore.

@benaadams
Copy link
Member Author

benaadams commented Apr 14, 2019

My aim is to maintain reference equality so not propagate the const cross assembly at AoT (as string consts are not deduped cross assembly at assembly load). This is to achieve a fast path for string IgnoreCase comparisons for known strings (e.g. Header names dotnet/aspnetcore#9341)

With that goal in mind would it make sense to still block for crossgen/R2R; but allow at Jit/Tier1?

i.e. would a cross-assembly ldstr maintain reference equality at Jit time?

@jkotas
Copy link
Member

jkotas commented Apr 14, 2019

C# sets CompilationRelaxations.NoStringInterning by default that gives runtime freedom in whether to de-dup cross-assembly.

I think it should be possible to adjust the system to de-dup more often or with less restrictions; but I am not sure whether we would want to start guaranteeing that everything is de-duped perfectly (ie ignore NoStringInterning).

@benaadams
Copy link
Member Author

benaadams commented Apr 14, 2019

Maybe hiding a readonly static string behind a static property getter is the best approach then?

As when it inlines it will just inline the pointer; whereas if it was a string const it would also inline the load?

@benaadams
Copy link
Member Author

benaadams commented Apr 14, 2019

e.g. with the readonly static + property I get

Inlines into 0600056A HttpRequestHeaders:TryGetValueFast(ref,byref):bool:this
  [1 IL=0143 TR=003198 060000E3] [below ALWAYS_INLINE size] HeaderNames:get_TE():ref
  [2 IL=0190 TR=003205 060000E3] [below ALWAYS_INLINE size] HeaderNames:get_TE():ref
  [0 IL=0197 TR=003210 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [3 IL=0246 TR=003070 060000ED] [below ALWAYS_INLINE size] HeaderNames:get_Via():ref
  [4 IL=0290 TR=003077 060000C5] [below ALWAYS_INLINE size] HeaderNames:get_DNT():ref
  [5 IL=0337 TR=003084 060000ED] [below ALWAYS_INLINE size] HeaderNames:get_Via():ref
  [0 IL=0344 TR=003089 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [6 IL=0387 TR=003096 060000C5] [below ALWAYS_INLINE size] HeaderNames:get_DNT():ref
  [0 IL=0394 TR=003101 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [7 IL=0443 TR=002875 060000CA] [below ALWAYS_INLINE size] HeaderNames:get_Host():ref
  [8 IL=0487 TR=002882 060000C4] [below ALWAYS_INLINE size] HeaderNames:get_Date():ref
  [9 IL=0527 TR=002889 060000C9] [below ALWAYS_INLINE size] HeaderNames:get_From():ref
  [10 IL=0571 TR=002896 060000CA] [below ALWAYS_INLINE size] HeaderNames:get_Host():ref
  [0 IL=0578 TR=002901 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [11 IL=0621 TR=002908 060000C4] [below ALWAYS_INLINE size] HeaderNames:get_Date():ref
  [0 IL=0628 TR=002913 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [12 IL=0667 TR=002920 060000C9] [below ALWAYS_INLINE size] HeaderNames:get_From():ref
  [0 IL=0674 TR=002925 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [13 IL=0720 TR=002747 060000B3] [below ALWAYS_INLINE size] HeaderNames:get_Allow():ref
  [14 IL=0764 TR=002754 060000DA] [below ALWAYS_INLINE size] HeaderNames:get_Range():ref
  [15 IL=0811 TR=002761 060000B3] [below ALWAYS_INLINE size] HeaderNames:get_Allow():ref
  [0 IL=0818 TR=002766 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [16 IL=0861 TR=002773 060000DA] [below ALWAYS_INLINE size] HeaderNames:get_Range():ref
  [0 IL=0868 TR=002778 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  [17 IL=0917 TR=002424 060000A5] [below ALWAYS_INLINE size] HeaderNames:get_Accept():ref
  [18 IL=0961 TR=002431 060000D7] [below ALWAYS_INLINE size] HeaderNames:get_Pragma():ref
  [19 IL=1002 TR=002438 060000C3] [below ALWAYS_INLINE size] HeaderNames:get_Cookie():ref
  [20 IL=1046 TR=002445 060000C8] [below ALWAYS_INLINE size] HeaderNames:get_Expect():ref
  [21 IL=1090 TR=002452 060000D5] [below ALWAYS_INLINE size] HeaderNames:get_Origin():ref
  [22 IL=1137 TR=002459 060000A5] [below ALWAYS_INLINE size] HeaderNames:get_Accept():ref
  [0 IL=1144 TR=002464 060006FD] [FAILED: noinline per IL/cached result] String:Equals(ref,int):bool:this
  etc..

And the inlined string becomes a constant mov e.g.

if (ReferenceEquals(HeaderNames.Host, key))

becomes

G_M13073_IG06:
       mov      rdx, 0x15B8E12EBA8        ; HeaderNames:get_Host():ref
       cmp      gword ptr [rdx], rdi      ; ReferenceEquals
       jne      SHORT G_M13073_IG07

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants