Skip to content

AF in in System.Globalization.CharUnicodeInfo:InternalGetNumericValue #11492

Closed
dotnet/coreclr
#21217

Description

@jkotas

Found by dotnet/coreclr#21048. Remove NoOptimization workaround and run crossgen System.Private.CoreLib.dll.

Assert failure(PID 2976 [0x00000ba0], Thread: 3816 [0x0ee8]): Assertion failed '((regMask & emitThisGCrefRegs) && (ins == INS_add)) || ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub))' in 'System.Globalization.CharUnicodeInfo:InternalGetNumericValue(int):double' (IL size 175)

Activity

  1. CarolEidt commented on Nov 16, 2018

    @CarolEidt
    Contributor

    @jkotas - do you have any insight as to what in dotnet/coreclr#21048 triggered this?

  2. jkotas commented on Nov 16, 2018

    @jkotas
    MemberAuthor

    This is the sequence that triggered this:

    1. Update Unicode data and optimize CharUnicodeInfo indexes coreclr#20983 changed the implementation of InternalGetNumericValue to use RVA-backed field ReadOnlySpan<byte>. Before this change, the bytes were copies from the IL image to byte[] array and then used. After this change, the bytes are used directly from the IL image.
    2. RVA-backed field ReadOnlySpan<byte> makes the Span to be used in new patterns that are not common today (but becoming increasingly more common as folks start taking advantage of this new C# feature). The Span indexer intrinsic expansion seems to have problems when combined with the RVA-backed field. We have not noticed it because of the Span indexer intrinsics were not getting detected properly when Update Unicode data and optimize CharUnicodeInfo indexes coreclr#20983 went in.
    3. Add explicit signatures for Span indexer coreclr#21048 repaired Span indexer intrinsics detection that surfaced this assert.
  3. AndyAyersMS commented on Nov 16, 2018

    @AndyAyersMS
    Member

    @CarolEidt I can look at this, if you haven't already started.

  4. CarolEidt commented on Nov 16, 2018

    @CarolEidt
    Contributor

    @AndyAyersMS - I haven't started, so you're welcome to it!

  5. self-assigned this
    on Nov 16, 2018
  6. AndyAyersMS commented on Nov 16, 2018

    @AndyAyersMS
    Member

    I think the issue is that we always report RVA static addresses as TYP_LONG and not TYP_BYREF. In this case the static address gets retyped as a byref via Unsafe.As and we end up with an implicit retyping across an assignment (after a bunch of inlinling, so we can't hope to get the types right when importing):

    [000389] ------------              *  STMT      void  (IL 0x030...  ???)
    [000343] #-----------              |  /--*  IND       long  
    [000342] ------------              |  |  \--*  CNS_INT(h) long   0x175ca888310 static Fseq[BB...]
    [000387] -AC---------              \--*  ASG       byref 
    [000386] -------N----                 \--*  FIELD     byref  _value
    [000385] L-----------                    \--*  ADDR      byref 
    [000384] ------------                       \--*  LCL_VAR   struct V18 tmp14         
    

    If left in this state we eventually get confused about the GC impact of the resulting mov instruction and since this feeds an add we assert about inconsistent GC state at the add.

    Seems like we could spot this in inlining when stitching things back up and retype the source, or we could try and handle things later, though not sure when would be the best time.

    Am going to investigate what retyping early looks like.

  7. pentp commented on Nov 16, 2018

    @pentp
    Contributor

    Unsafe.AsRef<T> (void* source) should have very similar behaviour (long used as a byref) - how is that handled?

  8. AndyAyersMS commented on Nov 16, 2018

    @AndyAyersMS
    Member

    Good question. I am not sure how it differs yet.

  9. AndyAyersMS commented on Nov 20, 2018

    @AndyAyersMS
    Member

    Targeted retyping early happens to fix this case but I'm not sure if it is fully general (or how carefully I should match just this pattern vs general type mismatches).

    We're pretty lax about letting longs and byrefs be interchangeable, especially across inlines. And for the most part this seems to be tolerated pretty well downstream. But in this particular case we have a long being reinterpreted as a byref, and the "birth" of the byref happens in a reg-reg move with the same source and destination register. So we don't emit the mov instruction and so lose track of birthpoint and this is what triggers the assert.

    However there must be more to this case, because when I flag places where we skip emitting movs where the target is a GC type and the source is not, there are a fair number of hits in corelib that don't trigger asserts.

    So, will keep digging.

  10. AndyAyersMS commented on Nov 20, 2018

    @AndyAyersMS
    Member

    Based on some spot checks, the reason this case asserts and other similar cases don't is that in this case the value is subsequently used in an add, and we have a check that if an add is going to produce a byref it needs to have started with a byref (or a gcref).

    So if we lose track of a byref birth (because of an implicit cast that turns into a self-move that gets dropped as it seems pointless) and then don't add to the result, we probably won't notice that we lost the byref birth.

    I'm not quite sure where to go with this... one take is that we just need to patch things up (at some point, possibly when swapping in inline ret exprs as this can introduce in-tree reinterprets); another is that this whole setup of allowing byrefs and ints/longs to interoperate is fundamentally broken and will lead to GC holes and we need to add explicit casts and/or attempt to retype more broadly.

    @dotnet/jit-contrib thoughts?

  11. AndyAyersMS commented on Nov 20, 2018

    @AndyAyersMS
    Member

    Here's the "targed fix" which just tries to handle this one case.

  12. CarolEidt commented on Nov 21, 2018

    @CarolEidt
    Contributor

    I suspect that the only practical fix, at least in the near term, is to patch things up. I've never been entirely comfortable with the degree to which the JIT allows byrefs and native ints to interoperate, but I would imagine that unraveling that would be rather complex.

  13. briansull commented on Nov 26, 2018

    @briansull
    Contributor

    I agree with Carol's comments. The targeted fix looks fine.
    (Also remove the comments above the NoOptimizations attributes that you removed)

  14. briansull commented on Nov 26, 2018

    @briansull
    Contributor

    I also have concerns in general about the new Unsafe.As method as it could be breaking many other type checks/assumptions

  15. transferred this issue fromdotnet/coreclron Jan 31, 2020
  16. added this to the 3.0 milestone on Jan 31, 2020
  17. ghost locked as resolved and limited conversation to collaborators on Dec 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIbug

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions