public IEnumerable <DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { ArrayBuilder <DependencyListEntry> result = new ArrayBuilder <DependencyListEntry>(); var lookupContext = new GenericLookupResultContext(_dictionaryOwner, typeInstantiation, methodInstantiation); switch (_id) { case ReadyToRunHelperId.GetGCStaticBase: case ReadyToRunHelperId.GetThreadStaticBase: { // If the type has a lazy static constructor, we also need the non-GC static base // because that's where the class constructor context is. TypeDesc type = (TypeDesc)_target; if (factory.TypeSystemContext.HasLazyStaticConstructor(type)) { result.Add( new DependencyListEntry( factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, lookupContext), "Dictionary dependency")); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo createInfo = (DelegateCreationInfo)_target; if (createInfo.NeedsVirtualMethodUseTracking) { MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); if (!factory.VTable(instantiatedTargetMethod.OwningType).HasFixedSlots) { result.Add( new DependencyListEntry( factory.VirtualMethodUse(instantiatedTargetMethod), "Dictionary dependency")); } // TODO: https://github.com/dotnet/corert/issues/3224 if (instantiatedTargetMethod.IsAbstract) { result.Add(new DependencyListEntry(factory.ReflectableMethod(instantiatedTargetMethod), "Abstract reflectable method")); } } } break; } // All generic lookups depend on the thing they point to result.Add(new DependencyListEntry( _lookupSignature.GetTarget(factory, lookupContext), "Dictionary dependency")); return(result.ToArray()); }
public IEnumerable <DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { DependencyList result = new DependencyList(); var lookupContext = new GenericLookupResultContext(_dictionaryOwner, typeInstantiation, methodInstantiation); switch (_id) { case ReadyToRunHelperId.GetGCStaticBase: case ReadyToRunHelperId.GetThreadStaticBase: { // If the type has a lazy static constructor, we also need the non-GC static base // because that's where the class constructor context is. TypeDesc type = (TypeDesc)_target; if (factory.PreinitializationManager.HasLazyStaticConstructor(type)) { result.Add( new DependencyListEntry( factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, lookupContext), "Dictionary dependency")); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo createInfo = (DelegateCreationInfo)_target; if (createInfo.NeedsVirtualMethodUseTracking) { MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); if (!factory.VTable(instantiatedTargetMethod.OwningType).HasFixedSlots) { result.Add( new DependencyListEntry( factory.VirtualMethodUse(instantiatedTargetMethod), "Dictionary dependency")); } factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, instantiatedTargetMethod); } } break; } // All generic lookups depend on the thing they point to result.Add(new DependencyListEntry( _lookupSignature.GetTarget(factory, lookupContext), "Dictionary dependency")); return(result.ToArray()); }
private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup) { #if DEBUG // In debug, write some bogus data to the struct to ensure we have filled everything // properly. fixed (CORINFO_LOOKUP* tmp = &pLookup) MemoryHelper.FillMemory((byte*)tmp, 0xcc, sizeof(CORINFO_LOOKUP)); #endif MethodDesc targetMethod = HandleToObject(pTargetMethod.hMethod); TypeDesc delegateTypeDesc = HandleToObject(delegateType); if (targetMethod.IsSharedByGenericInstantiations) { // If the method is not exact, fetch it as a runtime determined method. targetMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pTargetMethod); } bool isLdvirtftn = pTargetMethod.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldvirtftn; DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(delegateTypeDesc, targetMethod, isLdvirtftn); if (delegateInfo.NeedsRuntimeLookup) { pLookup.lookupKind.needsRuntimeLookup = true; MethodDesc contextMethod = methodFromContext(pTargetMethod.tokenContext); // We should not be inlining these. RyuJIT should have aborted inlining already. Debug.Assert(contextMethod == MethodBeingCompiled); pLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod); pLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.DelegateCtor; pLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(delegateInfo); } else { pLookup.lookupKind.needsRuntimeLookup = false; pLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, delegateInfo)); } }
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(); } }
public IEnumerable <DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { ArrayBuilder <DependencyListEntry> result = new ArrayBuilder <DependencyListEntry>(); switch (_id) { case ReadyToRunHelperId.GetGCStaticBase: case ReadyToRunHelperId.GetThreadStaticBase: { // If the type has a lazy static constructor, we also need the non-GC static base // because that's where the class constructor context is. TypeDesc type = (TypeDesc)_target; if (factory.TypeSystemContext.HasLazyStaticConstructor(type)) { // TODO: Make _dictionaryOwner a RuntimeDetermined type/method and make a substitution with // typeInstantiation/methodInstantiation to get a concrete type. Then pass the generic dictionary // node of the concrete type to the GetTarget call. Also change the signature of GetTarget to // take only the factory and dictionary as input. result.Add( new DependencyListEntry( factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, typeInstantiation, methodInstantiation, null), "Dictionary dependency")); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo createInfo = (DelegateCreationInfo)_target; if (createInfo.NeedsVirtualMethodUseTracking) { MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.InstantiateSignature(typeInstantiation, methodInstantiation); if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(instantiatedTargetMethod.OwningType)) { result.Add( new DependencyListEntry( factory.VirtualMethodUse(createInfo.TargetMethod.InstantiateSignature(typeInstantiation, methodInstantiation)), "Dictionary dependency")); } // TODO: https://github.com/dotnet/corert/issues/3224 if (instantiatedTargetMethod.IsAbstract) { result.Add(new DependencyListEntry(factory.ReflectableMethod(instantiatedTargetMethod), "Abstract reflectable method")); } } } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc instantiatedTarget = ((MethodDesc)_target).InstantiateSignature(typeInstantiation, methodInstantiation); if (!factory.CompilationModuleGroup.ShouldProduceFullVTable(instantiatedTarget.OwningType)) { result.Add( new DependencyListEntry( factory.VirtualMethodUse(instantiatedTarget), "Dictionary dependency")); } // TODO: https://github.com/dotnet/corert/issues/3224 if (instantiatedTarget.IsAbstract) { result.Add(new DependencyListEntry(factory.ReflectableMethod(instantiatedTarget), "Abstract reflectable method")); } } break; } // All generic lookups depend on the thing they point to result.Add(new DependencyListEntry( _lookupSignature.GetTarget(factory, typeInstantiation, methodInstantiation, null), "Dictionary dependency")); return(result.ToArray()); }
private ISymbolNode CreateDelegateCtorHelper(DelegateCreationInfo info, SignatureContext signatureContext) { return(info.Constructor); }
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(); } }
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 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(); } }
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: 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(); } }
private void ImportCall(ILOpcode opcode, int token) { // Strip runtime determined characteristics off of the method (because that's how RyuJIT operates) var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); MethodDesc method = runtimeDeterminedMethod; if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { method = runtimeDeterminedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } if (method.IsRawPInvoke()) { // Raw P/invokes don't have any dependencies. return; } string reason = null; switch (opcode) { case ILOpcode.newobj: reason = "newobj"; break; case ILOpcode.call: reason = "call"; break; case ILOpcode.callvirt: reason = "callvirt"; break; case ILOpcode.ldftn: reason = "ldftn"; break; case ILOpcode.ldvirtftn: reason = "ldvirtftn"; break; default: Debug.Assert(false); break; } if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (owningType.IsString) { // String .ctor handled specially below } else if (owningType.IsGCPointer) { if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); } if (owningType.IsMdArray) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); return; } else { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); } } if (owningType.IsDelegate) { // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) { // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) { int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); if (info.NeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } return; } } } } if (method.OwningType.IsDelegate && method.Name == "Invoke") { // TODO: might not want to do this if scanning for reflection. // This is expanded as an intrinsic, not a function call. return; } if (method.IsIntrinsic) { if (IsRuntimeHelpersInitializeArray(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsRuntimeTypeHandleGetValueInternal(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsActivatorDefaultConstructorOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]), reason); } else { MethodDesc ctor = method.Instantiation[0].GetDefaultConstructor(); if (ctor == null) { MetadataType activatorType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Activator"); MetadataType classWithMissingCtor = activatorType.GetKnownNestedType("ClassWithMissingConstructor"); ctor = classWithMissingCtor.GetParameterlessConstructor(); } _dependencies.Add(_factory.CanonicalEntrypoint(ctor), reason); } return; } if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value")) { return; } if (IsEETypePtrOf(method)) { if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.Instantiation[0]), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(method.Instantiation[0]), reason); } return; } } TypeDesc exactType = method.OwningType; if (method.IsNativeCallable && (opcode != ILOpcode.ldftn && opcode != ILOpcode.ldvirtftn)) { ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNativeCallable, method); } bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (_constrained != null) { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. TypeDesc constrained = _constrained; if (constrained.IsRuntimeDeterminedSubtype) { constrained = constrained.ConvertToCanonForm(CanonicalFormKind.Specific); } MethodDesc directMethod = constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (directMethod == null && constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrained, method); } if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; exactType = constrained; } else if (constrained.IsValueType) { // We'll need to box `this`. Note we use _constrained here, because the other one is canonical. AddBoxingDependencies(_constrained, reason); } } MethodDesc targetMethod = methodAfterConstraintResolution; bool exactContextNeedsRuntimeLookup; if (targetMethod.HasInstantiation) { exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { directCall = true; } } bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // Needs a single address to call this method but the method needs a hidden argument. // We need a fat function pointer for this that captures both things. if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); } } else if (directCall) { bool referencingArrayAddressMethod = false; if (targetMethod.IsIntrinsic) { // If this is an intrinsic method with a callsite-specific expansion, this will replace // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); // Array address method requires special dependency tracking. referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); } MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { _dependencies.Add(_factory.StringAllocator(targetMethod), reason); } else if (exactContextNeedsRuntimeLookup) { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); } else if (targetMethod.RequiresInstMethodTableArg()) { bool hasHiddenParameter = true; if (targetMethod.IsIntrinsic) { if (_factory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(targetMethod)) { hasHiddenParameter = false; } } if (hasHiddenParameter) { instParam = GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); } } if (instParam != null) { _dependencies.Add(instParam, reason); } _dependencies.Add(_factory.CanonicalEntrypoint(targetMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); if (targetMethod.RequiresInstMethodTableArg() && resolvedConstraint) { if (_constrained.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, _constrained), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(_constrained), reason); } } if (referencingArrayAddressMethod && !_isReadOnly) { // Address method is special - it expects an instantiation argument, unless a readonly prefix was applied. _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), reason); } } } else { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg() || (referencingArrayAddressMethod && !_isReadOnly)) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); } if (instParam != null) { _dependencies.Add(instParam, reason); if (!referencingArrayAddressMethod) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { // We don't want array Address method to be modeled in the generic dependency analysis. // The method doesn't actually have runtime determined dependencies (won't do // any generic lookups). _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } else if (targetMethod.AcquiresInstMethodTableFromThis()) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } } else if (method.HasInstantiation) { // Generic virtual method call if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } else if (method.OwningType.IsInterface) { if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); } } else if (_compilation.HasFixedSlotVTable(method.OwningType)) { // No dependencies: virtual call through the vtable } else { MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod).Normalize(); _dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason); } }
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 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; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.VirtualCall); } 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; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetNonGCStaticBase); } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetThreadStaticBase); } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetGCStaticBase); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.DelegateCtor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.ResolveVirtualFunction); } break; default: throw new NotImplementedException(); } }
private void ImportCall(ILOpcode opcode, int token) { // Strip runtime determined characteristics off of the method (because that's how RyuJIT operates) var runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); MethodDesc method = runtimeDeterminedMethod; if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) { method = runtimeDeterminedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } if (method.IsRawPInvoke()) { // Raw P/invokes don't have any dependencies. return; } string reason = null; switch (opcode) { case ILOpcode.newobj: reason = "newobj"; break; case ILOpcode.call: reason = "call"; break; case ILOpcode.callvirt: reason = "callvirt"; break; case ILOpcode.ldftn: reason = "ldftn"; break; case ILOpcode.ldvirtftn: reason = "ldvirtftn"; break; default: Debug.Assert(false); break; } // If we're scanning the fallback body because scanning the real body failed, don't trigger cctor. // Accessing the cctor could have been a reason why we failed. if (!_isFallbackBodyCompilation) { // Do we need to run the cctor? TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (_factory.TypeSystemContext.HasLazyStaticConstructor(owningType)) { // For beforefieldinit, we can wait for field access. if (!((MetadataType)owningType).IsBeforeFieldInit) { // Accessing the static base will trigger the cctor. if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetNonGCStaticBase, owningType), reason); } } } } if (opcode == ILOpcode.newobj) { TypeDesc owningType = runtimeDeterminedMethod.OwningType; if (owningType.IsString) { // String .ctor handled specially below } else { // Nullable needs to be unwrapped. if (owningType.IsNullable) { owningType = owningType.Instantiation[0]; } if (owningType.IsRuntimeDeterminedSubtype) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, owningType), reason); } else { _dependencies.Add(_factory.ConstructedTypeSymbol(owningType), reason); } if (owningType.IsMdArray) { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewMultiDimArr_NonVarArg), reason); return; } else { _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason); } } if (owningType.IsDelegate) { // If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1) { // TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj. ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]); if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn) { int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2); var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken); TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn); if (info.NeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } else { _dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason); } return; } } } } if (method.OwningType.IsDelegate && method.Name == "Invoke") { // TODO: might not want to do this if scanning for reflection. // This is expanded as an intrinsic, not a function call. return; } if (method.IsIntrinsic) { if (IsRuntimeHelpersInitializeArray(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } if (IsRuntimeTypeHandleGetValueInternal(method)) { if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.ldtoken) { return; } } } TypeDesc exactType = method.OwningType; bool resolvedConstraint = false; bool forceUseRuntimeLookup = false; MethodDesc methodAfterConstraintResolution = method; if (_constrained != null) { // We have a "constrained." call. Try a partial resolve of the constraint call. Note that this // will not necessarily resolve the call exactly, since we might be compiling // shared generic code - it may just resolve it to a candidate suitable for // JIT compilation, and require a runtime lookup for the actual code pointer // to call. MethodDesc directMethod = _constrained.GetClosestDefType().TryResolveConstraintMethodApprox(method.OwningType, method, out forceUseRuntimeLookup); if (directMethod == null && _constrained.IsEnum) { // Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference // type though, so we would fail to resolve and box. We have a special path for those to avoid boxing. directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(_constrained, method); } if (directMethod != null) { // Either // 1. no constraint resolution at compile time (!directMethod) // OR 2. no code sharing lookup in call // OR 3. we have have resolved to an instantiating stub methodAfterConstraintResolution = directMethod; Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface); resolvedConstraint = true; exactType = _constrained; } else if (_constrained.IsValueType) { // We'll need to box `this`. AddBoxingDependencies(_constrained, reason); } _constrained = null; } MethodDesc targetMethod = methodAfterConstraintResolution; bool exactContextNeedsRuntimeLookup; if (targetMethod.HasInstantiation) { exactContextNeedsRuntimeLookup = targetMethod.IsSharedByGenericInstantiations; } else { exactContextNeedsRuntimeLookup = exactType.IsCanonicalSubtype(CanonicalFormKind.Any); } // // Determine whether to perform direct call // bool directCall = false; if (targetMethod.Signature.IsStatic) { // Static methods are always direct calls directCall = true; } else if (targetMethod.OwningType.IsInterface) { // Force all interface calls to be interpreted as if they are virtual. directCall = false; } else if ((opcode != ILOpcode.callvirt && opcode != ILOpcode.ldvirtftn) || resolvedConstraint) { directCall = true; } else { if (!targetMethod.IsVirtual || targetMethod.IsFinal || targetMethod.OwningType.IsSealed()) { directCall = true; } } bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { // Needs a single address to call this method but the method needs a hidden argument. // We need a fat function pointer for this that captures both things. if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason); } } else if (directCall) { bool referencingArrayAddressMethod = false; if (targetMethod.IsIntrinsic) { // If this is an intrinsic method with a callsite-specific expansion, this will replace // the method with a method the intrinsic expands into. If it's not the special intrinsic, // method stays unchanged. targetMethod = _compilation.ExpandIntrinsicForCallsite(targetMethod, _canonMethod); // Array address method requires special dependency tracking. referencingArrayAddressMethod = targetMethod.IsArrayAddressMethod(); } MethodDesc concreteMethod = targetMethod; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.IsConstructor && targetMethod.OwningType.IsString) { _dependencies.Add(_factory.StringAllocator(targetMethod), reason); } else if (exactContextNeedsRuntimeLookup) { if (targetMethod.IsSharedByGenericInstantiations && !resolvedConstraint && !referencingArrayAddressMethod) { _dependencies.Add(_factory.RuntimeDeterminedMethod(runtimeDeterminedMethod), reason); } else { Debug.Assert(!forceUseRuntimeLookup); _dependencies.Add(_factory.MethodEntrypoint(targetMethod), reason); } } else { ISymbolNode instParam = null; if (targetMethod.RequiresInstMethodDescArg()) { instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod); } else if (targetMethod.RequiresInstMethodTableArg() || referencingArrayAddressMethod) { // Ask for a constructed type symbol because we need the vtable to get to the dictionary instParam = _compilation.NodeFactory.ConstructedTypeSymbol(concreteMethod.OwningType); } if (instParam != null) { _dependencies.Add(instParam, reason); if (!referencingArrayAddressMethod) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { // We don't want array Address method to be modeled in the generic dependency analysis. // The method doesn't actually have runtime determined dependencies (won't do // any generic lookups). _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } else if (targetMethod.AcquiresInstMethodTableFromThis()) { _dependencies.Add(_compilation.NodeFactory.ShadowConcreteMethod(concreteMethod), reason); } else { _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(targetMethod), reason); } } } else if (method.HasInstantiation) { // Generic virtual method call if (exactContextNeedsRuntimeLookup) { _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod), reason); } else { _dependencies.Add(_factory.RuntimeMethodHandle(runtimeDeterminedMethod), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); } else { ReadyToRunHelperId helper; if (opcode == ILOpcode.ldvirtftn) { helper = ReadyToRunHelperId.ResolveVirtualFunction; } else { Debug.Assert(opcode == ILOpcode.callvirt); helper = ReadyToRunHelperId.VirtualCall; } if (exactContextNeedsRuntimeLookup && targetMethod.OwningType.IsInterface) { _dependencies.Add(GetGenericLookupHelper(helper, runtimeDeterminedMethod), reason); } else { // Get the slot defining method to make sure our virtual method use tracking gets this right. // For normal C# code the targetMethod will always be newslot. MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); _dependencies.Add(_factory.ReadyToRunHelper(helper, slotDefiningMethod), reason); } } }
protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.NewHelper); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.VirtualCall); } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.IsInstanceOf); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.CastClass); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.NewArr1); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetNonGCStaticBase); } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetThreadStaticBase); } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.GetGCStaticBase); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.DelegateCtor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; ARMDebug.EmitHelperNYIAssert(factory, ref encoder, ReadyToRunHelperId.ResolveVirtualFunction); } break; default: throw new NotImplementedException(); } }