protected void EmitDictionaryLookup(NodeFactory factory, ref LoongArch64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) { // INVARIANT: must not trash context register // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); } // Load the generic dictionary cell encoder.EmitLD(result, context, dictionarySlot * factory.Target.PointerSize); switch (lookup.LookupResultReferenceType(factory)) { case GenericLookupResultReferenceType.Indirect: // Do another indirection encoder.EmitLD(result, result, 0); break; case GenericLookupResultReferenceType.ConditionalIndirect: // andi temp, result, 0x1 // BEQZ temp L1 // addi.d result, result,-1 // L1: throw new NotImplementedException(); default: break; } }
protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter instructionEncoder, bool relocsOnly) { switch (_thunkKind) { case Kind.Eager: break; case Kind.DelayLoadHelper: case Kind.VirtualStubDispatch: // T8 contains indirection cell // Do nothing T8=R20 contains our first param if (!relocsOnly) { // movz T0=R12, #index int index = _containingImportSection.IndexFromBeginningOfArray; instructionEncoder.EmitMOV(Register.R12, checked ((ushort)index)); } // get pc // pcaddi T1=R13, 0 instructionEncoder.EmitPC(Register.R13); // load Module* -> T1 instructionEncoder.EmitLD(Register.R13, Register.R13, 0x24); // ld_d R13, R13, 0 instructionEncoder.EmitLD(Register.R13, Register.R13, 0); break; case Kind.Lazy: // get pc // pcaddi R5, 0 instructionEncoder.EmitPC(Register.R5); // load Module* -> R5=A1 instructionEncoder.EmitLD(Register.R5, Register.R5, 0x24); // ld_d R5, R5, 0 instructionEncoder.EmitLD(Register.R5, Register.R5, 0); break; default: throw new NotImplementedException(); } // branch to helper instructionEncoder.EmitJMP(_helperCell); // Emit relocation for the Module* load above if (_thunkKind != Kind.Eager) { instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64); } }
protected override void EmitLoadGenericContext(NodeFactory factory, ref LoongArch64Emitter 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.EmitLD(contextRegister, contextRegister, slotOffset); }
protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emitter encoder, bool relocsOnly) { // First load the generic context into the context register. EmitLoadGenericContext(factory, ref encoder, relocsOnly); Register contextRegister = GetContextRegister(ref encoder); switch (_id) { case ReadyToRunHelperId.GetNonGCStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); MetadataType target = (MetadataType)_target; 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.EmitADD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg0, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, factory.Target.PointerSize); encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg2, 1); encoder.EmitRETIfEqual(Register.R21); 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.GetGCStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0); MetadataType target = (MetadataType)_target; 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. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); encoder.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); encoder.EmitRETIfEqual(Register.R21); 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.GetThreadStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); MetadataType target = (MetadataType)_target; // Look up the index cell EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); ISymbolNode helperEntrypoint; if (factory.PreinitializationManager.HasLazyStaticConstructor(target)) { // There is a lazy class constructor. We need the non-GC static base because that's where the // class constructor context lives. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); int cctorContextSize = -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target); encoder.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, cctorContextSize); helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); } else { helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); } // 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.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, 0); // Second arg: index of the type in the ThreadStatic section of the modules encoder.EmitLD(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize); encoder.EmitJMP(helperEntrypoint); } break; case ReadyToRunHelperId.DelegateCtor: { // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor // method expects. Codegen also passed us the generic context in Arg2. // We now need to load the delegate target method into Arg2 (using a dictionary lookup) // and the optional 4th parameter, and call the ctor. Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); var target = (DelegateCreationInfo)_target; EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); 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; // 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: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: case ReadyToRunHelperId.ConstrainedDirectCall: { EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter 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.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); encoder.EmitLD(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.EmitADD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, factory.Target.PointerSize); encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg2, 1); encoder.EmitRETIfEqual(encoder.TargetRegister.IntraProcedureCallScratch1); 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.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, 0); // Second arg: index of the type in the ThreadStatic section of the modules encoder.EmitLD(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.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); encoder.EmitJE(encoder.TargetRegister.IntraProcedureCallScratch1, 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.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0); 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.EmitADD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, factory.Target.PointerSize); encoder.EmitXOR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg3, 1); encoder.EmitRETIfEqual(Register.R21); 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.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1, 0); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); } Debug.Assert(slot != -1); encoder.EmitLD(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: { // Not tested encoder.EmitBreak(); 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.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0, 0); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize)); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }