Skip to content

Pinning a string should prefer string.GetPinnableReference over RuntimeHelpers.GetOffsetToStringData #9251

@GrabYourPitchforks

Description

@GrabYourPitchforks

In .NET Core 3.0 we introduced the method string.GetPinnableReference, which retrieves a readonly char& pointing to the first character of the string's data. (If the string is empty, it returns a reference to the null terminator.)

It is preferred for compilers to use this method instead of RuntimeHelpers.GetOffsetToStringData when pinning string instances. The GetOffsetToStringData method assumes that the character data is inline with the string object itself. We want to experiment with breaking this assumption in future versions of .NET, and we need to get compilers off of this API and onto the preferred GetPinnableReference method instead.

If the application is targeting a previous version of .NET - one that doesn't include string.GetPinnableReference - then the compiler should continue to fall back to GetOffsetToStringData.

Repro steps

#nowarn "9"
let foo(s : string) =
    use ptr = fixed s
    ptr

Expected IL

.method private hidebysig static 
	char* foo (
		string s
	) cil managed 
{
	// Method begins at RVA 0x2050
	// Code size 16 (0x10)
	.maxstack 1
	.locals init (
		[0] char& pinned
	)

	IL_0000: ldarg.0
	IL_0001: brtrue.s IL_0006

	IL_0003: ldc.i4.0
	IL_0004: conv.u
	IL_0005: ret

	IL_0006: ldarg.0
	IL_0007: call instance char& modreq([System.Runtime]System.Runtime.InteropServices.InAttribute) [System.Runtime]System.String::GetPinnableReference()
	IL_000c: stloc.0
	IL_000d: ldloc.0
	IL_000e: conv.u
	IL_000f: ret
} // end of method Program::foo

Actual IL

.method public static 
	char* foo (
		string s
	) cil managed 
{
	// Method begins at RVA 0x2050
	// Code size 16 (0x10)
	.maxstack 4
	.locals init (
		[0] string pinned
	)

	IL_0000: ldarg.0
	IL_0001: stloc.0
	IL_0002: ldarg.0
	IL_0003: brfalse.s IL_000e

	IL_0005: ldarg.0
	IL_0006: conv.i
	IL_0007: call int32 [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData()
	IL_000c: add
	IL_000d: ret

	IL_000e: ldarg.0
	IL_000f: ret
} // end of method Program::foo

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions