| File: Compiler\DependencyAnalysis\ReadyToRun\WasmVirtualDispatchThunkNode.cs | Web Access |
| Project: ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using ILCompiler.DependencyAnalysis.Wasm; using ILCompiler.DependencyAnalysisFramework; using ILCompiler.ObjectWriter; using ILCompiler.ObjectWriter.WasmInstructions; using Internal.JitInterface; using Internal.Text; using Internal.TypeSystem; using System; using System.Collections.Generic; using System.Diagnostics; namespace ILCompiler.DependencyAnalysis.ReadyToRun { /// <summary> /// Dispatches a virtual call through the vtable offsets stored in its portable entrypoint. /// </summary> public sealed class WasmVirtualDispatchThunkNode : StringDiscoverableAssemblyStubNode, INodeWithTypeSignature, ISymbolDefinitionNode, ISortableSymbolNode { private readonly TypeSystemContext _context; private readonly WasmSignature _wasmSignature; private readonly WasmTypeNode _typeNode; private readonly string _lookupString; public WasmVirtualDispatchThunkNode(NodeFactory factory, WasmSignature wasmSignature) { _context = factory.TypeSystemContext; _wasmSignature = wasmSignature; _typeNode = factory.WasmTypeNode(wasmSignature); _lookupString = GetLookupString(wasmSignature.FuncType); } public override bool StaticDependenciesAreComputed => true; public override bool IsShareable => false; public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection; public override string LookupString => _lookupString; MethodSignature INodeWithTypeSignature.Signature => WasmLowering.RaiseSignature(_wasmSignature, _context); bool INodeWithTypeSignature.IsUnmanagedCallersOnly => false; bool INodeWithTypeSignature.IsAsyncCall => _wasmSignature.SignatureString.Contains('a'); bool INodeWithTypeSignature.HasGenericContextArg => false; public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append("WasmVirtualDispatchThunk("u8); sb.Append(_lookupString); sb.Append(")"u8); } protected override string GetName(NodeFactory factory) { Utf8StringBuilder sb = new Utf8StringBuilder(); AppendMangledName(factory.NameMangler, sb); return sb.ToString(); } public override int ClassCode => 115732791; public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { WasmVirtualDispatchThunkNode otherNode = (WasmVirtualDispatchThunkNode)other; return _wasmSignature.FuncType.CompareTo(otherNode._wasmSignature.FuncType); } private static string GetLookupString(WasmFuncType funcType) { Utf8StringBuilder sb = new Utf8StringBuilder(); sb.Append('V'); if (funcType.Returns.Types.Length == 0) { sb.Append('v'); } else { foreach (WasmValueType type in funcType.Returns.Types) { AppendTypeCode(sb, type); } } foreach (WasmValueType type in funcType.Params.Types) { AppendTypeCode(sb, type); } return sb.ToString(); } private static void AppendTypeCode(Utf8StringBuilder sb, WasmValueType type) { sb.Append(type switch { WasmValueType.I32 => 'i', WasmValueType.I64 => 'l', WasmValueType.F32 => 'f', WasmValueType.F64 => 'd', WasmValueType.V128 => 'V', _ => throw new UnreachableException() }); } protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencies = base.ComputeNonRelocationBasedDependencies(factory); dependencies.Add(_typeNode, "Wasm virtual dispatch thunk requires type node"); return dependencies; } protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instructionEncoder, bool relocsOnly) { Debug.Assert(!instructionEncoder.Is64Bit); MethodSignature methodSignature = WasmLowering.RaiseSignature(_wasmSignature, _context); Debug.Assert(!methodSignature.IsStatic); int portableEntrypointLocalIndex = _typeNode.Type.Params.Types.Length - 1; int targetPortableEntrypointLocalIndex = _typeNode.Type.Params.Types.Length; int targetCodeLocalIndex = targetPortableEntrypointLocalIndex + 1; const int ThisLocalIndex = 1; const ulong PackedDispatchOffsetsField = 4; const ulong InitialEntryField = 8; List<WasmExpr> expressions = new List<WasmExpr>(); // targetPEP = *(*(*(this) + pep->offsetOfIndirection) + pep->offsetAfterIndirection) expressions.Add(Local.Get(ThisLocalIndex)); expressions.Add(I32.Load(0)); expressions.Add(Local.Get(portableEntrypointLocalIndex)); expressions.Add(I32.Load16_u(PackedDispatchOffsetsField)); expressions.Add(I32.Add); expressions.Add(I32.Load(0)); expressions.Add(Local.Get(portableEntrypointLocalIndex)); expressions.Add(I32.Load16_u(PackedDispatchOffsetsField + sizeof(ushort))); expressions.Add(I32.Add); expressions.Add(I32.Load(0)); expressions.Add(Local.Set(targetPortableEntrypointLocalIndex)); expressions.Add(Local.Get(targetPortableEntrypointLocalIndex)); expressions.Add(I32.Load(0)); expressions.Add(Local.Set(targetCodeLocalIndex)); // Redispatch through the original entrypoint if this receiver's target is not yet callable from R2R. expressions.Add(Local.Get(targetCodeLocalIndex)); expressions.Add(I32.Eqz); expressions.Add(Block.If(WasmBlockType.Empty)); expressions.Add(Local.Get(portableEntrypointLocalIndex)); expressions.Add(I32.Load(InitialEntryField)); expressions.Add(Local.Set(targetPortableEntrypointLocalIndex)); expressions.Add(Local.Get(targetPortableEntrypointLocalIndex)); expressions.Add(I32.Load(0)); expressions.Add(Local.Set(targetCodeLocalIndex)); expressions.Add(Block.End); for (int i = 0; i < portableEntrypointLocalIndex; i++) { expressions.Add(Local.Get(i)); } expressions.Add(Local.Get(targetPortableEntrypointLocalIndex)); expressions.Add(Local.Get(targetCodeLocalIndex)); expressions.Add(ControlFlow.CallIndirect(_typeNode, 0)); instructionEncoder.FunctionBody = new WasmFunctionBody( _typeNode.Type, new[] { WasmValueType.I32, WasmValueType.I32 }, expressions.ToArray()); } protected override void EmitCode(NodeFactory factory, ref X64.X64Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } protected override void EmitCode(NodeFactory factory, ref X86.X86Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } protected override void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } protected override void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } protected override void EmitCode(NodeFactory factory, ref LoongArch64.LoongArch64Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } protected override void EmitCode(NodeFactory factory, ref RiscV64.RiscV64Emitter instructionEncoder, bool relocsOnly) { throw new NotSupportedException(); } } }