Skip to content

Commit 00ca7be

Browse files
committed
Preinit: Support string manipulation
Makes it possible to allocate and preinitialize strings. Extends arithmetic support to handle `nint operator int32` operations Adds xor / not / shr support and extends casting for floating point types
1 parent d3eeb0e commit 00ca7be

File tree

3 files changed

+154
-14
lines changed

3 files changed

+154
-14
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.NativeAot.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public partial class String
1818
[Intrinsic]
1919
public static readonly string Empty = "";
2020

21+
[Intrinsic]
2122
internal static unsafe string FastAllocateString(int length)
2223
{
2324
// We allocate one extra char as an interop convenience so that our strings are null-

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs

Lines changed: 131 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -379,11 +379,11 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
379379
// and resetting it would lead to unpredictable analysis durations.
380380
int baseInstructionCounter = instructionCounter;
381381
Status status = nestedPreinit.TryScanMethod(field.OwningType.GetStaticConstructor(), null, recursionProtect, ref instructionCounter, out Value _);
382+
recursionProtect.Pop();
382383
if (!status.IsSuccessful)
383384
{
384385
return Status.Fail(methodIL.OwningMethod, opcode, "Nested cctor failed to preinit");
385386
}
386-
recursionProtect.Pop();
387387
Value value = nestedPreinit._fieldValues[field];
388388
if (value is ValueTypeValue)
389389
stack.PushFromLocation(field.FieldType, value);
@@ -489,17 +489,16 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
489489
}
490490

491491
Value retVal;
492-
if (!method.IsIntrinsic || !TryHandleIntrinsicCall(method, methodParams, out retVal))
492+
if (!method.IsIntrinsic || !TryHandleIntrinsicCall(context, method, methodParams, out retVal))
493493
{
494494
recursionProtect ??= new Stack<MethodDesc>();
495495
recursionProtect.Push(methodIL.OwningMethod);
496496
Status callResult = TryScanMethod(method, methodParams, recursionProtect, ref instructionCounter, out retVal);
497+
recursionProtect.Pop();
497498
if (!callResult.IsSuccessful)
498499
{
499-
recursionProtect.Pop();
500500
return callResult;
501501
}
502-
recursionProtect.Pop();
503502
}
504503

505504
if (!methodSig.ReturnType.IsVoid)
@@ -665,13 +664,11 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
665664
recursionProtect ??= new Stack<MethodDesc>();
666665
recursionProtect.Push(methodIL.OwningMethod);
667666
Status ctorCallResult = TryScanMethod(ctor, ctorParameters, recursionProtect, ref instructionCounter, out _);
667+
recursionProtect.Pop();
668668
if (!ctorCallResult.IsSuccessful)
669669
{
670-
recursionProtect.Pop();
671670
return ctorCallResult;
672671
}
673-
674-
recursionProtect.Pop();
675672
}
676673

677674
stack.PushFromLocation(owningType, instance);
@@ -824,6 +821,8 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
824821
case ILOpcode.conv_u2:
825822
case ILOpcode.conv_u4:
826823
case ILOpcode.conv_u8:
824+
case ILOpcode.conv_r4:
825+
case ILOpcode.conv_r8:
827826
{
828827
StackEntry popped = stack.Pop();
829828
if (popped.ValueKind.WithNormalizedNativeInt(context) == StackValueKind.Int32)
@@ -863,6 +862,12 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
863862
case ILOpcode.conv_u8:
864863
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((uint)val));
865864
break;
865+
case ILOpcode.conv_r4:
866+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
867+
break;
868+
case ILOpcode.conv_r8:
869+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(val));
870+
break;
866871
default:
867872
return Status.Fail(methodIL.OwningMethod, opcode);
868873
}
@@ -901,6 +906,12 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
901906
case ILOpcode.conv_u8:
902907
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(val));
903908
break;
909+
case ILOpcode.conv_r4:
910+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
911+
break;
912+
case ILOpcode.conv_r8:
913+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(val));
914+
break;
904915
default:
905916
return Status.Fail(methodIL.OwningMethod, opcode);
906917
}
@@ -910,15 +921,47 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
910921
double val = popped.Value.AsDouble();
911922
switch (opcode)
912923
{
924+
case ILOpcode.conv_u:
925+
case ILOpcode.conv_i:
926+
stack.Push(StackValueKind.NativeInt,
927+
context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((long)val) : ValueTypeValue.FromInt32((int)val));
928+
break;
929+
case ILOpcode.conv_i1:
930+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((sbyte)val));
931+
break;
932+
case ILOpcode.conv_i2:
933+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((short)val));
934+
break;
935+
case ILOpcode.conv_i4:
936+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val));
937+
break;
913938
case ILOpcode.conv_i8:
914939
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val));
915940
break;
941+
case ILOpcode.conv_u1:
942+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((byte)val));
943+
break;
944+
case ILOpcode.conv_u2:
945+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)val));
946+
break;
947+
case ILOpcode.conv_u4:
948+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val));
949+
break;
950+
case ILOpcode.conv_u8:
951+
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val));
952+
break;
953+
case ILOpcode.conv_r4:
954+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
955+
break;
956+
case ILOpcode.conv_r8:
957+
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(val));
958+
break;
916959
default:
917960
return Status.Fail(methodIL.OwningMethod, opcode);
918961
}
919962
}
920963
else if (popped.ValueKind == StackValueKind.ByRef
921-
&& (opcode == ILOpcode.conv_i || opcode == ILOpcode.conv_u)
964+
&& (opcode is ILOpcode.conv_i or ILOpcode.conv_u)
922965
&& (reader.PeekILOpcode() is (>= ILOpcode.ldind_i1 and <= ILOpcode.ldind_ref) or ILOpcode.ldobj))
923966
{
924967
// In the interpreter memory model, there's no conversion from a byref to an integer.
@@ -1379,13 +1422,29 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
13791422
StackEntry value = stack.Pop();
13801423
if (value.ValueKind == StackValueKind.Int32)
13811424
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(-value.Value.AsInt32()));
1425+
else if (value.ValueKind == StackValueKind.Int64)
1426+
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(-value.Value.AsInt64()));
1427+
else
1428+
return Status.Fail(methodIL.OwningMethod, opcode);
1429+
}
1430+
break;
1431+
1432+
case ILOpcode.not:
1433+
{
1434+
StackEntry value = stack.Pop();
1435+
if (value.ValueKind == StackValueKind.Int32)
1436+
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(~value.Value.AsInt32()));
1437+
else if (value.ValueKind == StackValueKind.Int64)
1438+
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(~value.Value.AsInt64()));
13821439
else
13831440
return Status.Fail(methodIL.OwningMethod, opcode);
13841441
}
13851442
break;
13861443

13871444
case ILOpcode.or:
1445+
case ILOpcode.xor:
13881446
case ILOpcode.shl:
1447+
case ILOpcode.shr:
13891448
case ILOpcode.add:
13901449
case ILOpcode.sub:
13911450
case ILOpcode.mul:
@@ -1395,13 +1454,24 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
13951454
case ILOpcode.rem:
13961455
case ILOpcode.rem_un:
13971456
{
1398-
bool isDivRem = opcode == ILOpcode.div || opcode == ILOpcode.div_un
1399-
|| opcode == ILOpcode.rem || opcode == ILOpcode.rem_un;
1457+
bool isDivRem = opcode is ILOpcode.div or ILOpcode.div_un
1458+
or ILOpcode.rem or ILOpcode.rem_un;
14001459

14011460
StackEntry value2 = stack.Pop();
14021461
StackEntry value1 = stack.Pop();
14031462

14041463
bool isNint = value1.ValueKind == StackValueKind.NativeInt || value2.ValueKind == StackValueKind.NativeInt;
1464+
if (isNint && context.Target.PointerSize == 8)
1465+
{
1466+
if (value1.ValueKind == StackValueKind.Int32)
1467+
{
1468+
value1 = new StackEntry(StackValueKind.NativeInt, ValueTypeValue.FromInt64(value1.Value.AsInt32()));
1469+
}
1470+
else if (value2.ValueKind == StackValueKind.Int32)
1471+
{
1472+
value2 = new StackEntry(StackValueKind.NativeInt, ValueTypeValue.FromInt64(value2.Value.AsInt32()));
1473+
}
1474+
}
14051475

14061476
if (value1.ValueKind.WithNormalizedNativeInt(context) == StackValueKind.Int32 && value2.ValueKind.WithNormalizedNativeInt(context) == StackValueKind.Int32)
14071477
{
@@ -1411,7 +1481,9 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
14111481
int result = opcode switch
14121482
{
14131483
ILOpcode.or => value1.Value.AsInt32() | value2.Value.AsInt32(),
1484+
ILOpcode.xor => value1.Value.AsInt32() ^ value2.Value.AsInt32(),
14141485
ILOpcode.shl => value1.Value.AsInt32() << value2.Value.AsInt32(),
1486+
ILOpcode.shr => value1.Value.AsInt32() >> value2.Value.AsInt32(),
14151487
ILOpcode.add => value1.Value.AsInt32() + value2.Value.AsInt32(),
14161488
ILOpcode.sub => value1.Value.AsInt32() - value2.Value.AsInt32(),
14171489
ILOpcode.and => value1.Value.AsInt32() & value2.Value.AsInt32(),
@@ -1433,6 +1505,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
14331505
long result = opcode switch
14341506
{
14351507
ILOpcode.or => value1.Value.AsInt64() | value2.Value.AsInt64(),
1508+
ILOpcode.xor => value1.Value.AsInt64() ^ value2.Value.AsInt64(),
14361509
ILOpcode.add => value1.Value.AsInt64() + value2.Value.AsInt64(),
14371510
ILOpcode.sub => value1.Value.AsInt64() - value2.Value.AsInt64(),
14381511
ILOpcode.and => value1.Value.AsInt64() & value2.Value.AsInt64(),
@@ -1451,7 +1524,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
14511524
if (isDivRem && value2.Value.AsDouble() == 0)
14521525
return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero");
14531526

1454-
if (opcode == ILOpcode.or || opcode == ILOpcode.shl || opcode == ILOpcode.and || opcode == ILOpcode.div_un || opcode == ILOpcode.rem_un)
1527+
if (opcode is ILOpcode.or or ILOpcode.xor or ILOpcode.shl or ILOpcode.shr or ILOpcode.and or ILOpcode.div_un or ILOpcode.rem_un)
14551528
ThrowHelper.ThrowInvalidProgramException();
14561529

14571530
double result = opcode switch
@@ -1467,9 +1540,14 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
14671540
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(result));
14681541
}
14691542
else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int32
1470-
&& opcode == ILOpcode.shl)
1543+
&& opcode is ILOpcode.shl or ILOpcode.shr)
14711544
{
1472-
long result = value1.Value.AsInt64() << value2.Value.AsInt32();
1545+
long result = opcode switch
1546+
{
1547+
ILOpcode.shl => value1.Value.AsInt64() << value2.Value.AsInt32(),
1548+
ILOpcode.shr => value1.Value.AsInt64() >> value2.Value.AsInt32(),
1549+
_ => throw new NotImplementedException(), // unreachable
1550+
};
14731551
stack.Push(isNint ? StackValueKind.NativeInt : StackValueKind.Int64, ValueTypeValue.FromInt64(result));
14741552
}
14751553
else if ((value1.ValueKind == StackValueKind.ByRef && value2.ValueKind != StackValueKind.ByRef)
@@ -1679,6 +1757,8 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
16791757
case ILOpcode.ldind_i4:
16801758
case ILOpcode.ldind_u4:
16811759
case ILOpcode.ldind_i8:
1760+
case ILOpcode.ldind_r4:
1761+
case ILOpcode.ldind_r8:
16821762
{
16831763
if (opcode == ILOpcode.ldobj)
16841764
{
@@ -1864,12 +1944,49 @@ private static BaseValueTypeValue NewUninitializedLocationValue(TypeDesc locatio
18641944
}
18651945
}
18661946

1867-
private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out Value retVal)
1947+
private bool TryHandleIntrinsicCall(TypeSystemContext context, MethodDesc method, Value[] parameters, out Value retVal)
18681948
{
18691949
retVal = default;
18701950

18711951
switch (method.Name)
18721952
{
1953+
case "Memmove":
1954+
if (method.OwningType is MetadataType spanHelpersType
1955+
&& spanHelpersType.Name == "SpanHelpers" && spanHelpersType.Namespace == "System"
1956+
&& spanHelpersType.Module == spanHelpersType.Context.SystemModule
1957+
&& parameters[0] is ByRefValue dest
1958+
&& parameters[1] is ByRefValue src
1959+
&& parameters[2] is ValueTypeValue len)
1960+
{
1961+
int length = context.Target.PointerSize == 8 ? checked ((int)len.AsInt64()) : len.AsInt32();
1962+
var srcSpan = new Span<byte>(src.PointedToBytes, src.PointedToOffset, length);
1963+
var dstSpan = new Span<byte>(dest.PointedToBytes, dest.PointedToOffset, length);
1964+
srcSpan.CopyTo(dstSpan);
1965+
return true;
1966+
}
1967+
return false;
1968+
case "IsReferenceOrContainsReferences":
1969+
if (method.OwningType is MetadataType rtType
1970+
&& rtType.Name == "RuntimeHelpers" && rtType.Namespace == "System.Runtime.CompilerServices"
1971+
&& rtType.Module == rtType.Context.SystemModule
1972+
&& method.Instantiation.Length == 1)
1973+
{
1974+
var type = method.Instantiation[0];
1975+
bool result = type.IsGCPointer || (type is DefType { ContainsGCPointers: true });
1976+
retVal = ValueTypeValue.FromSByte(result ? (sbyte)1 : (sbyte)0);
1977+
return true;
1978+
}
1979+
return false;
1980+
case "FastAllocateString":
1981+
if (method.OwningType is MetadataType stringType
1982+
&& stringType.Name == "String" && stringType.Namespace == "System"
1983+
&& stringType.Module == stringType.Context.SystemModule
1984+
&& parameters[0] is ValueTypeValue strSize)
1985+
{
1986+
retVal = new StringInstance(context.GetWellKnownType(WellKnownType.String), new string('\0', strSize.AsInt32()));
1987+
return true;
1988+
}
1989+
return false;
18731990
case "InitializeArray":
18741991
if (method.OwningType is MetadataType mdType
18751992
&& mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices"

0 commit comments

Comments
 (0)