From cc9ddb802db20fb060df5e5ea3984ec4cc4fc159 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sat, 30 Nov 2024 22:53:26 -0800 Subject: [PATCH 01/19] InstCompoundIf --- .../Instructions/Transpiler/InstCompoundIf.cs | 124 ++++++++++++++++++ .../Runtime/Transpiler/FunctionTranspiler.cs | 24 +++- 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs diff --git a/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs b/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs new file mode 100644 index 0000000..9648998 --- /dev/null +++ b/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs @@ -0,0 +1,124 @@ +// /* +// * Copyright 2024 Kelvin Nishikawa +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +using System; +using System.IO; +using FluentValidation; +using Wacs.Core.OpCodes; +using Wacs.Core.Runtime; +using Wacs.Core.Types; +using Wacs.Core.Utilities; +using Wacs.Core.Validation; + +namespace Wacs.Core.Instructions.Transpiler +{ + public class InstCompoundIf : BlockTarget, IBlockInstruction + { + private static readonly ByteCode IfOp = OpCode.If; + private static readonly ByteCode ElseOp = OpCode.Else; + private Block IfBlock = Block.Empty; + private Block ElseBlock = Block.Empty; + private int ElseCount; + public override ByteCode Op => IfOp; + + private Func valueFunc; + + public InstCompoundIf( + BlockType type, + InstructionSequence ifSeq, + InstructionSequence elseSeq, + ITypedValueProducer valueProducer) + { + IfBlock = new Block( + type: type, + seq: ifSeq + ); + ElseBlock = new Block( + type: type, + seq: elseSeq + ); + ElseCount = ElseBlock.Instructions.Count; + + valueFunc = valueProducer.GetFunc; + } + + public BlockType Type => IfBlock.Type; + + public int Count => ElseBlock.Length == 0 ? 1 : 2; + + public int Size => 1 + IfBlock.Size + ElseBlock.Size; + public Block GetBlock(int idx) => idx == 0 ? IfBlock : ElseBlock; + + // @Spec 3.3.8.5 if + public override void Validate(IWasmValidationContext context) + { + try + { + var ifType = context.Types.ResolveBlockType(IfBlock.Type); + context.Assert(ifType, "Invalid BlockType: {0}",IfBlock.Type); + + //Pop the predicate + // context.OpStack.PopI32(); + + //Check the parameters [t1*] and discard + context.OpStack.PopValues(ifType.ParameterTypes, ref _aside); + _aside.Clear(); + + //ControlStack will push the values back on (Control Frame is our Label) + context.PushControlFrame(IfOp, ifType); + + //Continue on to instructions in sequence + // *any (end) contained within will pop the control frame and check values + // *any (else) contained within will pop and repush the control frame + context.ValidateBlock(IfBlock); + + var elseType = context.Types.ResolveBlockType(ElseBlock.Type); + if (!ifType.Equivalent(elseType)) + throw new ValidationException($"If block returned type {ifType} without matching else block"); + + if (ElseBlock.Length == 0) + return; + + //Continue on to instructions in sequence + // *(end) contained within will pop the control frame and check values + context.ValidateBlock(ElseBlock, 1); + } + catch (IndexOutOfRangeException exc) + { + _ = exc; + //Types didn't hit + context.Assert(false, + "Instruction loop invalid. BlockType {0} did not exist in the Context.",IfBlock.Type); + } + } + + // @Spec 4.4.8.5. if + public override void Execute(ExecContext context) + { + int c = valueFunc(context); + if (c != 0) + { + context.EnterBlock(this, IfBlock); + } + else + { + if (ElseCount != 0) + context.EnterBlock(this, ElseBlock); + } + } + + } +} \ No newline at end of file diff --git a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs index e23103b..a3effd9 100644 --- a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs +++ b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs @@ -85,7 +85,29 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) case InstIf instIf: var newIfSeq = OptimizeSequence(instIf.GetBlock(0).Instructions); var newElseSeq = OptimizeSequence(instIf.GetBlock(1).Instructions); - var newIf = new InstIf().Immediate(instIf.Type, newIfSeq, newElseSeq); + + BlockTarget? newIf = null; + if (stack.Count > 0) + { + var prevInst = stack.Pop(); + var intProducer = prevInst switch { + ITypedValueProducer p => new UnwrapValueI32(p), + ITypedValueProducer p => new CastToI32(p), + ITypedValueProducer p => p, + _ => null + }; + if (intProducer != null) + { + newIf = new InstCompoundIf(instIf.Type, newIfSeq, newElseSeq, intProducer); + } + else + { + stack.Push(prevInst); + } + } + if (newIf == null) + newIf = new InstIf().Immediate(instIf.Type, newIfSeq, newElseSeq); + //copy stackheight newIf.Label = new Label(instIf.Label); From c5bc29d23796381f34b025bdebae96bbbdfb7476 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sat, 30 Nov 2024 23:08:21 -0800 Subject: [PATCH 02/19] Removing a DPA attribute --- Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs index a3effd9..e9b8086 100644 --- a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs +++ b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs @@ -54,7 +54,6 @@ public static void TranspileFunction(FunctionInstance function) function.SetBody(new Expression(function.Type.ResultType.Arity, newSeq, false)); } - [SuppressMessage("ReSharper.DPA", "DPA0000: DPA issues")] private static InstructionSequence OptimizeSequence(InstructionSequence seq) { if (seq.Count == 0) From 7f772a3df44c55a280b0e984efb7293b3baf634c Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sat, 30 Nov 2024 23:54:55 -0800 Subject: [PATCH 03/19] Optimizing Function Invoke --- Wacs.Core/Runtime/ExecContext.cs | 7 +++--- Wacs.Core/Runtime/OpStack.cs | 25 +++++++++++++++++++-- Wacs.Core/Runtime/Types/FunctionInstance.cs | 19 ++++++---------- Wacs.Core/Types/IndexSpace.cs | 5 ++++- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index d85a0e3..44cb904 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -124,7 +124,7 @@ public Frame ReserveFrame( frame.ClearLabels(); int capacity = type.ParameterTypes.Types.Length + locals.Length; var localData = _localsDataPool.Rent(capacity); - frame.Locals = new(localData, type.ParameterTypes.Types, locals); + frame.Locals = new(localData, type.ParameterTypes.Types, locals, true); frame.StackHeight = OpStack.Count; return frame; @@ -282,8 +282,6 @@ public void Invoke(FuncAddr addr) //2. var funcInst = Store[addr]; - if (funcInst.IsAsync) - throw new WasmRuntimeException("Cannot call asynchronous function synchronously"); switch (funcInst) { @@ -292,6 +290,9 @@ public void Invoke(FuncAddr addr) return; case HostFunction hostFunc: { + if (funcInst.IsAsync) + throw new WasmRuntimeException("Cannot call asynchronous function synchronously"); + var funcType = hostFunc.Type; //Fetch the parameters OpStack.PopScalars(funcType.ParameterTypes, hostFunc.ParameterBuffer, hostFunc.PassExecContext?1:0); diff --git a/Wacs.Core/Runtime/OpStack.cs b/Wacs.Core/Runtime/OpStack.cs index 0ae4785..f78dbf9 100644 --- a/Wacs.Core/Runtime/OpStack.cs +++ b/Wacs.Core/Runtime/OpStack.cs @@ -201,14 +201,35 @@ public void Clear() public Value Peek() => _stack.Peek(); - public void PopResults(ResultType type, ref Stack results) => PopResults(type.Arity, ref results); + public int PopResults(ResultType type, ref Value[] results) + { + int arity = type.Arity; + Count -= arity; + for (int i = arity - 1; i >= 0; --i) + { + //We could check the types here, but the spec just says to YOLO it. + results[i] = _stack.Pop(); + } + return arity; + } + + public void PopResults(ResultType type, ref Stack results) + { + Count -= type.Arity; + for (int i = 0, l = type.Arity; i < l; ++i) + { + //We could check the types here, but the spec just says to YOLO it. + results.Push(_stack.Pop()); + } + } public void PopResults(int arity, ref Stack results) { + Count -= arity; for (int i = 0, l = arity; i < l; ++i) { //We could check the types here, but the spec just says to YOLO it. - results.Push(PopAny()); + results.Push(_stack.Pop()); } } diff --git a/Wacs.Core/Runtime/Types/FunctionInstance.cs b/Wacs.Core/Runtime/Types/FunctionInstance.cs index 0ace4e7..ea1af80 100644 --- a/Wacs.Core/Runtime/Types/FunctionInstance.cs +++ b/Wacs.Core/Runtime/Types/FunctionInstance.cs @@ -96,30 +96,25 @@ public void Invoke(ExecContext context) //5. *Instructions will be handled in EnterSequence below //var seq = Body.Instructions; //6. +#if STRICT_EXECUTION context.Assert( context.OpStack.Count >= funcType.ParameterTypes.Arity, $"Function invocation failed. Operand Stack underflow."); //7. context.Assert(_asideVals.Count == 0, $"Shared temporary stack had values left in it."); - context.OpStack.PopResults(funcType.ParameterTypes, ref _asideVals); +#endif //8. //Push the frame and operate on the frame on the stack. var frame = context.ReserveFrame(Module, funcType, Index, t); - // frame.FuncId = wasmFunc.Id; - int li = 0; - int localCount = funcType.ParameterTypes.Arity + t.Length; //Load parameters - while (_asideVals.Count > 0) - { - // frame.Locals.Set((LocalIdx)li, _asideVals.Pop()); - frame.Locals.Data[li] = _asideVals.Pop(); - li += 1; - } + int li = context.OpStack.PopResults(funcType.ParameterTypes, ref frame.Locals.Data); + frame.StackHeight -= li; + + int localCount = funcType.ParameterTypes.Arity + t.Length; //Set the Locals to default for (int ti = 0; li < localCount; ++li, ++ti) { - // frame.Locals.Set((LocalIdx)li, new Value(t[ti])); frame.Locals.Data[li] = new Value(t[ti]); } @@ -132,7 +127,7 @@ public void Invoke(ExecContext context) frame.ReturnLabel.Arity = funcType.ResultType.Arity; frame.ReturnLabel.Instruction = LabelInst; frame.ReturnLabel.ContinuationAddress = context.GetPointer(); - frame.ReturnLabel.StackHeight = 0; + // frame.ReturnLabel.StackHeight = 0; context.EnterSequence(Body.Instructions); } diff --git a/Wacs.Core/Types/IndexSpace.cs b/Wacs.Core/Types/IndexSpace.cs index cffe22a..ffc7198 100644 --- a/Wacs.Core/Types/IndexSpace.cs +++ b/Wacs.Core/Types/IndexSpace.cs @@ -270,10 +270,13 @@ public void Set(LocalIdx idx, Value value) Data[idx.Value] = value; } - public LocalsSpace(Value[] data, ValType[] parameters, ValType[] locals) + public LocalsSpace(Value[] data, ValType[] parameters, ValType[] locals, bool skipInit = false) { Capacity = parameters.Length + locals.Length; Data = data; + if (skipInit) + return; + int idx = 0; foreach (var t in parameters) { From 2171fe5ce926e4d52718fdc804c5381ff7580b4b Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sun, 1 Dec 2024 14:02:49 -0800 Subject: [PATCH 04/19] Optimizing memory ops --- Wacs.Core/Instructions/LocalVariable.cs | 3 +- Wacs.Core/Instructions/Memory/FMemoryLoad.cs | 4 +- Wacs.Core/Instructions/Memory/FMemoryStore.cs | 4 +- .../Instructions/Memory/I32MemoryLoad.cs | 27 ++++++++----- .../Instructions/Memory/I64MemoryLoad.cs | 38 +++++++++++++------ Wacs.Core/Instructions/Memory/InstI32Store.cs | 6 +-- Wacs.Core/Instructions/Memory/InstI64Store.cs | 11 ++---- Wacs.Core/Instructions/SIMD/VMemory.cs | 31 ++++++++++----- Wacs.Core/Runtime/Types/MemoryInstance.cs | 2 +- Wacs.Core/Runtime/V128.cs | 2 +- Wacs.Core/Runtime/WasmRuntimeExecution.cs | 2 +- Wacs.WASIp1/FsFd.cs | 2 +- Wacs.WASIp1/Types/IoVec.cs | 2 +- 13 files changed, 81 insertions(+), 53 deletions(-) diff --git a/Wacs.Core/Instructions/LocalVariable.cs b/Wacs.Core/Instructions/LocalVariable.cs index c70e70b..c808b5c 100644 --- a/Wacs.Core/Instructions/LocalVariable.cs +++ b/Wacs.Core/Instructions/LocalVariable.cs @@ -85,8 +85,7 @@ public Value FetchFromLocals(ExecContext context) $"Instruction local.get could not get Local {Index}"); //3. // var value = context.Frame.Locals.Get(Index); - var value = context.Frame.Locals.Data[Index.Value]; - return value; + return context.Frame.Locals.Data[Index.Value]; } } diff --git a/Wacs.Core/Instructions/Memory/FMemoryLoad.cs b/Wacs.Core/Instructions/Memory/FMemoryLoad.cs index cb971d5..773b591 100644 --- a/Wacs.Core/Instructions/Memory/FMemoryLoad.cs +++ b/Wacs.Core/Instructions/Memory/FMemoryLoad.cs @@ -49,7 +49,7 @@ public float FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); return MemoryMarshal.Read(bs); } @@ -83,7 +83,7 @@ public double FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); return MemoryMarshal.Read(bs); } diff --git a/Wacs.Core/Instructions/Memory/FMemoryStore.cs b/Wacs.Core/Instructions/Memory/FMemoryStore.cs index 44b2cd5..f1aed99 100644 --- a/Wacs.Core/Instructions/Memory/FMemoryStore.cs +++ b/Wacs.Core/Instructions/Memory/FMemoryStore.cs @@ -58,7 +58,7 @@ public void SetMemoryValue(ExecContext context, uint offset, float cF32) //13,14,15 Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cF32); #else MemoryMarshal.Write(bs, ref cF32); @@ -102,7 +102,7 @@ public void SetMemoryValue(ExecContext context, uint offset, double cF64) //13,14,15 Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cF64); #else MemoryMarshal.Write(bs, ref cF64); diff --git a/Wacs.Core/Instructions/Memory/I32MemoryLoad.cs b/Wacs.Core/Instructions/Memory/I32MemoryLoad.cs index 29abfb7..1a6c163 100644 --- a/Wacs.Core/Instructions/Memory/I32MemoryLoad.cs +++ b/Wacs.Core/Instructions/Memory/I32MemoryLoad.cs @@ -49,9 +49,13 @@ public uint FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); + #if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); + #else return MemoryMarshal.Read(bs); + #endif } public Func GetFunc => FetchFromMemory; @@ -82,10 +86,8 @@ public int FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - int cS8 = (sbyte)bs[0]; - return cS8; + return (sbyte)mem.Data[(int)ea]; } public Func GetFunc => FetchFromMemory; @@ -116,11 +118,8 @@ public uint FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - - uint cU8 = bs[0]; - return cU8; + return mem.Data[(int)ea]; } public Func GetFunc => FetchFromMemory; @@ -151,9 +150,13 @@ public int FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -184,9 +187,13 @@ public uint FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; diff --git a/Wacs.Core/Instructions/Memory/I64MemoryLoad.cs b/Wacs.Core/Instructions/Memory/I64MemoryLoad.cs index a63eb4d..e0841c5 100644 --- a/Wacs.Core/Instructions/Memory/I64MemoryLoad.cs +++ b/Wacs.Core/Instructions/Memory/I64MemoryLoad.cs @@ -49,9 +49,12 @@ public ulong FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -82,7 +85,7 @@ public long FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); int cS8 = (sbyte)bs[0]; return cS8; @@ -116,7 +119,7 @@ public ulong FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); uint cU8 = bs[0]; return cU8; @@ -150,9 +153,12 @@ public long FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -183,9 +189,13 @@ public ulong FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -216,9 +226,12 @@ public long FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -249,9 +262,12 @@ public ulong FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; diff --git a/Wacs.Core/Instructions/Memory/InstI32Store.cs b/Wacs.Core/Instructions/Memory/InstI32Store.cs index 37a4a29..0a45474 100644 --- a/Wacs.Core/Instructions/Memory/InstI32Store.cs +++ b/Wacs.Core/Instructions/Memory/InstI32Store.cs @@ -100,10 +100,8 @@ public void SetMemoryValue(ExecContext context, uint offset, uint cU32) if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer out of bounds."); //13,14,15 - Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - - byte cU8 = (byte)(0xFF & cU32); - bs[0] = cU8; + // Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); + mem.Data[(int)ea] = (byte)(0xFF & cU32); } public Action GetFunc => SetMemoryValue; diff --git a/Wacs.Core/Instructions/Memory/InstI64Store.cs b/Wacs.Core/Instructions/Memory/InstI64Store.cs index 0b20911..8bcaac7 100644 --- a/Wacs.Core/Instructions/Memory/InstI64Store.cs +++ b/Wacs.Core/Instructions/Memory/InstI64Store.cs @@ -58,7 +58,7 @@ public void SetMemoryValue(ExecContext context, uint offset, ulong cU64) //13,14,15 Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cU64); #else MemoryMarshal.Write(bs, ref cU64); @@ -100,10 +100,7 @@ public void SetMemoryValue(ExecContext context, uint offset, ulong cU64) if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer out of bounds."); //13,14,15 - Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - - byte cU8 = (byte)(0xFF & cU64); - bs[0] = cU8; + mem.Data[(int)ea] = (byte)(0xFF & cU64); } public Action GetFunc => SetMemoryValue; @@ -144,7 +141,7 @@ public void SetMemoryValue(ExecContext context, uint offset, ulong cU64) Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); ushort cI16 = (ushort)cU64; -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cI16); // Assume you can change to 'in' #else MemoryMarshal.Write(bs, ref cI16); @@ -189,7 +186,7 @@ public void SetMemoryValue(ExecContext context, uint offset, ulong cU64) Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); uint cI32 = (uint)cU64; -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cI32); // Assume you can change to 'in' #else MemoryMarshal.Write(bs, ref cI32); diff --git a/Wacs.Core/Instructions/SIMD/VMemory.cs b/Wacs.Core/Instructions/SIMD/VMemory.cs index cbd3c34..bace0e2 100644 --- a/Wacs.Core/Instructions/SIMD/VMemory.cs +++ b/Wacs.Core/Instructions/SIMD/VMemory.cs @@ -52,8 +52,14 @@ public V128 FetchFromMemory(ExecContext context, uint offset) long ea = (long)i + (long)M.Offset; if (ea + WidthTByteSize > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{WidthTByteSize} out of bounds ({mem.Data.Length})."); - var bs = mem.Data.AsSpan((int)ea, WidthTByteSize); - return new V128(bs); + var bs = new ReadOnlySpan(mem.Data, (int)ea, WidthTByteSize); + + // return new V128(bs); +#if NET8_0_OR_GREATER + return MemoryMarshal.AsRef(bs); +#else + return MemoryMarshal.Read(bs); +#endif } public Func GetFunc => FetchFromMemory; @@ -93,7 +99,7 @@ public void SetMemoryValue(ExecContext context, uint offset, V128 cV128) //13,14,15 Span bs = mem.Data.AsSpan((int)ea, WidthTByteSize); -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bs, in cV128); #else MemoryMarshal.Write(bs, ref cV128); @@ -108,10 +114,15 @@ public class InstMemoryLoadMxN : InstructionBase private readonly int CountN; private readonly BitWidth WidthT; + private int WidthTByteSize; private MemArg M; - public InstMemoryLoadMxN(BitWidth width, int count) => - (WidthT, CountN) = (width, count); + public InstMemoryLoadMxN(BitWidth width, int count) + { + WidthT = width; + WidthTByteSize = WidthT.ByteSize(); + CountN = count; + } public override ByteCode Op => CountN switch { @@ -143,7 +154,7 @@ public override void Validate(IWasmValidationContext context) { context.Assert(context.Mems.Contains(M.M), "Instruction {0} failed with invalid context memory 0.",Op.GetMnemonic()); - context.Assert(M.Align.LinearSize() <= WidthT.ByteSize() * CountN, + context.Assert(M.Align.LinearSize() <= WidthTByteSize * CountN, "Instruction {0} failed with invalid alignment {1} <= {2}/8",Op.GetMnemonic(),M.Align.LinearSize(),WidthT); context.OpStack.PopI32(); @@ -171,18 +182,18 @@ public override void Execute(ExecContext context) //8. long ea = (long)i + (long)M.Offset; //9. - int mn = WidthT.ByteSize() * CountN; + int mn = WidthTByteSize * CountN; if (ea + mn > mem.Data.Length) throw new TrapException($"Instruction {Op.GetMnemonic()} failed. Memory pointer {ea}+{mn} out of bounds ({mem.Data.Length})."); //10. - var bs = mem.Data.AsSpan((int)ea, mn); + var bs = new ReadOnlySpan(mem.Data,(int)ea, mn); //11,12,13,14 - int m = WidthT.ByteSize(); + int m = WidthTByteSize; MV128 c = new MV128(); for (int k = 0; k < CountN; ++k) { int km = k * m; - int kmEnd = km + WidthT.ByteSize(); + int kmEnd = km + WidthTByteSize; var cell = bs[km..kmEnd]; switch (WidthT) { diff --git a/Wacs.Core/Runtime/Types/MemoryInstance.cs b/Wacs.Core/Runtime/Types/MemoryInstance.cs index ac82786..ff61f87 100644 --- a/Wacs.Core/Runtime/Types/MemoryInstance.cs +++ b/Wacs.Core/Runtime/Types/MemoryInstance.cs @@ -111,7 +111,7 @@ public int WriteStruct(uint ptr, ref T str) { int size = Marshal.SizeOf(); var buf = this[(int)ptr..(int)(ptr + size)]; -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(buf, in str); #else MemoryMarshal.Write(buf, ref str); diff --git a/Wacs.Core/Runtime/V128.cs b/Wacs.Core/Runtime/V128.cs index ac33b8b..a6742cb 100644 --- a/Wacs.Core/Runtime/V128.cs +++ b/Wacs.Core/Runtime/V128.cs @@ -267,7 +267,7 @@ public V128(ulong u64X20, ulong u64X21) public static implicit operator V128((ulong, ulong) tuple) => new(tuple.Item1, tuple.Item2); - public V128(Span data) + public V128(ReadOnlySpan data) { if (data.Length != 16) throw new InvalidDataException($"Cannot create V128 from {data.Length} bytes"); diff --git a/Wacs.Core/Runtime/WasmRuntimeExecution.cs b/Wacs.Core/Runtime/WasmRuntimeExecution.cs index b987201..099a2b0 100644 --- a/Wacs.Core/Runtime/WasmRuntimeExecution.cs +++ b/Wacs.Core/Runtime/WasmRuntimeExecution.cs @@ -378,7 +378,7 @@ public async Task ProcessThreadAsync(long gasLimit) } } - public async Task ProcessThreadWithOptions(InvokerOptions options) + public async Task ProcessThreadWithOptions(InvokerOptions options) { long highwatermark = 0; long gasLimit = options.GasLimit > 0 ? options.GasLimit : long.MaxValue; diff --git a/Wacs.WASIp1/FsFd.cs b/Wacs.WASIp1/FsFd.cs index 53fbfbf..ad2ebdf 100644 --- a/Wacs.WASIp1/FsFd.cs +++ b/Wacs.WASIp1/FsFd.cs @@ -387,7 +387,7 @@ public ErrNo FdReaddir(ExecContext ctx, fd fd, ptr bufPtr, size bufLen, dircooki var entryTarget = window[start..delim]; var nameTarget = window[delim..end]; var dirEnt = struc; -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(entryTarget, in dirEnt); #else MemoryMarshal.Write(entryTarget, ref dirEnt); diff --git a/Wacs.WASIp1/Types/IoVec.cs b/Wacs.WASIp1/Types/IoVec.cs index 22e3a16..3f7c3d5 100644 --- a/Wacs.WASIp1/Types/IoVec.cs +++ b/Wacs.WASIp1/Types/IoVec.cs @@ -43,7 +43,7 @@ public void FromWasmValue(object value) public Value ToWasmType() { byte[] bytes = new byte[8]; -#if NET8_0 +#if NET8_0_OR_GREATER MemoryMarshal.Write(bytes, in this); #else MemoryMarshal.Write(bytes, ref this); From 6b8ceb4ea47af446eb7bfa6ec3404680194082cd Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sun, 1 Dec 2024 16:59:06 -0800 Subject: [PATCH 05/19] Manually inlining Next() --- Wacs.Core/Runtime/ExecContext.cs | 6 +++--- Wacs.Core/Runtime/WasmRuntimeExecution.cs | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index 44cb904..7e32709 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -56,9 +56,9 @@ public class ExecContext public readonly Store Store; private InstructionSequence _currentSequence; - private int _sequenceCount; - private int _sequenceIndex; - private InstructionBase[] _sequenceInstructions; + public int _sequenceCount; + public int _sequenceIndex; + public InstructionBase[] _sequenceInstructions; public Frame Frame = NullFrame; diff --git a/Wacs.Core/Runtime/WasmRuntimeExecution.cs b/Wacs.Core/Runtime/WasmRuntimeExecution.cs index 099a2b0..f96b5b0 100644 --- a/Wacs.Core/Runtime/WasmRuntimeExecution.cs +++ b/Wacs.Core/Runtime/WasmRuntimeExecution.cs @@ -359,8 +359,13 @@ Value[] GenericDelegate(params object[] args) public async Task ProcessThreadAsync(long gasLimit) { if (gasLimit <= 0) gasLimit = long.MaxValue; - while (Context.Next() is { } inst) + + InstructionBase inst; + //Manually inline Next() + // while (Context.Next() is { } inst) + while (++Context._sequenceIndex < Context._sequenceCount) { + inst = Context._sequenceInstructions[Context._sequenceIndex]; if (inst.IsAsync) { await inst.ExecuteAsync(Context); @@ -382,8 +387,13 @@ public async Task ProcessThreadWithOptions(InvokerOptions options) { long highwatermark = 0; long gasLimit = options.GasLimit > 0 ? options.GasLimit : long.MaxValue; - while (Context.Next() is { } inst) + + InstructionBase inst; + //Manually inline Next() + // while (Context.Next() is { } inst) + while (++Context._sequenceIndex < Context._sequenceCount) { + inst = Context._sequenceInstructions[Context._sequenceIndex]; //Trace execution if (options.LogInstructionExecution != InstructionLogging.None) { From d1cea4dec77eb9b7ffb67cdca5b1a6fb4466bec3 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sun, 1 Dec 2024 16:59:31 -0800 Subject: [PATCH 06/19] Sealing classes --- Wacs.Core/Instructions/Control.cs | 16 ++++++++-------- Wacs.Core/Instructions/Numeric/Const.cs | 8 ++++---- Wacs.Core/Instructions/Numeric/Conversion.cs | 2 +- Wacs.Core/Instructions/Numeric/F32BinOp.cs | 2 +- Wacs.Core/Instructions/Numeric/F32RelOp.cs | 2 +- Wacs.Core/Instructions/Numeric/F32UnOp.cs | 2 +- Wacs.Core/Instructions/Numeric/F64BinOp.cs | 2 +- Wacs.Core/Instructions/Numeric/F64RelOp.cs | 2 +- Wacs.Core/Instructions/Numeric/F64UnOp.cs | 2 +- Wacs.Core/Instructions/Numeric/I32BinOp.cs | 6 +++--- Wacs.Core/Instructions/Numeric/I32RelOp.cs | 4 ++-- .../Instructions/Numeric/I32SignExtension.cs | 2 +- Wacs.Core/Instructions/Numeric/I32UnOp.cs | 2 +- Wacs.Core/Instructions/Numeric/I64BinOp.cs | 6 +++--- Wacs.Core/Instructions/Numeric/I64RelOp.cs | 4 ++-- .../Instructions/Numeric/I64SignExtension.cs | 2 +- Wacs.Core/Instructions/Numeric/I64UnOp.cs | 2 +- Wacs.Core/Instructions/Numeric/ITestOp.cs | 4 ++-- Wacs.Core/Runtime/Frame.cs | 2 +- 19 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index 8ab8745..6c95e94 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -35,7 +35,7 @@ namespace Wacs.Core.Instructions { //0x00 - public class InstUnreachable : InstructionBase + public sealed class InstUnreachable : InstructionBase { public static readonly InstUnreachable Inst = new(); public override ByteCode Op => OpCode.Unreachable; @@ -52,7 +52,7 @@ public override void Execute(ExecContext context) => } //0x01 - public class InstNop : InstructionBase + public sealed class InstNop : InstructionBase { public static readonly InstNop Inst = new(); public override ByteCode Op => OpCode.Nop; @@ -429,7 +429,7 @@ public override string RenderText(ExecContext? context) } //0x0C - public class InstBranch : InstructionBase, IBranchInstruction + public sealed class InstBranch : InstructionBase, IBranchInstruction { private static Stack _asideVals = new(); @@ -525,7 +525,7 @@ public override string RenderText(ExecContext? context) } //0x0D - public class InstBranchIf : InstructionBase, IBranchInstruction, INodeConsumer + public sealed class InstBranchIf : InstructionBase, IBranchInstruction, INodeConsumer { public LabelIdx L; public override ByteCode Op => OpCode.BrIf; @@ -590,7 +590,7 @@ public override string RenderText(ExecContext? context) } //0x0E - public class InstBranchTable : InstructionBase, IBranchInstruction, INodeConsumer + public sealed class InstBranchTable : InstructionBase, IBranchInstruction, INodeConsumer { private LabelIdx Ln; //Default m @@ -714,7 +714,7 @@ public override string RenderText(ExecContext? context) } //0x0F - public class InstReturn : InstructionBase + public sealed class InstReturn : InstructionBase { public static readonly InstReturn Inst = new(); public override ByteCode Op => OpCode.Return; @@ -742,7 +742,7 @@ public override void Execute(ExecContext context) } //0x10 - public class InstCall : InstructionBase, ICallInstruction + public sealed class InstCall : InstructionBase, ICallInstruction { public FuncIdx X; @@ -840,7 +840,7 @@ public override string RenderText(ExecContext? context) } //0x11 - public class InstCallIndirect : InstructionBase, ICallInstruction + public sealed class InstCallIndirect : InstructionBase, ICallInstruction { private TableIdx X; diff --git a/Wacs.Core/Instructions/Numeric/Const.cs b/Wacs.Core/Instructions/Numeric/Const.cs index 1ea57d2..b2a541b 100644 --- a/Wacs.Core/Instructions/Numeric/Const.cs +++ b/Wacs.Core/Instructions/Numeric/Const.cs @@ -26,7 +26,7 @@ namespace Wacs.Core.Instructions.Numeric { //0x41 - public class InstI32Const : InstructionBase, IConstInstruction, ITypedValueProducer + public sealed class InstI32Const : InstructionBase, IConstInstruction, ITypedValueProducer { private int Value; public override ByteCode Op => OpCode.I32Const; @@ -64,7 +64,7 @@ public IInstruction Immediate(int value) } //0x42 - public class InstI64Const : InstructionBase, IConstInstruction, ITypedValueProducer + public sealed class InstI64Const : InstructionBase, IConstInstruction, ITypedValueProducer { private long Value; public override ByteCode Op => OpCode.I64Const; @@ -97,7 +97,7 @@ public override IInstruction Parse(BinaryReader reader) { } //0x43 - public class InstF32Const : InstructionBase, IConstInstruction, ITypedValueProducer + public sealed class InstF32Const : InstructionBase, IConstInstruction, ITypedValueProducer { private float Value; public override ByteCode Op => OpCode.F32Const; @@ -138,7 +138,7 @@ public override string RenderText(ExecContext? context) } //0x44 - public class InstF64Const : InstructionBase, IConstInstruction, ITypedValueProducer + public sealed class InstF64Const : InstructionBase, IConstInstruction, ITypedValueProducer { private double Value; public override ByteCode Op => OpCode.F64Const; diff --git a/Wacs.Core/Instructions/Numeric/Conversion.cs b/Wacs.Core/Instructions/Numeric/Conversion.cs index 9fb63d5..4f8a515 100644 --- a/Wacs.Core/Instructions/Numeric/Conversion.cs +++ b/Wacs.Core/Instructions/Numeric/Conversion.cs @@ -27,7 +27,7 @@ // ReSharper disable InconsistentNaming namespace Wacs.Core.Instructions.Numeric { - public class InstConvert : InstructionBase + public sealed class InstConvert : InstructionBase { // @Spec 3.3.1.6 cvtop // [t1] -> [t2] diff --git a/Wacs.Core/Instructions/Numeric/F32BinOp.cs b/Wacs.Core/Instructions/Numeric/F32BinOp.cs index ee3fe5d..eb72307 100644 --- a/Wacs.Core/Instructions/Numeric/F32BinOp.cs +++ b/Wacs.Core/Instructions/Numeric/F32BinOp.cs @@ -24,7 +24,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF32BinOp : InstructionBase, INodeComputer + public sealed class InstF32BinOp : InstructionBase, INodeComputer { // Mask for the sign bit (most significant bit) private const uint F32SignMask = 0x8000_0000; diff --git a/Wacs.Core/Instructions/Numeric/F32RelOp.cs b/Wacs.Core/Instructions/Numeric/F32RelOp.cs index 7ee1c97..d82bd85 100644 --- a/Wacs.Core/Instructions/Numeric/F32RelOp.cs +++ b/Wacs.Core/Instructions/Numeric/F32RelOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF32RelOp : InstructionBase, INodeComputer + public sealed class InstF32RelOp : InstructionBase, INodeComputer { public static readonly InstF32RelOp F32Eq = new(OpCode.F32Eq, ExecuteF32Eq, NumericInst.ValidateOperands(pop1: ValType.F32, pop2: ValType.F32, push: ValType.I32)); diff --git a/Wacs.Core/Instructions/Numeric/F32UnOp.cs b/Wacs.Core/Instructions/Numeric/F32UnOp.cs index c26ec52..2a307bd 100644 --- a/Wacs.Core/Instructions/Numeric/F32UnOp.cs +++ b/Wacs.Core/Instructions/Numeric/F32UnOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF32UnOp : InstructionBase, INodeComputer + public sealed class InstF32UnOp : InstructionBase, INodeComputer { // @Spec 3.3.1.2. f.unop public static readonly InstF32UnOp F32Abs = new(OpCode.F32Abs , ExecuteF32Abs , NumericInst.ValidateOperands(pop: ValType.F32, push: ValType.F32)); diff --git a/Wacs.Core/Instructions/Numeric/F64BinOp.cs b/Wacs.Core/Instructions/Numeric/F64BinOp.cs index 5807ed6..60efe12 100644 --- a/Wacs.Core/Instructions/Numeric/F64BinOp.cs +++ b/Wacs.Core/Instructions/Numeric/F64BinOp.cs @@ -24,7 +24,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF64BinOp : InstructionBase, INodeComputer + public sealed class InstF64BinOp : InstructionBase, INodeComputer { // Mask for the sign bit (most significant bit) private const ulong F64SignMask = 0x8000_0000_0000_0000; diff --git a/Wacs.Core/Instructions/Numeric/F64RelOp.cs b/Wacs.Core/Instructions/Numeric/F64RelOp.cs index 9cce342..b192e5f 100644 --- a/Wacs.Core/Instructions/Numeric/F64RelOp.cs +++ b/Wacs.Core/Instructions/Numeric/F64RelOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF64RelOp : InstructionBase, INodeComputer + public sealed class InstF64RelOp : InstructionBase, INodeComputer { public static readonly InstF64RelOp F64Eq = new(OpCode.F64Eq, ExecuteF64Eq, NumericInst.ValidateOperands(pop1: ValType.F64, pop2: ValType.F64, push: ValType.I32)); diff --git a/Wacs.Core/Instructions/Numeric/F64UnOp.cs b/Wacs.Core/Instructions/Numeric/F64UnOp.cs index 37bd21f..a8e4d00 100644 --- a/Wacs.Core/Instructions/Numeric/F64UnOp.cs +++ b/Wacs.Core/Instructions/Numeric/F64UnOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstF64UnOp : InstructionBase, INodeComputer + public sealed class InstF64UnOp : InstructionBase, INodeComputer { // @Spec 3.3.1.2. f.unop public static readonly InstF64UnOp F64Abs = new(OpCode.F64Abs , ExecuteF64Abs , NumericInst.ValidateOperands(pop: ValType.F64, push: ValType.F64)); diff --git a/Wacs.Core/Instructions/Numeric/I32BinOp.cs b/Wacs.Core/Instructions/Numeric/I32BinOp.cs index 627f15e..529d54c 100644 --- a/Wacs.Core/Instructions/Numeric/I32BinOp.cs +++ b/Wacs.Core/Instructions/Numeric/I32BinOp.cs @@ -178,7 +178,7 @@ private static uint ExecuteI32Rotr(uint i1, int i2) return (i1 >> k) | (i1 << (32 - k)); } - private class Signed : InstI32BinOp, INodeComputer + private sealed class Signed : InstI32BinOp, INodeComputer { private readonly Func _execute; @@ -196,7 +196,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Unsigned : InstI32BinOp, INodeComputer + private sealed class Unsigned : InstI32BinOp, INodeComputer { private readonly Func _execute; @@ -214,7 +214,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Mixed : InstI32BinOp, INodeComputer + private sealed class Mixed : InstI32BinOp, INodeComputer { private readonly Func _execute; diff --git a/Wacs.Core/Instructions/Numeric/I32RelOp.cs b/Wacs.Core/Instructions/Numeric/I32RelOp.cs index 84bce8e..576ad21 100644 --- a/Wacs.Core/Instructions/Numeric/I32RelOp.cs +++ b/Wacs.Core/Instructions/Numeric/I32RelOp.cs @@ -79,7 +79,7 @@ private InstI32RelOp(ByteCode op, NumericInst.ValidationDelegate validate) private static int ExecuteI32GeS(int i1, int i2) => i1 >= i2 ? 1 : 0; private static int ExecuteI32GeU(uint i1, uint i2) => i1 >= i2 ? 1 : 0; - private class Signed : InstI32RelOp, INodeComputer + private sealed class Signed : InstI32RelOp, INodeComputer { private readonly Func _execute; @@ -97,7 +97,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Unsigned : InstI32RelOp, INodeComputer + private sealed class Unsigned : InstI32RelOp, INodeComputer { private readonly Func _execute; diff --git a/Wacs.Core/Instructions/Numeric/I32SignExtension.cs b/Wacs.Core/Instructions/Numeric/I32SignExtension.cs index b520d31..8c6a702 100644 --- a/Wacs.Core/Instructions/Numeric/I32SignExtension.cs +++ b/Wacs.Core/Instructions/Numeric/I32SignExtension.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstI32SignExtend : InstructionBase, INodeComputer + public sealed class InstI32SignExtend : InstructionBase, INodeComputer { private const uint ByteSign = 0x80; private const uint I32ByteExtend = 0xFFFF_FF80; diff --git a/Wacs.Core/Instructions/Numeric/I32UnOp.cs b/Wacs.Core/Instructions/Numeric/I32UnOp.cs index f6aed0e..fb95ab3 100644 --- a/Wacs.Core/Instructions/Numeric/I32UnOp.cs +++ b/Wacs.Core/Instructions/Numeric/I32UnOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstI32UnOp : InstructionBase, INodeComputer + public sealed class InstI32UnOp : InstructionBase, INodeComputer { // @Spec 3.3.1.2. i.unop public static readonly InstI32UnOp I32Clz = new(OpCode.I32Clz , ExecuteI32Clz , NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32)); diff --git a/Wacs.Core/Instructions/Numeric/I64BinOp.cs b/Wacs.Core/Instructions/Numeric/I64BinOp.cs index 69c7d2d..956c931 100644 --- a/Wacs.Core/Instructions/Numeric/I64BinOp.cs +++ b/Wacs.Core/Instructions/Numeric/I64BinOp.cs @@ -151,7 +151,7 @@ private static ulong ExecuteI64Rotr(ulong i1, long i2) return (i1 >> k) | (i1 << (64 - k)); } - private class Signed : InstI64BinOp, INodeComputer + private sealed class Signed : InstI64BinOp, INodeComputer { private readonly Func _execute; @@ -169,7 +169,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Unsigned : InstI64BinOp, INodeComputer + private sealed class Unsigned : InstI64BinOp, INodeComputer { private readonly Func _execute; @@ -187,7 +187,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Mixed : InstI64BinOp, INodeComputer + private sealed class Mixed : InstI64BinOp, INodeComputer { private readonly Func _execute; diff --git a/Wacs.Core/Instructions/Numeric/I64RelOp.cs b/Wacs.Core/Instructions/Numeric/I64RelOp.cs index 7cf6343..2677be8 100644 --- a/Wacs.Core/Instructions/Numeric/I64RelOp.cs +++ b/Wacs.Core/Instructions/Numeric/I64RelOp.cs @@ -88,7 +88,7 @@ private InstI64RelOp(ByteCode op, NumericInst.ValidationDelegate validate) private static int ExecuteI64GeU(ulong i1, ulong i2) => i1 >= i2 ? 1 : 0; - private class Signed : InstI64RelOp, INodeComputer + private sealed class Signed : InstI64RelOp, INodeComputer { private readonly Func _execute; @@ -106,7 +106,7 @@ public override void Execute(ExecContext context) public Func GetFunc => (_, i1, i2) => _execute(i1, i2); } - private class Unsigned : InstI64RelOp, INodeComputer + private sealed class Unsigned : InstI64RelOp, INodeComputer { private readonly Func _execute; diff --git a/Wacs.Core/Instructions/Numeric/I64SignExtension.cs b/Wacs.Core/Instructions/Numeric/I64SignExtension.cs index dbc566e..cf9c075 100644 --- a/Wacs.Core/Instructions/Numeric/I64SignExtension.cs +++ b/Wacs.Core/Instructions/Numeric/I64SignExtension.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstI64SignExtend : InstructionBase, INodeComputer + public sealed class InstI64SignExtend : InstructionBase, INodeComputer { private const uint ByteSign = 0x80; private const uint ByteMask = 0xFF; diff --git a/Wacs.Core/Instructions/Numeric/I64UnOp.cs b/Wacs.Core/Instructions/Numeric/I64UnOp.cs index a40b3c8..2cbc4b8 100644 --- a/Wacs.Core/Instructions/Numeric/I64UnOp.cs +++ b/Wacs.Core/Instructions/Numeric/I64UnOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstI64UnOp : InstructionBase, INodeComputer + public sealed class InstI64UnOp : InstructionBase, INodeComputer { // @Spec 3.3.1.2. i.unop public static readonly InstI64UnOp I64Clz = new(OpCode.I64Clz , ExecuteI64Clz , NumericInst.ValidateOperands(pop: ValType.I64, push: ValType.I64)); diff --git a/Wacs.Core/Instructions/Numeric/ITestOp.cs b/Wacs.Core/Instructions/Numeric/ITestOp.cs index 6264b3a..4cf6d69 100644 --- a/Wacs.Core/Instructions/Numeric/ITestOp.cs +++ b/Wacs.Core/Instructions/Numeric/ITestOp.cs @@ -23,7 +23,7 @@ namespace Wacs.Core.Instructions.Numeric { - public class InstI32TestOp : InstructionBase, INodeComputer + public sealed class InstI32TestOp : InstructionBase, INodeComputer { // @Spec 3.3.1.4 i.testop public static readonly InstI32TestOp I32Eqz = new(OpCode.I32Eqz, ExecuteI32Eqz, NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32)); @@ -54,7 +54,7 @@ public override void Execute(ExecContext context) private static int ExecuteI32Eqz(int i) => i == 0 ? 1 : 0; } - public class InstI64TestOp : InstructionBase, INodeComputer + public sealed class InstI64TestOp : InstructionBase, INodeComputer { public static readonly InstI64TestOp I64Eqz = new(OpCode.I64Eqz, ExecuteI64Eqz, NumericInst.ValidateOperands(pop: ValType.I64, push: ValType.I32)); private readonly Func _execute; diff --git a/Wacs.Core/Runtime/Frame.cs b/Wacs.Core/Runtime/Frame.cs index ad714a9..b336ec8 100644 --- a/Wacs.Core/Runtime/Frame.cs +++ b/Wacs.Core/Runtime/Frame.cs @@ -25,7 +25,7 @@ namespace Wacs.Core.Runtime { - public class Frame : IPoolable + public sealed class Frame : IPoolable { public InstructionPointer ContinuationAddress = InstructionPointer.Nil; public string FuncId = ""; From 6bf868f0fcc8847471f3f88e5ee5de15fc5011a8 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Sun, 1 Dec 2024 17:17:50 -0800 Subject: [PATCH 07/19] Removing IInstruction interface --- Wacs.Core/Instructions/Control.cs | 26 +++---- Wacs.Core/Instructions/GlobalVariable.cs | 4 +- Wacs.Core/Instructions/IInstruction.cs | 76 ------------------- Wacs.Core/Instructions/IInstructionFactory.cs | 4 +- Wacs.Core/Instructions/InstructionBase.cs | 26 ++++++- Wacs.Core/Instructions/LocalVariable.cs | 10 +-- Wacs.Core/Instructions/Memory.cs | 8 +- Wacs.Core/Instructions/MemoryBulk.cs | 16 ++-- Wacs.Core/Instructions/Numeric/Const.cs | 10 +-- Wacs.Core/Instructions/Parametric.cs | 2 +- Wacs.Core/Instructions/Reference.cs | 4 +- Wacs.Core/Instructions/SIMD/VConst.cs | 4 +- Wacs.Core/Instructions/SIMD/VLaneOp.cs | 2 +- Wacs.Core/Instructions/SIMD/VMemory.cs | 20 ++--- Wacs.Core/Instructions/SIMD/ViShuffleOp.cs | 2 +- .../Instructions/SpecFactory/SpecFactory.cs | 4 +- .../Instructions/SpecFactory/SpecFactoryFB.cs | 2 +- .../Instructions/SpecFactory/SpecFactoryFC.cs | 2 +- .../Instructions/SpecFactory/SpecFactoryFD.cs | 2 +- .../Instructions/SpecFactory/SpecFactoryFE.cs | 2 +- Wacs.Core/Instructions/Table.cs | 20 ++--- Wacs.Core/Instructions/TailCall.cs | 6 +- .../Instructions/Transpiler/Interfaces.cs | 6 +- Wacs.Core/Modules/InstructionSequence.cs | 12 +-- Wacs.Core/Modules/Module.cs | 6 +- Wacs.Core/Modules/Sections/ElementSection.cs | 6 +- Wacs.Core/Modules/Sections/FunctionSection.cs | 4 +- Wacs.Core/Modules/Sections/StackRenderer.cs | 2 +- Wacs.Core/Runtime/ExecContext.cs | 2 +- Wacs.Core/Runtime/RuntimeAttributes.cs | 2 +- .../Runtime/Transpiler/FunctionTranspiler.cs | 52 ++++++------- Wacs.Core/Runtime/WasmRuntimeExecution.cs | 26 +++---- Wacs.Core/Types/Expression.cs | 8 +- Wacs.Core/Validation/WasmValidationContext.cs | 2 +- 34 files changed, 163 insertions(+), 217 deletions(-) delete mode 100644 Wacs.Core/Instructions/IInstruction.cs diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index 6c95e94..0db2f1e 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -117,11 +117,11 @@ public override void Execute(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Block = new Block( type: Block.ParseBlockType(reader), - seq: new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, IInstruction.IsEnd)) + seq: new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, InstructionBase.IsEnd)) ); return this; } @@ -193,11 +193,11 @@ public override void Execute(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Block = new Block( type: Block.ParseBlockType(reader), - seq: new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, IInstruction.IsEnd)) + seq: new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, InstructionBase.IsEnd)) ); return this; } @@ -297,12 +297,12 @@ public override void Execute(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { IfBlock = new Block( type: Block.ParseBlockType(reader), new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, - IInstruction.IsElseOrEnd)) + InstructionBase.IsElseOrEnd)) ); if (IfBlock.Instructions.EndsWithElse) @@ -310,7 +310,7 @@ public override IInstruction Parse(BinaryReader reader) ElseBlock = new Block( type: IfBlock.Type, seq: new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, - IInstruction.IsEnd)) + InstructionBase.IsEnd)) ); } else if (!IfBlock.Instructions.HasExplicitEnd) @@ -510,7 +510,7 @@ public static void ExecuteInstruction(ExecContext context, LabelIdx labelIndex) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { L = (LabelIdx)reader.ReadLeb128_u32(); return this; @@ -569,7 +569,7 @@ private void BranchIf(ExecContext context, int c) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { L = (LabelIdx)reader.ReadLeb128_u32(); return this; @@ -667,7 +667,7 @@ private static LabelIdx ParseLabelIndex(BinaryReader reader) => /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Ls = reader.ParseVector(ParseLabelIndex); Ln = (LabelIdx)reader.ReadLeb128_u32(); @@ -795,13 +795,13 @@ public override async ValueTask ExecuteAsync(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (FuncIdx)reader.ReadLeb128_u32(); return this; } - public IInstruction Immediate(FuncIdx value) + public InstructionBase Immediate(FuncIdx value) { X = value; return this; @@ -998,7 +998,7 @@ public override async ValueTask ExecuteAsync(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Y = (TypeIdx)reader.ReadLeb128_u32(); X = (TableIdx)reader.ReadLeb128_u32(); diff --git a/Wacs.Core/Instructions/GlobalVariable.cs b/Wacs.Core/Instructions/GlobalVariable.cs index 0c38cc2..6602531 100644 --- a/Wacs.Core/Instructions/GlobalVariable.cs +++ b/Wacs.Core/Instructions/GlobalVariable.cs @@ -38,7 +38,7 @@ public bool IsConstant(IWasmValidationContext? context) => public int CalculateSize() => 1; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Index = (GlobalIdx)reader.ReadLeb128_u32(); return this; @@ -105,7 +105,7 @@ public bool IsConstant(IWasmValidationContext? context) => public override ByteCode Op => OpCode.GlobalSet; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Index = (GlobalIdx)reader.ReadLeb128_u32(); return this; diff --git a/Wacs.Core/Instructions/IInstruction.cs b/Wacs.Core/Instructions/IInstruction.cs deleted file mode 100644 index ea3e815..0000000 --- a/Wacs.Core/Instructions/IInstruction.cs +++ /dev/null @@ -1,76 +0,0 @@ -// /* -// * Copyright 2024 Kelvin Nishikawa -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ - -using System.IO; -using System.Threading.Tasks; -using Wacs.Core.Instructions.Numeric; -using Wacs.Core.OpCodes; -using Wacs.Core.Runtime; -using Wacs.Core.Validation; - -namespace Wacs.Core.Instructions -{ - /// - /// Represents a WebAssembly instruction. - /// - public interface IInstruction - { - public ByteCode Op { get; } - - void Validate(IWasmValidationContext context); - - /// - /// Synchronously executes the instruction within the given execution context. - /// - /// The execution context in which to execute the instruction. - /// The effective number of wasm instructions executed - void Execute(ExecContext context); - - /// - /// Asynchronously wraps the Execute function. - /// Individual instructions may override to perform async functions. - /// - /// - /// ValueTask containing the effective number of wasm instructions executed - public ValueTask ExecuteAsync(ExecContext context); - - /// - /// Parses an instruction from a binary reader. - /// - IInstruction Parse(BinaryReader reader); - - /// - /// Render the instruction at a given label stack depth - /// - string RenderText(ExecContext? context); - - public static bool IsEnd(IInstruction inst) => inst.Op.x00 == OpCode.End; - - public static bool IsElseOrEnd(IInstruction inst) => inst is InstEnd; - - public static bool IsBranch(IInstruction? inst) => inst is IBranchInstruction; - - public static bool IsNumeric(IInstruction? inst) => inst is NumericInst; - - public static bool IsVar(IInstruction? inst) => inst is IVarInstruction; - - public static bool IsLoad(IInstruction? inst) => inst is InstMemoryLoad; - - public static bool IsBound(ExecContext context, IInstruction? inst) => - inst is ICallInstruction ci && ci.IsBound(context); - } - -} \ No newline at end of file diff --git a/Wacs.Core/Instructions/IInstructionFactory.cs b/Wacs.Core/Instructions/IInstructionFactory.cs index 3bf9c24..f0ddb4b 100644 --- a/Wacs.Core/Instructions/IInstructionFactory.cs +++ b/Wacs.Core/Instructions/IInstructionFactory.cs @@ -19,9 +19,9 @@ namespace Wacs.Core.Instructions { //Inject this to change the instruction set - public interface IInstructionFactory + public interface InstructionBaseFactory { T CreateInstruction(ByteCode code) where T : InstructionBase; - IInstruction CreateInstruction(ByteCode code); + InstructionBase CreateInstruction(ByteCode code); } } \ No newline at end of file diff --git a/Wacs.Core/Instructions/InstructionBase.cs b/Wacs.Core/Instructions/InstructionBase.cs index ed52743..6790074 100644 --- a/Wacs.Core/Instructions/InstructionBase.cs +++ b/Wacs.Core/Instructions/InstructionBase.cs @@ -14,9 +14,11 @@ // * limitations under the License. // */ +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using Wacs.Core.Instructions.Numeric; using Wacs.Core.OpCodes; using Wacs.Core.Runtime; using Wacs.Core.Runtime.Exceptions; @@ -27,13 +29,15 @@ namespace Wacs.Core.Instructions /// /// An abstract base class for all instruction implementations, providing common functionality. /// - public abstract class InstructionBase : IInstruction + public abstract class InstructionBase { protected static Stack _aside = new(); public bool IsAsync = false; public int Size = 1; + public readonly Action Executor; + /// /// Gets the opcode associated with the instruction. /// @@ -67,8 +71,26 @@ public virtual async ValueTask ExecuteAsync(ExecContext context) /// /// /// The parsed instruction - public virtual IInstruction Parse(BinaryReader reader) => this; + public virtual InstructionBase Parse(BinaryReader reader) => this; + /// + /// Render the instruction at a given label stack depth + /// public virtual string RenderText(ExecContext? context) => Op.GetMnemonic(); + + public static bool IsEnd(InstructionBase inst) => inst.Op.x00 == OpCode.End; + + public static bool IsElseOrEnd(InstructionBase inst) => inst is InstEnd; + + public static bool IsBranch(InstructionBase? inst) => inst is IBranchInstruction; + + public static bool IsNumeric(InstructionBase? inst) => inst is NumericInst; + + public static bool IsVar(InstructionBase? inst) => inst is IVarInstruction; + + public static bool IsLoad(InstructionBase? inst) => inst is InstMemoryLoad; + + public static bool IsBound(ExecContext context, InstructionBase? inst) => + inst is ICallInstruction ci && ci.IsBound(context); } } \ No newline at end of file diff --git a/Wacs.Core/Instructions/LocalVariable.cs b/Wacs.Core/Instructions/LocalVariable.cs index c808b5c..adfeb73 100644 --- a/Wacs.Core/Instructions/LocalVariable.cs +++ b/Wacs.Core/Instructions/LocalVariable.cs @@ -35,13 +35,13 @@ public class InstLocalGet : InstructionBase, IVarInstruction, ITypedValueProduce public int CalculateSize() => 1; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Index = (LocalIdx)reader.ReadLeb128_u32(); return this; } - public IInstruction Immediate(LocalIdx index) + public InstructionBase Immediate(LocalIdx index) { Index = index; return this; @@ -94,7 +94,7 @@ public class InstLocalSet : InstructionBase, IVarInstruction, INodeConsumer OpCode.LocalSet; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Index = (LocalIdx)reader.ReadLeb128_u32(); return this; @@ -142,7 +142,7 @@ public override void Execute(ExecContext context) public Action GetFunc => SetLocal; - public IInstruction Immediate(LocalIdx idx) + public InstructionBase Immediate(LocalIdx idx) { Index = idx; return this; @@ -163,7 +163,7 @@ public class InstLocalTee : InstructionBase, IVarInstruction public LocalIdx GetIndex() => Index; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Index = (LocalIdx)reader.ReadLeb128_u32(); return this; diff --git a/Wacs.Core/Instructions/Memory.cs b/Wacs.Core/Instructions/Memory.cs index 2374bc6..b94fb6f 100644 --- a/Wacs.Core/Instructions/Memory.cs +++ b/Wacs.Core/Instructions/Memory.cs @@ -60,13 +60,13 @@ public override void Validate(IWasmValidationContext context) context.OpStack.PushType(Type); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); return this; } - public IInstruction Immediate(MemArg m) + public InstructionBase Immediate(MemArg m) { M = m; return this; @@ -121,12 +121,12 @@ public override void Validate(IWasmValidationContext context) context.OpStack.PopI32(); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); return this; } - public IInstruction Immediate(MemArg m) + public InstructionBase Immediate(MemArg m) { M = m; return this; diff --git a/Wacs.Core/Instructions/MemoryBulk.cs b/Wacs.Core/Instructions/MemoryBulk.cs index 3963634..186e86a 100644 --- a/Wacs.Core/Instructions/MemoryBulk.cs +++ b/Wacs.Core/Instructions/MemoryBulk.cs @@ -60,7 +60,7 @@ public override void Execute(ExecContext context) context.OpStack.PushU32(sz); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = (MemIdx)reader.ReadByte(); return this; @@ -117,7 +117,7 @@ public override void Execute(ExecContext context) } } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = (MemIdx)reader.ReadByte(); return this; @@ -224,7 +224,7 @@ public override void Execute(ExecContext context) } } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (DataIdx)reader.ReadLeb128_u32(); Y = (MemIdx)reader.ReadByte(); @@ -232,7 +232,7 @@ public override IInstruction Parse(BinaryReader reader) return this; } - public IInstruction Immediate(DataIdx x) + public InstructionBase Immediate(DataIdx x) { X = x; return this; @@ -271,13 +271,13 @@ public override void Execute(ExecContext context) context.Store.DropData(a); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (DataIdx)reader.ReadLeb128_u32(); return this; } - public IInstruction Immediate(DataIdx x) + public InstructionBase Immediate(DataIdx x) { X = x; return this; @@ -402,7 +402,7 @@ public override void Execute(ExecContext context) } } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { SrcX = (MemIdx)reader.ReadByte(); DstY = (MemIdx)reader.ReadByte(); @@ -487,7 +487,7 @@ public override void Execute(ExecContext context) } } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (MemIdx)reader.ReadByte(); return this; diff --git a/Wacs.Core/Instructions/Numeric/Const.cs b/Wacs.Core/Instructions/Numeric/Const.cs index b2a541b..deaa02f 100644 --- a/Wacs.Core/Instructions/Numeric/Const.cs +++ b/Wacs.Core/Instructions/Numeric/Const.cs @@ -48,12 +48,12 @@ public override void Execute(ExecContext context) context.OpStack.PushI32(Value); } - public override IInstruction Parse(BinaryReader reader) { + public override InstructionBase Parse(BinaryReader reader) { Value = reader.ReadLeb128_s32(); return this; } - public IInstruction Immediate(int value) + public InstructionBase Immediate(int value) { Value = value; return this; @@ -86,7 +86,7 @@ public override void Execute(ExecContext context) context.OpStack.PushI64(Value); } - public override IInstruction Parse(BinaryReader reader) { + public override InstructionBase Parse(BinaryReader reader) { Value = reader.ReadLeb128_s64(); return this; } @@ -119,7 +119,7 @@ public override void Execute(ExecContext context) context.OpStack.PushF32(Value); } - public override IInstruction Parse(BinaryReader reader) { + public override InstructionBase Parse(BinaryReader reader) { Value = reader.Read_f32(); return this; } @@ -160,7 +160,7 @@ public override void Execute(ExecContext context) context.OpStack.PushF64(Value); } - public override IInstruction Parse(BinaryReader reader) { + public override InstructionBase Parse(BinaryReader reader) { Value = reader.Read_f64(); return this; } diff --git a/Wacs.Core/Instructions/Parametric.cs b/Wacs.Core/Instructions/Parametric.cs index f938240..af38807 100644 --- a/Wacs.Core/Instructions/Parametric.cs +++ b/Wacs.Core/Instructions/Parametric.cs @@ -121,7 +121,7 @@ private Value Select(ExecContext _, Value val1, Value val2, int c) => c != 0 ? val1 : val2; public Func GetFunc => Select; - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { if (WithTypes) { Types = reader.ParseVector(ValTypeParser.Parse); diff --git a/Wacs.Core/Instructions/Reference.cs b/Wacs.Core/Instructions/Reference.cs index 3174668..cc836fe 100644 --- a/Wacs.Core/Instructions/Reference.cs +++ b/Wacs.Core/Instructions/Reference.cs @@ -29,7 +29,7 @@ public override void Execute(ExecContext context) { context.OpStack.PushRef(Value.RefNull(Type)); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Type = ReferenceTypeParser.Parse(reader); return this; @@ -93,7 +93,7 @@ public override void Execute(ExecContext context) context.OpStack.PushFuncref(new Value(ValType.Funcref, a.Value)); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { FunctionIndex = (FuncIdx)reader.ReadLeb128_u32(); return this; diff --git a/Wacs.Core/Instructions/SIMD/VConst.cs b/Wacs.Core/Instructions/SIMD/VConst.cs index 4ecc6cc..538ad54 100644 --- a/Wacs.Core/Instructions/SIMD/VConst.cs +++ b/Wacs.Core/Instructions/SIMD/VConst.cs @@ -42,13 +42,13 @@ public override void Execute(ExecContext context) context.OpStack.PushV128(V128); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { V128 = new V128(reader.ReadBytes(16)); return this; } - public IInstruction Immediate(V128 value) + public InstructionBase Immediate(V128 value) { V128 = value; return this; diff --git a/Wacs.Core/Instructions/SIMD/VLaneOp.cs b/Wacs.Core/Instructions/SIMD/VLaneOp.cs index f0f3778..286121c 100644 --- a/Wacs.Core/Instructions/SIMD/VLaneOp.cs +++ b/Wacs.Core/Instructions/SIMD/VLaneOp.cs @@ -162,7 +162,7 @@ public override void Execute(ExecContext context) _execute(context, X); } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = reader.ReadByte(); return this; diff --git a/Wacs.Core/Instructions/SIMD/VMemory.cs b/Wacs.Core/Instructions/SIMD/VMemory.cs index bace0e2..ee9ef39 100644 --- a/Wacs.Core/Instructions/SIMD/VMemory.cs +++ b/Wacs.Core/Instructions/SIMD/VMemory.cs @@ -209,13 +209,13 @@ public override void Execute(ExecContext context) context.OpStack.PushV128((V128)c); } - public IInstruction Immediate(MemArg m) + public InstructionBase Immediate(MemArg m) { M = m; return this; } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); return this; @@ -313,13 +313,13 @@ public override void Execute(ExecContext context) } } - public IInstruction Immediate(MemArg m) + public InstructionBase Immediate(MemArg m) { M = m; return this; } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); return this; @@ -409,13 +409,13 @@ public override void Execute(ExecContext context) } } - public IInstruction Immediate(MemArg m, LaneIdx l) + public InstructionBase Immediate(MemArg m, LaneIdx l) { M = m; return this; } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); return this; @@ -514,14 +514,14 @@ public override void Execute(ExecContext context) context.OpStack.PushV128(value); } - public IInstruction Immediate(MemArg m, LaneIdx l) + public InstructionBase Immediate(MemArg m, LaneIdx l) { M = m; X = l; return this; } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); X = reader.ReadByte(); @@ -559,7 +559,7 @@ public class InstMemoryStoreLane : InstructionBase _ => throw new InvalidDataException($"InstMemoryLoad instruction is malformed: {WidthN}"), }; - public IInstruction Immediate(MemArg m) + public InstructionBase Immediate(MemArg m) { M = m; return this; @@ -634,7 +634,7 @@ public override void Execute(ExecContext context) } } - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { M = MemArg.Parse(reader); X = reader.ReadByte(); diff --git a/Wacs.Core/Instructions/SIMD/ViShuffleOp.cs b/Wacs.Core/Instructions/SIMD/ViShuffleOp.cs index 397c012..98e43ec 100644 --- a/Wacs.Core/Instructions/SIMD/ViShuffleOp.cs +++ b/Wacs.Core/Instructions/SIMD/ViShuffleOp.cs @@ -60,7 +60,7 @@ public override void Execute(ExecContext context) public static V128 ParseLanes(BinaryReader reader) => new(reader.ReadBytes(16)); - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = ParseLanes(reader); return this; diff --git a/Wacs.Core/Instructions/SpecFactory/SpecFactory.cs b/Wacs.Core/Instructions/SpecFactory/SpecFactory.cs index 72d0fd4..28615a5 100644 --- a/Wacs.Core/Instructions/SpecFactory/SpecFactory.cs +++ b/Wacs.Core/Instructions/SpecFactory/SpecFactory.cs @@ -22,7 +22,7 @@ namespace Wacs.Core.Instructions { - public partial class SpecFactory : IInstructionFactory + public partial class SpecFactory : InstructionBaseFactory { public static readonly SpecFactory Factory = new(); public SpecFactory() {} @@ -31,7 +31,7 @@ public T CreateInstruction(ByteCode code) where T : InstructionBase => CreateInstruction(code) as T ?? throw new InvalidOperationException($"Could not create instruction of type {typeof(T).Name} for the given ByteCode."); - public IInstruction CreateInstruction(ByteCode opcode) => opcode.x00 switch { + public InstructionBase CreateInstruction(ByteCode opcode) => opcode.x00 switch { OpCode.FB => CreateInstruction(opcode.xFB), OpCode.FC => CreateInstruction(opcode.xFC), OpCode.FD => CreateInstruction(opcode.xFD), diff --git a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFB.cs b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFB.cs index f4ac53e..df861d1 100644 --- a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFB.cs +++ b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFB.cs @@ -21,7 +21,7 @@ namespace Wacs.Core.Instructions { public partial class SpecFactory { - public static IInstruction? CreateInstruction(GcCode opcode) => opcode switch + public static InstructionBase? CreateInstruction(GcCode opcode) => opcode switch { _ => throw new InvalidDataException($"Unsupported instruction {opcode.GetMnemonic()}. ByteCode: 0xFB{(byte)opcode:X2}") }; diff --git a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFC.cs b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFC.cs index d1ea3b6..0dcfef1 100644 --- a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFC.cs +++ b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFC.cs @@ -22,7 +22,7 @@ namespace Wacs.Core.Instructions { public partial class SpecFactory { - public static IInstruction? CreateInstruction(ExtCode opcode) => opcode switch + public static InstructionBase? CreateInstruction(ExtCode opcode) => opcode switch { //Non-Trapping Saturating Float to Int Conversion ExtCode.I32TruncSatF32S => NumericInst.I32TruncSatF32S, diff --git a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFD.cs b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFD.cs index 57b7651..7bef63b 100644 --- a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFD.cs +++ b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFD.cs @@ -26,7 +26,7 @@ namespace Wacs.Core.Instructions { public partial class SpecFactory { - public static IInstruction? CreateInstruction(SimdCode opcode) => opcode switch + public static InstructionBase? CreateInstruction(SimdCode opcode) => opcode switch { SimdCode.V128Const => new InstV128Const(), diff --git a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFE.cs b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFE.cs index 18fac18..35533de 100644 --- a/Wacs.Core/Instructions/SpecFactory/SpecFactoryFE.cs +++ b/Wacs.Core/Instructions/SpecFactory/SpecFactoryFE.cs @@ -21,7 +21,7 @@ namespace Wacs.Core.Instructions { public partial class SpecFactory { - public static IInstruction? CreateInstruction(AtomCode opcode) => opcode switch + public static InstructionBase? CreateInstruction(AtomCode opcode) => opcode switch { _ => throw new InvalidDataException($"Unsupported instruction {opcode.GetMnemonic()}. ByteCode: 0xFE{(byte)opcode:X2}") }; diff --git a/Wacs.Core/Instructions/Table.cs b/Wacs.Core/Instructions/Table.cs index 147420c..dc8063a 100644 --- a/Wacs.Core/Instructions/Table.cs +++ b/Wacs.Core/Instructions/Table.cs @@ -76,7 +76,7 @@ public static void ExecuteInstruction(ExecContext context, TableIdx tableIndex) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (TableIdx)reader.ReadLeb128_u32(); return this; @@ -138,7 +138,7 @@ public static void ExecuteInstruction(ExecContext context, TableIdx tableIndex) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (TableIdx)reader.ReadLeb128_u32(); return this; @@ -245,7 +245,7 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { //!!! `table.init x y` is parsed y then x Y = (ElemIdx)reader.ReadLeb128_u32(); @@ -253,7 +253,7 @@ public override IInstruction Parse(BinaryReader reader) return this; } - public IInstruction Immediate(TableIdx x, ElemIdx y) + public InstructionBase Immediate(TableIdx x, ElemIdx y) { X = x; Y = y; @@ -292,13 +292,13 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (ElemIdx)reader.ReadLeb128_u32(); return this; } - public IInstruction Immediate(ElemIdx value) + public InstructionBase Immediate(ElemIdx value) { X = value; return this; @@ -423,7 +423,7 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { DstX = (TableIdx)reader.ReadLeb128_u32(); SrcY = (TableIdx)reader.ReadLeb128_u32(); @@ -490,7 +490,7 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (TableIdx)reader.ReadLeb128_u32(); return this; @@ -534,7 +534,7 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (TableIdx)reader.ReadLeb128_u32(); return this; @@ -620,7 +620,7 @@ public override void Execute(ExecContext context) } // @Spec 5.4.5. Table Instructions - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { X = (TableIdx)reader.ReadLeb128_u32(); return this; diff --git a/Wacs.Core/Instructions/TailCall.cs b/Wacs.Core/Instructions/TailCall.cs index 420b54f..e31f23b 100644 --- a/Wacs.Core/Instructions/TailCall.cs +++ b/Wacs.Core/Instructions/TailCall.cs @@ -115,14 +115,14 @@ public override async ValueTask ExecuteAsync(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { IsAsync = true; X = (FuncIdx)reader.ReadLeb128_u32(); return this; } - public IInstruction Immediate(FuncIdx value) + public InstructionBase Immediate(FuncIdx value) { X = value; return this; @@ -351,7 +351,7 @@ public override async ValueTask ExecuteAsync(ExecContext context) /// /// @Spec 5.4.1 Control Instructions /// - public override IInstruction Parse(BinaryReader reader) + public override InstructionBase Parse(BinaryReader reader) { Y = (TypeIdx)reader.ReadLeb128_u32(); X = (TableIdx)reader.ReadLeb128_u32(); diff --git a/Wacs.Core/Instructions/Transpiler/Interfaces.cs b/Wacs.Core/Instructions/Transpiler/Interfaces.cs index e60c641..4e691e0 100644 --- a/Wacs.Core/Instructions/Transpiler/Interfaces.cs +++ b/Wacs.Core/Instructions/Transpiler/Interfaces.cs @@ -19,7 +19,7 @@ namespace Wacs.Core.Instructions.Transpiler { - public interface IInstructionAnalog + public interface InstructionBaseAnalog { public int CalculateSize(); } @@ -27,12 +27,12 @@ public interface IInstructionAnalog public interface IConvertableValueProducer {} - public interface ITypedValueProducer : IInstructionAnalog, IConvertableValueProducer + public interface ITypedValueProducer : InstructionBaseAnalog, IConvertableValueProducer { public Func GetFunc { get; } } - public interface IOptimizationTarget : IInstruction {} + public interface IOptimizationTarget {} public interface IValueConsumer {} public interface IValueConsumer {} diff --git a/Wacs.Core/Modules/InstructionSequence.cs b/Wacs.Core/Modules/InstructionSequence.cs index f20215f..c96d79e 100644 --- a/Wacs.Core/Modules/InstructionSequence.cs +++ b/Wacs.Core/Modules/InstructionSequence.cs @@ -28,22 +28,22 @@ namespace Wacs.Core /// We'll use this instead of arrays or lists, so we can abstract /// pointers into the code. /// - public class InstructionSequence : IEnumerable + public class InstructionSequence : IEnumerable { - public static readonly InstructionSequence Empty = new(new List()); + public static readonly InstructionSequence Empty = new(new List()); //public for direct array access on critical path public readonly InstructionBase[] _instructions; public readonly int Count; - public InstructionSequence(IList list) + public InstructionSequence(IList list) { _instructions = list.Cast().ToArray(); Count = _instructions.Length; } - public IInstruction? this[int index] + public InstructionBase? this[int index] { get { @@ -73,9 +73,9 @@ public int Size } } - public IInstruction LastInstruction => _instructions[^1]; + public InstructionBase LastInstruction => _instructions[^1]; - public IEnumerator GetEnumerator() => ((IEnumerable)_instructions).GetEnumerator(); + public IEnumerator GetEnumerator() => ((IEnumerable)_instructions).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public bool IsConstant(IWasmValidationContext? ctx) => diff --git a/Wacs.Core/Modules/Module.cs b/Wacs.Core/Modules/Module.cs index 5f145d2..b1cac8d 100644 --- a/Wacs.Core/Modules/Module.cs +++ b/Wacs.Core/Modules/Module.cs @@ -72,9 +72,9 @@ public static partial class BinaryModuleParser }; static readonly HashSet MemoryInstructions = new HashSet { ExtCode.MemoryInit, ExtCode.DataDrop }; - public static IInstructionFactory InstructionFactory { get; private set; } = SpecFactory.Factory; + public static InstructionBaseFactory InstructionFactory { get; private set; } = SpecFactory.Factory; - public static void UseInstructionFactory(IInstructionFactory factory) => + public static void UseInstructionFactory(InstructionBaseFactory factory) => InstructionFactory = factory; /// @@ -288,7 +288,7 @@ private static SectionId ParseSection(BinaryReader reader, Module module) /// @Spec 5.4 Instructions /// Parse an instruction sequence, return null for End (0x0B) /// - public static IInstruction? ParseInstruction(BinaryReader reader) + public static InstructionBase? ParseInstruction(BinaryReader reader) { //Splice another byte if the first byte is a prefix diff --git a/Wacs.Core/Modules/Sections/ElementSection.cs b/Wacs.Core/Modules/Sections/ElementSection.cs index d603228..52a1a75 100644 --- a/Wacs.Core/Modules/Sections/ElementSection.cs +++ b/Wacs.Core/Modules/Sections/ElementSection.cs @@ -39,7 +39,7 @@ public partial class Module /// public class ElementSegment : IRenderable { - private ElementSegment(ReferenceType type, IInstruction[] funcIndices, ElementMode mode) + private ElementSegment(ReferenceType type, InstructionBase[] funcIndices, ElementMode mode) { Type = type; Initializers = funcIndices.Select(inst => new Expression(inst, 1)).ToArray(); @@ -55,7 +55,7 @@ private ElementSegment(ReferenceType type, Expression[] expressions, ElementMode Mode.SegmentType = Type; } - private ElementSegment(TableIdx tableIndex, Expression e, ReferenceType type, IInstruction[] funcIndices) + private ElementSegment(TableIdx tableIndex, Expression e, ReferenceType type, InstructionBase[] funcIndices) { Type = type; Initializers = funcIndices.Select(inst => new Expression(inst, 1)).ToArray(); @@ -120,7 +120,7 @@ public void RenderText(StreamWriter writer, Module module, string indent) /// /// Generate a InstRefFunc for a funcidx /// - private static IInstruction ParseFuncIdxInstructions(BinaryReader reader) => + private static InstructionBase ParseFuncIdxInstructions(BinaryReader reader) => BinaryModuleParser.InstructionFactory.CreateInstruction(OpCode.RefFunc).Parse(reader); private static ReferenceType ParseElementKind(BinaryReader reader) => diff --git a/Wacs.Core/Modules/Sections/FunctionSection.cs b/Wacs.Core/Modules/Sections/FunctionSection.cs index 2bc11a8..72b46c4 100644 --- a/Wacs.Core/Modules/Sections/FunctionSection.cs +++ b/Wacs.Core/Modules/Sections/FunctionSection.cs @@ -126,9 +126,9 @@ private void RenderInstructions(StreamWriter writer, string indent, int depth, M RenderInstructions(writer, blockIndent, depth, module, blockSeq, stackRenderer.SubRenderer()); var lastInst = blockSeq.LastInstruction; - if (IInstruction.IsElseOrEnd(lastInst)) + if (InstructionBase.IsElseOrEnd(lastInst)) { - stackRenderer.FakeContext.LastEvent = IInstruction.IsEnd(lastInst) ? "[" : "]["; + stackRenderer.FakeContext.LastEvent = InstructionBase.IsEnd(lastInst) ? "[" : "]["; stackRenderer.ProcessInstruction(inst); stackRenderer.FakeContext.DummyContext.Frame.ForceLabels(depth); var endLabel = $" (;< @{depth} ;)"; diff --git a/Wacs.Core/Modules/Sections/StackRenderer.cs b/Wacs.Core/Modules/Sections/StackRenderer.cs index 40fbe3b..4e30e72 100644 --- a/Wacs.Core/Modules/Sections/StackRenderer.cs +++ b/Wacs.Core/Modules/Sections/StackRenderer.cs @@ -348,7 +348,7 @@ public StackRenderer(StackRenderer? parent, bool doesWrite, int width = 40, Fake public StackRenderer SubRenderer() => new(this, DoesWrite, Width); - public void ProcessInstruction(IInstruction inst) + public void ProcessInstruction(InstructionBase inst) { FakeContext.LastEvent = ""; inst.Validate(FakeContext); diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index 7e32709..40bc020 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -87,7 +87,7 @@ public ExecContext(Store store, RuntimeAttributes? attributes = default) OpStack = new(Attributes.MaxOpStack); } - public IInstructionFactory InstructionFactory => Attributes.InstructionFactory; + public InstructionBaseFactory InstructionFactory => Attributes.InstructionFactory; public MemoryInstance DefaultMemory => Store[Frame.Module.MemAddrs[default]]; public InstructionPointer GetPointer() => new(_currentSequence, _sequenceIndex); diff --git a/Wacs.Core/Runtime/RuntimeAttributes.cs b/Wacs.Core/Runtime/RuntimeAttributes.cs index 4d523ac..99fc19b 100644 --- a/Wacs.Core/Runtime/RuntimeAttributes.cs +++ b/Wacs.Core/Runtime/RuntimeAttributes.cs @@ -34,7 +34,7 @@ public class RuntimeAttributes public int MaxFunctionLocals = 2048; public int MaxOpStack = 1024; - public IInstructionFactory InstructionFactory { get; set; } = SpecFactory.Factory; + public InstructionBaseFactory InstructionFactory { get; set; } = SpecFactory.Factory; } } \ No newline at end of file diff --git a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs index e9b8086..cb36923 100644 --- a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs +++ b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs @@ -59,7 +59,7 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) if (seq.Count == 0) return InstructionSequence.Empty; - Stack stack = new(seq.Count); + Stack stack = new(seq.Count); foreach (var inst in seq) { @@ -114,7 +114,7 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) break; case InstLocalTee instTee: //Split into local.set/local.get - var newSet = new InstLocalSet().Immediate(instTee.GetIndex()) as IOptimizationTarget; + var newSet = new InstLocalSet().Immediate(instTee.GetIndex()); var newGet = new InstLocalGet().Immediate(instTee.GetIndex()); if (newSet != null) { @@ -126,7 +126,7 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) stack.Push(inst); break; case IOptimizationTarget target: - var newInst = OptimizeInstruction(target, stack); + var newInst = OptimizeInstruction(target as InstructionBase, stack); stack.Push(newInst); break; default: @@ -140,7 +140,7 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) return newSeq; } - private static IInstruction OptimizeInstruction(IOptimizationTarget inst, Stack stack) + private static InstructionBase OptimizeInstruction(InstructionBase inst, Stack stack) { switch (inst) { @@ -179,7 +179,7 @@ private static IInstruction OptimizeInstruction(IOptimizationTarget inst, Stack< } } - private static IInstruction BindAnyValue(IOptimizationTarget inst, Stack stack, IValueConsumer valueConsumer) + private static InstructionBase BindAnyValue(InstructionBase inst, Stack stack, IValueConsumer valueConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -206,7 +206,7 @@ private static IInstruction BindAnyValue(IOptimizationTarget inst, Stack stack, IValueConsumer uintConsumer) + private static InstructionBase BindU32(InstructionBase inst, Stack stack, IValueConsumer uintConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -233,7 +233,7 @@ private static IInstruction BindU32(IOptimizationTarget inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindI32(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -255,7 +255,7 @@ private static IInstruction BindI32(IInstruction inst, Stack stack return inst; } - private static IInstruction BindI32I32(IInstruction inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindI32I32(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 2) return inst; @@ -289,7 +289,7 @@ private static IInstruction BindI32I32(IInstruction inst, Stack st return inst; } - private static IInstruction BindU32U32(IInstruction inst, Stack stack, IValueConsumer uintConsumer) + private static InstructionBase BindU32U32(InstructionBase inst, Stack stack, IValueConsumer uintConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -324,7 +324,7 @@ private static IInstruction BindU32U32(IInstruction inst, Stack st return inst; } - private static IInstruction BindU32U64(IInstruction inst, Stack stack, IValueConsumer ulongConsumer) + private static InstructionBase BindU32U64(InstructionBase inst, Stack stack, IValueConsumer ulongConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -356,7 +356,7 @@ private static IInstruction BindU32U64(IInstruction inst, Stack st stack.Push(i2); return inst; } - private static IInstruction BindU32F32(IInstruction inst, Stack stack, IValueConsumer floatConsumer) + private static InstructionBase BindU32F32(InstructionBase inst, Stack stack, IValueConsumer floatConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -387,7 +387,7 @@ private static IInstruction BindU32F32(IInstruction inst, Stack st stack.Push(i2); return inst; } - private static IInstruction BindU32F64(IInstruction inst, Stack stack, IValueConsumer doubleConsumer) + private static InstructionBase BindU32F64(InstructionBase inst, Stack stack, IValueConsumer doubleConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -418,7 +418,7 @@ private static IInstruction BindU32F64(IInstruction inst, Stack st stack.Push(i2); return inst; } - private static IInstruction BindU32V128(IInstruction inst, Stack stack, IValueConsumer vecConsumer) + private static InstructionBase BindU32V128(InstructionBase inst, Stack stack, IValueConsumer vecConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -449,7 +449,7 @@ private static IInstruction BindU32V128(IInstruction inst, Stack s stack.Push(i2); return inst; } - private static IInstruction BindU32I32(IInstruction inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindU32I32(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -482,7 +482,7 @@ private static IInstruction BindU32I32(IInstruction inst, Stack st return inst; } - private static IInstruction BindU64(IOptimizationTarget inst, Stack stack, IValueConsumer ulongConsumer) + private static InstructionBase BindU64(InstructionBase inst, Stack stack, IValueConsumer ulongConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -507,7 +507,7 @@ private static IInstruction BindU64(IOptimizationTarget inst, Stack stack, IValueConsumer longConsumer) + private static InstructionBase BindI64(InstructionBase inst, Stack stack, IValueConsumer longConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -531,7 +531,7 @@ private static IInstruction BindI64(IInstruction inst, Stack stack return inst; } - private static IInstruction BindI64I64(IInstruction inst, Stack stack, IValueConsumer longConsumer) + private static InstructionBase BindI64I64(InstructionBase inst, Stack stack, IValueConsumer longConsumer) { if (stack.Count < 2) return inst; @@ -567,7 +567,7 @@ private static IInstruction BindI64I64(IInstruction inst, Stack st return inst; } - private static IInstruction BindU64U64(IInstruction inst, Stack stack, IValueConsumer longConsumer) + private static InstructionBase BindU64U64(InstructionBase inst, Stack stack, IValueConsumer longConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -602,7 +602,7 @@ private static IInstruction BindU64U64(IInstruction inst, Stack st return inst; } - private static IInstruction BindU64I64(IInstruction inst, Stack stack, IValueConsumer longConsumer) + private static InstructionBase BindU64I64(InstructionBase inst, Stack stack, IValueConsumer longConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -635,7 +635,7 @@ private static IInstruction BindU64I64(IInstruction inst, Stack st return inst; } - private static IInstruction BindU32Value(IInstruction inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindU32Value(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 1) return inst; var i2 = stack.Pop(); @@ -681,9 +681,9 @@ private static IInstruction BindU32Value(IInstruction inst, Stack return inst; } - private static IInstruction BindValueValueI32(IInstruction inst, Stack stack, IValueConsumer valueConsumer) + private static InstructionBase BindValueValueI32(InstructionBase inst, Stack stack, IValueConsumer valueConsumer) { - Stack operands = new(); + Stack operands = new(); bool tryStack = true; ITypedValueProducer? i1Producer = null; @@ -758,7 +758,7 @@ private static IInstruction BindValueValueI32(IInstruction inst, Stack stack, IValueConsumer floatConsumer) + private static InstructionBase BindF32(InstructionBase inst, Stack stack, IValueConsumer floatConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -779,7 +779,7 @@ private static IInstruction BindF32(IOptimizationTarget inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindF32F32(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); @@ -812,7 +812,7 @@ private static IInstruction BindF32F32(IInstruction inst, Stack st return inst; } - private static IInstruction BindF64(IOptimizationTarget inst, Stack stack, IValueConsumer doubleConsumer) + private static InstructionBase BindF64(InstructionBase inst, Stack stack, IValueConsumer doubleConsumer) { if (stack.Count < 1) return inst; var top = stack.Pop(); @@ -833,7 +833,7 @@ private static IInstruction BindF64(IOptimizationTarget inst, Stack stack, IValueConsumer intConsumer) + private static InstructionBase BindF64F64(InstructionBase inst, Stack stack, IValueConsumer intConsumer) { if (stack.Count < 2) return inst; var i2 = stack.Pop(); diff --git a/Wacs.Core/Runtime/WasmRuntimeExecution.cs b/Wacs.Core/Runtime/WasmRuntimeExecution.cs index f96b5b0..e6d2d49 100644 --- a/Wacs.Core/Runtime/WasmRuntimeExecution.cs +++ b/Wacs.Core/Runtime/WasmRuntimeExecution.cs @@ -31,7 +31,7 @@ namespace Wacs.Core.Runtime { public partial class WasmRuntime { - private IInstruction? lastInstruction = null; + private InstructionBase? lastInstruction = null; public TDelegate CreateInvoker(FuncAddr funcAddr, InvokerOptions? options = default) where TDelegate : Delegate @@ -450,18 +450,18 @@ public async Task ProcessThreadWithOptions(InvokerOptions options) } } - private void LogPreInstruction(InvokerOptions options, IInstruction inst) + private void LogPreInstruction(InvokerOptions options, InstructionBase inst) { switch ((OpCode)inst.Op) { //Handle these post - case var _ when IInstruction.IsNumeric(inst): break; - case var _ when IInstruction.IsVar(inst): break; - case var _ when IInstruction.IsLoad(inst): break; + case var _ when InstructionBase.IsNumeric(inst): break; + case var _ when InstructionBase.IsVar(inst): break; + case var _ when InstructionBase.IsLoad(inst): break; - case OpCode.Call when ((int)options.LogInstructionExecution&(int)InstructionLogging.Binds)!=0 && IInstruction.IsBound(Context, inst): - case OpCode.CallIndirect when ((int)options.LogInstructionExecution&(int)InstructionLogging.Binds)!=0 && IInstruction.IsBound(Context, inst): - // case OpCode.CallRef when options.LogInstructionExecution&(int)InstructionLogging.Binds) && IInstruction.IsBound(Context, inst): + case OpCode.Call when ((int)options.LogInstructionExecution&(int)InstructionLogging.Binds)!=0 && InstructionBase.IsBound(Context, inst): + case OpCode.CallIndirect when ((int)options.LogInstructionExecution&(int)InstructionLogging.Binds)!=0 && InstructionBase.IsBound(Context, inst): + // case OpCode.CallRef when options.LogInstructionExecution&(int)InstructionLogging.Binds) && InstructionBase.IsBound(Context, inst): case OpCode.Call when ((int)options.LogInstructionExecution&(int)InstructionLogging.Calls)!=0: case OpCode.CallIndirect when ((int)options.LogInstructionExecution&(int)InstructionLogging.Calls)!=0: @@ -481,7 +481,7 @@ private void LogPreInstruction(InvokerOptions options, IInstruction inst) case OpCode.BrIf when ((int)options.LogInstructionExecution&(int)InstructionLogging.Branches)!=0: case OpCode.BrTable when ((int)options.LogInstructionExecution&(int)InstructionLogging.Branches)!=0: - case var _ when IInstruction.IsBranch(lastInstruction) && ((int)options.LogInstructionExecution&(int)InstructionLogging.Branches)!=0: + case var _ when InstructionBase.IsBranch(lastInstruction) && ((int)options.LogInstructionExecution&(int)InstructionLogging.Branches)!=0: case var _ when ((int)options.LogInstructionExecution&(int)InstructionLogging.Computes)!=0: string location = ""; if (options.CalculateLineNumbers) @@ -505,16 +505,16 @@ private void LogPreInstruction(InvokerOptions options, IInstruction inst) } } - private void LogPostInstruction(InvokerOptions options, IInstruction inst) + private void LogPostInstruction(InvokerOptions options, InstructionBase inst) { if ((options.LogInstructionExecution & InstructionLogging.Computes) == 0) return; switch ((OpCode)inst.Op) { - case var _ when IInstruction.IsLoad(inst): - case var _ when IInstruction.IsNumeric(inst): - case var _ when IInstruction.IsVar(inst): + case var _ when InstructionBase.IsLoad(inst): + case var _ when InstructionBase.IsNumeric(inst): + case var _ when InstructionBase.IsVar(inst): string location = ""; if (options.CalculateLineNumbers) { diff --git a/Wacs.Core/Types/Expression.cs b/Wacs.Core/Types/Expression.cs index 44e16cd..44650f7 100644 --- a/Wacs.Core/Types/Expression.cs +++ b/Wacs.Core/Types/Expression.cs @@ -77,10 +77,10 @@ public Expression(int arity, InstructionSequence seq, bool isStatic) } //Single Initializer - public Expression(IInstruction single, int arity) + public Expression(InstructionBase single, int arity) { IsStatic = true; - Instructions = new InstructionSequence(new List { single }); + Instructions = new InstructionSequence(new List { single }); LabelTarget = new (new Label { Arity = arity, @@ -164,10 +164,10 @@ public void ExecuteInitializer(ExecContext context) /// @Spec 5.4.9 Expressions /// public static Expression Parse(BinaryReader reader) => - new(new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, IInstruction.IsEnd)), true); + new(new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, InstructionBase.IsEnd)), true); public static Expression ParseInitializer(BinaryReader reader) => - new(1, new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, IInstruction.IsEnd)), true); + new(1, new InstructionSequence(reader.ParseUntil(BinaryModuleParser.ParseInstruction, InstructionBase.IsEnd)), true); /// /// For Single instruction renders (globals, elements) diff --git a/Wacs.Core/Validation/WasmValidationContext.cs b/Wacs.Core/Validation/WasmValidationContext.cs index 6963d40..de4fb9e 100644 --- a/Wacs.Core/Validation/WasmValidationContext.cs +++ b/Wacs.Core/Validation/WasmValidationContext.cs @@ -208,7 +208,7 @@ public void SetExecFrame(FunctionType funcType, ValType[] localTypes) }; } - public class InstructionValidator : AbstractValidator + public class InstructionValidator : AbstractValidator { public InstructionValidator() { From cec2d6d64a6b2a65c93fd2868f3df3ca83ff8c6b Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 01:00:49 -0800 Subject: [PATCH 08/19] Migrating OpStack to a mutable value struct --- Spec.Test/BindingTests.cs | 2 +- Wacs.Core/Instructions/Control.cs | 11 +-- Wacs.Core/Instructions/Parametric.cs | 1 + Wacs.Core/Runtime/ExecContext.cs | 9 ++- Wacs.Core/Runtime/OpStack.cs | 103 +++++++++++++++---------- Wacs.Core/Runtime/RuntimeAttributes.cs | 2 +- Wacs.Core/Runtime/Value.cs | 40 +++++----- 7 files changed, 94 insertions(+), 74 deletions(-) diff --git a/Spec.Test/BindingTests.cs b/Spec.Test/BindingTests.cs index 792ba42..af1d509 100644 --- a/Spec.Test/BindingTests.cs +++ b/Spec.Test/BindingTests.cs @@ -54,7 +54,7 @@ public void BindStackBinder() var fa = runtime.GetExportedFunction(("binding", "4x4")); var invoker = runtime.CreateStackInvoker(fa); - var result = invoker(new Value[]{1, 1, 1f, 1.0}); + var result = invoker(new Value[]{1, 1L, 1f, 1.0}); Assert.Equal(1 * 2, (int)result[0]); Assert.Equal(1 * 3, (long)result[1]); diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index 0db2f1e..4b87fea 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -409,7 +409,7 @@ public override string RenderText(ExecContext? context) if (context.Attributes.Live) { sb.Append(" "); - Stack values = new Stack(); + var values = new Stack(); context.OpStack.PopResults(func.Type.ResultType, ref values); sb.Append("["); while (values.Count > 0) @@ -484,6 +484,7 @@ public static void ExecuteInstruction(ExecContext context, LabelIdx labelIndex) // An ideal solution would be to slice the OpStack array, but we don't have access. if (context.OpStack.Count > context.Frame.StackHeight + label.StackHeight + label.Arity) { + //TODO Move the elements in OpStack's registers array. context.OpStack.PopResults(label.Arity, ref _asideVals); //6. context.ResetStack(label); @@ -583,7 +584,7 @@ public override string RenderText(ExecContext? context) string taken = ""; if (context.Attributes.Live) { - taken = (int)context.OpStack.Peek() != 0 ? "-> " : "X: "; + taken = context.OpStack.Peek().Int32 != 0 ? "-> " : "X: "; } return $"{base.RenderText(context)} {L.Value} (;{taken}@{depth - L.Value};)"; } @@ -683,7 +684,7 @@ public override string RenderText(ExecContext? context) int index = -2; if (context.Attributes.Live) { - int c = context.OpStack.Peek(); + int c = context.OpStack.Peek().Int32; if (c < Ls.Length) { index = c; @@ -819,7 +820,7 @@ public override string RenderText(ExecContext? context) if (context.Attributes.Live) { sb.Append(" "); - Stack values = new Stack(); + var values = new Stack(); context.OpStack.PopResults(func.Type.ParameterTypes, ref values); sb.Append("["); while (values.Count > 0) @@ -859,7 +860,7 @@ public bool IsBound(ExecContext context) { var ta = context.Frame.Module.TableAddrs[X]; var tab = context.Store[ta]; - int i = context.OpStack.Peek(); + int i = context.OpStack.Peek().Int32; if (i >= tab.Elements.Count) throw new TrapException($"Instruction call_indirect could not find element {i}"); var r = tab.Elements[i]; diff --git a/Wacs.Core/Instructions/Parametric.cs b/Wacs.Core/Instructions/Parametric.cs index af38807..8379dc5 100644 --- a/Wacs.Core/Instructions/Parametric.cs +++ b/Wacs.Core/Instructions/Parametric.cs @@ -112,6 +112,7 @@ public override void Validate(IWasmValidationContext context) public override void Execute(ExecContext context) { int c = context.OpStack.PopI32(); + //TODO implement in OpStack with array move Value val2 = context.OpStack.PopAny(); Value val1 = context.OpStack.PopAny(); context.OpStack.PushValue(Select(context, val1, val2, c)); diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index 40bc020..599205b 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -155,10 +155,11 @@ public InstructionPointer PopFrame() public void ResetStack(Label label) { - for (int c = OpStack.Count, h = label.StackHeight + Frame.StackHeight; c > h; --c) - { - OpStack.PopAny(); - } + // for (int c = OpStack.Count, h = label.StackHeight + Frame.StackHeight; c > h; --c) + // { + // OpStack.PopAny(); + // } + OpStack.PopTo(label.StackHeight + Frame.StackHeight); } public void FlushCallStack() diff --git a/Wacs.Core/Runtime/OpStack.cs b/Wacs.Core/Runtime/OpStack.cs index f78dbf9..ba23974 100644 --- a/Wacs.Core/Runtime/OpStack.cs +++ b/Wacs.Core/Runtime/OpStack.cs @@ -24,14 +24,14 @@ namespace Wacs.Core.Runtime { public class OpStack { - private readonly Stack _stack; + private readonly Value[] _registers; private readonly int _stackLimit; public int Count; public OpStack(int limit) { _stackLimit = limit; - _stack = new(limit); + _registers = new Value[limit]; Count = 0; } @@ -49,56 +49,63 @@ public void PushI32(int value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.I32; + _registers[Count - 1].Int32 = value; } public void PushU32(uint value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.I32; + _registers[Count - 1].UInt32 = value; } public void PushI64(long value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.I64; + _registers[Count - 1].Int64 = value; } public void PushU64(ulong value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.I64; + _registers[Count - 1].UInt64 = value; } public void PushF32(float value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.F32; + _registers[Count - 1].Float32 = value; } public void PushF64(double value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.F64; + _registers[Count - 1].Float64 = value; } public void PushV128(V128 value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(new Value(value)); + + _registers[Count - 1].Type = ValType.V128; + _registers[Count - 1].V128 = value; } public void PushFuncref(Value value) @@ -126,68 +133,75 @@ public void PushValue(Value value) { if (++Count > _stackLimit) throw new WasmRuntimeException($"Operand stack exhausted {Count}"); - - _stack.Push(value); + _registers[Count - 1] = value; } public int PopI32() { --Count; - return _stack.Pop().Int32; + return _registers[Count].Int32; } public uint PopU32() { --Count; - return _stack.Pop().UInt32; + return _registers[Count].UInt32; } public long PopI64() { --Count; - return _stack.Pop().Int64; + return _registers[Count].Int64; } public ulong PopU64() { --Count; - return _stack.Pop().UInt64; + return _registers[Count].UInt64; } public float PopF32() { --Count; - return _stack.Pop().Float32; + return _registers[Count].Float32; } public double PopF64() { --Count; - return _stack.Pop().Float64; + return _registers[Count].Float64; } public V128 PopV128() { --Count; - return _stack.Pop().V128; + return _registers[Count].V128; } public Value PopRefType() { --Count; - return _stack.Pop(); + return _registers[Count]; } - public Value PopAny() + public Value PopAny() { --Count; - return _stack.Pop(); + return _registers[Count]; + } + + public void PopTo(int height) + { + Count = height; } public Value PopType(ValType type) { --Count; - var val = _stack.Pop(); + if (Count < 0) + throw new InvalidDataException($"Stackunderflow"); + + var val = _registers[Count]; if (val.Type != type) throw new InvalidDataException($"OperandStack contained wrong type {val.Type} expected {type}"); return val; @@ -195,49 +209,54 @@ public Value PopType(ValType type) public void Clear() { - _stack.Clear(); Count = 0; } - public Value Peek() => _stack.Peek(); + public Value Peek() + { + return _registers[Count-1]; + } public int PopResults(ResultType type, ref Value[] results) { int arity = type.Arity; - Count -= arity; for (int i = arity - 1; i >= 0; --i) { - //We could check the types here, but the spec just says to YOLO it. - results[i] = _stack.Pop(); + results[i] = PopType(type.Types[i]); } return arity; } public void PopResults(ResultType type, ref Stack results) { - Count -= type.Arity; for (int i = 0, l = type.Arity; i < l; ++i) { - //We could check the types here, but the spec just says to YOLO it. - results.Push(_stack.Pop()); + results.Push(PopType(type.Types[i])); } } public void PopResults(int arity, ref Stack results) { - Count -= arity; for (int i = 0, l = arity; i < l; ++i) { - //We could check the types here, but the spec just says to YOLO it. - results.Push(_stack.Pop()); + results.Push(PopAny()); } } public void PopScalars(ResultType type, object[] targetBuf, int firstParameter) { - for (int i = type.Arity - 1 + firstParameter; i >= firstParameter; --i) + for (int i = type.Arity - 1; i >= 0; --i) { - targetBuf[i] = PopAny().Scalar; + targetBuf[i + firstParameter] = type.Types[i] switch + { + ValType.I32 => PopI32(), + ValType.I64 => PopI64(), + ValType.U32 => PopU32(), + ValType.F32 => PopF32(), + ValType.F64 => PopF64(), + ValType.V128 => PopV128(), + _ => throw new InvalidDataException($"Unsupported value type {type.Types[i]}") + }; } } diff --git a/Wacs.Core/Runtime/RuntimeAttributes.cs b/Wacs.Core/Runtime/RuntimeAttributes.cs index 99fc19b..558ba90 100644 --- a/Wacs.Core/Runtime/RuntimeAttributes.cs +++ b/Wacs.Core/Runtime/RuntimeAttributes.cs @@ -33,7 +33,7 @@ public class RuntimeAttributes public int MaxFunctionLocals = 2048; - public int MaxOpStack = 1024; + public int MaxOpStack = 256; public InstructionBaseFactory InstructionFactory { get; set; } = SpecFactory.Factory; } diff --git a/Wacs.Core/Runtime/Value.cs b/Wacs.Core/Runtime/Value.cs index 043dc42..95a2bca 100644 --- a/Wacs.Core/Runtime/Value.cs +++ b/Wacs.Core/Runtime/Value.cs @@ -29,38 +29,35 @@ namespace Wacs.Core.Runtime /// @Spec 4.2.1. Values /// [StructLayout(LayoutKind.Explicit)] - public readonly struct Value : IComparable + public struct Value { - public static readonly Value Unknown = new(ValType.Unknown); - - [FieldOffset(0)] public readonly ValType Type; + [FieldOffset(0)] public ValType Type; + [FieldOffset(4)] public byte U8; + + [FieldOffset(4)] public short Int16; - [FieldOffset(1)] public readonly byte U8; - - [FieldOffset(1)] public readonly short Int16; - // 32-bit integer - [FieldOffset(1)] public readonly int Int32; - [FieldOffset(1)] public readonly uint UInt32; - + [FieldOffset(4)] public int Int32; + [FieldOffset(4)] public uint UInt32; + // Ref Address - [FieldOffset(1)] public readonly int Ptr; + [FieldOffset(4)] public int Ptr; // 64-bit integer - [FieldOffset(1)] public readonly long Int64; - [FieldOffset(1)] public readonly ulong UInt64; + [FieldOffset(4)] public long Int64; + [FieldOffset(4)] public ulong UInt64; // 32-bit float - [FieldOffset(1)] public readonly float Float32; + [FieldOffset(4)] public float Float32; // 64-bit float - [FieldOffset(1)] public readonly double Float64; + [FieldOffset(4)] public double Float64; // 128-bit vector - [FieldOffset(1)] public readonly V128 V128; + [FieldOffset(4)] public MV128 V128; // ref.extern externIdx - [FieldOffset(1)] public readonly ulong ExternIdx; + [FieldOffset(4)] public ulong ExternIdx; // Constructors for each type public Value(int value) @@ -286,7 +283,7 @@ public Value(ValType type) Float64 = 0.0d; break; case ValType.V128: - V128 = (0L, 0L); + V128 = (V128)(0L, 0L); break; case ValType.Funcref: Ptr = -1; @@ -353,7 +350,7 @@ public Value(object externalValue) break; case BigInteger bi: Type = ValType.V128; - V128 = bi.ToV128(); + V128 = (V128)bi.ToV128(); break; case V128 v128: Type = ValType.V128; @@ -410,7 +407,7 @@ public Value(ValType type, object externalValue) throw new InvalidDataException($"Cannot define StackValue of type {type}"); } } - + public Value Default => new Value(Type); public object Scalar => Type switch @@ -438,6 +435,7 @@ public object CastScalar() throw new InvalidCastException($"Cannot cast ValType {Type} to Scalar"); } + public static readonly Value Unknown = new(ValType.Unknown); public static readonly Value NullFuncRef = new(ValType.Funcref); public static readonly Value NullExternRef = new(ValType.Externref); public static readonly Value Void = new (ValType.Nil); From da1948b441fb811da66b5a6884d4094559732e23 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 10:32:31 -0800 Subject: [PATCH 09/19] Adding WacsCode opcode section --- .../Transpiler/InstAggregate1_0.cs | 2 +- .../Transpiler/InstAggregate1_1.cs | 2 +- .../Transpiler/InstAggregate2_0.cs | 2 +- .../Transpiler/InstAggregate2_1.cs | 2 +- .../Transpiler/InstAggregate3_1.cs | 2 +- .../Transpiler/InstStackProducer.cs | 6 +-- Wacs.Core/OpCodes/Admin.cs | 38 +++++++++++++++++++ Wacs.Core/OpCodes/ByteCode.cs | 17 ++++++++- Wacs.Core/OpCodes/OpCode.cs | 6 +-- Wacs.Core/Runtime/ExecContext.cs | 4 ++ 10 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 Wacs.Core/OpCodes/Admin.cs diff --git a/Wacs.Core/Instructions/Transpiler/InstAggregate1_0.cs b/Wacs.Core/Instructions/Transpiler/InstAggregate1_0.cs index 0d1c307..cc623d2 100644 --- a/Wacs.Core/Instructions/Transpiler/InstAggregate1_0.cs +++ b/Wacs.Core/Instructions/Transpiler/InstAggregate1_0.cs @@ -33,7 +33,7 @@ public InstAggregate1_0(ITypedValueProducer inA, INodeConsumer consume Size = inA.CalculateSize() + 1; } - public override ByteCode Op => OpCode.Aggr; + public override ByteCode Op => WacsCode.Aggr1_0; public override void Validate(IWasmValidationContext context) { diff --git a/Wacs.Core/Instructions/Transpiler/InstAggregate1_1.cs b/Wacs.Core/Instructions/Transpiler/InstAggregate1_1.cs index 7b8fb10..6c31017 100644 --- a/Wacs.Core/Instructions/Transpiler/InstAggregate1_1.cs +++ b/Wacs.Core/Instructions/Transpiler/InstAggregate1_1.cs @@ -46,7 +46,7 @@ public InstAggregate1_1(ITypedValueProducer in1, INodeComputer c else throw new InvalidDataException($"Could not bind aggregate type {typeof(TOut)}"); } - public override ByteCode Op => OpCode.Aggr; + public override ByteCode Op => WacsCode.Aggr1_1; public int CalculateSize() => Size; diff --git a/Wacs.Core/Instructions/Transpiler/InstAggregate2_0.cs b/Wacs.Core/Instructions/Transpiler/InstAggregate2_0.cs index 0a720b3..ae1f8f2 100644 --- a/Wacs.Core/Instructions/Transpiler/InstAggregate2_0.cs +++ b/Wacs.Core/Instructions/Transpiler/InstAggregate2_0.cs @@ -36,7 +36,7 @@ public InstAggregate2_0(ITypedValueProducer in1, ITypedValueProducer Size = in1.CalculateSize() + in2.CalculateSize() + 1; } - public override ByteCode Op => OpCode.Aggr; + public override ByteCode Op => WacsCode.Aggr2_0; public override void Validate(IWasmValidationContext context) { diff --git a/Wacs.Core/Instructions/Transpiler/InstAggregate2_1.cs b/Wacs.Core/Instructions/Transpiler/InstAggregate2_1.cs index dea2cc3..4bfb333 100644 --- a/Wacs.Core/Instructions/Transpiler/InstAggregate2_1.cs +++ b/Wacs.Core/Instructions/Transpiler/InstAggregate2_1.cs @@ -48,7 +48,7 @@ public InstAggregate2_1(ITypedValueProducer in1, ITypedValueProducer else throw new InvalidDataException($"Could not bind aggregate type {typeof(TOut)}"); } - public override ByteCode Op => OpCode.Aggr; + public override ByteCode Op => WacsCode.Aggr2_1; public int CalculateSize() => Size; diff --git a/Wacs.Core/Instructions/Transpiler/InstAggregate3_1.cs b/Wacs.Core/Instructions/Transpiler/InstAggregate3_1.cs index 0cd0faa..de44da2 100644 --- a/Wacs.Core/Instructions/Transpiler/InstAggregate3_1.cs +++ b/Wacs.Core/Instructions/Transpiler/InstAggregate3_1.cs @@ -54,7 +54,7 @@ public InstAggregate3_1( else throw new InvalidDataException($"Could not bind aggregate type {typeof(TOut)}"); } - public override ByteCode Op => OpCode.Aggr; + public override ByteCode Op => WacsCode.Aggr3_1; public int CalculateSize() => Size; diff --git a/Wacs.Core/Instructions/Transpiler/InstStackProducer.cs b/Wacs.Core/Instructions/Transpiler/InstStackProducer.cs index a21027b..5ede8a1 100644 --- a/Wacs.Core/Instructions/Transpiler/InstStackProducer.cs +++ b/Wacs.Core/Instructions/Transpiler/InstStackProducer.cs @@ -29,7 +29,7 @@ public InstStackProducerValue() Size = 0; } - public override ByteCode Op => OpCode.StackVal; + public override ByteCode Op => WacsCode.StackVal; public Func GetFunc => FetchFromStack; @@ -56,7 +56,7 @@ public InstStackProducerU32() Size = 0; } - public override ByteCode Op => OpCode.StackVal; + public override ByteCode Op => WacsCode.StackU32; public Func GetFunc => FetchFromStack; @@ -82,7 +82,7 @@ public InstStackProducerI32() Size = 0; } - public override ByteCode Op => OpCode.StackVal; + public override ByteCode Op => WacsCode.StackI32; public Func GetFunc => FetchFromStack; diff --git a/Wacs.Core/OpCodes/Admin.cs b/Wacs.Core/OpCodes/Admin.cs new file mode 100644 index 0000000..a1a71e8 --- /dev/null +++ b/Wacs.Core/OpCodes/Admin.cs @@ -0,0 +1,38 @@ +// /* +// * Copyright 2024 Kelvin Nishikawa +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +using Wacs.Core.Attributes; + +namespace Wacs.Core.OpCodes +{ + /// + /// Administrative OpCodes used by WACS + /// + public enum WacsCode : byte + { + [OpCode("stack.val")] StackVal = 0x10, + [OpCode("stack.i32")] StackI32 = 0x11, + [OpCode("stack.u32")] StackU32 = 0x12, + + [OpCode("aggr.1x0")] Aggr1_0 = 0x20, + [OpCode("aggr.1x1")] Aggr1_1 = 0x21, + [OpCode("aggr.2x0")] Aggr2_0 = 0x22, + [OpCode("aggr.2x1")] Aggr2_1 = 0x23, + [OpCode("aggr.3x1")] Aggr3_1 = 0x24, + + [OpCode("i32.fused.add")] I32FusedAdd = 0x30, + } +} \ No newline at end of file diff --git a/Wacs.Core/OpCodes/ByteCode.cs b/Wacs.Core/OpCodes/ByteCode.cs index affbf50..709a2a9 100644 --- a/Wacs.Core/OpCodes/ByteCode.cs +++ b/Wacs.Core/OpCodes/ByteCode.cs @@ -22,11 +22,12 @@ namespace Wacs.Core.OpCodes [StructLayout(LayoutKind.Explicit)] public struct ByteCode : IComparable { - [FieldOffset(0)] public readonly OpCode x00; + [FieldOffset(0)] public readonly OpCode x00; [FieldOffset(1)] public readonly GcCode xFB; [FieldOffset(1)] public readonly ExtCode xFC; [FieldOffset(1)] public readonly SimdCode xFD; [FieldOffset(1)] public readonly AtomCode xFE; + [FieldOffset(1)] public readonly WacsCode xFF; public ByteCode(OpCode b) { @@ -61,6 +62,13 @@ public ByteCode(AtomCode b) x00 = OpCode.FE; xFE = b; } + + public ByteCode(WacsCode b) + { + this = default; + x00 = OpCode.FF; + xFF = b; + } public static explicit operator ByteCode(ushort bytes) => (byte)(bytes >> 8) switch @@ -69,6 +77,7 @@ public static explicit operator ByteCode(ushort bytes) => 0xFC => new ByteCode((ExtCode)(byte)(bytes & 0xFF)), 0xFD => new ByteCode((SimdCode)(byte)(bytes & 0xFF)), 0xFE => new ByteCode((AtomCode)(byte)(bytes & 0xFF)), + 0xFF => new ByteCode((WacsCode)(byte)(bytes & 0xFF)), _ => new ByteCode((OpCode)(byte)(bytes >> 8)), }; @@ -79,6 +88,7 @@ public static explicit operator ushort(ByteCode byteCode) => OpCode.FC => (ushort)((byte)byteCode.x00 << 8 | (byte)byteCode.xFC), OpCode.FD => (ushort)((byte)byteCode.x00 << 8 | (byte)byteCode.xFD), OpCode.FE => (ushort)((byte)byteCode.x00 << 8 | (byte)byteCode.xFE), + OpCode.FF => (ushort)((byte)byteCode.x00 << 8 | (byte)byteCode.xFF), _ => (ushort)((byte)byteCode.x00 << 8), }; @@ -88,6 +98,7 @@ public static explicit operator ushort(ByteCode byteCode) => public static implicit operator ByteCode(ExtCode b) => new ByteCode(b); public static implicit operator ByteCode(SimdCode b) => new ByteCode(b); public static implicit operator ByteCode(AtomCode b) => new ByteCode(b); + public static implicit operator ByteCode(WacsCode b) => new ByteCode(b); public override bool Equals(object obj) => obj is ByteCode other && x00 == other.x00 && x00 switch { @@ -95,6 +106,7 @@ public override bool Equals(object obj) => OpCode.FC => xFC.Equals(other.xFC), OpCode.FD => xFD.Equals(other.xFD), OpCode.FE => xFE.Equals(other.xFE), + OpCode.FF => xFE.Equals(other.xFF), _ => true }; @@ -104,6 +116,7 @@ public override int GetHashCode() => OpCode.FC => HashCode.Combine(x00,xFC), OpCode.FD => HashCode.Combine(x00,xFD), OpCode.FE => HashCode.Combine(x00,xFE), + OpCode.FF => HashCode.Combine(x00,xFF), _ => HashCode.Combine(x00) }; @@ -135,6 +148,7 @@ public int CompareTo(ByteCode other) case OpCode.FC: return xFC.CompareTo(other.xFC); case OpCode.FD: return xFD.CompareTo(other.xFD); case OpCode.FE: return xFE.CompareTo(other.xFE); + case OpCode.FF: return xFE.CompareTo(other.xFF); default: return 0; } } @@ -145,6 +159,7 @@ public int CompareTo(ByteCode other) OpCode.FC => $"(Ext){xFC}", OpCode.FD => $"(SIMD){xFD}", OpCode.FE => $"(Threads){xFE}", + OpCode.FF => $"(Wacs){xFF}", _ => $"{x00}" }; diff --git a/Wacs.Core/OpCodes/OpCode.cs b/Wacs.Core/OpCodes/OpCode.cs index f4140eb..c480b29 100644 --- a/Wacs.Core/OpCodes/OpCode.cs +++ b/Wacs.Core/OpCodes/OpCode.cs @@ -310,11 +310,11 @@ public enum OpCode : byte // Prefix Threads FE = 0xFE, + // Admin + FF = 0xFF, + //Custom [OpCode("func")] Func = 0xF0, [OpCode("expr")] Expr = 0xF1, - - [OpCode("aggregate")] Aggr = 0xF2, - [OpCode("stackval")] StackVal = 0xF3, } } \ No newline at end of file diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index 599205b..fd12099 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -394,6 +394,10 @@ public void ResetStats() { Stats[(ushort)(ByteCode)opcode] = new ExecStat(); } + foreach (WacsCode opcode in Enum.GetValues(typeof(WacsCode))) + { + Stats[(ushort)(ByteCode)opcode] = new ExecStat(); + } } public OpCode GetEndFor() From 46ddc89aff888f9f570880933709ed5fd8210c9b Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 10:51:11 -0800 Subject: [PATCH 10/19] Optimize locals data --- Wacs.Core/Instructions/LocalVariable.cs | 3 +-- Wacs.Core/Types/IndexSpace.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Wacs.Core/Instructions/LocalVariable.cs b/Wacs.Core/Instructions/LocalVariable.cs index adfeb73..6633e08 100644 --- a/Wacs.Core/Instructions/LocalVariable.cs +++ b/Wacs.Core/Instructions/LocalVariable.cs @@ -73,8 +73,7 @@ public override void Validate(IWasmValidationContext context) public override void Execute(ExecContext context) { - var value = FetchFromLocals(context); - context.OpStack.PushValue(value); + context.OpStack.PushValue(FetchFromLocals(context)); } // @Spec 4.4.5.1. local.get diff --git a/Wacs.Core/Types/IndexSpace.cs b/Wacs.Core/Types/IndexSpace.cs index ffc7198..e499393 100644 --- a/Wacs.Core/Types/IndexSpace.cs +++ b/Wacs.Core/Types/IndexSpace.cs @@ -250,7 +250,7 @@ public override bool Contains(GlobalIdx idx) => public struct LocalsSpace { - public Value[]? Data; + public Value[] Data; public int Capacity { get; } From cad0ba9d9b5211a49964420aa7806cc7ee26f274 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 10:51:31 -0800 Subject: [PATCH 11/19] Adding transpiler fusedadd --- Wacs.Core/Instructions/Numeric/Const.cs | 2 +- .../Instructions/Transpiler/InstFusedAdd.cs | 36 +++++++++++++++++++ .../Runtime/Transpiler/FunctionTranspiler.cs | 22 ++++++------ 3 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs diff --git a/Wacs.Core/Instructions/Numeric/Const.cs b/Wacs.Core/Instructions/Numeric/Const.cs index deaa02f..f925b70 100644 --- a/Wacs.Core/Instructions/Numeric/Const.cs +++ b/Wacs.Core/Instructions/Numeric/Const.cs @@ -28,7 +28,7 @@ namespace Wacs.Core.Instructions.Numeric //0x41 public sealed class InstI32Const : InstructionBase, IConstInstruction, ITypedValueProducer { - private int Value; + public int Value; public override ByteCode Op => OpCode.I32Const; public Func GetFunc => FetchImmediate; public int CalculateSize() => 1; diff --git a/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs b/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs new file mode 100644 index 0000000..671df2a --- /dev/null +++ b/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs @@ -0,0 +1,36 @@ +using System; +using Wacs.Core.Instructions.Transpiler; +using Wacs.Core.OpCodes; +using Wacs.Core.Runtime; +using Wacs.Core.Types; +using Wacs.Core.Validation; + +namespace Wacs.Core.Instructions.Numeric +{ + public class InstFusedI32AddS : InstructionBase, ITypedValueProducer + { + private readonly Func _previous; + private int _constant; + private int Size; + public InstFusedI32AddS(ITypedValueProducer prev, int constant) + { + _previous = prev.GetFunc; + _constant = constant; + Size = prev.CalculateSize() + 1; + } + + static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32); + public override ByteCode Op => WacsCode.I32FusedAdd; + public override void Validate(IWasmValidationContext context) => _validate(context); + + public override void Execute(ExecContext context) + { + context.OpStack.PushI32(FusedAdd(context)); + } + + public int FusedAdd(ExecContext context) => _previous(context) + _constant; + + public Func GetFunc => FusedAdd; + public int CalculateSize() => Size; + } +} \ No newline at end of file diff --git a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs index cb36923..440851c 100644 --- a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs +++ b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs @@ -20,7 +20,9 @@ using System.IO; using System.Linq; using Wacs.Core.Instructions; +using Wacs.Core.Instructions.Numeric; using Wacs.Core.Instructions.Transpiler; +using Wacs.Core.OpCodes; using Wacs.Core.Runtime.Types; using Wacs.Core.Types; @@ -116,17 +118,12 @@ private static InstructionSequence OptimizeSequence(InstructionSequence seq) //Split into local.set/local.get var newSet = new InstLocalSet().Immediate(instTee.GetIndex()); var newGet = new InstLocalGet().Immediate(instTee.GetIndex()); - if (newSet != null) - { - var setInst = OptimizeInstruction(newSet, stack); - stack.Push(setInst); - stack.Push(newGet); - break; - } - stack.Push(inst); + var setInst = OptimizeInstruction(newSet, stack); + stack.Push(setInst); + stack.Push(newGet); break; - case IOptimizationTarget target: - var newInst = OptimizeInstruction(target as InstructionBase, stack); + case IOptimizationTarget: + var newInst = OptimizeInstruction(inst, stack); stack.Push(newInst); break; default: @@ -276,6 +273,11 @@ private static InstructionBase BindI32I32(InstructionBase inst, Stack null }; + if (i1Producer is not null && i2 is InstI32Const c && inst.Op == OpCode.I32Add) + { + return new InstFusedI32AddS(i1Producer, c.Value); + } + if (i1Producer != null && i2Producer != null) { switch (intConsumer) { From 848c70939d260bb080aebc0bb3cc07f743db9612 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 10:55:49 -0800 Subject: [PATCH 12/19] Fixing WacsCode GetMnemonic --- Wacs.Core/OpCodes/OpCodeExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Wacs.Core/OpCodes/OpCodeExtensions.cs b/Wacs.Core/OpCodes/OpCodeExtensions.cs index 2b07bbe..63e997d 100644 --- a/Wacs.Core/OpCodes/OpCodeExtensions.cs +++ b/Wacs.Core/OpCodes/OpCodeExtensions.cs @@ -28,6 +28,7 @@ public static class OpCodeExtensions private static readonly ConcurrentDictionary MnemonicCacheFC = new(); private static readonly ConcurrentDictionary MnemonicCacheFD = new(); private static readonly ConcurrentDictionary MnemonicCacheFE = new(); + private static readonly ConcurrentDictionary MnemonicCacheFF = new(); /// /// Retrieves the WAT mnemonic associated with the given opcode. @@ -40,6 +41,7 @@ public static string GetMnemonic(this ByteCode opcode) => OpCode.FC => opcode.xFC.GetMnemonic(), OpCode.FD => opcode.xFD.GetMnemonic(), OpCode.FE => opcode.xFE.GetMnemonic(), + OpCode.FF => opcode.xFF.GetMnemonic(), _ => opcode.x00.GetMnemonic() }; @@ -75,5 +77,6 @@ private static string GetOpCode(T opcode, ConcurrentDictionary cac public static string GetMnemonic(this ExtCode opcode) => GetOpCode(opcode, MnemonicCacheFC); public static string GetMnemonic(this SimdCode opcode) => GetOpCode(opcode, MnemonicCacheFD); public static string GetMnemonic(this AtomCode opcode) => GetOpCode(opcode, MnemonicCacheFE); + public static string GetMnemonic(this WacsCode opcode) => GetOpCode(opcode, MnemonicCacheFF); } } \ No newline at end of file From 8b0bd47c287e91d9cb81e5b2560a749e0df1a793 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Mon, 2 Dec 2024 13:47:55 -0800 Subject: [PATCH 13/19] Fused instructions --- Wacs.Core/Instructions/LocalVariable.cs | 6 +- Wacs.Core/Instructions/Numeric/Const.cs | 8 +- .../Instructions/Transpiler/InstFusedAdd.cs | 36 ------- .../Transpiler/InstFusedLocalSet.cs | 70 ++++++++++++++ .../Instructions/Transpiler/InstI32Fused.cs | 96 +++++++++++++++++++ .../Instructions/Transpiler/InstI64Fused.cs | 96 +++++++++++++++++++ Wacs.Core/OpCodes/Admin.cs | 13 +++ .../Runtime/Transpiler/FunctionTranspiler.cs | 66 ++++++++++++- 8 files changed, 348 insertions(+), 43 deletions(-) delete mode 100644 Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs create mode 100644 Wacs.Core/Instructions/Transpiler/InstFusedLocalSet.cs create mode 100644 Wacs.Core/Instructions/Transpiler/InstI32Fused.cs create mode 100644 Wacs.Core/Instructions/Transpiler/InstI64Fused.cs diff --git a/Wacs.Core/Instructions/LocalVariable.cs b/Wacs.Core/Instructions/LocalVariable.cs index 6633e08..2d28371 100644 --- a/Wacs.Core/Instructions/LocalVariable.cs +++ b/Wacs.Core/Instructions/LocalVariable.cs @@ -31,6 +31,8 @@ public class InstLocalGet : InstructionBase, IVarInstruction, ITypedValueProduce private LocalIdx Index; public override ByteCode Op => OpCode.LocalGet; + public LocalIdx GetIndex() => Index; + public Func GetFunc => FetchFromLocals; public int CalculateSize() => 1; @@ -91,6 +93,8 @@ public Value FetchFromLocals(ExecContext context) public class InstLocalSet : InstructionBase, IVarInstruction, INodeConsumer { private LocalIdx Index; + + public LocalIdx GetIndex() => Index; public override ByteCode Op => OpCode.LocalSet; public override InstructionBase Parse(BinaryReader reader) @@ -127,7 +131,7 @@ public override void Execute(ExecContext context) { //2. context.Assert( context.Frame.Locals.Contains(Index), - $"Instruction local.get could not get Local {Index}"); + $"Instruction local.set could not set Local {Index}"); //3. context.Assert( context.OpStack.HasValue, $"Operand Stack underflow in instruction local.set"); diff --git a/Wacs.Core/Instructions/Numeric/Const.cs b/Wacs.Core/Instructions/Numeric/Const.cs index f925b70..3034800 100644 --- a/Wacs.Core/Instructions/Numeric/Const.cs +++ b/Wacs.Core/Instructions/Numeric/Const.cs @@ -59,7 +59,7 @@ public InstructionBase Immediate(int value) return this; } - public int FetchImmediate(ExecContext context) => Value; + public int FetchImmediate(ExecContext _) => Value; public override string RenderText(ExecContext? context) => $"{base.RenderText(context)} {Value}"; } @@ -91,7 +91,7 @@ public override InstructionBase Parse(BinaryReader reader) { return this; } - public long FetchImmediate(ExecContext context) => Value; + public long FetchImmediate(ExecContext _) => Value; public override string RenderText(ExecContext? context) => $"{base.RenderText(context)} {Value}"; } @@ -124,7 +124,7 @@ public override InstructionBase Parse(BinaryReader reader) { return this; } - public float FetchImmediate(ExecContext context) => Value; + public float FetchImmediate(ExecContext _) => Value; public override string RenderText(ExecContext? context) { @@ -165,7 +165,7 @@ public override InstructionBase Parse(BinaryReader reader) { return this; } - public double FetchImmediate(ExecContext context) => Value; + public double FetchImmediate(ExecContext _) => Value; public override string RenderText(ExecContext? context) { diff --git a/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs b/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs deleted file mode 100644 index 671df2a..0000000 --- a/Wacs.Core/Instructions/Transpiler/InstFusedAdd.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Wacs.Core.Instructions.Transpiler; -using Wacs.Core.OpCodes; -using Wacs.Core.Runtime; -using Wacs.Core.Types; -using Wacs.Core.Validation; - -namespace Wacs.Core.Instructions.Numeric -{ - public class InstFusedI32AddS : InstructionBase, ITypedValueProducer - { - private readonly Func _previous; - private int _constant; - private int Size; - public InstFusedI32AddS(ITypedValueProducer prev, int constant) - { - _previous = prev.GetFunc; - _constant = constant; - Size = prev.CalculateSize() + 1; - } - - static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32); - public override ByteCode Op => WacsCode.I32FusedAdd; - public override void Validate(IWasmValidationContext context) => _validate(context); - - public override void Execute(ExecContext context) - { - context.OpStack.PushI32(FusedAdd(context)); - } - - public int FusedAdd(ExecContext context) => _previous(context) + _constant; - - public Func GetFunc => FusedAdd; - public int CalculateSize() => Size; - } -} \ No newline at end of file diff --git a/Wacs.Core/Instructions/Transpiler/InstFusedLocalSet.cs b/Wacs.Core/Instructions/Transpiler/InstFusedLocalSet.cs new file mode 100644 index 0000000..eaadc51 --- /dev/null +++ b/Wacs.Core/Instructions/Transpiler/InstFusedLocalSet.cs @@ -0,0 +1,70 @@ +// /* +// * Copyright 2024 Kelvin Nishikawa +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ + +using Wacs.Core.OpCodes; +using Wacs.Core.Runtime; +using Wacs.Core.Types; +using Wacs.Core.Validation; + +namespace Wacs.Core.Instructions.Transpiler +{ + public class InstLocalGetSet : InstructionBase + { + private int _from; + private int _to; + public InstLocalGetSet(InstLocalGet from, InstLocalSet to) + { + _from = from.GetIndex().Value; + _to = to.GetIndex().Value; + Size = 2; + } + public override ByteCode Op => WacsCode.LocalGetSet; + public override void Validate(IWasmValidationContext context) {} + public override void Execute(ExecContext context) + { + context.Assert( context.Frame.Locals.Contains((LocalIdx)_from), + $"Instruction local.getset could not get Local {_from}"); + context.Assert( context.Frame.Locals.Contains((LocalIdx)_to), + $"Instruction local.getset could not set Local {_to}"); + + context.Frame.Locals.Data[_to] = context.Frame.Locals.Data[_from]; + } + } + + public class InstLocalConstSet : InstructionBase + { + private Value _constantValue; + private ValType type; + private int _to; + + public InstLocalConstSet(T c, InstLocalSet to) + { + _to = to.GetIndex().Value; + _constantValue = new Value(typeof(T).ToValType(), c!); + Size = 2; + } + public override ByteCode Op => WacsCode.LocalConstSet; + public override void Validate(IWasmValidationContext context) { } + + public override void Execute(ExecContext context) + { + context.Assert( context.Frame.Locals.Contains((LocalIdx)_to), + $"Instruction local.getset could not set Local {_to}"); + + context.Frame.Locals.Data[_to] = _constantValue; + } + } +} \ No newline at end of file diff --git a/Wacs.Core/Instructions/Transpiler/InstI32Fused.cs b/Wacs.Core/Instructions/Transpiler/InstI32Fused.cs new file mode 100644 index 0000000..a19b886 --- /dev/null +++ b/Wacs.Core/Instructions/Transpiler/InstI32Fused.cs @@ -0,0 +1,96 @@ +using System; +using Wacs.Core.Instructions.Transpiler; +using Wacs.Core.OpCodes; +using Wacs.Core.Runtime; +using Wacs.Core.Types; +using Wacs.Core.Validation; + +namespace Wacs.Core.Instructions.Numeric +{ + public abstract class InstFusedI32Const : InstructionBase, ITypedValueProducer + { + protected readonly Func _previous; + protected int _constant; + protected Func _execute = null!; + + protected InstFusedI32Const(ITypedValueProducer prev, int constant) + { + _previous = prev.GetFunc; + _constant = constant; + Size = prev.CalculateSize() + 1; + } + + static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32); + + public override void Validate(IWasmValidationContext context) => _validate(context); + + public override void Execute(ExecContext context) + { + context.OpStack.PushI32(_execute(context)); + } + + public int CalculateSize() => Size; + public Func GetFunc => _execute; + } + + public class InstFusedI32Add : InstFusedI32Const + { + public override ByteCode Op => WacsCode.I32FusedAdd; + public InstFusedI32Add(ITypedValueProducer prev, int constant) : base(prev, constant) => + _execute = context => _previous(context) + _constant; + } + + public class InstFusedI32Sub : InstFusedI32Const + { + public override ByteCode Op => WacsCode.I32FusedSub; + public InstFusedI32Sub(ITypedValueProducer prev, int constant) : base(prev, constant) => + _execute = context => _previous(context) - _constant; + } + + public class InstFusedI32Mul : InstFusedI32Const + { + public override ByteCode Op => WacsCode.I32FusedMul; + public InstFusedI32Mul(ITypedValueProducer prev, int constant) : base(prev, constant) => + _execute = context => _previous(context) * _constant; + } + + public abstract class InstFusedU32Const : InstructionBase, ITypedValueProducer + { + protected readonly Func _previous; + protected uint _constant; + protected Func _execute = null!; + + protected InstFusedU32Const(ITypedValueProducer prev, uint constant) + { + _previous = prev.GetFunc; + _constant = constant; + Size = prev.CalculateSize() + 1; + } + + static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I32, push: ValType.I32); + + public override void Validate(IWasmValidationContext context) => _validate(context); + + public override void Execute(ExecContext context) + { + context.OpStack.PushU32(_execute(context)); + } + + public int CalculateSize() => Size; + public Func GetFunc => _execute; + } + + public class InstFusedU32And : InstFusedU32Const + { + public override ByteCode Op => WacsCode.I32FusedAnd; + public InstFusedU32And(ITypedValueProducer prev, uint constant) : base(prev, constant) => + _execute = context => _previous(context) & _constant; + } + + public class InstFusedU32Or : InstFusedU32Const + { + public override ByteCode Op => WacsCode.I32FusedOr; + public InstFusedU32Or(ITypedValueProducer prev, uint constant) : base(prev, constant) => + _execute = context => _previous(context) | _constant; + } +} \ No newline at end of file diff --git a/Wacs.Core/Instructions/Transpiler/InstI64Fused.cs b/Wacs.Core/Instructions/Transpiler/InstI64Fused.cs new file mode 100644 index 0000000..0707a71 --- /dev/null +++ b/Wacs.Core/Instructions/Transpiler/InstI64Fused.cs @@ -0,0 +1,96 @@ +using System; +using Wacs.Core.Instructions.Transpiler; +using Wacs.Core.OpCodes; +using Wacs.Core.Runtime; +using Wacs.Core.Types; +using Wacs.Core.Validation; + +namespace Wacs.Core.Instructions.Numeric +{ + public abstract class InstFusedI64Const : InstructionBase, ITypedValueProducer + { + protected readonly Func _previous; + protected long _constant; + protected Func _execute = null!; + + protected InstFusedI64Const(ITypedValueProducer prev, long constant) + { + _previous = prev.GetFunc; + _constant = constant; + Size = prev.CalculateSize() + 1; + } + + static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I64, push: ValType.I64); + + public override void Validate(IWasmValidationContext context) => _validate(context); + + public override void Execute(ExecContext context) + { + context.OpStack.PushI64(_execute(context)); + } + + public int CalculateSize() => Size; + public Func GetFunc => _execute; + } + + public class InstFusedI64Add : InstFusedI64Const + { + public override ByteCode Op => WacsCode.I64FusedAdd; + public InstFusedI64Add(ITypedValueProducer prev, long constant) : base(prev, constant) => + _execute = context => _previous(context) + _constant; + } + + public class InstFusedI64Sub : InstFusedI64Const + { + public override ByteCode Op => WacsCode.I64FusedSub; + public InstFusedI64Sub(ITypedValueProducer prev, long constant) : base(prev, constant) => + _execute = context => _previous(context) - _constant; + } + + public class InstFusedI64Mul : InstFusedI64Const + { + public override ByteCode Op => WacsCode.I64FusedMul; + public InstFusedI64Mul(ITypedValueProducer prev, long constant) : base(prev, constant) => + _execute = context => _previous(context) * _constant; + } + + public abstract class InstFusedU64Const : InstructionBase, ITypedValueProducer + { + protected readonly Func _previous; + protected ulong _constant; + protected Func _execute = null!; + + protected InstFusedU64Const(ITypedValueProducer prev, ulong constant) + { + _previous = prev.GetFunc; + _constant = constant; + Size = prev.CalculateSize() + 1; + } + + static NumericInst.ValidationDelegate _validate = NumericInst.ValidateOperands(pop: ValType.I64, push: ValType.I64); + + public override void Validate(IWasmValidationContext context) => _validate(context); + + public override void Execute(ExecContext context) + { + context.OpStack.PushU64(_execute(context)); + } + + public int CalculateSize() => Size; + public Func GetFunc => _execute; + } + + public class InstFusedU64And : InstFusedU64Const + { + public override ByteCode Op => WacsCode.I64FusedAnd; + public InstFusedU64And(ITypedValueProducer prev, ulong constant) : base(prev, constant) => + _execute = context => _previous(context) & _constant; + } + + public class InstFusedU64Or : InstFusedU64Const + { + public override ByteCode Op => WacsCode.I64FusedOr; + public InstFusedU64Or(ITypedValueProducer prev, ulong constant) : base(prev, constant) => + _execute = context => _previous(context) | _constant; + } +} \ No newline at end of file diff --git a/Wacs.Core/OpCodes/Admin.cs b/Wacs.Core/OpCodes/Admin.cs index a1a71e8..0052b6a 100644 --- a/Wacs.Core/OpCodes/Admin.cs +++ b/Wacs.Core/OpCodes/Admin.cs @@ -34,5 +34,18 @@ public enum WacsCode : byte [OpCode("aggr.3x1")] Aggr3_1 = 0x24, [OpCode("i32.fused.add")] I32FusedAdd = 0x30, + [OpCode("i32.fused.sub")] I32FusedSub = 0x31, + [OpCode("i32.fused.mul")] I32FusedMul = 0x32, + [OpCode("i32.fused.and")] I32FusedAnd = 0x33, + [OpCode("i32.fused.or")] I32FusedOr = 0x34, + + [OpCode("i64.fused.add")] I64FusedAdd = 0x38, + [OpCode("i64.fused.sub")] I64FusedSub = 0x39, + [OpCode("i64.fused.mul")] I64FusedMul = 0x3A, + [OpCode("i64.fused.and")] I64FusedAnd = 0x3B, + [OpCode("i64.fused.or")] I64FusedOr = 0x3C, + + [OpCode("local.getset")] LocalGetSet = 0x40, + [OpCode("local.constset")] LocalConstSet = 0x41, } } \ No newline at end of file diff --git a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs index 440851c..fc350f5 100644 --- a/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs +++ b/Wacs.Core/Runtime/Transpiler/FunctionTranspiler.cs @@ -180,6 +180,35 @@ private static InstructionBase BindAnyValue(InstructionBase inst, Stack p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + case ITypedValueProducer p: + return new InstLocalConstSet(p.GetFunc(null!), set); + } + break; + } + } + if (valueConsumer is INodeConsumer nodeConsumer) { switch (top) { @@ -273,9 +302,14 @@ private static InstructionBase BindI32I32(InstructionBase inst, Stack null }; - if (i1Producer is not null && i2 is InstI32Const c && inst.Op == OpCode.I32Add) + if (i1Producer is not null && i2 is InstI32Const c) { - return new InstFusedI32AddS(i1Producer, c.Value); + switch (inst.Op.x00) + { + case OpCode.I32Add: return new InstFusedI32Add(i1Producer, c.Value); + case OpCode.I32Sub: return new InstFusedI32Sub(i1Producer, c.Value); + case OpCode.I32Mul: return new InstFusedI32Mul(i1Producer, c.Value); + } } if (i1Producer != null && i2Producer != null) @@ -311,6 +345,15 @@ private static InstructionBase BindU32U32(InstructionBase inst, Stack null }; + if (i1Producer is not null && i2 is InstI32Const c) + { + switch (inst.Op.x00) + { + case OpCode.I32And: return new InstFusedU32And(i1Producer, (uint)c.Value); + case OpCode.I32Or: return new InstFusedU32Or(i1Producer, (uint)c.Value); + } + } + if (i1Producer != null && i2Producer != null) { switch (uintConsumer) { @@ -553,6 +596,16 @@ private static InstructionBase BindI64I64(InstructionBase inst, Stack p => p, _ => null }; + + if (i1Producer is not null && i2 is InstI32Const c) + { + switch (inst.Op.x00) + { + case OpCode.I64Add: return new InstFusedI64Add(i1Producer, c.Value); + case OpCode.I64Sub: return new InstFusedI64Sub(i1Producer, c.Value); + case OpCode.I64Mul: return new InstFusedI64Mul(i1Producer, c.Value); + } + } if (i1Producer != null && i2Producer != null) { @@ -589,6 +642,15 @@ private static InstructionBase BindU64U64(InstructionBase inst, Stack null }; + if (i1Producer is not null && i2 is InstI32Const c) + { + switch (inst.Op.x00) + { + case OpCode.I64And: return new InstFusedU64And(i1Producer, (ulong)c.Value); + case OpCode.I64Or: return new InstFusedU64Or(i1Producer, (ulong)c.Value); + } + } + if (i1Producer != null && i2Producer != null) { switch (longConsumer) { From 2c7aecd113788ca6bc8d64b08a18d843fd1ee092 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 12:56:09 -0800 Subject: [PATCH 14/19] Fixing Wacs.Console in strict AOT mode --- Wacs.Console/CommandLineOptions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Wacs.Console/CommandLineOptions.cs b/Wacs.Console/CommandLineOptions.cs index 08defa5..e51d2fb 100644 --- a/Wacs.Console/CommandLineOptions.cs +++ b/Wacs.Console/CommandLineOptions.cs @@ -14,6 +14,8 @@ // * limitations under the License. // */ +using System; +using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using CommandLine; using Wacs.Core.Runtime; @@ -21,8 +23,14 @@ namespace Wacs.Console { // ReSharper disable once ClassNeverInstantiated.Global + + public class CommandLineOptions { + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties, + typeof(CommandLineOptions))] + public CommandLineOptions() {} + [Option('e',"env", Separator = ',', HelpText = "Comma-separated list of environment variables (format: KEY=VALUE)")] public IEnumerable EnvironmentVars { get; set; } = new List(); @@ -70,6 +78,7 @@ public class CommandLineOptions public string WasmModule { get; set; } = ""; public IEnumerable ExecutableArgs { get; set; } = new List(); + } } \ No newline at end of file From f211766cd7d1a555dc54c7d871384f6eb8f60197 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 14:18:38 -0800 Subject: [PATCH 15/19] Optimizing Block EnterSequence --- Wacs.Core/Instructions/Control.cs | 3 +-- Wacs.Core/Runtime/ExecContext.cs | 18 ++++++++++------ Wacs.Core/Runtime/Frame.cs | 24 +++++++++++++-------- Wacs.Core/Runtime/Types/FunctionInstance.cs | 5 ++--- Wacs.Core/Types/Expression.cs | 2 +- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index 4b87fea..6a3a562 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -110,8 +110,7 @@ public override void Validate(IWasmValidationContext context) // @Spec 4.4.8.3. block public override void Execute(ExecContext context) { - if (Block.Instructions.Count != 0) - context.EnterBlock(this, Block); + context.EnterBlock(this, Block); } /// diff --git a/Wacs.Core/Runtime/ExecContext.cs b/Wacs.Core/Runtime/ExecContext.cs index fd12099..48f5051 100644 --- a/Wacs.Core/Runtime/ExecContext.cs +++ b/Wacs.Core/Runtime/ExecContext.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.Extensions.ObjectPool; using Wacs.Core.Instructions; @@ -176,6 +177,7 @@ public void FlushCallStack() _sequenceIndex = -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnterSequence(InstructionSequence seq) { _currentSequence = seq; @@ -205,19 +207,23 @@ public void RewindSequence() } // @Spec 4.4.9.1. Enter Block + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnterBlock(BlockTarget target, Block block) { //HACK: Labels are a linked list with each node residing on its respective block instruction. Frame.PushLabel(target); + //Manually inline PushLabel + // Frame.TopLabel = target; + // Frame.LabelCount++; + // Frame.Label = target.Label; //Sets the Pointer to the start of the block sequence - // EnterSequence(block.Instructions); - + EnterSequence(block.Instructions); //Manually inline EnterSequence - _currentSequence = block.Instructions; - _sequenceCount = _currentSequence.Count; - _sequenceInstructions = _currentSequence._instructions; - _sequenceIndex = -1; + // _currentSequence = block.Instructions; + // _sequenceCount = _currentSequence.Count; + // _sequenceInstructions = _currentSequence._instructions; + // _sequenceIndex = -1; } // @Spec 4.4.9.2. Exit Block diff --git a/Wacs.Core/Runtime/Frame.cs b/Wacs.Core/Runtime/Frame.cs index b336ec8..570db81 100644 --- a/Wacs.Core/Runtime/Frame.cs +++ b/Wacs.Core/Runtime/Frame.cs @@ -17,6 +17,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using Wacs.Core.Instructions; using Wacs.Core.OpCodes; using Wacs.Core.Runtime.Types; @@ -96,29 +97,34 @@ public void ClearLabels() LabelCount = 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetLabel(BlockTarget baselabel) + { + TopLabel = baselabel; + LabelCount = 1; + Label = ReturnLabel; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PushLabel(BlockTarget target) { TopLabel = target; - LabelCount += 1; - - Label = LabelCount > 1 - ? TopLabel.Label - : ReturnLabel; + LabelCount++; + Label = TopLabel.Label; } public InstructionPointer PopLabels(int idx) { if (LabelCount <= idx + 1) throw new InvalidDataException("Label Stack underflow"); - + + idx = LabelCount - (idx + 1); BlockTarget oldLabel; do { oldLabel = TopLabel; TopLabel = TopLabel.EnclosingBlock; - idx -= 1; - LabelCount -= 1; - } while (idx >= 0); + } while (--LabelCount > idx); Label = LabelCount > 1 ? TopLabel.Label diff --git a/Wacs.Core/Runtime/Types/FunctionInstance.cs b/Wacs.Core/Runtime/Types/FunctionInstance.cs index ea1af80..6d0c746 100644 --- a/Wacs.Core/Runtime/Types/FunctionInstance.cs +++ b/Wacs.Core/Runtime/Types/FunctionInstance.cs @@ -122,12 +122,11 @@ public void Invoke(ExecContext context) context.PushFrame(frame); //10. - frame.PushLabel(Body.LabelTarget); - frame.ReturnLabel.Arity = funcType.ResultType.Arity; frame.ReturnLabel.Instruction = LabelInst; frame.ReturnLabel.ContinuationAddress = context.GetPointer(); - // frame.ReturnLabel.StackHeight = 0; + + frame.SetLabel(Body.LabelTarget); context.EnterSequence(Body.Instructions); } diff --git a/Wacs.Core/Types/Expression.cs b/Wacs.Core/Types/Expression.cs index 44650f7..ab468c9 100644 --- a/Wacs.Core/Types/Expression.cs +++ b/Wacs.Core/Types/Expression.cs @@ -151,7 +151,7 @@ public void ExecuteInitializer(ExecContext context) if (context.OpStack.Count != 0) throw new InvalidDataException("OpStack should be empty"); frame.ReturnLabel = LabelTarget.Label; - frame.PushLabel(LabelTarget); + frame.SetLabel(LabelTarget); context.PushFrame(frame); foreach (var inst in Instructions) { From 58c2eaab2c8543b78bcc7d319189377453a58aa0 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 14:40:46 -0800 Subject: [PATCH 16/19] Removing gas calculation from the fastest path --- Wacs.Core/Runtime/WasmRuntimeExecution.cs | 44 ++++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/Wacs.Core/Runtime/WasmRuntimeExecution.cs b/Wacs.Core/Runtime/WasmRuntimeExecution.cs index e6d2d49..6f880b2 100644 --- a/Wacs.Core/Runtime/WasmRuntimeExecution.cs +++ b/Wacs.Core/Runtime/WasmRuntimeExecution.cs @@ -358,27 +358,45 @@ Value[] GenericDelegate(params object[] args) public async Task ProcessThreadAsync(long gasLimit) { - if (gasLimit <= 0) gasLimit = long.MaxValue; - InstructionBase inst; - //Manually inline Next() - // while (Context.Next() is { } inst) - while (++Context._sequenceIndex < Context._sequenceCount) + if (gasLimit <= 0) { - inst = Context._sequenceInstructions[Context._sequenceIndex]; - if (inst.IsAsync) + //Manually inline Next() + // while (Context.Next() is { } inst) + while (++Context._sequenceIndex < Context._sequenceCount) { - await inst.ExecuteAsync(Context); - Context.steps += inst.Size; - if (Context.steps >= gasLimit) - throw new InsufficientGasException($"Invocation ran out of gas (limit:{gasLimit})."); + inst = Context._sequenceInstructions[Context._sequenceIndex]; + if (inst.IsAsync) + { + await inst.ExecuteAsync(Context); + } + else + { + inst.Execute(Context); + } } - else + } + else + { + //Manually inline Next() + // while (Context.Next() is { } inst) + while (++Context._sequenceIndex < Context._sequenceCount) { - inst.Execute(Context); + inst = Context._sequenceInstructions[Context._sequenceIndex]; + if (inst.IsAsync) + { + await inst.ExecuteAsync(Context); + } + else + { + inst.Execute(Context); + } + //Counting gas costs about 18% throughput! Context.steps += inst.Size; if (Context.steps >= gasLimit) + { throw new InsufficientGasException($"Invocation ran out of gas (limit:{gasLimit})."); + } } } } From fce7cc473d0e689e3bf043c3d55fc6a39367e684 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 15:32:55 -0800 Subject: [PATCH 17/19] Relaxing Next() for optional execution --- Wacs.Core/Runtime/WasmRuntimeExecution.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Wacs.Core/Runtime/WasmRuntimeExecution.cs b/Wacs.Core/Runtime/WasmRuntimeExecution.cs index 6f880b2..bf6ad23 100644 --- a/Wacs.Core/Runtime/WasmRuntimeExecution.cs +++ b/Wacs.Core/Runtime/WasmRuntimeExecution.cs @@ -406,12 +406,8 @@ public async Task ProcessThreadWithOptions(InvokerOptions options) long highwatermark = 0; long gasLimit = options.GasLimit > 0 ? options.GasLimit : long.MaxValue; - InstructionBase inst; - //Manually inline Next() - // while (Context.Next() is { } inst) - while (++Context._sequenceIndex < Context._sequenceCount) + while (Context.Next() is { } inst) { - inst = Context._sequenceInstructions[Context._sequenceIndex]; //Trace execution if (options.LogInstructionExecution != InstructionLogging.None) { From fb4f9cfcf82358f590990d227d6e0b7792fcdfd9 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 20:16:30 -0800 Subject: [PATCH 18/19] Fixing broken tests shared stack concurrency --- Wacs.Core/Instructions/Control.cs | 38 ++++++++----------- Wacs.Core/Instructions/InstructionBase.cs | 2 - Wacs.Core/Instructions/TailCall.cs | 18 +++++---- .../Instructions/Transpiler/InstCompoundIf.cs | 3 +- Wacs.Core/Modules/Sections/StackCalculator.cs | 4 ++ Wacs.Core/Modules/Sections/StackRenderer.cs | 4 ++ Wacs.Core/Validation/ValidationOpStack.cs | 10 +++++ 7 files changed, 44 insertions(+), 35 deletions(-) diff --git a/Wacs.Core/Instructions/Control.cs b/Wacs.Core/Instructions/Control.cs index 6a3a562..c2038bb 100644 --- a/Wacs.Core/Instructions/Control.cs +++ b/Wacs.Core/Instructions/Control.cs @@ -89,8 +89,7 @@ public override void Validate(IWasmValidationContext context) context.Assert(funcType, "Invalid BlockType: {0}",Block.Type); //Check the parameters [t1*] and discard - context.OpStack.PopValues(funcType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(funcType.ParameterTypes); //ControlStack will push the values back on (Control Frame is our Label) context.PushControlFrame(BlockOp, funcType); @@ -164,8 +163,7 @@ public override void Validate(IWasmValidationContext context) context.Assert(funcType, "Invalid BlockType: {0}",Block.Type); //Check the parameters [t1*] and discard - context.OpStack.PopValues(funcType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(funcType.ParameterTypes); //ControlStack will push the values back on (Control Frame is our Label) context.PushControlFrame(LoopOp, funcType); @@ -247,8 +245,7 @@ public override void Validate(IWasmValidationContext context) context.OpStack.PopI32(); //Check the parameters [t1*] and discard - context.OpStack.PopValues(ifType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(ifType.ParameterTypes); //ControlStack will push the values back on (Control Frame is our Label) context.PushControlFrame(IfOp, ifType); @@ -444,8 +441,7 @@ public override void Validate(IWasmValidationContext context) var nthFrame = context.ControlStack.PeekAt((int)L.Value); //Validate results, but leave them on the stack - context.OpStack.PopValues(nthFrame.LabelTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(nthFrame.LabelTypes); // if (!context.Unreachable) // nthFrame.ConditionallyReachable = true; @@ -544,7 +540,7 @@ public override void Validate(IWasmValidationContext context) // nthFrame.ConditionallyReachable = true; //Pop values like we branch - context.OpStack.PopValues(nthFrame.LabelTypes, ref _aside); + context.OpStack.DiscardValues(nthFrame.LabelTypes); //But actually, we don't, so push them back on. context.OpStack.PushResult(nthFrame.LabelTypes); } @@ -610,7 +606,8 @@ public override void Validate(IWasmValidationContext context) // if (!context.Unreachable) // mthFrame.ConditionallyReachable = true; - + + Stack aside = new(); foreach (var lidx in Ls) { context.Assert(context.ContainsLabel(lidx.Value), @@ -619,16 +616,12 @@ public override void Validate(IWasmValidationContext context) var nthFrame = context.ControlStack.PeekAt((int)lidx.Value); context.Assert(nthFrame.LabelTypes.Arity == arity, "Instruction br_table invalid. Label {0} had different arity {1} =/= {2}", lidx, nthFrame.LabelTypes.Arity,arity); - - // if (!context.Unreachable) - // nthFrame.ConditionallyReachable = true; - context.OpStack.PopValues(nthFrame.LabelTypes, ref _aside); - context.OpStack.PushValues(_aside); + context.OpStack.PopValues(nthFrame.LabelTypes, ref aside); + context.OpStack.PushValues(aside); } - context.OpStack.PopValues(mthFrame.LabelTypes, ref _aside); - _aside.Clear(); + context.OpStack.PopValues(mthFrame.LabelTypes, ref aside); context.SetUnreachable(); } @@ -723,8 +716,9 @@ public sealed class InstReturn : InstructionBase public override void Validate(IWasmValidationContext context) { //keep the results for the block or function to validate - context.OpStack.PopValues(context.ReturnType, ref _aside); - context.OpStack.PushValues(_aside); + Stack aside = new(); + context.OpStack.PopValues(context.ReturnType, ref aside); + context.OpStack.PushValues(aside); context.SetUnreachable(); } @@ -770,8 +764,7 @@ public override void Validate(IWasmValidationContext context) "Instruction call was invalid. Function {0} was not in the Context.",X); var func = context.Funcs[X]; var type = context.Types[func.TypeIndex]; - context.OpStack.PopValues(type.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(type.ParameterTypes); context.OpStack.PushResult(type.ResultType); } @@ -891,8 +884,7 @@ public override void Validate(IWasmValidationContext context) var funcType = context.Types[Y]; context.OpStack.PopI32(); - context.OpStack.PopValues(funcType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(funcType.ParameterTypes); context.OpStack.PushResult(funcType.ResultType); } diff --git a/Wacs.Core/Instructions/InstructionBase.cs b/Wacs.Core/Instructions/InstructionBase.cs index 6790074..e76371e 100644 --- a/Wacs.Core/Instructions/InstructionBase.cs +++ b/Wacs.Core/Instructions/InstructionBase.cs @@ -31,8 +31,6 @@ namespace Wacs.Core.Instructions /// public abstract class InstructionBase { - protected static Stack _aside = new(); - public bool IsAsync = false; public int Size = 1; diff --git a/Wacs.Core/Instructions/TailCall.cs b/Wacs.Core/Instructions/TailCall.cs index e31f23b..ae43eb0 100644 --- a/Wacs.Core/Instructions/TailCall.cs +++ b/Wacs.Core/Instructions/TailCall.cs @@ -53,8 +53,9 @@ public bool IsBound(ExecContext context) public override void Validate(IWasmValidationContext context) { //Return - context.OpStack.PopValues(context.ReturnType, ref _aside); - context.OpStack.PushValues(_aside); + Stack aside = new(); + context.OpStack.PopValues(context.ReturnType, ref aside); + context.OpStack.PushValues(aside); context.SetUnreachable(); //Call @@ -62,8 +63,8 @@ public override void Validate(IWasmValidationContext context) "Instruction call was invalid. Function {0} was not in the Context.",X); var func = context.Funcs[X]; var type = context.Types[func.TypeIndex]; - context.OpStack.PopValues(type.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.PopValues(type.ParameterTypes, ref aside); + aside.Clear(); context.OpStack.PushResult(type.ResultType); } @@ -203,8 +204,9 @@ public bool IsBound(ExecContext context) public override void Validate(IWasmValidationContext context) { //Return - context.OpStack.PopValues(context.ReturnType, ref _aside); - context.OpStack.PushValues(_aside); + Stack aside = new(); + context.OpStack.PopValues(context.ReturnType, ref aside); + context.OpStack.PushValues(aside); context.SetUnreachable(); //Call Indirect @@ -218,8 +220,8 @@ public override void Validate(IWasmValidationContext context) var funcType = context.Types[Y]; context.OpStack.PopI32(); - context.OpStack.PopValues(funcType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.PopValues(funcType.ParameterTypes, ref aside); + aside.Clear(); context.OpStack.PushResult(funcType.ResultType); } diff --git a/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs b/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs index 9648998..45419fa 100644 --- a/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs +++ b/Wacs.Core/Instructions/Transpiler/InstCompoundIf.cs @@ -74,8 +74,7 @@ public override void Validate(IWasmValidationContext context) // context.OpStack.PopI32(); //Check the parameters [t1*] and discard - context.OpStack.PopValues(ifType.ParameterTypes, ref _aside); - _aside.Clear(); + context.OpStack.DiscardValues(ifType.ParameterTypes); //ControlStack will push the values back on (Control Frame is our Label) context.PushControlFrame(IfOp, ifType); diff --git a/Wacs.Core/Modules/Sections/StackCalculator.cs b/Wacs.Core/Modules/Sections/StackCalculator.cs index d42d927..3807f82 100644 --- a/Wacs.Core/Modules/Sections/StackCalculator.cs +++ b/Wacs.Core/Modules/Sections/StackCalculator.cs @@ -57,6 +57,10 @@ public void PushResult(ResultType types) public void PushValues(Stack vals) { while (vals.Count > 0) _context.Push(vals.Pop().Type); } + + public void DiscardValues(ResultType types) { + foreach (var type in types.Types.Reverse()) PopType(type); + } public Value PopI32() => _context.Pop(ValType.I32); public Value PopI64() => _context.Pop(ValType.I64); diff --git a/Wacs.Core/Modules/Sections/StackRenderer.cs b/Wacs.Core/Modules/Sections/StackRenderer.cs index 4e30e72..caf1444 100644 --- a/Wacs.Core/Modules/Sections/StackRenderer.cs +++ b/Wacs.Core/Modules/Sections/StackRenderer.cs @@ -60,6 +60,10 @@ public void PushResult(ResultType types) public void PushValues(Stack vals) { while (vals.Count > 0) _context.Push(vals.Pop().Type); } + + public void DiscardValues(ResultType types) { + foreach (var type in types.Types.Reverse()) PopType(type); + } public Value PopI32() => _context.Pop(ValType.I32); public Value PopI64() => _context.Pop(ValType.I64); diff --git a/Wacs.Core/Validation/ValidationOpStack.cs b/Wacs.Core/Validation/ValidationOpStack.cs index bed1870..2a4db83 100644 --- a/Wacs.Core/Validation/ValidationOpStack.cs +++ b/Wacs.Core/Validation/ValidationOpStack.cs @@ -46,6 +46,8 @@ public interface IValidationOpStack Value PopAny(); void PopValues(ResultType types, ref Stack aside); + void DiscardValues(ResultType types); + public void ReturnResults(ResultType type); } @@ -258,6 +260,14 @@ public void PopValues(ResultType types, ref Stack aside) aside.Push(PopType(type)); } } + + public void DiscardValues(ResultType types) + { + foreach (var type in types.Types.Reverse()) + { + PopType(type); + } + } public void ReturnResults(ResultType types) { From ab58880911ba4866db97ee88e47ff0d7450573b9 Mon Sep 17 00:00:00 2001 From: Kelvin Nishikawa Date: Tue, 3 Dec 2024 20:17:32 -0800 Subject: [PATCH 19/19] Adding Transpiled test for tailcall --- Spec.Test/ExtensionTests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Spec.Test/ExtensionTests.cs b/Spec.Test/ExtensionTests.cs index 1b85420..cfe6a3b 100644 --- a/Spec.Test/ExtensionTests.cs +++ b/Spec.Test/ExtensionTests.cs @@ -28,6 +28,25 @@ public class ExtensionTests public void TailCallFactorial() { var runtime = new WasmRuntime(); + runtime.TranspileModules = false; + + using var fileStream = new FileStream("../../../engine/tailcalls.wasm", FileMode.Open); + var module = BinaryModuleParser.ParseWasm(fileStream); + var moduleInst = runtime.InstantiateModule(module); + runtime.RegisterModule("tailcalls", moduleInst); + + var fa = runtime.GetExportedFunction(("tailcalls", "factorial")); + var invoker = runtime.CreateInvoker>(fa); + + Assert.Equal(479_001_600, invoker(12)); + Assert.Equal(2_432_902_008_176_640_000 , invoker(20)); + } + + [Fact] + public void TailCallFactorialTranspiled() + { + var runtime = new WasmRuntime(); + runtime.TranspileModules = true; using var fileStream = new FileStream("../../../engine/tailcalls.wasm", FileMode.Open); var module = BinaryModuleParser.ParseWasm(fileStream);