Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Commit 3ce25bb

Browse files
committed
Merge pull request #633 from jkotas/jit-helpers
Proper implementations of JIT helpers
2 parents 53935c5 + 1ea2ab4 commit 3ce25bb

File tree

20 files changed

+467
-189
lines changed

20 files changed

+467
-189
lines changed

src/Common/src/Internal/Runtime/EETypeBuilderHelpers.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,29 @@ public static UInt16 ComputeFlags(TypeDesc type)
124124
return flags;
125125
}
126126

127+
public static bool ComputeRequiresAlign8(TypeDesc type)
128+
{
129+
if (type.Context.Target.Architecture != TargetArchitecture.ARM)
130+
{
131+
return false;
132+
}
133+
134+
if (type.IsArray)
135+
{
136+
var elementType = ((ArrayType)type).ElementType;
137+
if ((elementType.IsValueType) && ((DefType)elementType).InstanceByteAlignment > 4)
138+
{
139+
return true;
140+
}
141+
}
142+
else if (type is DefType && ((DefType)type).InstanceByteAlignment > 4)
143+
{
144+
return true;
145+
}
146+
147+
return false;
148+
}
149+
127150
// These masks and paddings have been chosen so that the ValueTypePadding field can always fit in a byte of data
128151
// if the alignment is 8 bytes or less. If the alignment is higher then there may be a need for more bits to hold
129152
// the rest of the padding data.

src/Common/src/TypeSystem/IL/Stubs/ArrayMethodILEmitter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private void EmitILForAccessor()
145145
codeStream.EmitLabel(rangeExceptionLabel); // Assumes that there is one "int" pushed on the stack
146146
codeStream.Emit(ILOpcode.pop);
147147

148-
MethodDesc throwHelper = _method.Context.GetHelperEntryPoint("ArrayMethodILHelpers", "ThrowIndexOutOfRangeException");
148+
MethodDesc throwHelper = _method.Context.GetHelperEntryPoint("ThrowHelpers", "ThrowIndexOutOfRangeException");
149149
codeStream.EmitCallThrowHelper(_emitter, throwHelper);
150150

151151
#if false

src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -239,29 +239,6 @@ private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData)
239239
objData.EmitShort((short)flags);
240240
}
241241

242-
private static bool ComputeRequiresAlign8(TypeDesc type)
243-
{
244-
if (type.Context.Target.Architecture != TargetArchitecture.ARM)
245-
{
246-
return false;
247-
}
248-
249-
if (type.IsArray)
250-
{
251-
var elementType = ((ArrayType)type).ElementType;
252-
if ((elementType.IsValueType) && ((DefType)elementType).InstanceByteAlignment > 4)
253-
{
254-
return true;
255-
}
256-
}
257-
else if (type is DefType && ((DefType)type).InstanceByteAlignment > 4)
258-
{
259-
return true;
260-
}
261-
262-
return false;
263-
}
264-
265242
private void OutputBaseSize(ref ObjectDataBuilder objData)
266243
{
267244
int pointerSize = _type.Context.Target.PointerSize;
@@ -440,7 +417,7 @@ void ComputeRareFlags()
440417
flags |= (uint)EETypeRareFlags.HasCctorFlag;
441418
}
442419

443-
if (ComputeRequiresAlign8(_type))
420+
if (EETypeBuilderHelpers.ComputeRequiresAlign8(_type))
444421
{
445422
flags |= (uint)EETypeRareFlags.RequiresAlign8Flag;
446423
}

src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo
2020
switch (Id)
2121
{
2222
case ReadyToRunHelperId.NewHelper:
23-
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Target));
24-
encoder.EmitJMP(factory.ExternSymbol("__allocate_object"));
23+
{
24+
TypeDesc target = (TypeDesc)Target;
25+
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
26+
encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target)));
27+
}
2528
break;
2629

2730
case ReadyToRunHelperId.VirtualCall:
@@ -40,33 +43,52 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo
4043
break;
4144

4245
case ReadyToRunHelperId.IsInstanceOf:
43-
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
44-
encoder.EmitJMP(factory.ExternSymbol("__isinst_class"));
46+
{
47+
TypeDesc target = (TypeDesc)Target;
48+
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
49+
encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false)));
50+
}
4551
break;
4652

4753
case ReadyToRunHelperId.CastClass:
48-
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
49-
encoder.EmitJMP(factory.ExternSymbol("__castclass_class"));
54+
{
55+
TypeDesc target = (TypeDesc)Target;
56+
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
57+
encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true)));
58+
}
5059
break;
5160

5261
case ReadyToRunHelperId.NewArr1:
53-
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
54-
encoder.EmitJMP(factory.ExternSymbol("__allocate_array"));
62+
{
63+
TypeDesc target = (TypeDesc)Target;
64+
65+
66+
// TODO: Swap argument order instead
67+
// mov arg1, arg0
68+
encoder.Builder.EmitByte(0x48);
69+
encoder.Builder.EmitShort((short)((encoder.TargetRegister.Arg0 == Register.RCX) ? 0xD18B : 0xF78B));
70+
71+
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.NecessaryTypeSymbol(target));
72+
encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target)));
73+
}
5574
break;
5675

5776
case ReadyToRunHelperId.GetNonGCStaticBase:
58-
if (!((MetadataType)Target).HasStaticConstructor)
5977
{
60-
Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase);
61-
encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Target));
62-
encoder.EmitRET();
63-
}
64-
else
65-
{
66-
// We need to trigger the cctor before returning the base
67-
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target));
68-
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Target));
69-
encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
78+
MetadataType target = (MetadataType)Target;
79+
if (!target.HasStaticConstructor)
80+
{
81+
Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase);
82+
encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target));
83+
encoder.EmitRET();
84+
}
85+
else
86+
{
87+
// We need to trigger the cctor before returning the base
88+
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target));
89+
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol(target));
90+
encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
91+
}
7092
}
7193
break;
7294

@@ -75,23 +97,26 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo
7597
break;
7698

7799
case ReadyToRunHelperId.GetGCStaticBase:
78-
if (!((MetadataType)Target).HasStaticConstructor)
79-
{
80-
encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Target));
81-
AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
82-
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
83-
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
84-
encoder.EmitRET();
85-
}
86-
else
87100
{
88-
// We need to trigger the cctor before returning the base
89-
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target));
90-
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Target));
91-
AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
92-
encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
93-
encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
94-
encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
101+
MetadataType target = (MetadataType)Target;
102+
if (!target.HasStaticConstructor)
103+
{
104+
encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
105+
AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
106+
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
107+
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
108+
encoder.EmitRET();
109+
}
110+
else
111+
{
112+
// We need to trigger the cctor before returning the base
113+
encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target));
114+
encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol(target));
115+
AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
116+
encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
117+
encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
118+
encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
119+
}
95120
}
96121
break;
97122

src/ILCompiler.Compiler/src/Compiler/JitHelper.cs

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
using Internal.TypeSystem;
1111

12+
using Internal.IL;
13+
using Internal.Runtime;
14+
1215
namespace ILCompiler
1316
{
1417
public enum JitHelperId
@@ -91,44 +94,118 @@ public enum JitHelperId
9194

9295
internal class JitHelper
9396
{
94-
static public string GetMangledName(JitHelperId id)
97+
/// <summary>
98+
/// Returns JIT helper entrypoint. JIT helpers can be either implemented by entrypoint with given mangled name or
99+
/// by a method in class library.
100+
/// </summary>
101+
static public void GetEntryPoint(TypeSystemContext context, JitHelperId id, out string mangledName, out MethodDesc methodDesc)
95102
{
103+
mangledName = null;
104+
methodDesc = null;
105+
96106
switch (id)
97107
{
108+
case JitHelperId.Throw:
109+
mangledName = "RhpThrowEx";
110+
break;
111+
case JitHelperId.Rethrow:
112+
mangledName = "RhpRethrow";
113+
break;
114+
115+
case JitHelperId.Overflow:
116+
methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowOverflowException");
117+
break;
98118
case JitHelperId.RngChkFail:
99-
return "__range_check_fail";
119+
methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowIndexOutOfRangeException");
120+
break;
121+
case JitHelperId.FailFast:
122+
mangledName = "__fail_fast"; // TODO: Report stack buffer overrun
123+
break;
124+
case JitHelperId.ThrowNullRef:
125+
methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowNullReferenceException");
126+
break;
127+
case JitHelperId.ThrowDivZero:
128+
methodDesc = context.GetHelperEntryPoint("ThrowHelpers", "ThrowDivideByZeroException");
129+
break;
100130

101131
case JitHelperId.WriteBarrier:
102-
return "RhpAssignRef";
103-
132+
mangledName = "RhpAssignRef";
133+
break;
104134
case JitHelperId.CheckedWriteBarrier:
105-
return "RhpCheckedAssignRef";
106-
135+
mangledName = "RhpCheckedAssignRef";
136+
break;
107137
case JitHelperId.ByRefWriteBarrier:
108-
return "RhpByRefAssignRef";
109-
110-
case JitHelperId.Throw:
111-
return "__throw_exception";
112-
113-
case JitHelperId.FailFast:
114-
return "__fail_fast";
138+
mangledName = "RhpByRefAssignRef";
139+
break;
115140

116141
case JitHelperId.NewMultiDimArr:
117-
return "RhNewMDArray";
142+
mangledName = "RhNewMDArray";
143+
break;
118144

119145
case JitHelperId.Stelem_Ref:
120-
return "__stelem_ref";
146+
mangledName = "RhpStelemRef";
147+
break;
121148
case JitHelperId.Ldelema_Ref:
122-
return "__ldelema_ref";
149+
mangledName = "RhpLdelemaRef";
150+
break;
123151

124152
case JitHelperId.MemCpy:
125-
return "memcpy";
153+
mangledName = "memcpy"; // TODO: Null reference handling
154+
break;
155+
case JitHelperId.MemSet:
156+
mangledName = "memset"; // TODO: Null reference handling
157+
break;
158+
159+
case JitHelperId.GetRuntimeTypeHandle: // TODO: Reflection
160+
case JitHelperId.GetRuntimeMethodHandle:
161+
case JitHelperId.GetRuntimeFieldHandle:
162+
mangledName = "__fail_fast";
163+
break;
126164

127165
default:
128-
// TODO: Uncomment once all helpers are implemented
129-
// throw new NotImplementedException();
130-
return "__fail_fast";
166+
throw new NotImplementedException(id.ToString());
167+
}
168+
}
169+
170+
//
171+
// These methods are static compiler equivalent of RhGetRuntimeHelperForType
172+
//
173+
static public string GetNewObjectHelperForType(TypeDesc type)
174+
{
175+
if (EETypeBuilderHelpers.ComputeRequiresAlign8(type))
176+
{
177+
if (type.HasFinalizer)
178+
return "RhpNewFinalizableAlign8";
179+
180+
if (type.IsValueType)
181+
return "RhpNewFastMisalign";
182+
183+
return "RhpNewFastAlign8";
131184
}
185+
186+
if (type.HasFinalizer)
187+
return "RhpNewFinalizable";
188+
189+
return "RhpNewFast";
190+
}
191+
192+
static public string GetNewArrayHelperForType(TypeDesc type)
193+
{
194+
if (EETypeBuilderHelpers.ComputeRequiresAlign8(type))
195+
return "RhpNewArrayAlign8";
196+
197+
return "RhpNewArray";
198+
}
199+
200+
static public string GetCastingHelperNameForType(TypeDesc type, bool throwing)
201+
{
202+
if (type.IsSzArray)
203+
return throwing ? "RhTypeCast_CheckCastArray" : "RhTypeCast_IsInstanceOfArray";
204+
205+
if (type.IsInterface)
206+
return throwing ? "RhTypeCast_CheckCastInterface" : "RhTypeCast_IsInstanceOfInterface";
207+
208+
return throwing ? "RhTypeCast_CheckCastClass" : "RhTypeCast_IsInstanceOfClass";
132209
}
133210
}
134211
}

src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ private void ImportCasting(ILOpcode opcode, int token)
611611

612612
AddTypeReference(type, false);
613613

614-
Append(opcode == ILOpcode.isinst ? "__isinst_class" : "__castclass_class");
614+
Append(opcode == ILOpcode.isinst ? "__isinst" : "__castclass");
615615
Append("(");
616616
Append(value.Value.Name);
617617
Append(", ");

src/JitInterface/src/CorInfoImpl.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,10 +1606,21 @@ private uint getThreadTLSIndex(ref void* ppIndirection)
16061606
case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: id = JitHelperId.FltRound; break;
16071607

16081608
default:
1609-
throw new NotImplementedException();
1609+
throw new NotImplementedException(ftnNum.ToString());
16101610
}
16111611

1612-
return (void*)ObjectToHandle(_compilation.NodeFactory.ExternSymbol(JitHelper.GetMangledName(id)));
1612+
string mangledName;
1613+
MethodDesc methodDesc;
1614+
JitHelper.GetEntryPoint(_compilation.TypeSystemContext, id, out mangledName, out methodDesc);
1615+
Debug.Assert(mangledName != null || methodDesc != null);
1616+
1617+
ISymbolNode entryPoint;
1618+
if (mangledName != null)
1619+
entryPoint = _compilation.NodeFactory.ExternSymbol(mangledName);
1620+
else
1621+
entryPoint = _compilation.NodeFactory.MethodEntrypoint(methodDesc);
1622+
1623+
return (void*)ObjectToHandle(entryPoint);
16131624
}
16141625

16151626
private void getFunctionEntryPoint(CORINFO_METHOD_STRUCT_* ftn, ref CORINFO_CONST_LOOKUP pResult, CORINFO_ACCESS_FLAGS accessFlags)

0 commit comments

Comments
 (0)