void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) { var entryCountReservation = builder.ReserveInt(); int entryCount = 0; for (int interfaceIndex = 0; interfaceIndex < _type.RuntimeInterfaces.Length; interfaceIndex++) { var interfaceType = _type.RuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList <MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; var implMethod = _type.GetClosestDefType().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { builder.EmitShort(checked ((short)interfaceIndex)); builder.EmitShort(checked ((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); builder.EmitShort(checked ((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod))); entryCount++; } } } builder.EmitInt(entryCountReservation, entryCount); }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory); // The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the // synchronization mechanism of the two values in the runtime. objData.Alignment = _targetMethod.Context.Target.PointerSize * 2; objData.DefinedSymbols.Add(this); objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); // The second cell field uses the two lower-order bits to communicate the contents. // We add 1 to signal IDC_CachePointerIsInterfacePointer. See src\Native\Runtime\inc\rhbinder.h. objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), 1); // End the run of dispatch cells objData.EmitZeroPointer(); // Avoid consulting VTable slots until they're guaranteed complete during final data emission if (!relocsOnly) { int interfaceMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod); if (factory.Target.PointerSize == 8) { objData.EmitLong(interfaceMethodSlot); } else { throw new NotImplementedException(); } } return(objData.ToObjectData()); }
public int Compare(InterfaceDispatchCellNode x, InterfaceDispatchCellNode y) { MethodDesc methodX = x.TargetMethod; MethodDesc methodY = y.TargetMethod; // The primary purpose of this comparer is to sort everything by slot int slotX = VirtualMethodSlotHelper.GetVirtualMethodSlot(_factory, methodX, methodX.OwningType); int slotY = VirtualMethodSlotHelper.GetVirtualMethodSlot(_factory, methodY, methodY.OwningType); int result = slotX - slotY; if (result != 0) { return(result); } // If slots are the same, compare the method and callsite identifier to get // a deterministic order within the group. result = _comparer.Compare(methodX, methodY); if (result != 0) { return(result); } result = StringComparer.Ordinal.Compare(x.CallSiteIdentifier, y.CallSiteIdentifier); if (result != 0) { return(result); } Debug.Assert(x == y); return(0); }
/// <summary> /// ICastable is a special interface whose two methods are not invoked using regular interface dispatch. /// Instead, their VTable slots are recorded on the EEType of an object implementing ICastable and are /// called directly. /// </summary> protected virtual void ComputeICastableVirtualMethodSlots(NodeFactory factory) { if (_type.IsInterface || !EmitVirtualSlotsAndInterfaces) { return; } foreach (DefType itf in _type.RuntimeInterfaces) { if (itf == factory.ICastableInterface) { MethodDesc isInstDecl = itf.GetKnownMethod("IsInstanceOfInterface", null); MethodDesc getImplTypeDecl = itf.GetKnownMethod("GetImplType", null); MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl); MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl); int isInstMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, isInstMethodImpl); int getImplTypeMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, getImplTypeMethodImpl); Debug.Assert(isInstMethodSlot != -1 && getImplTypeMethodSlot != -1); _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableIsInstSlot, (uint)isInstMethodSlot); _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableGetImplTypeSlot, (uint)getImplTypeMethodSlot); } } }
DispatchMapEntry[] BuildDispatchMap(NodeFactory factory) { ArrayBuilder <DispatchMapEntry> dispatchMapEntries = new ArrayBuilder <DispatchMapEntry>(); for (int i = 0; i < _type.RuntimeInterfaces.Length; i++) { var interfaceType = _type.RuntimeInterfaces[i]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList <MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; for (int j = 0; j < virtualSlots.Count; j++) { MethodDesc declMethod = virtualSlots[j]; var implMethod = _type.GetClosestMetadataType().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { var entry = new DispatchMapEntry(); entry.InterfaceIndex = checked ((short)i); entry.InterfaceMethodSlot = checked ((short)j); entry.ImplementationMethodSlot = checked ((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod)); dispatchMapEntries.Add(entry); } } } return(dispatchMapEntries.ToArray()); }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory); // The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the // synchronization mechanism of the two values in the runtime. objData.RequireInitialAlignment(_targetMethod.Context.Target.PointerSize * 2); objData.AddSymbol(this); if (factory.Target.Architecture == TargetArchitecture.ARM) { objData.EmitPointerReloc(factory.InitialInterfaceDispatchStub); } else { objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); } if (factory.Target.Abi == TargetAbi.CoreRT) { // TODO: Enable Indirect Pointer for Interface Dispatch Cell. See https://github.com/dotnet/corert/issues/2542 objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), (int)InterfaceDispatchCellCachePointerFlags.CachePointerIsInterfacePointerOrMetadataToken); } else { if (factory.CompilationModuleGroup.ContainsType(_targetMethod.OwningType)) { objData.EmitReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), RelocType.IMAGE_REL_BASED_RELPTR32, (int)InterfaceDispatchCellCachePointerFlags.CachePointerIsInterfaceRelativePointer); } else { objData.EmitReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), RelocType.IMAGE_REL_BASED_RELPTR32, (int)InterfaceDispatchCellCachePointerFlags.CachePointerIsIndirectedInterfaceRelativePointer); } if (objData.TargetPointerSize == 8) { // IMAGE_REL_BASED_RELPTR is a 32-bit relocation. However, the cell needs a full pointer // width there since a pointer to the cache will be written into the cell. Emit additional // 32 bits on targets whose pointer size is 64 bit. objData.EmitInt(0); } } // End the run of dispatch cells objData.EmitZeroPointer(); // Avoid consulting VTable slots until they're guaranteed complete during final data emission if (!relocsOnly) { objData.EmitNaturalInt(VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod)); } return(objData.ToObjectData()); }
// // returns the DebugInfoBlob containing the method token to virtual method slot mapping // .mdinfo format // offset 0, 4 bytes: version number // offset 4, 4 bytes: count of assemblies // // for each assembly // offset 0, 4 bytes: count of methods with a virtual method slot // // for each method // offset 0, 4 bytes: method def token (as they are in the input assembly) // offset 4, 2 bytes: length of the per method information [ currently always 4 ] // offset 6, 4 bytes: virtual slot number // methods are sorted by their method def token internal DebugInfoBlob GetDebugMethodInfoMap(NodeFactory factory) { Dictionary <EcmaAssembly, uint> originalAssemblyOrder = new Dictionary <EcmaAssembly, uint>(); List <SortedDictionary <uint, int> > moduleMethods = new List <SortedDictionary <uint, int> >(); // re-construct orginal assembly input order foreach (MergedAssemblyRecord mergedAssembly in _mergedAssemblies.MergedAssemblies) { uint assemblyIndex = AdjustIndex(mergedAssembly.AssemblyIndex, _mergedAssemblies.CorLibIndex); originalAssemblyOrder.Add(mergedAssembly.Assembly, assemblyIndex); moduleMethods.Add(new SortedDictionary <uint, int>()); } foreach (TypeDesc type in factory.MetadataManager.GetTypesWithConstructedEETypes()) { // skip if sealed if (type.IsSealed()) { continue; } // no generic support yet if (type is EcmaType) { EcmaType ecmaType = (EcmaType)type; EcmaAssembly ecmaAssembly = (EcmaAssembly)ecmaType.EcmaModule; int assemblyIndex = (int)originalAssemblyOrder[ecmaAssembly]; SortedDictionary <uint, int> methodList = moduleMethods[assemblyIndex]; foreach (MethodDesc md in type.GetAllMethods()) { // skip non-virtual and final methods if (!md.IsVirtual || md.IsFinal) { continue; } // skip generic if (md.HasInstantiation) { continue; } // method token. EntityHandle methodHandle = ((EcmaMethod)md).Handle; uint methodToken = (uint)MetadataTokens.GetToken(methodHandle); // find virtual method slot. MethodDesc declaringMethodForSlot = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(md.GetTypicalMethodDefinition()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, type); if (slot != -1 && !methodList.ContainsKey(methodToken)) { methodList.Add(methodToken, slot); } } } } return(ConvertToDebugInfoBlob(moduleMethods)); }
void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) { var entryCountReservation = builder.ReserveInt(); int entryCount = 0; TypeDesc declType = _type.GetClosestDefType(); // Catch any runtime interface collapsing. We shouldn't have any Debug.Assert(declType.RuntimeInterfaces.Length == declType.GetTypeDefinition().RuntimeInterfaces.Length); for (int interfaceIndex = 0; interfaceIndex < declType.RuntimeInterfaces.Length; interfaceIndex++) { var interfaceType = declType.RuntimeInterfaces[interfaceIndex]; var interfaceDefinitionType = declType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList <MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; if (!interfaceType.IsTypeDefinition) { declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); } var implMethod = declType.GetTypeDefinition().ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { TypeDesc implType = declType; while (!implType.HasSameTypeDefinition(implMethod.OwningType)) { implType = implType.BaseType; } MethodDesc targetMethod = implMethod; if (!implType.IsTypeDefinition) { targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); } builder.EmitShort(checked ((short)interfaceIndex)); builder.EmitShort(checked ((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); builder.EmitShort(checked ((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, declType))); entryCount++; } } } builder.EmitInt(entryCountReservation, entryCount); }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { Debug.Assert((EETypeNode.GetVTableOffset(factory.Target.PointerSize) % factory.Target.PointerSize) == 0, "vtable offset must be aligned"); ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); objData.AddSymbol(this); if (!relocsOnly) { var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize; objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod, _targetMethod.OwningType)); } return(objData.ToObjectData()); }
protected override void EmitLoadGenericContext(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { // We start with context register pointing to the MethodTable Register contextRegister = GetContextRegister(ref encoder); // Locate the VTable slot that points to the dictionary int vtableSlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); } int pointerSize = factory.Target.PointerSize; int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); // Load the dictionary pointer from the VTable encoder.EmitLDR(contextRegister, contextRegister, slotOffset); }
protected override void EmitLoadGenericContext(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { // We start with Arg0 pointing to the EEType // Locate the VTable slot that points to the dictionary int vtableSlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); } int pointerSize = factory.Target.PointerSize; int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); // Load the dictionary pointer from the VTable AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary); }
/// <summary> /// ICastable is a special interface whose two methods are not invoked using regular interface dispatch. /// Instead, their VTable slots are recorded on the EEType of an object implementing ICastable and are /// called directly. /// </summary> void ComputeICastableVirtualMethodSlots(NodeFactory factory) { // TODO: This method is untested (we don't support interfaces yet) if (_type.IsInterface) { return; } foreach (DefType itf in _type.RuntimeInterfaces) { if (itf == factory.ICastableInterface) { var isInstMethod = itf.GetMethod("IsInstanceOfInterface", null); var getImplTypeMethod = itf.GetMethod("GetImplType", null); Debug.Assert(isInstMethod != null && getImplTypeMethod != null); int isInstMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, isInstMethod); int getImplTypeMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, getImplTypeMethod); if (isInstMethodSlot != -1 || getImplTypeMethodSlot != -1) { var rareFlags = _optionalFieldsBuilder.GetFieldValue(EETypeOptionalFieldsElement.RareFlags, 0); rareFlags |= (uint)EETypeRareFlags.ICastableFlag; _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.RareFlags, rareFlags); } if (isInstMethodSlot != -1) { _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.ICastableIsInstSlot, (uint)isInstMethodSlot); } if (getImplTypeMethodSlot != -1) { _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.ICastableGetImplTypeSlot, (uint)getImplTypeMethodSlot); } } } }
private void GetCodeForReadyToRunGenericHelper(WebAssemblyCodegenCompilation compilation, ReadyToRunGenericHelperNode node, NodeFactory factory) { LLVMBuilderRef builder = compilation.Module.Context.CreateBuilder(); var args = new List <LLVMTypeRef>(); MethodDesc delegateCtor = null; if (node.Id == ReadyToRunHelperId.DelegateCtor) { DelegateCreationInfo target = (DelegateCreationInfo)node.Target; delegateCtor = target.Constructor.Method; bool isStatic = delegateCtor.Signature.IsStatic; int argCount = delegateCtor.Signature.Length; if (!isStatic) { argCount++; } for (int i = 0; i < argCount; i++) { TypeDesc argType; if (i == 0 && !isStatic) { argType = delegateCtor.OwningType; } else { argType = delegateCtor.Signature[i - (isStatic ? 0 : 1)]; } args.Add(ILImporter.GetLLVMTypeForTypeDesc(argType)); } } LLVMValueRef helperFunc = Module.GetNamedFunction(node.GetMangledName(factory.NameMangler)); if (helperFunc.Handle == IntPtr.Zero) { throw new Exception("if the function is requested here, it should have been created earlier"); } var helperBlock = helperFunc.AppendBasicBlock("genericHelper"); builder.PositionAtEnd(helperBlock); var importer = new ILImporter(builder, compilation, Module, helperFunc, delegateCtor); LLVMValueRef ctx; string gepName; if (node is ReadyToRunGenericLookupFromTypeNode) { // Locate the VTable slot that points to the dictionary int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)node.DictionaryOwner); int pointerSize = factory.Target.PointerSize; // Load the dictionary pointer from the VTable int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); var slotGep = builder.BuildGEP(helperFunc.GetParam(1), new[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)slotOffset, false) }, "slotGep"); var slotGepPtrPtr = builder.BuildPointerCast(slotGep, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "slotGepPtrPtr"); ctx = builder.BuildLoad(slotGepPtrPtr, "dictGep"); gepName = "typeNodeGep"; } else { ctx = helperFunc.GetParam(1); gepName = "paramGep"; } LLVMValueRef resVar = OutputCodeForDictionaryLookup(builder, factory, node, node.LookupSignature, ctx, gepName); switch (node.Id) { case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)node.Target; if (compilation.TypeSystemContext.HasLazyStaticConstructor(target)) { importer.OutputCodeForTriggerCctor(target, resVar); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)node.Target; var ptrPtrPtr = builder.BuildBitCast(resVar, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), "ptrPtrPtr"); resVar = builder.BuildLoad(builder.BuildLoad(ptrPtrPtr, "ind1"), "ind2"); if (compilation.TypeSystemContext.HasLazyStaticConstructor(target)) { GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); var nonGcStaticsBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "lazyGep"); importer.OutputCodeForTriggerCctor(target, nonGcStaticsBase); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)node.Target; if (compilation.TypeSystemContext.HasLazyStaticConstructor(target)) { GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); var threadStaticBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "tsGep"); importer.OutputCodeForTriggerCctor(target, threadStaticBase); } resVar = importer.OutputCodeForGetThreadStaticBaseForType(resVar).ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), builder); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)node.Target; MethodDesc constructor = target.Constructor.Method; var fatPtr = ILImporter.MakeFatPointer(builder, resVar, compilation); importer.OutputCodeForDelegateCtorInit(builder, helperFunc, constructor, fatPtr); } break; // These are all simple: just get the thing from the dictionary and we're done case ReadyToRunHelperId.TypeHandle: case ReadyToRunHelperId.MethodHandle: case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: case ReadyToRunHelperId.VirtualDispatchCell: case ReadyToRunHelperId.DefaultConstructor: break; default: throw new NotImplementedException(); } if (node.Id != ReadyToRunHelperId.DelegateCtor) { builder.BuildRet(resVar); } else { builder.BuildRetVoid(); } }
protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly) { if (relocsOnly) { return; } // The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the // synchronization mechanism of the two values in the runtime. builder.RequireInitialAlignment(factory.Target.PointerSize * 2); // This number chosen to be high enough that the cost of recording slot numbers is cheap. const int InterfaceDispatchCellRunLength = 32; const int NoSlot = -1; // // We emit the individual dispatch cells in groups. The purpose of the grouping is to save // us the number of slots we need to emit. The grouping looks like this: // // DispatchCell1 // DispatchCell2 // ... // DispatchCellN // Null // Slot of the above dispatch cells // int runLength = 0; int currentSlot = NoSlot; foreach (InterfaceDispatchCellNode node in NodesList) { MethodDesc targetMethod = node.TargetMethod; int targetSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); if (currentSlot == NoSlot) { // This is the first dispatch cell we're emitting currentSlot = targetSlot; } else if (currentSlot != targetSlot || runLength == InterfaceDispatchCellRunLength) { // Make sure we are sorted Debug.Assert(targetSlot >= currentSlot); // End the run of dispatch cells builder.EmitZeroPointer(); builder.EmitNaturalInt(currentSlot); currentSlot = targetSlot; runLength = 0; } node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); node.EncodeData(ref builder, factory, relocsOnly); builder.AddSymbol(node); runLength++; } if (runLength > 0) { // End the run of dispatch cells builder.EmitZeroPointer(); builder.EmitNaturalInt(currentSlot); } }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) { return(new ObjectData(Array.Empty <byte>(), Array.Empty <Relocation>(), 1, new ISymbolDefinitionNode[] { this })); } // Ensure the native layout blob has been saved factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); var writer = new NativeWriter(); var typeMapHashTable = new VertexHashtable(); Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); Dictionary <int, HashSet <TypeDesc> > methodsEmitted = new Dictionary <int, HashSet <TypeDesc> >(); // Get a list of all methods that have a method body and metadata from the metadata manager. foreach (var mappingEntry in factory.MetadataManager.GetMethodMapping(factory)) { MethodDesc method = mappingEntry.Entity; // The current format requires us to have an EEType for the owning type. We might want to lift this. if (!factory.MetadataManager.TypeGeneratesEEType(method.OwningType)) { continue; } // We have a method body, we have a metadata token, but we can't get an invoke stub. Bail. if (!factory.MetadataManager.IsReflectionInvokable(method)) { continue; } // Only virtual methods are interesting if (!NeedsVirtualInvokeInfo(method)) { continue; } // // When working with the ProjectN ABI, the entries in the map are based on the definition types. All // instantiations of these definition types will have the same vtable method entries. // On CoreRT, the vtable entries for each instantiated type might not necessarily exist. // Example 1: // If there's a call to Foo<string>.Method1 and a call to Foo<int>.Method2, Foo<string> will // not have Method2 in its vtable and Foo<int> will not have Method1. // Example 2: // If there's a call to Foo<string>.Method1 and a call to Foo<object>.Method2, given that both // of these instantiations share the same canonical form, Foo<__Canon> will have both method // entries, and therefore Foo<string> and Foo<object> will have both entries too. // For this reason, the entries that we write to the map in CoreRT will be based on the canonical form // of the method's containing type instead of the open type definition. // // Similarly, given that we use the open type definition for ProjectN, the slot numbers ignore dictionary // entries in the vtable, and computing the correct slot number in the presence of dictionary entries is // done at runtime. When working with the CoreRT ABI, the correct slot numbers will be written to the map, // and no adjustments will be performed at runtime. // TypeDesc containingTypeKey; if (factory.Target.Abi == TargetAbi.ProjectN) { containingTypeKey = method.OwningType.GetTypeDefinition(); } else { containingTypeKey = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); } HashSet <TypeDesc> cache; if (!methodsEmitted.TryGetValue(mappingEntry.MetadataHandle, out cache)) { methodsEmitted[mappingEntry.MetadataHandle] = cache = new HashSet <TypeDesc>(); } // Only one record is needed for any instantiation. if (!cache.Add(containingTypeKey)) { continue; } // Grammar of an entry in the hash table: // Virtual Method uses a normal slot // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1) + slot // OR // Generic Virtual Method // TypeKey + NameAndSig metadata offset into the native layout metadata + (NumberOfStepsUpParentHierarchyToType << 1 + 1) int parentHierarchyDistance; MethodDesc declaringMethodForSlot = GetDeclaringVirtualMethodAndHierarchyDistance(method, out parentHierarchyDistance); Vertex vertex = null; ISymbolNode containingTypeKeyNode = factory.NecessaryTypeSymbol(containingTypeKey); NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); if (method.HasInstantiation) { vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset), writer.GetUnsignedConstant(((uint)parentHierarchyDistance << 1) + VirtualInvokeTableEntry.GenericVirtualMethod)); } else { // Get the declaring method for slot on the instantiated declaring type int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, factory.Target.Abi != TargetAbi.ProjectN); Debug.Assert(slot != -1); vertex = writer.GetTuple( writer.GetUnsignedConstant(_externalReferences.GetIndex(containingTypeKeyNode)), writer.GetUnsignedConstant((uint)placedNameAndSig.SavedVertex.VertexOffset)); vertex = writer.GetTuple(vertex, writer.GetUnsignedConstant((uint)parentHierarchyDistance << 1), writer.GetUnsignedConstant((uint)slot)); } int hashCode = containingTypeKey.GetHashCode(); typeMapHashTable.Append((uint)hashCode, hashTableSection.Place(vertex)); } byte[] hashTableBytes = writer.Save(); _endSymbol.SetSymbolOffset(hashTableBytes.Length); return(new ObjectData(hashTableBytes, Array.Empty <Relocation>(), 1, new ISymbolDefinitionNode[] { this, _endSymbol })); }
void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) { var entryCountReservation = builder.ReserveShort(); var defaultEntryCountReservation = builder.ReserveShort(); int entryCount = 0; TypeDesc declType = _type.GetClosestDefType(); TypeDesc declTypeDefinition = declType.GetTypeDefinition(); DefType[] declTypeRuntimeInterfaces = declType.RuntimeInterfaces; DefType[] declTypeDefinitionRuntimeInterfaces = declTypeDefinition.RuntimeInterfaces; // Catch any runtime interface collapsing. We shouldn't have any Debug.Assert(declTypeRuntimeInterfaces.Length == declTypeDefinitionRuntimeInterfaces.Length); var defaultImplementations = new List <(int InterfaceIndex, int InterfaceMethodSlot, int ImplMethodSlot)>(); // Resolve all the interfaces, but only emit non-default implementations for (int interfaceIndex = 0; interfaceIndex < declTypeRuntimeInterfaces.Length; interfaceIndex++) { var interfaceType = declTypeRuntimeInterfaces[interfaceIndex]; var interfaceDefinitionType = declTypeDefinitionRuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList <MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; if (!interfaceType.IsTypeDefinition) { declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); } var implMethod = declTypeDefinition.ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { TypeDesc implType = declType; while (!implType.HasSameTypeDefinition(implMethod.OwningType)) { implType = implType.BaseType; } MethodDesc targetMethod = implMethod; if (!implType.IsTypeDefinition) { targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); } builder.EmitShort((short)checked ((ushort)interfaceIndex)); builder.EmitShort((short)checked ((ushort)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0)))); builder.EmitShort((short)checked ((ushort)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, declType))); entryCount++; } else { // Is there a default implementation? int?implSlot = null; DefaultInterfaceMethodResolution result = declTypeDefinition.ResolveInterfaceMethodToDefaultImplementationOnType(declMethod, out implMethod); if (result == DefaultInterfaceMethodResolution.DefaultImplementation) { DefType providingInterfaceDefinitionType = (DefType)implMethod.OwningType; implMethod = implMethod.InstantiateSignature(declType.Instantiation, Instantiation.Empty); implSlot = VirtualMethodSlotHelper.GetDefaultInterfaceMethodSlot(factory, implMethod, declType, providingInterfaceDefinitionType); } else if (result == DefaultInterfaceMethodResolution.Reabstraction) { implSlot = SpecialDispatchMapSlot.Reabstraction; } else if (result == DefaultInterfaceMethodResolution.Diamond) { implSlot = SpecialDispatchMapSlot.Diamond; } if (implSlot.HasValue) { defaultImplementations.Add(( interfaceIndex, interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0), implSlot.Value)); } } } } // Now emit the default implementations foreach (var defaultImplementation in defaultImplementations) { builder.EmitShort((short)checked ((ushort)defaultImplementation.InterfaceIndex)); builder.EmitShort((short)checked ((ushort)defaultImplementation.InterfaceMethodSlot)); builder.EmitShort((short)checked ((ushort)defaultImplementation.ImplMethodSlot)); } // Update the header builder.EmitShort(entryCountReservation, (short)checked ((ushort)entryCount)); builder.EmitShort(defaultEntryCountReservation, (short)checked ((ushort)defaultImplementations.Count)); }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); { int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead // mov arg1, arg0 encoder.Builder.EmitByte(0x48); encoder.Builder.EmitShort((short)((encoder.TargetRegister.Arg0 == Register.RCX) ? 0xD18B : 0xF78B)); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; if (!target.HasStaticConstructor) { Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; if (!target.HasStaticConstructor) { encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateInfo target = (DelegateInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target)); if (target.ShuffleThunk != null) { encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk)); } encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor)); } break; case ReadyToRunHelperId.InterfaceDispatch: { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target); if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; case ReadyToRunHelperId.GenericLookupFromThis: { int pointerSize = factory.Target.PointerSize; var lookupInfo = (GenericLookupDescriptor)Target; // Arg0 points to the EEType // Locate the VTable slot that points to the dictionary int vtableSlot = 0; if (!relocsOnly) { vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)lookupInfo.CanonicalOwner); } int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); // Load the dictionary pointer from the VTable AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary); // What's left now is the actual dictionary lookup goto case ReadyToRunHelperId.GenericLookupFromDictionary; } case ReadyToRunHelperId.GenericLookupFromDictionary: { var lookupInfo = (GenericLookupDescriptor)Target; // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data. dictionarySlot = factory.GenericDictionaryLayout(lookupInfo.CanonicalOwner).GetSlotForEntry(lookupInfo.Signature); } // Load the generic dictionary cell AddrMode loadEntry = new AddrMode( encoder.TargetRegister.Arg0, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadEntry); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; Debug.Assert(!targetMethod.OwningType.IsInterface); int pointerSize = factory.Target.PointerSize; int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); } encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.Arg0, 0); encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.InterproceduralScratch, (short)(EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize))); encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitSUB(encoder.TargetRegister.Arg2, ((byte)NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target))); // cmp [r2 + ptrSize], 1 encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, ((short)factory.Target.PointerSize)); encoder.EmitCMP(encoder.TargetRegister.Arg3, ((byte)1)); // return if cmp encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitMOV(encoder.TargetRegister.Arg0 /*Result*/, encoder.TargetRegister.Arg2); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); // Second arg: index of the type in the ThreadStatic section of the modules encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, ((short)factory.Target.PointerSize)); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); } else { encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitSUB(encoder.TargetRegister.Arg2, (byte)(NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target))); // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); // Get cctor pointer: offset is usually equal to the double size of the pointer, therefore we can use arm sub imm encoder.EmitSUB(encoder.TargetRegister.Arg2, (byte)(NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target))); // cmp [r2 + ptrSize], 1 encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, ((short)factory.Target.PointerSize)); encoder.EmitCMP(encoder.TargetRegister.Arg3, (byte)1); // return if cmp encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitMOV(encoder.TargetRegister.Arg0 /*Result*/, encoder.TargetRegister.Arg2); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod); } Debug.Assert(slot != -1); encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); } else { ISymbolNode targetMethodNode = target.GetTargetNode(factory); encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.ResolveVirtualFunction); /* *** ***NOT TESTED!!! *** ***MethodDesc targetMethod = (MethodDesc)Target; ***if (targetMethod.OwningType.IsInterface) ***{ ***encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); ***encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); ***} ***else ***{ ***if (relocsOnly) *** break; *** ***encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); *** ***int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); ***Debug.Assert(slot != -1); ***encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result, *** ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); ***encoder.EmitRET(); ***} */ } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target); if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; case ReadyToRunHelperId.ResolveGenericVirtualMethod: encoder.EmitINT3(); break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); } else { encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod); } Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot); } else { int delta = 0; ISymbolNode targetMethodNode = target.GetTargetNode(factory); if (targetMethodNode is IFatFunctionPointerNode) { delta = FatFunctionPointerConstants.Offset; } encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory), delta); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }
void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) { var entryCountReservation = builder.ReserveShort(); var defaultEntryCountReservation = builder.ReserveShort(); var staticEntryCountReservation = builder.ReserveShort(); var defaultStaticEntryCountReservation = builder.ReserveShort(); int entryCount = 0; TypeDesc declType = _type.GetClosestDefType(); TypeDesc declTypeDefinition = declType.GetTypeDefinition(); DefType[] declTypeRuntimeInterfaces = declType.RuntimeInterfaces; DefType[] declTypeDefinitionRuntimeInterfaces = declTypeDefinition.RuntimeInterfaces; // Catch any runtime interface collapsing. We shouldn't have any Debug.Assert(declTypeRuntimeInterfaces.Length == declTypeDefinitionRuntimeInterfaces.Length); var defaultImplementations = new List <(int InterfaceIndex, int InterfaceMethodSlot, int ImplMethodSlot)>(); var staticImplementations = new List <(int InterfaceIndex, int InterfaceMethodSlot, int ImplMethodSlot, int Context)>(); var staticDefaultImplementations = new List <(int InterfaceIndex, int InterfaceMethodSlot, int ImplMethodSlot, int Context)>(); // Resolve all the interfaces, but only emit non-static and non-default implementations for (int interfaceIndex = 0; interfaceIndex < declTypeRuntimeInterfaces.Length; interfaceIndex++) { var interfaceType = declTypeRuntimeInterfaces[interfaceIndex]; var interfaceDefinitionType = declTypeDefinitionRuntimeInterfaces[interfaceIndex]; Debug.Assert(interfaceType.IsInterface); IReadOnlyList <MethodDesc> virtualSlots = factory.VTable(interfaceType).Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; if (!interfaceType.IsTypeDefinition) { declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)interfaceDefinitionType); } var implMethod = declMethod.Signature.IsStatic ? declTypeDefinition.ResolveInterfaceMethodToStaticVirtualMethodOnType(declMethod) : declTypeDefinition.ResolveInterfaceMethodToVirtualMethodOnType(declMethod); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { TypeDesc implType = declType; while (!implType.HasSameTypeDefinition(implMethod.OwningType)) { implType = implType.BaseType; } MethodDesc targetMethod = implMethod; if (!implType.IsTypeDefinition) { targetMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(implMethod.GetTypicalMethodDefinition(), (InstantiatedType)implType); } int emittedInterfaceSlot = interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0); int emittedImplSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, declType); if (targetMethod.Signature.IsStatic) { // If this is a static virtual, also remember whether we need generic context. // The implementation is not callable without the generic context. // Instance methods acquire the generic context from `this` and don't need it. // The pointer to the generic context is stored in the owning type's vtable. int genericContext = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg() ? StaticVirtualMethodContextSource.ContextFromThisClass : StaticVirtualMethodContextSource.None; staticImplementations.Add((interfaceIndex, emittedInterfaceSlot, emittedImplSlot, genericContext)); } else { builder.EmitShort((short)checked ((ushort)interfaceIndex)); builder.EmitShort((short)checked ((ushort)emittedInterfaceSlot)); builder.EmitShort((short)checked ((ushort)emittedImplSlot)); entryCount++; } } else { // Is there a default implementation? int?implSlot = null; DefaultInterfaceMethodResolution result = declTypeDefinition.ResolveInterfaceMethodToDefaultImplementationOnType(declMethod, out implMethod); DefType providingInterfaceDefinitionType = null; if (result == DefaultInterfaceMethodResolution.DefaultImplementation) { providingInterfaceDefinitionType = (DefType)implMethod.OwningType; implMethod = implMethod.InstantiateSignature(declType.Instantiation, Instantiation.Empty); implSlot = VirtualMethodSlotHelper.GetDefaultInterfaceMethodSlot(factory, implMethod, declType, providingInterfaceDefinitionType); } else if (result == DefaultInterfaceMethodResolution.Reabstraction) { implSlot = SpecialDispatchMapSlot.Reabstraction; } else if (result == DefaultInterfaceMethodResolution.Diamond) { implSlot = SpecialDispatchMapSlot.Diamond; } if (implSlot.HasValue) { int emittedInterfaceSlot = interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0); if (declMethod.Signature.IsStatic) { int genericContext = StaticVirtualMethodContextSource.None; if (result == DefaultInterfaceMethodResolution.DefaultImplementation && implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // If this is a static virtual, also remember whether we need generic context. // The implementation is not callable without the generic context. // Instance methods acquire the generic context from `this` and don't need it. // For default interface methods, the generic context is acquired by indexing // into the interface list of the owning type. Debug.Assert(providingInterfaceDefinitionType != null); int indexOfInterface = Array.IndexOf(declTypeDefinitionRuntimeInterfaces, providingInterfaceDefinitionType); Debug.Assert(indexOfInterface >= 0); genericContext = StaticVirtualMethodContextSource.ContextFromFirstInterface + indexOfInterface; } staticDefaultImplementations.Add(( interfaceIndex, emittedInterfaceSlot, implSlot.Value, genericContext)); } else { defaultImplementations.Add(( interfaceIndex, emittedInterfaceSlot, implSlot.Value)); } } } } } // Now emit the default implementations foreach (var defaultImplementation in defaultImplementations) { builder.EmitShort((short)checked ((ushort)defaultImplementation.InterfaceIndex)); builder.EmitShort((short)checked ((ushort)defaultImplementation.InterfaceMethodSlot)); builder.EmitShort((short)checked ((ushort)defaultImplementation.ImplMethodSlot)); } // Now emit the static implementations foreach (var staticImplementation in staticImplementations) { builder.EmitShort((short)checked ((ushort)staticImplementation.InterfaceIndex)); builder.EmitShort((short)checked ((ushort)staticImplementation.InterfaceMethodSlot)); builder.EmitShort((short)checked ((ushort)staticImplementation.ImplMethodSlot)); builder.EmitShort((short)checked ((ushort)staticImplementation.Context)); } // Now emit the static default implementations foreach (var staticImplementation in staticDefaultImplementations) { builder.EmitShort((short)checked ((ushort)staticImplementation.InterfaceIndex)); builder.EmitShort((short)checked ((ushort)staticImplementation.InterfaceMethodSlot)); builder.EmitShort((short)checked ((ushort)staticImplementation.ImplMethodSlot)); builder.EmitShort((short)checked ((ushort)staticImplementation.Context)); } // Update the header builder.EmitShort(entryCountReservation, (short)checked ((ushort)entryCount)); builder.EmitShort(defaultEntryCountReservation, (short)checked ((ushort)defaultImplementations.Count)); builder.EmitShort(staticEntryCountReservation, (short)checked ((ushort)staticImplementations.Count)); builder.EmitShort(defaultStaticEntryCountReservation, (short)checked ((ushort)staticDefaultImplementations.Count)); }
protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; Debug.Assert(!targetMethod.OwningType.IsInterface); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); int pointerSize = factory.Target.PointerSize; int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); } encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitSUB(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, (short)factory.Target.PointerSize); encoder.EmitCMP(encoder.TargetRegister.Arg2, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). encoder.EmitLDR(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); // Second arg: index of the type in the ThreadStatic section of the modules encoder.EmitLDR(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); } else { encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, (short)factory.Target.PointerSize); encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); encoder.EmitJE(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, (short)factory.Target.PointerSize); encoder.EmitCMP(encoder.TargetRegister.Arg3, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); } Debug.Assert(slot != -1); encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); } else { encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { // Not tested encoder.EmitINT3(); encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result, ((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)))); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; Debug.Assert(!targetMethod.OwningType.IsInterface); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); } Debug.Assert(((NativeSequencePoint[])((INodeWithDebugInfo)this).GetNativeSequencePoints())[1].NativeOffset == encoder.Builder.CountBytes); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); } else { encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitJE(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); } Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot); } else { ISymbolNode targetMethodNode = target.GetTargetNode(factory); encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_object")); break; case ReadyToRunHelperId.VirtualCall: if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); { int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.IsInstanceOf: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__isinst_class")); break; case ReadyToRunHelperId.CastClass: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__castclass_class")); break; case ReadyToRunHelperId.NewArr1: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_array")); break; case ReadyToRunHelperId.GetNonGCStaticBase: if (!((MetadataType)Target).HasStaticConstructor) { Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Target)); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Target)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: if (!((MetadataType)Target).HasStaticConstructor) { encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Target)); AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateInfo target = (DelegateInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target)); if (target.ShuffleThunk != null) { encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk)); } encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor)); } break; default: throw new NotImplementedException(); } }