Skip to content

IntPtr and int/long assignment compatibility #74258

@AnakinRaW

Description

@AnakinRaW

Description

Context:
While working with IL bytecode I noticed that e.g. in PresentationCore.dll of WPF there is a method which uses the cpblk instruction original code. The compiled IL code looks as follows:

.method assembly static int16 MS.Internal.TtfDelta.ReadBytes (
    valuetype MS.Internal.TtfDelta.TTFACC_FILEBUFFERINFO* pInputBufferInfo,
    uint8* puchBuffer,
    uint32 modopt([System.Runtime.CompilerServices.VisualC]System.Runtime.CompilerServices.IsLong) ulOffset,
    uint32 modopt([System.Runtime.CompilerServices.VisualC]System.Runtime.CompilerServices.IsLong) Count
) cil managed 

IL Code					Stack (with unsigned types)
[...]
IL_000e: ldarg.1			[byte*]
IL_000f: ldarg.2			[uint, byte*]
IL_0010: conv.u8			[ulong, byte*]
IL_0011: ldarg.0			[TTFACC_FILEBUFFERINFO*, ulong, byte*]
IL_0012: ldind.i8			[long, ulong, byte*]
IL_0013: add				[ulong, byte*]
IL_0014: ldarg.3			[uint, ulong, byte*]
IL_0015: conv.u8			[ulong, ulong, byte*]
IL_0016: unaligned. 1
IL_0019: cpblk				[EMPTY]
[...]

I know the stack only knows int32, int64, nint, F, O, & . I just have the real types in the diagram for demonstration what the code is effectively doing

According to III.3.30 cpblk requires the inputs of destaddr and srcaddr either to be IntPtr or managed pointer &.
However, supposing my stack diagram is correct, the instruction in this code gets an ulong for srcaddr. I'm a little confused what's going on here or why this is correct. For older PresentationCore.dll I also found situations where uint was used for srcaddr but I suppose this is because of the target architecture being x86 or x64?

Reproduction Steps

I tried to create a simple example app which should work like the example form PresentationCore.

It looks as follows:

.assembly A
{
    .ver 0:0:0:0
}

.class public auto ansi abstract sealed beforefieldinit C
    extends [mscorlib]System.Object
{
    .method private hidebysig static void Main () cil managed 
    {
        .maxstack 8
        .entrypoint
        .locals init (
            [0] valuetype S,
            [1] valuetype S
        )

        IL_0000: ldloca.s 0
        IL_0002: ldloca.s 1
        IL_0003: ldind.u8
        IL_0004: ldc.i8 8
        IL_000d: conv.u8
        IL_000e: unaligned. 1
        IL_000f: cpblk
        IL_0011: ret
    }
}

.class private sequential ansi sealed beforefieldinit S
    extends [mscorlib]System.ValueType
{
}

Expected behavior

Actually, I would expect that the code provided in the description does not even exist, because the spec does not cover the case you can observe in the PresentationCore.dll code.

However on I.8.7.3 ECMA says:

A location type T is assignable-to a location type U if one of the following holds:
    [...]
    4. T has intermediate type native int and U has intermediate type int32, or vice-versa.

However, I'm not sure whether this rule applies to operation on the stack. I'd expect that even if nint assignable-to int32 IL code requires explicit conversion, since cpblk is not a method call. If it was a method then of course III.1.6 implicit argument coercion would apply.

Furthermore the mentioned rule 4 in I.8.7.3 seems not to be implemented anywhere in CLR and NativeAOT (CastingHelpers.cs) .
E.g.

typeof(int).IsAssignableTo(typeof(nint)); // False
typeof(int).IsAssignableTo(typeof(IntPtr)); // False
typeof(nint).IsAssignableTo(typeof(int)); // False
typeof(nint).IsAssignableTo(typeof(int)); // False

always returns false. As far as I can tell rules from I.8.7.3 are never mentioned, just the ones from I.8.7.1 or I.8.7.2.

Actual behavior

Running the code form the Repro text, results in a NullReferenceException. This is kind of expected and even the spec tells to throw this exceptions in situations where an invalid address was detected.

Yet this still does not explain why such code gets compiled at all, either crafted (as the repro examble) or in binaries like PresentationCore.dll

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

As i said, I'm a little confused for the whole situation. It could be possible that I over/misread some parts of the ecma spec, tho currently to me it feels as there is some documentation missing regarding nint and int/long compatibility on opcode input parameters.
Regarding the not covered rule 4 of I.8.7.3 I'd like to know if this really is not implemented in CLR or, if it is, where I can find it.

category:correctness
theme:floating-point
skill-level:intermediate
cost:small
impact:small

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIquestionAnswer questions and provide assistance, not an issue with source code or documentation.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions