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

Implicit conversion from uint32& to uint32* should be reconsidered #39040

Closed
AaronRobinsonMSFT opened this issue Jul 9, 2020 · 9 comments · Fixed by #39105
Closed

Implicit conversion from uint32& to uint32* should be reconsidered #39040

AaronRobinsonMSFT opened this issue Jul 9, 2020 · 9 comments · Fixed by #39105
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Milestone

Comments

@AaronRobinsonMSFT
Copy link
Member

AaronRobinsonMSFT commented Jul 9, 2020

The implicit conversion was previously performed by the JIT and during subsequent test passes it was discovered that IL stub generated depended on this behavior. The IL stub generation code issue was fixed in ddd458e and an assert was added:

// We should not be passing gc typed args to an unmanaged call.
GenTree* arg = argUse.GetNode();
if (varTypeIsGC(arg->TypeGet()))
{
assert(!"*** invalid IL: gc type passed to unmanaged call");
}

During a recent investigation of a WPF issue, dotnet/wpf#3226, it was discovered that the C++/CLI compiler also emits IL that depends on the previous behavior. The WPF issue doesn't appear related to this change, but that investigation is on going. The FontFace::GetFileZero() function exhibits this behavior in a Windows_NT.x64.Checked build.

JIT dump section:

    [ 0]  19 (0x013) ldloc.s 6
    [ 1]  21 (0x015) ldloca.s 2 // uint32& instead of uint32* defined by signature below.
    [ 2]  23 (0x017) ldc.i4.0 0
    [ 3]  24 (0x018) conv.i8
    [ 3]  25 (0x019) ldloc.s 6
    [ 4]  27 (0x01b) ldind.i8
    [ 4]  28 (0x01c) ldc.i4.s 32
    [ 5]  30 (0x01e) conv.i8
    [ 5]  31 (0x01f) add
    [ 4]  32 (0x020) ldind.i8
    [ 4]  33 (0x021) calli 11000030
lvaGrabTemp returning 10 (V10 tmp1) called for impImportIndirectCall.


STMT00004 (IL 0x013...  ???)
               [000025] -A-XG-------              *  ASG       long  
               [000024] D------N----              +--*  LCL_VAR   long   V10 tmp1         
               [000023] *--XG-------              \--*  IND       long  
               [000022] ---XG-------                 \--*  ADD       long  
               [000019] *--XG-------                    +--*  IND       long  
               [000018] ------------                    |  \--*  LCL_VAR   long   V07 loc6         
               [000021] ------------                    \--*  CAST      long <- int
               [000020] ------------                       \--*  CNS_INT   int    32

In Compiler::impImportCall: opcode is calli, kind=0, callRetType is int, structSize is 0

Inline a CALLI PINVOKE call from method MS.Internal.Text.TextInterface.FontFace:GetFileZero():MS.Internal.Text.TextInterface.FontFile:this

At this point it appears that it would be prudent to bring back this behavior. See #35026 (comment).

/cc @AndyAyersMS @BruceForstall @JulieLeeMSFT @jkotas @jeffschwMSFT

@AaronRobinsonMSFT AaronRobinsonMSFT added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 9, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Jul 9, 2020
@AndyAyersMS
Copy link
Member

cc @dotnet/jit-contrib

We still have little to no C++/CLI testing in the runtime repo, which seems unfortunate, given how quirky its IL generation has been. In past releases we had been getting some coverage of this from desktop mirroring of jit changes, but no longer.

Actual jit behavior hasn't changed; we can presumably back off on asserting in some "safe" cases (eg address of a local) to reduce the spam from a checked jit.

@AaronRobinsonMSFT
Copy link
Member Author

Actual jit behavior hasn't changed; we can presumably back off on asserting in some "safe" cases (eg address of a local) to reduce the spam from a checked jit.

@AndyAyersMS That is good to know. Should that information about the local status be contained in the GenTree* or GenTreeCall::Use&?

@AndyAyersMS
Copy link
Member

Something like this should work (rearranged so the extra checking is clearly done only in debug/checked builds):

@@ -6881,14 +6881,22 @@ void Compiler::impPopArgsForUnmanagedCall(GenTree* call, CORINFO_SIG_INFO* sig)

     for (GenTreeCall::Use& argUse : GenTreeCall::UseList(args))
     {
-        // We should not be passing gc typed args to an unmanaged call.
         GenTree* arg = argUse.GetNode();
+        call->gtFlags |= arg->gtFlags & GTF_GLOB_EFFECT;
+
+#ifdef DEBUG
+        // We should not be passing gc typed args to an unmanaged call.
         if (varTypeIsGC(arg->TypeGet()))
         {
-            assert(!"*** invalid IL: gc type passed to unmanaged call");
-        }
+            // Tolerate, if this is clearly a stack address.
+            GenTreeLclVarCommon* lclNode = arg->IsLocalAddrExpr();

-        call->gtFlags |= arg->gtFlags & GTF_GLOB_EFFECT;
+            if (lclNode == nullptr)
+            {
+                assert(!"*** invalid IL: gc type passed to unmanaged call");
+            }
+        }
+#endif
``

@jkotas
Copy link
Member

jkotas commented Jul 10, 2020

Actual jit behavior hasn't changed; we can presumably back off on asserting in some "safe" cases

Do these "safe" cases have a risk of exposing us to a crash from #34279 again?

@AaronRobinsonMSFT
Copy link
Member Author

Applying the logic above handles several issues I had found. However it doesn't handle the case in FontFace::GetArrayOfGlyphIndices.

The problem signature is defined in ILDAsm as:

  IL_001b:  calli      unmanaged cdecl int32
               modopt([System.Runtime.CompilerServices.VisualC]System.Runtime.CompilerServices.IsLong)
               modopt([System.Runtime.CompilerServices.VisualC]System.Runtime.CompilerServices.CallConvCdecl)
                          ( native int,
                            uint32 modopt([System.Runtime]System.Runtime.CompilerServices.IsConst)*,
                            uint32,
                            uint16* )

JIT dump section:

****** START compiling MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this (MethodHash=dec00e2c)
Generating code for Windows x64
OPTIONS: Tier-1 compilation
OPTIONS: compCodeOpt = FAST_CODE
OPTIONS: compDbgCode = false
OPTIONS: compDbgInfo = true
OPTIONS: compDbgEnC  = false
OPTIONS: compProcedureSplitting   = false
OPTIONS: compProcedureSplittingEH = false
IL to import:
IL_0000  03                ldarg.1     
IL_0001  0c                stloc.2     
IL_0002  05                ldarg.3     
IL_0003  0b                stloc.1     
IL_0004  02                ldarg.0     
IL_0005  7b 2c 03 00 04    ldfld        0x400032C
IL_000a  28 39 03 00 06    call         0x6000339
IL_000f  0a                stloc.0     
IL_0010  06                ldloc.0     
IL_0011  08                ldloc.2     
IL_0012  04                ldarg.2     
IL_0013  07                ldloc.1     
IL_0014  06                ldloc.0     
IL_0015  4c                ldind.i8    
IL_0016  1f 58             ldc.i4.s     0x58
IL_0018  6a                conv.i8     
IL_0019  58                add         
IL_001a  4c                ldind.i8    
IL_001b  29 04 00 00 11    calli        0x11000004
IL_0020  02                ldarg.0     
IL_0021  7b 2c 03 00 04    ldfld        0x400032C
IL_0026  28 34 00 00 0a    call         0xA000034
IL_002b  28 3a 02 00 06    call         0x600023A
IL_0030  2a                ret         

lvaSetClass: setting class for V00 to (00007FFA911AA1B0) MS.Internal.Text.TextInterface.FontFace 
'this'    passed in register rcx
Arg #1    passed in register(s) rdx
Arg #2    passed in register(s) r8
Arg #3    passed in register(s) r9
Setting lvPinned for V05
Setting lvPinned for V06

lvaGrabTemp returning 7 (V07 tmp0) (a long lifetime temp) called for OutgoingArgSpace.
; Initial local variable assignments
;
;  V00 this              ref  this class-hnd
;  V01 arg1             long 
;  V02 arg2              int 
;  V03 arg3             long 
;  V04 loc0             long 
;  V05 loc1            byref  pinned
;  V06 loc2            byref  pinned
;  V07 OutArgs        lclBlk <na>  "OutgoingArgSpace"
*************** In compInitDebuggingInfo() for MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this
getVars() returned cVars = 0, extendOthers = true
info.compVarScopesCount = 7
    	VarNum 	LVNum 	      Name 	Beg 	End
 0: 	00h 	00h 	  V00 this 	000h   	031h
 1: 	01h 	01h 	  V01 arg1 	000h   	031h
 2: 	02h 	02h 	  V02 arg2 	000h   	031h
 3: 	03h 	03h 	  V03 arg3 	000h   	031h
 4: 	04h 	04h 	  V04 loc0 	000h   	031h
 5: 	05h 	05h 	  V05 loc1 	000h   	031h
 6: 	06h 	06h 	  V06 loc2 	000h   	031h
info.compStmtOffsetsCount    = 0
info.compStmtOffsetsImplicit = 0005h ( STACK_EMPTY CALL_SITE )
*************** In fgFindBasicBlocks() for MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this
Marked V04 as a single def local
Marked V05 as a single def local
Marked V06 as a single def local
Jump targets:
  none
New Basic Block BB01 [0000] created.
BB01 [000..031)
IL Code Size,Instr   49,  24, Basic Block count   1, Local Variable Num,Ref count   8, 12 for method MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this
OPTIONS: opts.MinOpts() == false
Basic block list for 'MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this'

-----------------------------------------------------------------------------------------------------------------------------------------
BBnum BBid ref try hnd                 weight    lp [IL range]     [jump]      [EH region]         [flags]
-----------------------------------------------------------------------------------------------------------------------------------------
BB01 [0000]  1                             1        [000..031)        (return)                     
-----------------------------------------------------------------------------------------------------------------------------------------

*************** Starting PHASE Pre-import

*************** Finishing PHASE Pre-import

*************** Starting PHASE Importation
*************** In impImport() for MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this

impImportBlockPending for BB01

Importing BB01 (PC=000) of 'MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this'
    [ 0]   0 (0x000) ldarg.1
    [ 1]   1 (0x001) stloc.2

STMT00000 (IL 0x000...  ???)
               [000002] -A----------              *  ASG       byref 
               [000001] D------N----              +--*  LCL_VAR   byref  V06 loc2         
               [000000] ------------              \--*  LCL_VAR   byref  V01 arg1         

    [ 0]   2 (0x002) ldarg.3
    [ 1]   3 (0x003) stloc.1

STMT00001 (IL 0x002...  ???)
               [000005] -A----------              *  ASG       byref 
               [000004] D------N----              +--*  LCL_VAR   byref  V05 loc1         
               [000003] ------------              \--*  LCL_VAR   byref  V03 arg3         

    [ 0]   4 (0x004) ldarg.0
    [ 1]   5 (0x005) ldfld 0400032C
    [ 1]  10 (0x00a) call 06000339
In Compiler::impImportCall: opcode is call, kind=0, callRetType is long, structSize is 0


STMT00002 (IL 0x004...  ???)
               [000008] I-CXG-------              *  CALL      long   MS.Internal.Text.TextInterface.Generics.NativePointerCriticalHandle<MS::Internal::Text::TextInterface::Native::IDWriteFontFace>.get_Value (exactContextHnd=0x00007FFA911A9871)
               [000007] ---XG------- this in rcx  \--*  FIELD     ref    _fontFace
               [000006] ------------                 \--*  LCL_VAR   ref    V00 this         

    [ 1]  15 (0x00f) stloc.0

STMT00003 (IL   ???...  ???)
               [000011] -AC---------              *  ASG       long  
               [000010] D------N----              +--*  LCL_VAR   long   V04 loc0         
               [000009] --C---------              \--*  RET_EXPR  long  (inl return from call [000008])

    [ 0]  16 (0x010) ldloc.0
    [ 1]  17 (0x011) ldloc.2
    [ 2]  18 (0x012) ldarg.2
    [ 3]  19 (0x013) ldloc.1
    [ 4]  20 (0x014) ldloc.0
    [ 5]  21 (0x015) ldind.i8
    [ 5]  22 (0x016) ldc.i4.s 88
    [ 6]  24 (0x018) conv.i8
    [ 6]  25 (0x019) add
    [ 5]  26 (0x01a) ldind.i8
    [ 5]  27 (0x01b) calli 11000004
lvaGrabTemp returning 8 (V08 tmp1) called for impImportIndirectCall.


STMT00004 (IL 0x010...  ???)
               [000023] -A-XG-------              *  ASG       long  
               [000022] D------N----              +--*  LCL_VAR   long   V08 tmp1         
               [000021] *--XG-------              \--*  IND       long  
               [000020] ---XG-------                 \--*  ADD       long  
               [000017] *--XG-------                    +--*  IND       long  
               [000016] ------------                    |  \--*  LCL_VAR   long   V04 loc0         
               [000019] ------------                    \--*  CAST      long <- int
               [000018] ------------                       \--*  CNS_INT   int    88

In Compiler::impImportCall: opcode is calli, kind=0, callRetType is int, structSize is 0

Inline a CALLI PINVOKE call from method MS.Internal.Text.TextInterface.FontFace:GetArrayOfGlyphIndices(long,int,long):this

@AndyAyersMS
Copy link
Member

Do these "safe" cases have a risk of exposing us to a crash from #34279 again?

We didn't change jit behavior, so we're already exposed.

In addition to not asserting, the jit can retype args that resolve to local addresses as native ints to avoid some cases.

@jkotas
Copy link
Member

jkotas commented Jul 10, 2020

Would it be possible to retype all GC byrefs and not just local addresses?

@AndyAyersMS
Copy link
Member

Yes, we could do that.

@AndyAyersMS
Copy link
Member

@AaronRobinsonMSFT I take it you're just running some WPF sample to repro on x86 to repro the failures you're seeing? Would like to confirm my fix gets past all the sticking points.

@AndyAyersMS AndyAyersMS removed the untriaged New issue has not been triaged by the area owner label Jul 10, 2020
@AndyAyersMS AndyAyersMS added this to the 5.0.0 milestone Jul 10, 2020
AndyAyersMS added a commit to AndyAyersMS/runtime that referenced this issue Jul 10, 2020
Make the jit more robust in cases where the IL producer is passing a byref
to an unmanaged caller, by retyping the argument as native int.

Allows the jit to produce self-consistent GC info and avoid the issues
seen in dotnet#34279, at least for byrefs.

Closes dotnet#39040.
AndyAyersMS added a commit that referenced this issue Jul 11, 2020
Make the jit more robust in cases where the IL producer is passing a byref
to an unmanaged caller, by retyping the argument as native int.

Allows the jit to produce self-consistent GC info and avoid the issues
seen in #34279, at least for byrefs.

Closes #39040.
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
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
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants