From 49215cd7dd963e4692cfd2a2e2083f813abe60f5 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 12 Nov 2023 23:40:04 +0100 Subject: [PATCH 001/146] branch preparations --- src/Nethermind/Directory.Packages.props | 3 +- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 29 +++++++++++++++---- .../Nethermind.Evm/CodeAnalysis/ILAnalizer.cs | 17 +++++++++++ .../Nethermind.Evm/CodeAnalysis/ILInfo.cs | 21 ++++++++++++++ .../Nethermind.Evm/Nethermind.Evm.csproj | 1 + .../Nethermind.Evm/VirtualMachine.cs | 2 ++ 6 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index 32559d8eb16..1e2aa381c1c 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -68,6 +68,7 @@ + @@ -83,4 +84,4 @@ - + \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index d8cec2c0adb..22cfaf21769 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -8,14 +8,32 @@ namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo { + private static int ILAnalyzerTreshold = 23; + + private const int SampledCodeLength = 10_001; private const int PercentageOfPush1 = 40; private const int NumberOfSamples = 100; private static Random _rand = new(); + private ILInfo? _iLInfo; public byte[] MachineCode { get; set; } public IPrecompile? Precompile { get; set; } - private ICodeInfoAnalyzer? _analyzer; + private ICodeInfoAnalyzer? _jumpAnalyzer; + private ILAnalizer? _ilAnalyzer; + + public int callCount = 0; + public int CallCount { + get => callCount; + set + { + callCount = value; + if(callCount > ILAnalyzerTreshold) + { + _iLInfo = _ilAnalyzer.Analyze(MachineCode); + } + } + } public CodeInfo(byte[] code) { @@ -23,6 +41,7 @@ public CodeInfo(byte[] code) } public bool IsPrecompile => Precompile is not null; + public bool IsILed => _iLInfo is not null; public CodeInfo(IPrecompile precompile) { @@ -32,12 +51,12 @@ public CodeInfo(IPrecompile precompile) public bool ValidateJump(int destination, bool isSubroutine) { - if (_analyzer is null) + if (_jumpAnalyzer is null) { CreateAnalyzer(); } - return _analyzer.ValidateJump(destination, isSubroutine); + return _jumpAnalyzer.ValidateJump(destination, isSubroutine); } /// @@ -65,11 +84,11 @@ private void CreateAnalyzer() // If there are many PUSH1 ops then use the JUMPDEST analyzer. // The JumpdestAnalyzer can perform up to 40% better than the default Code Data Analyzer // in a scenario when the code consists only of PUSH1 instructions. - _analyzer = push1Count > PercentageOfPush1 ? new JumpdestAnalyzer(MachineCode) : new CodeDataAnalyzer(MachineCode); + _jumpAnalyzer = push1Count > PercentageOfPush1 ? new JumpdestAnalyzer(MachineCode) : new CodeDataAnalyzer(MachineCode); } else { - _analyzer = new CodeDataAnalyzer(MachineCode); + _jumpAnalyzer = new CodeDataAnalyzer(MachineCode); } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs new file mode 100644 index 00000000000..649e1fd5105 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.CodeAnalysis; +internal class ILAnalizer +{ + internal ILInfo Analyze(byte[] machineCode) + { + throw new NotImplementedException(); + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs new file mode 100644 index 00000000000..452e4961d54 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Sigil.NonGeneric; + +namespace Nethermind.Evm.CodeAnalysis; +internal struct ILInfo +{ + public ushort[] Pcs; + public Emit[] IlMethod; + + Emit HasOverride(ushort pc) + { + return IlMethod[pc]; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj index bf4f06ca167..42454c3aa63 100644 --- a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj +++ b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj @@ -16,5 +16,6 @@ + diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index bc228390dc4..4c7f2c6b679 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -724,6 +724,8 @@ private CallResult ExecuteCall(EvmState vmState, byte[]? p goto Empty; } + vmState.Env.CodeInfo.CallCount++; + vmState.InitStacks(); EvmStack stack = new(vmState.DataStack.AsSpan(), vmState.DataStackHead, _txTracer); long gasAvailable = vmState.GasAvailable; From 1c8dfe73cd4fd06d4fd1751979b1c27e690b64c7 Mon Sep 17 00:00:00 2001 From: scooletz Date: Mon, 13 Nov 2023 14:09:23 +0100 Subject: [PATCH 002/146] minor amedments and moving pieces --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 42 +++++++++++-------- .../Nethermind.Evm/CodeAnalysis/ILAnalizer.cs | 17 -------- .../Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs | 36 ++++++++++++++++ .../Nethermind.Evm/CodeAnalysis/ILInfo.cs | 11 ++++- .../Nethermind.Evm/VirtualMachine.cs | 2 +- 5 files changed, 72 insertions(+), 36 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 22cfaf21769..03e00981832 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -2,38 +2,38 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Threading; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo { - private static int ILAnalyzerTreshold = 23; - - private const int SampledCodeLength = 10_001; private const int PercentageOfPush1 = 40; private const int NumberOfSamples = 100; private static Random _rand = new(); - private ILInfo? _iLInfo; public byte[] MachineCode { get; set; } public IPrecompile? Precompile { get; set; } private ICodeInfoAnalyzer? _jumpAnalyzer; - private ILAnalizer? _ilAnalyzer; - public int callCount = 0; - public int CallCount { - get => callCount; - set + // IL-EVM + private volatile ILInfo? _il; + private int _callCount; + + public void NoticeExecution() + { + // IL-EVM info already created + if (_il != null) + return; + + // use Interlocked just in case of concurrent execution to run it only once + if (Interlocked.Increment(ref _callCount) == IlAnalyzer.IlAnalyzerThreshold) { - callCount = value; - if(callCount > ILAnalyzerTreshold) - { - _iLInfo = _ilAnalyzer.Analyze(MachineCode); - } + IlAnalyzer.StartAnalysis(MachineCode, this); } - } + } public CodeInfo(byte[] code) { @@ -41,7 +41,13 @@ public CodeInfo(byte[] code) } public bool IsPrecompile => Precompile is not null; - public bool IsILed => _iLInfo is not null; + public bool IsILed + { + get + { + return _il != null && !ReferenceEquals(_il, ILInfo.NoIlEVM); + } + } public CodeInfo(IPrecompile precompile) { @@ -56,7 +62,7 @@ public bool ValidateJump(int destination, bool isSubroutine) CreateAnalyzer(); } - return _jumpAnalyzer.ValidateJump(destination, isSubroutine); + return _jumpAnalyzer!.ValidateJump(destination, isSubroutine); } /// @@ -91,5 +97,7 @@ private void CreateAnalyzer() _jumpAnalyzer = new CodeDataAnalyzer(MachineCode); } } + + internal void SetIlInfo(ILInfo info) => _il = info; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs deleted file mode 100644 index 649e1fd5105..00000000000 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalizer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Nethermind.Evm.CodeAnalysis; -internal class ILAnalizer -{ - internal ILInfo Analyze(byte[] machineCode) - { - throw new NotImplementedException(); - } -} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs new file mode 100644 index 00000000000..da51a9ef5d0 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading.Tasks; + +namespace Nethermind.Evm.CodeAnalysis; +internal static class IlAnalyzer +{ + /// + /// Starts the analyzing in a background task and outputs the value in the . + /// + /// The code to analyze. + /// The destination output. + public static void StartAnalysis(byte[] machineCode, CodeInfo codeInfo) + { + Task.Run(() => + { + ILInfo info = Analysis(machineCode); + codeInfo.SetIlInfo(info); + }); + } + + /// + /// For now, return null always to default to EVM. + /// + private static ILInfo Analysis(byte[] machineCode) + { + // TODO: implement actual analysis. + return ILInfo.NoIlEVM; + } + + /// + /// How many execution a should perform before trying to get its opcodes optimized. + /// + public const int IlAnalyzerThreshold = 23; +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs index 452e4961d54..0e18e735aaf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs @@ -9,8 +9,17 @@ using Sigil.NonGeneric; namespace Nethermind.Evm.CodeAnalysis; -internal struct ILInfo + +/// +/// Represents the IL-EVM information about the contract. +/// +internal class ILInfo { + /// + /// Represents an information about IL-EVM being not able to optimize the given . + /// + public static readonly ILInfo NoIlEVM = new(); + public ushort[] Pcs; public Emit[] IlMethod; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 4c7f2c6b679..294fafb831a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -724,7 +724,7 @@ private CallResult ExecuteCall(EvmState vmState, byte[]? p goto Empty; } - vmState.Env.CodeInfo.CallCount++; + vmState.Env.CodeInfo.NoticeExecution(); vmState.InitStacks(); EvmStack stack = new(vmState.DataStack.AsSpan(), vmState.DataStackHead, _txTracer); From db5484c9318d7b8d2a98e5c09b001499aa27fafe Mon Sep 17 00:00:00 2001 From: scooletz Date: Tue, 14 Nov 2023 11:12:34 +0100 Subject: [PATCH 003/146] moving pieces --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 15 ++++-- .../{ILAnalyzer.cs => IL/IlAnalyzer.cs} | 14 +++-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 51 +++++++++++++++++++ .../CodeAnalysis/IL/InstructionChunk.cs | 13 +++++ .../Nethermind.Evm/CodeAnalysis/ILInfo.cs | 30 ----------- .../Nethermind.Evm/VirtualMachine.cs | 15 ++++++ 6 files changed, 99 insertions(+), 39 deletions(-) rename src/Nethermind/Nethermind.Evm/CodeAnalysis/{ILAnalyzer.cs => IL/IlAnalyzer.cs} (76%) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs delete mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 03e00981832..3f43c091291 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -3,6 +3,8 @@ using System; using System.Threading; +using Nethermind.Core.Specs; +using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Precompiles; namespace Nethermind.Evm.CodeAnalysis @@ -19,7 +21,7 @@ public class CodeInfo private ICodeInfoAnalyzer? _jumpAnalyzer; // IL-EVM - private volatile ILInfo? _il; + private volatile IlInfo? _il; private int _callCount; public void NoticeExecution() @@ -41,11 +43,16 @@ public CodeInfo(byte[] code) } public bool IsPrecompile => Precompile is not null; - public bool IsILed + + /// + /// Gets information whether this code info has IL-EVM optimizations ready. + /// + internal IlInfo? IlInfo { get { - return _il != null && !ReferenceEquals(_il, ILInfo.NoIlEVM); + IlInfo? il = _il; + return il != null && !ReferenceEquals(il, IlInfo.NoIlEVM) ? il : null; } } @@ -98,6 +105,6 @@ private void CreateAnalyzer() } } - internal void SetIlInfo(ILInfo info) => _il = info; + internal void SetIlInfo(IlInfo info) => _il = info; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs similarity index 76% rename from src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs rename to src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index da51a9ef5d0..278c6f60de9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -1,9 +1,13 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System.Threading.Tasks; -namespace Nethermind.Evm.CodeAnalysis; +namespace Nethermind.Evm.CodeAnalysis.IL; + +/// +/// Provides +/// internal static class IlAnalyzer { /// @@ -15,7 +19,7 @@ public static void StartAnalysis(byte[] machineCode, CodeInfo codeInfo) { Task.Run(() => { - ILInfo info = Analysis(machineCode); + IlInfo info = Analysis(machineCode); codeInfo.SetIlInfo(info); }); } @@ -23,10 +27,10 @@ public static void StartAnalysis(byte[] machineCode, CodeInfo codeInfo) /// /// For now, return null always to default to EVM. /// - private static ILInfo Analysis(byte[] machineCode) + private static IlInfo Analysis(byte[] machineCode) { // TODO: implement actual analysis. - return ILInfo.NoIlEVM; + return IlInfo.NoIlEVM; } /// diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs new file mode 100644 index 00000000000..3cbf61a6813 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -0,0 +1,51 @@ +using System; +using System.Runtime.CompilerServices; +using Nethermind.Core.Specs; + +namespace Nethermind.Evm.CodeAnalysis.IL; + +/// +/// Represents the IL-EVM information about the contract. +/// +internal class IlInfo +{ + private const int NotFound = -1; + + /// + /// Represents an information about IL-EVM being not able to optimize the given . + /// + public static readonly IlInfo NoIlEVM = new(); + + /// + /// No overrides. + /// + private IlInfo() + { + _pCs = Array.Empty(); + _chunks = Array.Empty(); + } + + public IlInfo(ushort[] pcs, InstructionChunk[] chunks) + { + _pCs = pcs; + _chunks = chunks; + } + + // assumes small number of ILed + private readonly ushort[] _pCs; + private readonly InstructionChunk[] _chunks; + + public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) + where TTracingInstructions : struct, VirtualMachine.IIsTracing + { + if (programCounter > ushort.MaxValue) + return false; + + int at = _pCs.AsSpan().IndexOf((ushort)programCounter); + if (at == NotFound) + return false; + + _chunks[at](vmState, spec, ref programCounter, ref gasAvailable, ref stack); + return true; + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs new file mode 100644 index 00000000000..22908b40c5b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Specs; + +namespace Nethermind.Evm.CodeAnalysis.IL; + +/// +/// Represents a chunk of s that is optimized and ready to be run in an efficient manner. +/// +delegate void InstructionChunk(EvmState vmState, IReleaseSpec spec, ref int programCounter, + ref long gasAvailable, + ref EvmStack stack); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs deleted file mode 100644 index 0e18e735aaf..00000000000 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/ILInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Sigil.NonGeneric; - -namespace Nethermind.Evm.CodeAnalysis; - -/// -/// Represents the IL-EVM information about the contract. -/// -internal class ILInfo -{ - /// - /// Represents an information about IL-EVM being not able to optimize the given . - /// - public static readonly ILInfo NoIlEVM = new(); - - public ushort[] Pcs; - public Emit[] IlMethod; - - Emit HasOverride(ushort pc) - { - return IlMethod[pc]; - } -} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 294fafb831a..ba457c2752f 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -20,6 +20,7 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Runtime.Intrinsics; +using Nethermind.Evm.CodeAnalysis.IL; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; @@ -782,6 +783,12 @@ private CallResult ExecuteCode code = env.CodeInfo.MachineCode.AsSpan(); EvmExceptionType exceptionType = EvmExceptionType.None; + IlInfo? ilInfo = (typeof(TTracingInstructions) == typeof(NotTracing) && + typeof(TTracingRefunds) == typeof(NotTracing) && + typeof(TTracingStorage) == typeof(NotTracing)) + ? env.CodeInfo.IlInfo + : null; + bool isRevert = false; #if DEBUG DebugTracer? debugger = _txTracer.GetTracer(); @@ -793,6 +800,7 @@ private CallResult ExecuteCode Date: Fri, 15 Dec 2023 00:04:46 +0100 Subject: [PATCH 004/146] fix build issue --- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 4 ++-- .../CodeAnalysis/IL/InstructionChunk.cs | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 3cbf61a6813..0f6c5cc4177 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using Nethermind.Core.Specs; @@ -45,7 +45,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec if (at == NotFound) return false; - _chunks[at](vmState, spec, ref programCounter, ref gasAvailable, ref stack); + _chunks[at].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index 22908b40c5b..79462d1d70a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -1,13 +1,19 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Specs; +using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.CodeAnalysis.IL; /// /// Represents a chunk of s that is optimized and ready to be run in an efficient manner. /// -delegate void InstructionChunk(EvmState vmState, IReleaseSpec spec, ref int programCounter, - ref long gasAvailable, - ref EvmStack stack); +/// +interface InstructionChunk +{ + void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, + ref long gasAvailable, + ref EvmStack stack) where T : struct, IIsTracing; + +} From 6943d6ccfacc1ff8b39da7eb242692e115b8c126 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 15 Dec 2023 23:32:34 +0100 Subject: [PATCH 005/146] add naive analyzer for testing --- .../CodeAnalysis/IL/IlAnalyzer.cs | 51 ++++++++++++++++++- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 20 ++++---- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 278c6f60de9..187d596d23e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -1,7 +1,13 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using static System.Net.Mime.MediaTypeNames; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -10,6 +16,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal static class IlAnalyzer { + /// /// Starts the analyzing in a background task and outputs the value in the . /// @@ -29,8 +36,48 @@ public static void StartAnalysis(byte[] machineCode, CodeInfo codeInfo) /// private static IlInfo Analysis(byte[] machineCode) { + byte[] StripByteCode(byte[] machineCode) + { + byte[] opcodes = new byte[machineCode.Length]; + int j = 0; + for (int i = 0; i < machineCode.Length; i++, j++) + { + Instruction opcode = (Instruction)machineCode[i]; + opcodes[i] = (byte)opcode; + if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) + { + int immediatesCount = opcode - Instruction.PUSH0; + i += immediatesCount; + } + } + return opcodes[..j]; + } + + byte[] strippedBytecode = StripByteCode(machineCode); + Dictionary patternFound = new Dictionary(); + + foreach (var (pattern, mapping) in IlInfo.Patterns) + { + + for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) + { + bool found = true; + for (int j = 0; j < pattern.Length && found; j++) + { + found = strippedBytecode[i + j] == pattern[j]; + + } + + if (found) + { + patternFound.Add((ushort)i, mapping); + i += pattern.Length; + } + } + } + // TODO: implement actual analysis. - return IlInfo.NoIlEVM; + return new IlInfo(patternFound.ToFrozenDictionary()); } /// diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 0f6c5cc4177..771c017ed95 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Runtime.CompilerServices; using Nethermind.Core.Specs; @@ -9,7 +10,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal class IlInfo { - private const int NotFound = -1; + public static FrozenDictionary Patterns { get; } = FrozenDictionary.Empty; /// /// Represents an information about IL-EVM being not able to optimize the given . @@ -21,19 +22,16 @@ internal class IlInfo /// private IlInfo() { - _pCs = Array.Empty(); - _chunks = Array.Empty(); + _chunks = FrozenDictionary.Empty; } - public IlInfo(ushort[] pcs, InstructionChunk[] chunks) + public IlInfo(FrozenDictionary mappedOpcodes) { - _pCs = pcs; - _chunks = chunks; + _chunks = mappedOpcodes; } // assumes small number of ILed - private readonly ushort[] _pCs; - private readonly InstructionChunk[] _chunks; + private readonly FrozenDictionary _chunks; public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -41,11 +39,11 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec if (programCounter > ushort.MaxValue) return false; - int at = _pCs.AsSpan().IndexOf((ushort)programCounter); - if (at == NotFound) + bool hasProgramCounter = _chunks.ContainsKey((ushort)programCounter); + if (!hasProgramCounter) return false; - _chunks[at].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + _chunks[(ushort)programCounter].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); return true; } } From c7c951f68863d4d731c13aa96a0decd64f2cbb60 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 16 Dec 2023 02:32:12 +0100 Subject: [PATCH 006/146] Added placeholder tests --- .../CodeAnalysis/IlEvmTests.cs | 65 +++++++++++++++++++ .../CodeAnalysis/IL/IlAnalyzer.cs | 11 ++-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 11 ++-- .../CodeAnalysis/IL/InstructionChunk.cs | 1 - 4 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs new file mode 100644 index 00000000000..5b450fb0c4b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using FluentAssertions; +using Nethermind.Core.Specs; +using Nethermind.Evm.CodeAnalysis; +using Nethermind.Evm.CodeAnalysis.IL; +using Nethermind.Int256; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Evm.Test.CodeAnalysis +{ + public class P01P01ADD : InstructionChunk + { + public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + { + UInt256 lhs = vmState.Env.CodeInfo.MachineCode[programCounter + 1]; + UInt256 rhs = vmState.Env.CodeInfo.MachineCode[programCounter + 3]; + stack.PushUInt256(lhs + rhs); + } + } + + [TestFixture] + public class IlEvmTests + { + private const string AnalyzerField = "_analyzer"; + + [SetUp] + public void Setup() + { + Dictionary patterns = new Dictionary + { + { [96, 96, 01], new P01P01ADD() } + }; + + IlAnalyzer.Patterns = patterns.ToFrozenDictionary(); + } + + [Test] + public async Task Il_Analyzer_Find_All_Instance_Of_Pattern() + { + byte[] bytecode = + Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .Done; + + CodeInfo codeInfo = new CodeInfo(bytecode); + + await IlAnalyzer.StartAnalysis(bytecode, codeInfo); + + codeInfo.IlInfo.Chunks.Count.Should().Be(2); + } + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 187d596d23e..d5d5a64ce77 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -16,15 +16,16 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal static class IlAnalyzer { + public static FrozenDictionary Patterns = FrozenDictionary.Empty; /// /// Starts the analyzing in a background task and outputs the value in the . /// /// The code to analyze. /// The destination output. - public static void StartAnalysis(byte[] machineCode, CodeInfo codeInfo) + public static Task StartAnalysis(byte[] machineCode, CodeInfo codeInfo) { - Task.Run(() => + return Task.Run(() => { IlInfo info = Analysis(machineCode); codeInfo.SetIlInfo(info); @@ -43,7 +44,7 @@ byte[] StripByteCode(byte[] machineCode) for (int i = 0; i < machineCode.Length; i++, j++) { Instruction opcode = (Instruction)machineCode[i]; - opcodes[i] = (byte)opcode; + opcodes[j] = (byte)opcode; if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) { int immediatesCount = opcode - Instruction.PUSH0; @@ -56,7 +57,7 @@ byte[] StripByteCode(byte[] machineCode) byte[] strippedBytecode = StripByteCode(machineCode); Dictionary patternFound = new Dictionary(); - foreach (var (pattern, mapping) in IlInfo.Patterns) + foreach (var (pattern, mapping) in Patterns) { for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) @@ -71,7 +72,7 @@ byte[] StripByteCode(byte[] machineCode) if (found) { patternFound.Add((ushort)i, mapping); - i += pattern.Length; + i += pattern.Length - 1; } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 771c017ed95..46170d44925 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -10,7 +10,6 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal class IlInfo { - public static FrozenDictionary Patterns { get; } = FrozenDictionary.Empty; /// /// Represents an information about IL-EVM being not able to optimize the given . @@ -22,16 +21,16 @@ internal class IlInfo /// private IlInfo() { - _chunks = FrozenDictionary.Empty; + Chunks = FrozenDictionary.Empty; } public IlInfo(FrozenDictionary mappedOpcodes) { - _chunks = mappedOpcodes; + Chunks = mappedOpcodes; } // assumes small number of ILed - private readonly FrozenDictionary _chunks; + public FrozenDictionary Chunks { get; init; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -39,11 +38,11 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec if (programCounter > ushort.MaxValue) return false; - bool hasProgramCounter = _chunks.ContainsKey((ushort)programCounter); + bool hasProgramCounter = Chunks.ContainsKey((ushort)programCounter); if (!hasProgramCounter) return false; - _chunks[(ushort)programCounter].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + Chunks[(ushort)programCounter].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index 79462d1d70a..5fa882d8968 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -15,5 +15,4 @@ interface InstructionChunk void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; - } From 52ee53b70fed8e037de8db00843e255f71e25e7e Mon Sep 17 00:00:00 2001 From: scooletz Date: Mon, 15 Jan 2024 13:43:46 +0100 Subject: [PATCH 007/146] simplification --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 2 +- src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 1 - .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 7 +++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 5b450fb0c4b..3f2c44771f4 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -57,7 +57,7 @@ public async Task Il_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(bytecode, codeInfo); + await IlAnalyzer.StartAnalysis(codeInfo); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index b56ca8c391a..795ea67b055 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -3,7 +3,6 @@ using System; using System.Threading; -using Nethermind.Core.Specs; using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Precompiles; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index d5d5a64ce77..aaf34dc5781 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// -/// Provides +/// Provides /// internal static class IlAnalyzer { @@ -21,13 +21,12 @@ internal static class IlAnalyzer /// /// Starts the analyzing in a background task and outputs the value in the . /// - /// The code to analyze. /// The destination output. - public static Task StartAnalysis(byte[] machineCode, CodeInfo codeInfo) + public static Task StartAnalysis(CodeInfo codeInfo) { return Task.Run(() => { - IlInfo info = Analysis(machineCode); + IlInfo info = Analysis(codeInfo.MachineCode); codeInfo.SetIlInfo(info); }); } From 8e47c0bb1b748b09fd6e7bfcdf0e7b8cd7dea97f Mon Sep 17 00:00:00 2001 From: scooletz Date: Mon, 15 Jan 2024 14:35:53 +0100 Subject: [PATCH 008/146] shorcircuit one path --- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 2 -- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index aaf34dc5781..130c64c30ed 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -58,14 +58,12 @@ byte[] StripByteCode(byte[] machineCode) foreach (var (pattern, mapping) in Patterns) { - for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) { bool found = true; for (int j = 0; j < pattern.Length && found; j++) { found = strippedBytecode[i + j] == pattern[j]; - } if (found) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 46170d44925..4557022c20f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -38,11 +38,12 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec if (programCounter > ushort.MaxValue) return false; - bool hasProgramCounter = Chunks.ContainsKey((ushort)programCounter); - if (!hasProgramCounter) + if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk) == false) + { return false; + } - Chunks[(ushort)programCounter].Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); return true; } } From 452076140fad6eff3e11386638719fb2eb820ccb Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 29 Feb 2024 18:03:58 +0100 Subject: [PATCH 009/146] added more tests and refactors --- .../CodeAnalysis/IlEvmTests.cs | 75 ++++++++++++++++--- .../CodeAnalysis/IL/IlAnalyzer.cs | 41 ++++++++-- .../CodeAnalysis/IL/InstructionChunk.cs | 1 + 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 5b450fb0c4b..93fa9ab2435 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -5,45 +5,53 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; +using DotNetty.Common.Utilities; using FluentAssertions; +using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Core.Test.Blockchain; +using Nethermind.Core.Test.Builders; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.CodeAnalysis.IL; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Int256; using NSubstitute; using NUnit.Framework; +using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.Test.CodeAnalysis { public class P01P01ADD : InstructionChunk { + public static byte[] Pattern => [96, 96, 01]; + public byte CallCount { get; set; } = 0; + public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { - UInt256 lhs = vmState.Env.CodeInfo.MachineCode[programCounter + 1]; - UInt256 rhs = vmState.Env.CodeInfo.MachineCode[programCounter + 3]; + CallCount++; + UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; + UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; stack.PushUInt256(lhs + rhs); } } [TestFixture] - public class IlEvmTests + public class IlEvmTests : VirtualMachineTestsBase { private const string AnalyzerField = "_analyzer"; [SetUp] - public void Setup() + public override void Setup() { - Dictionary patterns = new Dictionary - { - { [96, 96, 01], new P01P01ADD() } - }; - - IlAnalyzer.Patterns = patterns.ToFrozenDictionary(); + base.Setup(); + IlAnalyzer.AddPattern(P01P01ADD.Pattern, new P01P01ADD()); } [Test] - public async Task Il_Analyzer_Find_All_Instance_Of_Pattern() + public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() { byte[] bytecode = Prepare.EvmCode @@ -61,5 +69,50 @@ public async Task Il_Analyzer_Find_All_Instance_Of_Pattern() codeInfo.IlInfo.Chunks.Count.Should().Be(2); } + + [Test] + public void Execution_Swap_Happens_When_Pattern_Occurs() + { + P01P01ADD pattern = (P01P01ADD)IlAnalyzer.GetPatternHandler(P01P01ADD.Pattern); + + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(23) + .PushSingle(7) + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .JUMP(0) + .Done; + + /* + byte[] initcode = + Prepare.EvmCode + .StoreDataInMemory(0, bytecode) + .Return(bytecode.Length, 0) + .Done; + + byte[] code = + Prepare.EvmCode + .PushData(0) + .PushData(0) + .PushData(0) + .PushData(0) + .PushData(0) + .Create(initcode, 1) + .PushData(1000) + .CALL() + .Done; + var address = receipts.TxReceipts[0].ContractAddress; + */ + + for(int i = 0; i < IlAnalyzer.IlAnalyzerThreshold * 2; i++) { + ExecuteBlock(new NullBlockTracer(), bytecode); + } + + Assert.Greater(pattern.CallCount, 0); + } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index d5d5a64ce77..629563f9525 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -16,14 +16,45 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal static class IlAnalyzer { - public static FrozenDictionary Patterns = FrozenDictionary.Empty; + public class ByteArrayComparer : IEqualityComparer + { + public bool Equals(byte[] left, byte[] right) + { + if (left == null || right == null) + { + return left == right; + } + return left.SequenceEqual(right); + } + public int GetHashCode(byte[] key) + { + if (key == null) + throw new ArgumentNullException("key"); + return key.Sum(b => b); + } + } + + private static Dictionary Patterns = new Dictionary(new ByteArrayComparer()); + public static Dictionary AddPattern(byte[] pattern, InstructionChunk chunk) + { + lock(Patterns) + { + Patterns[pattern] = chunk; + } + return Patterns; + } + public static InstructionChunk GetPatternHandler(byte[] pattern) + { + return Patterns[pattern]; + } + /// /// Starts the analyzing in a background task and outputs the value in the . /// /// The code to analyze. /// The destination output. - public static Task StartAnalysis(byte[] machineCode, CodeInfo codeInfo) + public static Task StartAnalysis(ReadOnlyMemory machineCode, CodeInfo codeInfo) { return Task.Run(() => { @@ -35,9 +66,9 @@ public static Task StartAnalysis(byte[] machineCode, CodeInfo codeInfo) /// /// For now, return null always to default to EVM. /// - private static IlInfo Analysis(byte[] machineCode) + private static IlInfo Analysis(ReadOnlyMemory machineCode) { - byte[] StripByteCode(byte[] machineCode) + byte[] StripByteCode(ReadOnlySpan machineCode) { byte[] opcodes = new byte[machineCode.Length]; int j = 0; @@ -54,7 +85,7 @@ byte[] StripByteCode(byte[] machineCode) return opcodes[..j]; } - byte[] strippedBytecode = StripByteCode(machineCode); + byte[] strippedBytecode = StripByteCode(machineCode.Span); Dictionary patternFound = new Dictionary(); foreach (var (pattern, mapping) in Patterns) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index 5fa882d8968..a180b22e5c4 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -12,6 +12,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// interface InstructionChunk { + static byte[] Pattern { get; } void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; From a9ee5f728327f373e9b73b7a996c30e1c83c483e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 5 Mar 2024 13:13:44 +0100 Subject: [PATCH 010/146] segmented pure code sections, in prepration for attempting IL weaving on the fly --- .../CodeAnalysis/IL/IlAnalyzer.cs | 40 ++++++++++++++++--- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 4 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 17 ++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 0e21fbc0a0e..3a4936ee213 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -67,24 +67,52 @@ public static Task StartAnalysis(ReadOnlyMemory machineCode, CodeInfo code /// private static IlInfo Analysis(ReadOnlyMemory machineCode) { - byte[] StripByteCode(ReadOnlySpan machineCode) + OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) { - byte[] opcodes = new byte[machineCode.Length]; + OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; int j = 0; for (int i = 0; i < machineCode.Length; i++, j++) { Instruction opcode = (Instruction)machineCode[i]; - opcodes[j] = (byte)opcode; + byte[] args = null; if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) { int immediatesCount = opcode - Instruction.PUSH0; + args = machineCode.Slice(i+1, immediatesCount).ToArray(); i += immediatesCount; } + opcodes[j] = new OpcodeInfo(opcode, args.AsMemory()); } return opcodes[..j]; } - byte[] strippedBytecode = StripByteCode(machineCode.Span); + OpcodeInfo[][] SegmentCode(OpcodeInfo[] codeData) + { + List opcodeInfos = []; + List segment = []; + foreach (var opcode in codeData) + { + if(opcode.Instruction.IsStateful()) + { + if(segment.Count > 0) + { + opcodeInfos.Add([.. segment]); + segment.Clear(); + } + } else + { + segment.Add(opcode); + } + } + if(segment.Count > 0) + { + opcodeInfos.Add([.. segment]); + } + return [.. opcodeInfos]; + } + + + OpcodeInfo[] strippedBytecode = StripByteCode(machineCode.Span); Dictionary patternFound = new Dictionary(); foreach (var (pattern, mapping) in Patterns) @@ -94,7 +122,7 @@ byte[] StripByteCode(ReadOnlySpan machineCode) bool found = true; for (int j = 0; j < pattern.Length && found; j++) { - found = strippedBytecode[i + j] == pattern[j]; + found = ((byte)strippedBytecode[i + j].Instruction == pattern[j]); } if (found) @@ -106,7 +134,7 @@ byte[] StripByteCode(ReadOnlySpan machineCode) } // TODO: implement actual analysis. - return new IlInfo(patternFound.ToFrozenDictionary()); + return new IlInfo(patternFound.ToFrozenDictionary(), SegmentCode(strippedBytecode)); } /// diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 4557022c20f..7df4ab7f6be 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -24,13 +24,15 @@ private IlInfo() Chunks = FrozenDictionary.Empty; } - public IlInfo(FrozenDictionary mappedOpcodes) + public IlInfo(FrozenDictionary mappedOpcodes, OpcodeInfo[][] segments) { Chunks = mappedOpcodes; + Segments = segments; } // assumes small number of ILed public FrozenDictionary Chunks { get; init; } + public OpcodeInfo[][] Segments { get; init; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index ab43e5c3888..d9e928c33ae 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Diagnostics.CodeAnalysis; using FastEnumUtility; using Nethermind.Core.Specs; @@ -176,8 +177,24 @@ public enum Instruction : byte SELFDESTRUCT = 0xff, } + public struct OpcodeInfo(Instruction instruction, ReadOnlyMemory? arguments) + { + public Instruction Instruction { get; set; } = instruction; + public ReadOnlyMemory? Arguments { get; set; } = arguments; + } + public static class InstructionExtensions { + public static bool IsStateful(this Instruction instruction) => instruction switch + { + Instruction.CREATE or Instruction.CREATE2 => true, + Instruction.CALL or Instruction.CALLCODE or Instruction.DELEGATECALL or Instruction.STATICCALL => true, + Instruction.SLOAD or Instruction.SSTORE => true, + Instruction.TLOAD or Instruction.TSTORE => true, + Instruction.EXTCODESIZE or Instruction.EXTCODECOPY or Instruction.EXTCODEHASH => true, + _ => false, + }; + public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) => instruction switch { From 1e44612ca7a7e2471dca6dd06d52e37fe9759a1c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 14 Mar 2024 23:45:59 +0000 Subject: [PATCH 011/146] simple prototype building on old IL-VM branch [WIP] --- .../CodeAnalysis/IL/ILCompiler.cs | 236 ++++++++++++++++++ .../CodeAnalysis/IL/ILGeneratorExt.cs | 231 +++++++++++++++++ .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 103 ++++++++ src/Nethermind/Nethermind.Evm/Instruction.cs | 47 +++- 4 files changed, 615 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs new file mode 100644 index 00000000000..ce81ccfbeb0 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -0,0 +1,236 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Evm.IL; +using Nethermind.Int256; +using Sigil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; +using Label = System.Reflection.Emit.Label; + +namespace Nethermind.Evm.CodeAnalysis.IL; +internal class ILCompiler +{ + public static Func Build(byte[] code) + { + Dictionary gasCost = BuildCostLookup(code); + + // TODO: stack invariants, gasCost application + + string name = "ILVM_" + Guid.NewGuid(); + + DynamicMethod method = new(name, typeof(EvmExceptionType), new[] { typeof(long) }, typeof(ILCompiler).Assembly.Modules.First(), true) + { + InitLocals = false + }; + + ILGenerator il = method.GetILGenerator(); + + LocalBuilder jmpDestination = il.DeclareLocal(Word.Int0Field.FieldType); + LocalBuilder address = il.DeclareLocal(typeof(Address)); + LocalBuilder consumeJumpCondition = il.DeclareLocal(typeof(int)); + LocalBuilder uint256A = il.DeclareLocal(typeof(UInt256)); + LocalBuilder uint256B = il.DeclareLocal(typeof(UInt256)); + LocalBuilder uint256C = il.DeclareLocal(typeof(UInt256)); + LocalBuilder uint256R = il.DeclareLocal(typeof(UInt256)); + LocalBuilder gasAvailable = il.DeclareLocal(typeof(long)); + + LocalBuilder stack = il.DeclareLocal(typeof(Word*)); + LocalBuilder current = il.DeclareLocal(typeof(Word*)); + + const int wordToAlignTo = 32; + + il.Emit(OpCodes.Ldc_I4, EvmStack.MaxStackSize * Word.Size + wordToAlignTo); + il.Emit(OpCodes.Localloc); + + // align to the boundary, so that the Word can be written using the aligned longs. + il.LoadValue(wordToAlignTo); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Ldc_I4, ~(wordToAlignTo - 1)); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.And); + + il.Store(stack); // store as start + + il.Load(stack); + il.Store(current); // copy to the current + + // gas + il.Emit(OpCodes.Ldarg_0); + il.Store(gasAvailable); + Label outOfGas = il.DefineLabel(); + + Label ret = il.DefineLabel(); // the label just before return + Label invalidAddress = il.DefineLabel(); // invalid jump address + Label jumpTable = il.DefineLabel(); // jump table + + Dictionary jumpDestinations = new(); + + for(int pc = 0; pc < code.Length; pc++) + { + OpcodeInfo op = OpcodeInfo.Operations[(Instruction)code[pc]]; + + // load gasAvailable + il.Emit(OpCodes.Ldloc, gasAvailable); + + // get pc gas cost + il.LoadValue(gasCost[pc]); + il.Emit(OpCodes.Sub); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Blt, outOfGas); + il.Store(gasAvailable); + + switch (op.Instruction) + { + case Instruction.JUMPDEST: + jumpDestinations[pc] = il.DefineLabel(); + il.MarkLabel(jumpDestinations[pc]); + break; + case Instruction.JUMP: + il.Emit(OpCodes.Br , jumpTable); + break; + case Instruction.JUMPI: + Label noJump = il.DefineLabel(); + il.StackLoadPrevious(current, 2); + il.EmitCall(OpCodes.Call, Word.GetIsZero, null); + il.Emit(OpCodes.Brtrue, noJump); + + // load the jump address + il.LoadValue(1); + il.Store(consumeJumpCondition); + il.Emit(OpCodes.Br, jumpTable); + + il.MarkLabel(noJump); + il.StackPop(current, 2); + break; + case Instruction.PUSH1: + case Instruction.PUSH2: + case Instruction.PUSH3: + case Instruction.PUSH4: + case Instruction.PUSH5: + case Instruction.PUSH6: + case Instruction.PUSH7: + case Instruction.PUSH8: + case Instruction.PUSH9: + case Instruction.PUSH10: + case Instruction.PUSH11: + case Instruction.PUSH12: + case Instruction.PUSH13: + case Instruction.PUSH14: + case Instruction.PUSH15: + case Instruction.PUSH16: + case Instruction.PUSH17: + case Instruction.PUSH18: + case Instruction.PUSH19: + case Instruction.PUSH20: + case Instruction.PUSH21: + case Instruction.PUSH22: + case Instruction.PUSH23: + case Instruction.PUSH24: + case Instruction.PUSH25: + case Instruction.PUSH26: + case Instruction.PUSH27: + case Instruction.PUSH28: + case Instruction.PUSH29: + case Instruction.PUSH30: + case Instruction.PUSH31: + case Instruction.PUSH32: + Span bytes = code.AsSpan(pc + 1, op.AdditionalBytes); + il.LoadArray(bytes); + il.LoadValue(0); + il.EmitCall(OpCodes.Call, typeof(BitConverter).GetProperty(nameof(BitConverter.IsLittleEndian)).GetMethod, null); + il.Emit(OpCodes.Newobj, typeof(UInt256).GetConstructor(new[] { typeof(Span), typeof(bool) })); + + il.StackPush(current); + pc += op.AdditionalBytes; + break; + case Instruction.ADD: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); + break; + + case Instruction.SUB: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); + break; + + case Instruction.MUL: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); + break; + + case Instruction.MOD: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!); + break; + + case Instruction.DIV: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!); + break; + + case Instruction.EXP: + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + break; + } + } + } + + private static void EmitBinaryUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operation) + { + il.StackLoadPrevious(current, 1); + il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.StackLoadPrevious(current, 2); + il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.EmitCall(OpCodes.Call, operation, null); + il.Store(uint256R); + il.StackPop(current, 2); + + il.Load(current); + il.Load(uint256R); // stack: word*, uint256 + il.EmitCall(OpCodes.Call, Word.SetUInt256, null); + il.StackPush(current); + } + + private static Dictionary BuildCostLookup(ReadOnlySpan code) + { + Dictionary costs = new(); + int costStart = 0; + long costCurrent = 0; + + for (int pc = 0; pc < code.Length; pc++) + { + OpcodeInfo op = OpcodeInfo.Operations[(Instruction)code[pc]] + switch (op.Instruction) + { + case Instruction.JUMPDEST: + costs[costStart] = costCurrent; // remember the current chain of opcodes + costStart = pc; + costCurrent = op.GasCost; + break; + case Instruction.JUMPI: + case Instruction.JUMP: + costCurrent += op.GasCost; + costs[costStart] = costCurrent; // remember the current chain of opcodes + costStart = pc + 1; // start with the next again + costCurrent = 0; + break; + default: + costCurrent += op.GasCost; + break; + } + + pc += op.AdditionalBytes; + } + + if (costCurrent > 0) + { + costs[costStart] = costCurrent; + } + + return costs; + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs new file mode 100644 index 00000000000..6cbb42392a8 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs @@ -0,0 +1,231 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Evm.CodeAnalysis.IL; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Nethermind.Evm.IL; + +/// +/// Extensions for . +/// +static class EmitExtensions +{ + public static void Load(this ILGenerator il, LocalBuilder local) + { + switch (local.LocalIndex) + { + case 0: + il.Emit(OpCodes.Ldloc_0); + break; + case 1: + il.Emit(OpCodes.Ldloc_1); + break; + case 2: + il.Emit(OpCodes.Ldloc_2); + break; + case 3: + il.Emit(OpCodes.Ldloc_3); + break; + default: + if (local.LocalIndex < 255) + { + il.Emit(OpCodes.Ldloc_S, (byte)local.LocalIndex); + } + else + { + il.Emit(OpCodes.Ldloc, local.LocalIndex); + } + break; + } + } + + public static void LoadAddress(this ILGenerator il, LocalBuilder local) + { + if (local.LocalIndex <= 255) + { + il.Emit(OpCodes.Ldloca_S, (byte)local.LocalIndex); + } + else + { + il.Emit(OpCodes.Ldloca, local.LocalIndex); + } + } + + public static void Load(this ILGenerator il, LocalBuilder local, FieldInfo wordField) + { + if (local.LocalType != typeof(Word*)) + { + throw new ArgumentException($"Only Word* can be used. This variable is of type {local.LocalType}"); + } + + if (wordField.DeclaringType != typeof(Word)) + { + throw new ArgumentException($"Only Word fields can be used. This field is declared for {wordField.DeclaringType}"); + } + + il.Load(local); + il.Emit(OpCodes.Ldfld, wordField); + } + + public static void CleanWord(this ILGenerator il, LocalBuilder local) + { + if (local.LocalType != typeof(Word*)) + { + throw new ArgumentException( + $"Only {nameof(Word)} pointers are supported. The passed local was type of {local.LocalType}."); + } + + il.Load(local); + il.Emit(OpCodes.Initobj, typeof(Word)); + } + + /// + /// Advances the stack one word up. + /// + public static void StackPush(this ILGenerator il, LocalBuilder local) + { + il.Load(local); + il.LoadValue(Word.Size); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.Add); + il.Store(local); + } + + /// + /// Moves the stack words down. + /// + public static void StackPop(this ILGenerator il, LocalBuilder local, int count = 1) + { + il.Load(local); + il.LoadValue(Word.Size * count); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.Sub); + il.Store(local); + } + + /// + /// Moves the stack words down. + /// + public static void StackPop(this ILGenerator il, LocalBuilder local, LocalBuilder count) + { + il.Load(local); + il.LoadValue(Word.Size); + il.Load(count); + il.Emit(OpCodes.Mul); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.Sub); + il.Store(local); + } + + /// + /// Loads the previous EVM stack value on top of .NET stack. + /// + public static void StackLoadPrevious(this ILGenerator il, LocalBuilder local, int count = 1) + { + il.Load(local); + il.LoadValue(Word.Size * count); + il.Emit(OpCodes.Conv_I); + il.Emit(OpCodes.Sub); + } + + public static void Store(this ILGenerator il, LocalBuilder local) + { + switch (local.LocalIndex) + { + case 0: + il.Emit(OpCodes.Stloc_0); + break; + case 1: + il.Emit(OpCodes.Stloc_1); + break; + case 2: + il.Emit(OpCodes.Stloc_2); + break; + case 3: + il.Emit(OpCodes.Stloc_3); + break; + default: + if (local.LocalIndex < 255) + { + il.Emit(OpCodes.Stloc_S, (byte)local.LocalIndex); + } + else + { + il.Emit(OpCodes.Stloc, local.LocalIndex); + } + break; + } + } + + public static void LoadArray(this ILGenerator il, Span value) + { + // declare il data + il.Emit(OpCodes.Ldc_I4, value.Length); + il.Emit(OpCodes.Newarr, typeof(byte)); + + // fill the array + for (int i = 0; i < value.Length; i++) + { + il.Emit(OpCodes.Dup); + il.LoadValue(i); + il.LoadValue(value[i]); + il.Emit(OpCodes.Stelem_I1); + } + + Span arr = value.ToArray(); + + // invoke System.MemoryExtensions::AsSpan to get the span + il.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), new[] { typeof(byte[]) })); + // invoke [System.Runtime]System.Span`1::op_Implicit(valuetype [System.Runtime]System.Span`1 + il.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); + + } + + public static void LoadValue(this ILGenerator il, long value) + { + il.Emit(OpCodes.Ldc_I8, value); + } + + public static void LoadValue(this ILGenerator il, int value) + { + switch (value) + { + case 0: + il.Emit(OpCodes.Ldc_I4_0); + break; + case 1: + il.Emit(OpCodes.Ldc_I4_1); + break; + case 2: + il.Emit(OpCodes.Ldc_I4_2); + break; + case 3: + il.Emit(OpCodes.Ldc_I4_3); + break; + case 4: + il.Emit(OpCodes.Ldc_I4_4); + break; + case 5: + il.Emit(OpCodes.Ldc_I4_5); + break; + case 6: + il.Emit(OpCodes.Ldc_I4_6); + break; + case 7: + il.Emit(OpCodes.Ldc_I4_7); + break; + case 8: + il.Emit(OpCodes.Ldc_I4_8); + break; + default: + if (value <= 255) + il.Emit(OpCodes.Ldc_I4_S, (byte)value); + else + il.Emit(OpCodes.Ldc_I4, value); + break; + } + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs new file mode 100644 index 00000000000..29d4bbba8fc --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Int256; +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.CodeAnalysis.IL; + +[StructLayout(LayoutKind.Explicit, Size = 32)] +internal struct Word +{ + public const int Size = 32; + + [FieldOffset(0)] public unsafe fixed byte _buffer[Size]; + + [FieldOffset(Size - sizeof(byte))] + public byte Byte0; + + [FieldOffset(Size - sizeof(int))] + public int Int0; + + [FieldOffset(Size - sizeof(int))] + public uint UInt0; + + [FieldOffset(Size - 2 * sizeof(int))] + public uint UInt1; + + [FieldOffset(Size - 1 * sizeof(ulong))] + public ulong Ulong0; + + [FieldOffset(Size - 2 * sizeof(ulong))] + public ulong Ulong1; + + [FieldOffset(Size - 3 * sizeof(ulong))] + public ulong Ulong2; + + [FieldOffset(Size - 4 * sizeof(ulong))] + public ulong Ulong3; + + public bool IsZero => (Ulong0 | Ulong1 | Ulong2 | Ulong3) == 0; + + public UInt256 UInt256 + { + get + { + ulong u3 = Ulong3; + ulong u2 = Ulong2; + ulong u1 = Ulong1; + ulong u0 = Ulong0; + + if (BitConverter.IsLittleEndian) + { + u3 = BinaryPrimitives.ReverseEndianness(u3); + u2 = BinaryPrimitives.ReverseEndianness(u2); + u1 = BinaryPrimitives.ReverseEndianness(u1); + u0 = BinaryPrimitives.ReverseEndianness(u0); + } + + return new UInt256(u0, u1, u2, u3); + } + set + { + if (BitConverter.IsLittleEndian) + { + Ulong3 = BinaryPrimitives.ReverseEndianness(value.u3); + Ulong2 = BinaryPrimitives.ReverseEndianness(value.u2); + Ulong1 = BinaryPrimitives.ReverseEndianness(value.u1); + Ulong0 = BinaryPrimitives.ReverseEndianness(value.u0); + } + else + { + Ulong3 = value.u3; + Ulong2 = value.u2; + Ulong1 = value.u1; + Ulong0 = value.u0; + } + } + } + + public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(Byte0)); + + public static readonly FieldInfo Int0Field = typeof(Word).GetField(nameof(Int0)); + + public static readonly FieldInfo UInt0Field = typeof(Word).GetField(nameof(UInt0)); + public static readonly FieldInfo UInt1Field = typeof(Word).GetField(nameof(UInt1)); + + public static readonly FieldInfo Ulong0Field = typeof(Word).GetField(nameof(Ulong0)); + public static readonly FieldInfo Ulong1Field = typeof(Word).GetField(nameof(Ulong1)); + public static readonly FieldInfo Ulong2Field = typeof(Word).GetField(nameof(Ulong2)); + public static readonly FieldInfo Ulong3Field = typeof(Word).GetField(nameof(Ulong3)); + + public static readonly MethodInfo GetIsZero = typeof(Word).GetProperty(nameof(IsZero))!.GetMethod; + + public static readonly MethodInfo GetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.GetMethod; + public static readonly MethodInfo SetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.SetMethod; +} diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index d9e928c33ae..d524147b721 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Frozen; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using FastEnumUtility; using Nethermind.Core.Specs; @@ -177,9 +180,49 @@ public enum Instruction : byte SELFDESTRUCT = 0xff, } - public struct OpcodeInfo(Instruction instruction, ReadOnlyMemory? arguments) + public struct OpcodeInfo(Instruction instruction, ReadOnlyMemory? arguments, long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) { - public Instruction Instruction { get; set; } = instruction; + /// + /// The actual instruction + /// + public Instruction Instruction { get; } + + /// + /// The gas cost. + /// + public long GasCost { get; } = gasCost; + + /// + /// How many following bytes does this instruction have. + /// + public byte AdditionalBytes { get; } = additionalBytes; + + /// + /// How many bytes are popped by this instruction. + /// + public byte StackBehaviorPop { get; } = stackBehaviorPop; + + /// + /// How many bytes are pushed by this instruction. + /// + public byte StackBehaviorPush { get; } = stackBehaviorPush; + + public static readonly IReadOnlyDictionary Operations = + new OpcodeInfo[] + { + new(Instruction.POP, ReadOnlyMemory.Empty, GasCostOf.Base, 0, 1, 0), + new(Instruction.PC, ReadOnlyMemory.Empty, GasCostOf.Base, 0, 0, 1), + new(Instruction.PUSH1, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 1, 0, 1), + new(Instruction.PUSH2, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 2, 0, 1), + new(Instruction.PUSH4, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 4, 0, 1), + new(Instruction.JUMPDEST, ReadOnlyMemory.Empty, GasCostOf.JumpDest, 0, 0, 0), + new(Instruction.JUMP, ReadOnlyMemory.Empty, GasCostOf.Mid, 0, 1, 0), + new(Instruction.JUMPI, ReadOnlyMemory.Empty, GasCostOf.High, 0, 2, 0), + new(Instruction.SUB, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 0, 2, 1), + new(Instruction.DUP1, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 0, 1, 2), + new(Instruction.SWAP1, ReadOnlyMemory.Empty, GasCostOf.VeryLow, 0, 2, 2) + }.ToFrozenDictionary(op => op.Instruction); + public Instruction Operation { get; set; } = instruction; public ReadOnlyMemory? Arguments { get; set; } = arguments; } From 1a2d3a6e7a5136789f4b970e9152186d2145e1e2 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 15 Mar 2024 07:34:14 +0000 Subject: [PATCH 012/146] more experimentation with IL-emition --- .../CodeAnalysis/IL/ILCompiler.cs | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index ce81ccfbeb0..4108ad7bb1b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -165,26 +165,90 @@ public static Func Build(byte[] code) break; case Instruction.MOD: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + il => { + Label label = il.DefineLabel(); + + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + + il.Emit(OpCodes.Brfalse, label); + + il.Emit(OpCodes.Ldc_I4_0); + + il.MarkLabel(label); + }); break; case Instruction.DIV: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, + il => { + Label label = il.DefineLabel(); + + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + + il.Emit(OpCodes.Brfalse, label); + + il.Emit(OpCodes.Ldc_I4_0); + + il.MarkLabel(label); + }); break; case Instruction.EXP: EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); break; + case Instruction.LT: + EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); + break; + case Instruction.GT: + EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + break; + case Instruction.EQ: + EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); + break; + case Instruction.ISZERO: + il.StackLoadPrevious(current, 1); + il.EmitCall(OpCodes.Call, Word.GetIsZero, null); + il.StackPush(current); + il.StackPop(current, 1); + break; + } } + + Func del = method.CreateDelegate>(); + return del; + } + + private static void EmitComparaisonUInt256Method(ILGenerator il, LocalBuilder current, MethodInfo operatin) + { + il.StackLoadPrevious(current, 1); + il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.StackLoadPrevious(current, 2); + il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + // invoke op < on the uint256 + il.EmitCall(OpCodes.Call, operatin, null); + // if true, push 1, else 0 + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + il.StackPush(current); + il.StackPop(current, 2); } - private static void EmitBinaryUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operation) + private static void EmitBinaryUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operation, Action customHandling = null) { il.StackLoadPrevious(current, 1); il.EmitCall(OpCodes.Call, Word.GetUInt256, null); il.StackLoadPrevious(current, 2); il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + + customHandling.Invoke(il); + + il.EmitCall(OpCodes.Call, operation, null); il.Store(uint256R); il.StackPop(current, 2); @@ -203,7 +267,7 @@ private static Dictionary BuildCostLookup(ReadOnlySpan code) for (int pc = 0; pc < code.Length; pc++) { - OpcodeInfo op = OpcodeInfo.Operations[(Instruction)code[pc]] + OpcodeInfo op = OpcodeInfo.Operations[(Instruction)code[pc]]; switch (op.Instruction) { case Instruction.JUMPDEST: From 6a7a7c6313585950e34693377e2dec926598f868 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 15 Mar 2024 07:56:14 +0000 Subject: [PATCH 013/146] more opcodes added --- .../CodeAnalysis/IL/ILCompiler.cs | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 4108ad7bb1b..7fb152bb626 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -202,13 +202,13 @@ public static Func Build(byte[] code) EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.LT: - EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.GT: - EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.EQ: - EmitComparaisonUInt256Method(il, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.ISZERO: il.StackLoadPrevious(current, 1); @@ -216,6 +216,72 @@ public static Func Build(byte[] code) il.StackPush(current); il.StackPop(current, 1); break; + case Instruction.CODESIZE: + il.LoadValue(code.Length); + il.EmitCall(OpCodes.Call, typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) }), null); + il.StackPush(current); + break; + case Instruction.POP: + il.StackPop(current); + break; + case Instruction.DUP1: + case Instruction.DUP2: + case Instruction.DUP3: + case Instruction.DUP4: + case Instruction.DUP5: + case Instruction.DUP6: + case Instruction.DUP7: + case Instruction.DUP8: + case Instruction.DUP9: + case Instruction.DUP10: + case Instruction.DUP11: + case Instruction.DUP12: + case Instruction.DUP13: + case Instruction.DUP14: + case Instruction.DUP15: + case Instruction.DUP16: + int count = (int)op.Instruction - (int)Instruction.DUP1 + 1; + il.Load(current); + il.StackLoadPrevious(current, count); + il.Emit(OpCodes.Ldobj, typeof(Word)); + il.Emit(OpCodes.Stobj, typeof(Word)); + il.StackPush(current); + + break; + case Instruction.SWAP1: + case Instruction.SWAP2: + case Instruction.SWAP3: + case Instruction.SWAP4: + case Instruction.SWAP5: + case Instruction.SWAP6: + case Instruction.SWAP7: + case Instruction.SWAP8: + case Instruction.SWAP9: + case Instruction.SWAP10: + case Instruction.SWAP11: + case Instruction.SWAP12: + case Instruction.SWAP13: + case Instruction.SWAP14: + case Instruction.SWAP15: + case Instruction.SWAP16: + count = (int)op.Instruction - (int)Instruction.SWAP1 + 1; + + il.LoadAddress(uint256R); + il.StackLoadPrevious(current, 1); + il.Emit(OpCodes.Ldobj, typeof(Word)); + il.Emit(OpCodes.Stobj, typeof(Word)); + + il.StackLoadPrevious(current, 1); + il.StackLoadPrevious(current, count); + il.Emit(OpCodes.Ldobj, typeof(Word)); + il.Emit(OpCodes.Stobj, typeof(Word)); + + il.StackLoadPrevious(current, count); + il.LoadAddress(uint256R); + il.Emit(OpCodes.Ldobj, typeof(Word)); + il.Emit(OpCodes.Stobj, typeof(Word)); + break; + } } @@ -224,7 +290,7 @@ public static Func Build(byte[] code) return del; } - private static void EmitComparaisonUInt256Method(ILGenerator il, LocalBuilder current, MethodInfo operatin) + private static void EmitComparaisonUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operatin) { il.StackLoadPrevious(current, 1); il.EmitCall(OpCodes.Call, Word.GetUInt256, null); @@ -235,8 +301,17 @@ private static void EmitComparaisonUInt256Method(ILGenerator il, LocalBuilder cu // if true, push 1, else 0 il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ceq); - il.StackPush(current); + + // convert to conv_i + il.Emit(OpCodes.Conv_I); + il.EmitCall(OpCodes.Call, typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) }), null); + il.Store(uint256R); il.StackPop(current, 2); + + il.Load(current); + il.Load(uint256R); // stack: word*, uint256 + il.EmitCall(OpCodes.Call, Word.SetUInt256, null); + il.StackPush(current); } private static void EmitBinaryUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operation, Action customHandling = null) From 95500d89a7ed54adc312e6a3b8b91b7ae3002195 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 20 Mar 2024 17:16:45 +0000 Subject: [PATCH 014/146] minor changes --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index bd5043a90a0..ea399739053 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -143,10 +143,11 @@ public static Func Build(OpcodeInfo[] code) case Instruction.PUSH30: case Instruction.PUSH31: case Instruction.PUSH32: - ReadOnlySpan bytes = op.Arguments.Value.Span; - il.LoadArray(bytes); + int count = (int)op.Operation - (int)Instruction.PUSH0; + ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); + il.LoadArray(bytes.ToArray()); + il.LoadValue(0); il.LoadValue(0); - il.EmitCall(OpCodes.Call, typeof(BitConverter).GetProperty(nameof(BitConverter.IsLittleEndian)).GetMethod, null); il.Emit(OpCodes.Newobj, typeof(UInt256).GetConstructor(new[] { typeof(Span), typeof(bool) })); il.StackPush(current); @@ -240,7 +241,7 @@ public static Func Build(OpcodeInfo[] code) case Instruction.DUP14: case Instruction.DUP15: case Instruction.DUP16: - int count = (int)op.Operation - (int)Instruction.DUP1 + 1; + count = (int)op.Operation - (int)Instruction.DUP1 + 1; il.Load(current); il.StackLoadPrevious(current, count); il.Emit(OpCodes.Ldobj, typeof(Word)); From 41d7bfe9c0ea580e566651e45a30dc6cda79ee60 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 21 Mar 2024 13:19:59 +0000 Subject: [PATCH 015/146] [wip] sigil-ify the code --- .../CodeAnalysis/IL/ILCompiler.cs | 66 ++++++++----------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index ea399739053..5543b9ae251 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -9,70 +9,60 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; -using Label = System.Reflection.Emit.Label; +using Label = Sigil.Label; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func Build(OpcodeInfo[] code) + public static Func Build(CodeInfo codeinfo, int segmentId) { - Dictionary gasCost = BuildCostLookup(code); + Dictionary gasCost = BuildCostLookup(codeinfo.IlInfo.Segments[segmentId]); // TODO: stack invariants, gasCost application string name = "ILVM_" + Guid.NewGuid(); - DynamicMethod method = new(name, typeof(EvmExceptionType), new[] { typeof(long) }, typeof(ILCompiler).Assembly.Modules.First(), true) - { - InitLocals = false - }; - - ILGenerator il = method.GetILGenerator(); + Emit method = Emit.NewDynamicMethod($"{codeinfo.GetHashCode()}_{segmentId}", doVerify: true, strictBranchVerification: true); - LocalBuilder jmpDestination = il.DeclareLocal(Word.Int0Field.FieldType); - LocalBuilder address = il.DeclareLocal(typeof(Address)); - LocalBuilder consumeJumpCondition = il.DeclareLocal(typeof(int)); - LocalBuilder uint256A = il.DeclareLocal(typeof(UInt256)); - LocalBuilder uint256B = il.DeclareLocal(typeof(UInt256)); - LocalBuilder uint256C = il.DeclareLocal(typeof(UInt256)); - LocalBuilder uint256R = il.DeclareLocal(typeof(UInt256)); - LocalBuilder gasAvailable = il.DeclareLocal(typeof(long)); + Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); + Local address = method.DeclareLocal(typeof(Address)); + Local consumeJumpCondition = method.DeclareLocal(typeof(int)); + Local uint256A = method.DeclareLocal(typeof(UInt256)); + Local uint256B = method.DeclareLocal(typeof(UInt256)); + Local uint256C = method.DeclareLocal(typeof(UInt256)); + Local uint256R = method.DeclareLocal(typeof(UInt256)); + Local gasAvailable = method.DeclareLocal(typeof(long)); - LocalBuilder stack = il.DeclareLocal(typeof(Word*)); - LocalBuilder current = il.DeclareLocal(typeof(Word*)); + Local stack = method.DeclareLocal(typeof(Word*)); + Local current = method.DeclareLocal(typeof(Word*)); const int wordToAlignTo = 32; - il.Emit(OpCodes.Ldc_I4, EvmStack.MaxStackSize * Word.Size + wordToAlignTo); - il.Emit(OpCodes.Localloc); - // align to the boundary, so that the Word can be written using the aligned longs. - il.LoadValue(wordToAlignTo); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Ldc_I4, ~(wordToAlignTo - 1)); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.And); + // allocate stack + method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); + method.LocalAllocate(); - il.Store(stack); // store as start + method.StoreLocal(stack); - il.Load(stack); - il.Store(current); // copy to the current + method.LoadLocal(stack); + method.StoreLocal(current); // copy to the current // gas - il.Emit(OpCodes.Ldarg_0); - il.Store(gasAvailable); - Label outOfGas = il.DefineLabel(); + method.LoadArgument(0); + method.StoreLocal(gasAvailable); + Label outOfGas = method.DefineLabel("OutOfGas"); - Label ret = il.DefineLabel(); // the label just before return - Label invalidAddress = il.DefineLabel(); // invalid jump address - Label jumpTable = il.DefineLabel(); // jump table + Label ret = method.DefineLabel("Return"); // the label just before return + Label invalidAddress = method.DefineLabel("InvalidAddress"); // invalid jump address + Label jumpTable = method.DefineLabel("Jumptable"); // jump table Dictionary jumpDestinations = new(); + + OpcodeInfo[] code = codeinfo.IlInfo.Segments[segmentId]; for(int pc = 0; pc < code.Length; pc++) { OpcodeInfo op = code[pc]; From 0dd5fc0fea5dee0396e0e67cd26060985ffa44b8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 21 Mar 2024 14:21:29 +0000 Subject: [PATCH 016/146] sigli-fy the current progress --- .../CodeAnalysis/IL/ILCompiler.cs | 199 +++++++-------- .../CodeAnalysis/IL/ILExtensions.cs | 112 +++++++++ .../CodeAnalysis/IL/ILGeneratorExt.cs | 231 ------------------ 3 files changed, 212 insertions(+), 330 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs delete mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5543b9ae251..4fa69f36c32 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; using Label = Sigil.Label; @@ -24,7 +25,7 @@ public static Func Build(CodeInfo codeinfo, int segmentI string name = "ILVM_" + Guid.NewGuid(); - Emit method = Emit.NewDynamicMethod($"{codeinfo.GetHashCode()}_{segmentId}", doVerify: true, strictBranchVerification: true); + Emit> method = Emit>.NewDynamicMethod($"{codeinfo.GetHashCode()}_{segmentId}", doVerify: true, strictBranchVerification: true); Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); Local address = method.DeclareLocal(typeof(Address)); @@ -68,38 +69,38 @@ public static Func Build(CodeInfo codeinfo, int segmentI OpcodeInfo op = code[pc]; // load gasAvailable - il.Emit(OpCodes.Ldloc, gasAvailable); + method.LoadLocal(gasAvailable); // get pc gas cost - il.LoadValue(gasCost[pc]); - il.Emit(OpCodes.Sub); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Blt, outOfGas); - il.Store(gasAvailable); + method.LoadConstant(gasCost[pc]); + method.Subtract(); + method.Duplicate(); + method.LoadConstant(0); + method.BranchIfLess(outOfGas); + method.StoreLocal(gasAvailable); switch (op.Operation) { case Instruction.JUMPDEST: - jumpDestinations[pc] = il.DefineLabel(); - il.MarkLabel(jumpDestinations[pc]); + jumpDestinations[pc] = method.DefineLabel(); + method.MarkLabel(jumpDestinations[pc]); break; case Instruction.JUMP: - il.Emit(OpCodes.Br , jumpTable); + method.Branch(jumpTable); break; case Instruction.JUMPI: - Label noJump = il.DefineLabel(); - il.StackLoadPrevious(current, 2); - il.EmitCall(OpCodes.Call, Word.GetIsZero, null); - il.Emit(OpCodes.Brtrue, noJump); + Label noJump = method.DefineLabel(); + method.StackLoadPrevious(current, 2); + method.Call(Word.GetIsZero, null); + method.BranchIfTrue(noJump); // load the jump address - il.LoadValue(1); - il.Store(consumeJumpCondition); - il.Emit(OpCodes.Br, jumpTable); + method.LoadConstant(1); + method.StoreLocal(consumeJumpCondition); + method.Branch(jumpTable); - il.MarkLabel(noJump); - il.StackPop(current, 2); + method.MarkLabel(noJump); + method.StackPop(current, 2); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -135,85 +136,86 @@ public static Func Build(CodeInfo codeinfo, int segmentI case Instruction.PUSH32: int count = (int)op.Operation - (int)Instruction.PUSH0; ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); - il.LoadArray(bytes.ToArray()); - il.LoadValue(0); - il.LoadValue(0); - il.Emit(OpCodes.Newobj, typeof(UInt256).GetConstructor(new[] { typeof(Span), typeof(bool) })); + method.LoadArray(bytes.ToArray()); + method.LoadConstant(0); + method.NewObject(typeof(UInt256), typeof(Span), typeof(bool)); - il.StackPush(current); - pc += op.Metadata?.AdditionalBytes ?? 0; + method.StackPush(current); break; case Instruction.ADD: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.SUB: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.MUL: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.MOD: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, - il => { + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel) => { Label label = il.DefineLabel(); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); + il.Duplicate(); + il.LoadConstant(0); + il.CompareEqual(); - il.Emit(OpCodes.Brfalse, label); - - il.Emit(OpCodes.Ldc_I4_0); + il.BranchIfFalse(label); + + il.LoadConstant(0); + il.Branch(postInstructionLabel); il.MarkLabel(label); }); break; case Instruction.DIV: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, - il => { + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel) => { Label label = il.DefineLabel(); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); + il.Duplicate(); + il.LoadConstant(0); + il.CompareEqual(); - il.Emit(OpCodes.Brfalse, label); + il.BranchIfFalse(label); - il.Emit(OpCodes.Ldc_I4_0); + il.LoadConstant(0); + il.Branch(postInstructionLabel); il.MarkLabel(label); }); break; case Instruction.EXP: - EmitBinaryUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.LT: - EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.GT: - EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.EQ: - EmitComparaisonUInt256Method(il, uint256R, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.ISZERO: - il.StackLoadPrevious(current, 1); - il.EmitCall(OpCodes.Call, Word.GetIsZero, null); - il.StackPush(current); - il.StackPop(current, 1); + method.StackLoadPrevious(current, 1); + method.Call(Word.GetIsZero); + method.StackPush(current); + method.StackPop(current, 1); break; case Instruction.CODESIZE: - il.LoadValue(code.Length); - il.EmitCall(OpCodes.Call, typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) }), null); - il.StackPush(current); + method.LoadConstant(code.Length); + method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + method.StackPush(current); break; case Instruction.POP: - il.StackPop(current); + method.Pop(); + method.StackPop(current); break; case Instruction.DUP1: case Instruction.DUP2: @@ -232,12 +234,11 @@ public static Func Build(CodeInfo codeinfo, int segmentI case Instruction.DUP15: case Instruction.DUP16: count = (int)op.Operation - (int)Instruction.DUP1 + 1; - il.Load(current); - il.StackLoadPrevious(current, count); - il.Emit(OpCodes.Ldobj, typeof(Word)); - il.Emit(OpCodes.Stobj, typeof(Word)); - il.StackPush(current); - + method.LoadLocal(current); + method.StackLoadPrevious(current, count); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + method.StackPush(current); break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -257,71 +258,71 @@ public static Func Build(CodeInfo codeinfo, int segmentI case Instruction.SWAP16: count = (int)op.Operation - (int)Instruction.SWAP1 + 1; - il.LoadAddress(uint256R); - il.StackLoadPrevious(current, 1); - il.Emit(OpCodes.Ldobj, typeof(Word)); - il.Emit(OpCodes.Stobj, typeof(Word)); + method.LoadLocalAddress(uint256R); + method.StackLoadPrevious(current, 1); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); - il.StackLoadPrevious(current, 1); - il.StackLoadPrevious(current, count); - il.Emit(OpCodes.Ldobj, typeof(Word)); - il.Emit(OpCodes.Stobj, typeof(Word)); + method.StackLoadPrevious(current, 1); + method.StackLoadPrevious(current, count); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); - il.StackLoadPrevious(current, count); - il.LoadAddress(uint256R); - il.Emit(OpCodes.Ldobj, typeof(Word)); - il.Emit(OpCodes.Stobj, typeof(Word)); + method.StackLoadPrevious(current, count); + method.LoadLocalAddress(uint256R); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); break; - - } } - Func del = method.CreateDelegate>(); + Func del = method.CreateDelegate(); return del; } - private static void EmitComparaisonUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operatin) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local current, MethodInfo operatin) { il.StackLoadPrevious(current, 1); - il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.Call(Word.GetUInt256); il.StackLoadPrevious(current, 2); - il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.Call(Word.GetUInt256); // invoke op < on the uint256 - il.EmitCall(OpCodes.Call, operatin, null); + il.Call(operatin, null); // if true, push 1, else 0 - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); + il.LoadConstant(0); + il.CompareEqual(); // convert to conv_i - il.Emit(OpCodes.Conv_I); - il.EmitCall(OpCodes.Call, typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) }), null); - il.Store(uint256R); + il.Convert(); + il.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.StackPop(current, 2); - il.Load(current); - il.Load(uint256R); // stack: word*, uint256 - il.EmitCall(OpCodes.Call, Word.SetUInt256, null); + il.LoadLocal(current); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); il.StackPush(current); } - private static void EmitBinaryUInt256Method(ILGenerator il, LocalBuilder uint256R, LocalBuilder current, MethodInfo operation, Action customHandling = null) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local current, MethodInfo operation, Action, Label> customHandling = null) { + Label label = il.DefineLabel(); + il.StackLoadPrevious(current, 1); - il.EmitCall(OpCodes.Call, Word.GetUInt256, null); + il.Call(Word.GetUInt256); il.StackLoadPrevious(current, 2); - il.EmitCall(OpCodes.Call, Word.GetUInt256, null); - - customHandling.Invoke(il); + il.Call(Word.GetUInt256); + customHandling.Invoke(il, label); - il.EmitCall(OpCodes.Call, operation, null); - il.Store(uint256R); + il.Call(operation); + il.StoreLocal(uint256R); il.StackPop(current, 2); - il.Load(current); - il.Load(uint256R); // stack: word*, uint256 - il.EmitCall(OpCodes.Call, Word.SetUInt256, null); + il.MarkLabel(label); + il.LoadLocal(current); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); il.StackPush(current); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs new file mode 100644 index 00000000000..53190b41fe1 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Evm.CodeAnalysis.IL; +using Sigil; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Nethermind.Evm.IL; + +/// +/// Extensions for . +/// +static class EmitExtensions +{ + public static void Load(this Emit il, Local local, FieldInfo wordField) + { + if (local.LocalType != typeof(Word*)) + { + throw new ArgumentException($"Only Word* can be used. This variable is of type {local.LocalType}"); + } + + if (wordField.DeclaringType != typeof(Word)) + { + throw new ArgumentException($"Only Word fields can be used. This field is declared for {wordField.DeclaringType}"); + } + + il.LoadLocal(local); + il.LoadField(wordField); + } + + public static void CleanWord(this Emit il, Local local) + { + if (local.LocalType != typeof(Word*)) + { + throw new ArgumentException( + $"Only {nameof(Word)} pointers are supported. The passed local was type of {local.LocalType}."); + } + + il.LoadLocal(local); + il.InitializeObject(typeof(Word)); + } + + /// + /// Advances the stack one word up. + /// + public static void StackPush(this Emit il, Local local) + { + il.LoadLocal(local); + il.LoadConstant(Word.Size); + il.Convert(); + il.Add(); + il.StoreLocal(local); + } + + /// + /// Moves the stack words down. + /// + public static void StackPop(this Emit il, Local local, int count = 1) + { + il.LoadLocal(local); + il.LoadConstant(Word.Size * count); + il.Convert(); + il.Subtract(); + il.StoreLocal(local); + } + + /// + /// Moves the stack words down. + /// + public static void StackPop(this Emit il, Local local, Local count) + { + il.LoadLocal(local); + il.LoadConstant(Word.Size); + il.LoadLocal(count); + il.Multiply(); + il.Convert(); + il.Subtract(); + il.StoreLocal(local); + } + + /// + /// Loads the previous EVM stack value on top of .NET stack. + /// + public static void StackLoadPrevious(this Emit il, Local local, int count = 1) + { + il.LoadLocal(local); + il.LoadConstant(Word.Size * count); + il.Convert(); + il.Subtract(); + } + + public static void LoadArray(this Emit il, ReadOnlySpan value) + { + + il.LoadConstant(value.Length); + il.NewArray(); + + for (int i = 0; i < value.Length; i++) + { + il.Duplicate(); + il.LoadConstant(i); + il.LoadConstant(value[i]); + il.StoreElement(); + } + + il.Call(typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), new[] { typeof(byte[]) })); + il.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); + + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs deleted file mode 100644 index 917414186e4..00000000000 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILGeneratorExt.cs +++ /dev/null @@ -1,231 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Evm.CodeAnalysis.IL; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace Nethermind.Evm.IL; - -/// -/// Extensions for . -/// -static class EmitExtensions -{ - public static void Load(this ILGenerator il, LocalBuilder local) - { - switch (local.LocalIndex) - { - case 0: - il.Emit(OpCodes.Ldloc_0); - break; - case 1: - il.Emit(OpCodes.Ldloc_1); - break; - case 2: - il.Emit(OpCodes.Ldloc_2); - break; - case 3: - il.Emit(OpCodes.Ldloc_3); - break; - default: - if (local.LocalIndex < 255) - { - il.Emit(OpCodes.Ldloc_S, (byte)local.LocalIndex); - } - else - { - il.Emit(OpCodes.Ldloc, local.LocalIndex); - } - break; - } - } - - public static void LoadAddress(this ILGenerator il, LocalBuilder local) - { - if (local.LocalIndex <= 255) - { - il.Emit(OpCodes.Ldloca_S, (byte)local.LocalIndex); - } - else - { - il.Emit(OpCodes.Ldloca, local.LocalIndex); - } - } - - public static void Load(this ILGenerator il, LocalBuilder local, FieldInfo wordField) - { - if (local.LocalType != typeof(Word*)) - { - throw new ArgumentException($"Only Word* can be used. This variable is of type {local.LocalType}"); - } - - if (wordField.DeclaringType != typeof(Word)) - { - throw new ArgumentException($"Only Word fields can be used. This field is declared for {wordField.DeclaringType}"); - } - - il.Load(local); - il.Emit(OpCodes.Ldfld, wordField); - } - - public static void CleanWord(this ILGenerator il, LocalBuilder local) - { - if (local.LocalType != typeof(Word*)) - { - throw new ArgumentException( - $"Only {nameof(Word)} pointers are supported. The passed local was type of {local.LocalType}."); - } - - il.Load(local); - il.Emit(OpCodes.Initobj, typeof(Word)); - } - - /// - /// Advances the stack one word up. - /// - public static void StackPush(this ILGenerator il, LocalBuilder local) - { - il.Load(local); - il.LoadValue(Word.Size); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Add); - il.Store(local); - } - - /// - /// Moves the stack words down. - /// - public static void StackPop(this ILGenerator il, LocalBuilder local, int count = 1) - { - il.Load(local); - il.LoadValue(Word.Size * count); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Sub); - il.Store(local); - } - - /// - /// Moves the stack words down. - /// - public static void StackPop(this ILGenerator il, LocalBuilder local, LocalBuilder count) - { - il.Load(local); - il.LoadValue(Word.Size); - il.Load(count); - il.Emit(OpCodes.Mul); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Sub); - il.Store(local); - } - - /// - /// Loads the previous EVM stack value on top of .NET stack. - /// - public static void StackLoadPrevious(this ILGenerator il, LocalBuilder local, int count = 1) - { - il.Load(local); - il.LoadValue(Word.Size * count); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Sub); - } - - public static void Store(this ILGenerator il, LocalBuilder local) - { - switch (local.LocalIndex) - { - case 0: - il.Emit(OpCodes.Stloc_0); - break; - case 1: - il.Emit(OpCodes.Stloc_1); - break; - case 2: - il.Emit(OpCodes.Stloc_2); - break; - case 3: - il.Emit(OpCodes.Stloc_3); - break; - default: - if (local.LocalIndex < 255) - { - il.Emit(OpCodes.Stloc_S, (byte)local.LocalIndex); - } - else - { - il.Emit(OpCodes.Stloc, local.LocalIndex); - } - break; - } - } - - public static void LoadArray(this ILGenerator il, ReadOnlySpan value) - { - // declare il data - il.Emit(OpCodes.Ldc_I4, value.Length); - il.Emit(OpCodes.Newarr, typeof(byte)); - - // fill the array - for (int i = 0; i < value.Length; i++) - { - il.Emit(OpCodes.Dup); - il.LoadValue(i); - il.LoadValue(value[i]); - il.Emit(OpCodes.Stelem_I1); - } - - Span arr = value.ToArray(); - - // invoke System.MemoryExtensions::AsSpan to get the span - il.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), new[] { typeof(byte[]) })); - // invoke [System.Runtime]System.Span`1::op_Implicit(valuetype [System.Runtime]System.Span`1 - il.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); - - } - - public static void LoadValue(this ILGenerator il, long value) - { - il.Emit(OpCodes.Ldc_I8, value); - } - - public static void LoadValue(this ILGenerator il, int value) - { - switch (value) - { - case 0: - il.Emit(OpCodes.Ldc_I4_0); - break; - case 1: - il.Emit(OpCodes.Ldc_I4_1); - break; - case 2: - il.Emit(OpCodes.Ldc_I4_2); - break; - case 3: - il.Emit(OpCodes.Ldc_I4_3); - break; - case 4: - il.Emit(OpCodes.Ldc_I4_4); - break; - case 5: - il.Emit(OpCodes.Ldc_I4_5); - break; - case 6: - il.Emit(OpCodes.Ldc_I4_6); - break; - case 7: - il.Emit(OpCodes.Ldc_I4_7); - break; - case 8: - il.Emit(OpCodes.Ldc_I4_8); - break; - default: - if (value <= 255) - il.Emit(OpCodes.Ldc_I4_S, (byte)value); - else - il.Emit(OpCodes.Ldc_I4, value); - break; - } - } -} From 3352ad12c2f9e265616a27315427853bb3ee063a Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 25 Mar 2024 19:50:28 +0000 Subject: [PATCH 017/146] ported jumptable from old IL-EVM branch --- .../CodeAnalysis/IL/ILCompiler.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 4fa69f36c32..d79263d9a45 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -6,6 +6,7 @@ using Nethermind.Int256; using Sigil; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -276,6 +277,71 @@ public static Func Build(CodeInfo codeinfo, int segmentI } } + method.LoadConstant((int)EvmExceptionType.None); + method.Branch(ret); + + method.MarkLabel(jumpTable); + method.StackPop(current); + + // emit the jump table + // if (jumpDest > uint.MaxValue) + // ULong3 | Ulong2 | Ulong1 | Uint1 | Ushort1 + method.LoadLocal(current); + method.LoadConstant(uint.MaxValue); + method.Call(Word.GetUInt256); + method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + + method.BranchIfTrue(invalidAddress); + + const int jumpFanOutLog = 7; // 128 + const int bitMask = (1 << jumpFanOutLog) - 1; + Label[] jumps = new Label[jumpFanOutLog]; + for (int i = 0; i < jumpFanOutLog; i++) + { + jumps[i] = method.DefineLabel(); + } + + method.Load(current, Word.Int0Field); + method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); + method.StoreLocal(jmpDestination); + + method.LoadLocal(jmpDestination); + method.LoadConstant(bitMask); + method.And(); + + method.Switch(jumps); + int[] destinations = jumpDestinations.Keys.ToArray(); + + for (int i = 0; i < jumpFanOutLog; i++) + { + method.MarkLabel(jumps[i]); + + // for each destination matching the bit mask emit check for the equality + foreach (int dest in destinations.Where(dest => (dest & bitMask) == i)) + { + method.LoadLocal(jmpDestination); + method.LoadConstant(dest); + method.BranchIfEqual(jumpDestinations[dest]); + } + + // each bucket ends with a jump to invalid access to do not fall through to another one + method.Branch(invalidAddress); + } + + // out of gas + method.MarkLabel(outOfGas); + method.LoadConstant((int)EvmExceptionType.OutOfGas); + method.Branch(ret); + + // invalid address return + method.MarkLabel(invalidAddress); + method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); + method.Branch(ret); + + // return + method.MarkLabel(ret); + method.Return(); + Func del = method.CreateDelegate(); return del; } From 1ea0d342c9116c6fcd62f2f8280d7a78c871144c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 26 Mar 2024 01:08:22 +0000 Subject: [PATCH 018/146] minor refactors and code changes --- .../CodeAnalysis/IL/ILCompiler.cs | 44 ++++++------- .../CodeAnalysis/IL/IlAnalyzer.cs | 66 ++++++++++--------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 41 ++++++++++-- src/Nethermind/Nethermind.Evm/Instruction.cs | 3 +- 4 files changed, 92 insertions(+), 62 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index d79263d9a45..2400b68bdad 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -18,27 +18,21 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func Build(CodeInfo codeinfo, int segmentId) + public static Func CompileSegment(string segmentName, OpcodeInfo[] code) { - Dictionary gasCost = BuildCostLookup(codeinfo.IlInfo.Segments[segmentId]); + Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); - // TODO: stack invariants, gasCost application + using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); + using Local address = method.DeclareLocal(typeof(Address)); + using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); + using Local uint256A = method.DeclareLocal(typeof(UInt256)); + using Local uint256B = method.DeclareLocal(typeof(UInt256)); + using Local uint256C = method.DeclareLocal(typeof(UInt256)); + using Local uint256R = method.DeclareLocal(typeof(UInt256)); + using Local gasAvailable = method.DeclareLocal(typeof(long)); - string name = "ILVM_" + Guid.NewGuid(); - - Emit> method = Emit>.NewDynamicMethod($"{codeinfo.GetHashCode()}_{segmentId}", doVerify: true, strictBranchVerification: true); - - Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); - Local address = method.DeclareLocal(typeof(Address)); - Local consumeJumpCondition = method.DeclareLocal(typeof(int)); - Local uint256A = method.DeclareLocal(typeof(UInt256)); - Local uint256B = method.DeclareLocal(typeof(UInt256)); - Local uint256C = method.DeclareLocal(typeof(UInt256)); - Local uint256R = method.DeclareLocal(typeof(UInt256)); - Local gasAvailable = method.DeclareLocal(typeof(long)); - - Local stack = method.DeclareLocal(typeof(Word*)); - Local current = method.DeclareLocal(typeof(Word*)); + using Local stack = method.DeclareLocal(typeof(Word*)); + using Local current = method.DeclareLocal(typeof(Word*)); const int wordToAlignTo = 32; @@ -64,8 +58,8 @@ public static Func Build(CodeInfo codeinfo, int segmentI Dictionary jumpDestinations = new(); - OpcodeInfo[] code = codeinfo.IlInfo.Segments[segmentId]; - for(int pc = 0; pc < code.Length; pc++) + Dictionary gasCost = BuildCostLookup(code); + for (int pc = 0; pc < code.Length; pc++) { OpcodeInfo op = code[pc]; @@ -101,7 +95,7 @@ public static Func Build(CodeInfo codeinfo, int segmentI method.Branch(jumpTable); method.MarkLabel(noJump); - method.StackPop(current, 2); + method.StackPop(current, 2); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -157,7 +151,8 @@ public static Func Build(CodeInfo codeinfo, int segmentI case Instruction.MOD: EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, - (il, postInstructionLabel) => { + (il, postInstructionLabel) => + { Label label = il.DefineLabel(); il.Duplicate(); @@ -165,7 +160,7 @@ public static Func Build(CodeInfo codeinfo, int segmentI il.CompareEqual(); il.BranchIfFalse(label); - + il.LoadConstant(0); il.Branch(postInstructionLabel); @@ -175,7 +170,8 @@ public static Func Build(CodeInfo codeinfo, int segmentI case Instruction.DIV: EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, - (il, postInstructionLabel) => { + (il, postInstructionLabel) => + { Label label = il.DefineLabel(); il.Duplicate(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index b58354bc5c1..fd83025887d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -71,74 +71,78 @@ OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) { OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; int j = 0; - for (int i = 0; i < machineCode.Length; i++, j++) + for (ushort i = 0; i < machineCode.Length; i++, j++) { Instruction opcode = (Instruction)machineCode[i]; byte[] args = null; + ushort pc = i; if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) { - int immediatesCount = opcode - Instruction.PUSH0; - args = machineCode.Slice(i+1, immediatesCount).ToArray(); + ushort immediatesCount = opcode - Instruction.PUSH0; + args = machineCode.Slice(i + 1, immediatesCount).ToArray(); i += immediatesCount; } - opcodes[j] = new OpcodeInfo(opcode, args.AsMemory(), OpcodeMetadata.Operations.GetValueOrDefault(opcode)); + opcodes[j] = new OpcodeInfo(pc, opcode, args.AsMemory(), OpcodeMetadata.Operations.GetValueOrDefault(opcode)); } return opcodes[..j]; } - OpcodeInfo[][] SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) { - List opcodeInfos = []; + Dictionary> opcodeInfos = []; + List segment = []; foreach (var opcode in codeData) { - if(opcode.Operation.IsStateful()) + if (opcode.Operation.IsStateful()) { - if(segment.Count > 0) + if (segment.Count > 0) { - opcodeInfos.Add([.. segment]); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray())); segment.Clear(); } - } else + } + else { segment.Add(opcode); - } + } } - if(segment.Count > 0) + if (segment.Count > 0) { - opcodeInfos.Add([.. segment]); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray())); } - return [.. opcodeInfos]; + return opcodeInfos.ToFrozenDictionary(); } - - OpcodeInfo[] strippedBytecode = StripByteCode(machineCode.Span); - Dictionary patternFound = new Dictionary(); - - foreach (var (pattern, mapping) in Patterns) + FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode, out OpcodeInfo[] strippedBytecode) { - for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) + strippedBytecode = StripByteCode(machineCode.Span); + var patternFound = new Dictionary(); + foreach (var (pattern, mapping) in Patterns) { - bool found = true; - for (int j = 0; j < pattern.Length && found; j++) + for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) { - found = ((byte)strippedBytecode[i + j].Operation == pattern[j]); - } + bool found = true; + for (int j = 0; j < pattern.Length && found; j++) + { + found = ((byte)strippedBytecode[i + j].Operation == pattern[j]); + } - if (found) - { - patternFound.Add((ushort)i, mapping); - i += pattern.Length - 1; + if (found) + { + patternFound.Add((ushort)i, mapping); + i += pattern.Length - 1; + } } } + return patternFound.ToFrozenDictionary(); } - - // TODO: implement actual analysis. - return new IlInfo(patternFound.ToFrozenDictionary(), SegmentCode(strippedBytecode)); + return new IlInfo(CheckPatterns(machineCode, out OpcodeInfo[] strippedBytecode), SegmentCode(strippedBytecode)); } /// /// How many execution a should perform before trying to get its opcodes optimized. /// public const int IlAnalyzerThreshold = 23; + public const int IlCompilerThreshold = 57; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 7df4ab7f6be..0ce108b3db7 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Frozen; +using System.Collections.Generic; using System.Runtime.CompilerServices; using Nethermind.Core.Specs; @@ -10,12 +11,22 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal class IlInfo { + public enum ILMode + { + PatternMatching = 0, + SubsegmentsCompiling = 1 + } /// /// Represents an information about IL-EVM being not able to optimize the given . /// public static readonly IlInfo NoIlEVM = new(); + /// + /// Represents what mode of IL-EVM is used. 0 is the default. [0 = Pattern matching, 1 = subsegments compiling] + /// + public static readonly ILMode Mode = ILMode.PatternMatching; + /// /// No overrides. /// @@ -24,7 +35,7 @@ private IlInfo() Chunks = FrozenDictionary.Empty; } - public IlInfo(FrozenDictionary mappedOpcodes, OpcodeInfo[][] segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) { Chunks = mappedOpcodes; Segments = segments; @@ -32,7 +43,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, OpcodeIn // assumes small number of ILed public FrozenDictionary Chunks { get; init; } - public OpcodeInfo[][] Segments { get; init; } + public FrozenDictionary> Segments { get; init; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -40,12 +51,30 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec if (programCounter > ushort.MaxValue) return false; - if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk) == false) + switch(Mode) { - return false; + case ILMode.PatternMatching: + { + if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk) == false) + { + return false; + } + chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + break; + } + case ILMode.SubsegmentsCompiling: + { + if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) + { + return false; + } + var exception = method.Invoke(gasAvailable); + // ToDo : Tidy up the exception handling + // ToDo : Add context switch, migrate stack from IL to EVM and map memory + // ToDo : Add context switch, prepare IL stack before executing the segment and map memory + break; + } } - - chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); return true; } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index c9bb471cf28..79aa1c27528 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -217,10 +217,11 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta [Instruction.SWAP1 ] = new(GasCostOf.VeryLow, 0, 2, 2) }.ToFrozenDictionary(); } - public struct OpcodeInfo(Instruction instruction, ReadOnlyMemory? arguments, OpcodeMetadata? metadata) + public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments, OpcodeMetadata? metadata) { public OpcodeMetadata? Metadata { get; } = metadata; public Instruction Operation { get; set; } = instruction; + public ushort ProgramCounter { get; set; } = pc; public ReadOnlyMemory? Arguments { get; set; } = arguments; } From 8ef208f5fe5389ab927d367191423f3727bd83a9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 27 Mar 2024 20:04:21 +0000 Subject: [PATCH 019/146] add test to keep track of emitable opcodes coverage --- .../CodeAnalysis/IlEvmTests.cs | 28 +++++++++++++++++++ .../CodeAnalysis/IL/ILCompiler.cs | 2 ++ .../CodeAnalysis/IL/IlAnalyzer.cs | 1 + 3 files changed, 31 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 3a7695c17ba..2309d4e21f4 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -114,5 +115,32 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() Assert.Greater(pattern.CallCount, 0); } + + [Test] + public void Pure_Opcode_Emition_Coveraga() + { + Instruction[] instructions = + System.Enum.GetValues() + .Where(opcode => !opcode.IsStateful()) + .ToArray(); + + + List notYetImplemented = []; + foreach (var instruction in instructions) + { + string name = $"ILEVM_TEST_{instruction}"; + OpcodeInfo opcode = new OpcodeInfo(0, instruction, null, null); + try + { + ILCompiler.CompileSegment(name, [opcode]); + } catch (NotSupportedException) + { + notYetImplemented.Add(instruction); + } + } + + Assert.That(notYetImplemented.Count, Is.EqualTo(0)); + } + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2400b68bdad..b8f242b2c21 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -270,6 +270,8 @@ public static Func CompileSegment(string segmentName, Op method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); break; + default: + throw new NotSupportedException(); } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index fd83025887d..e5699f3eed3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -137,6 +137,7 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma } return patternFound.ToFrozenDictionary(); } + return new IlInfo(CheckPatterns(machineCode, out OpcodeInfo[] strippedBytecode), SegmentCode(strippedBytecode)); } From 99b3e19793e3b251cee8904d1bf00124feadfdc1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 17 Apr 2024 06:05:06 +0100 Subject: [PATCH 020/146] context swap (wip) --- .../CodeAnalysis/IL/ILCompiler.cs | 142 +++++++++++------- .../CodeAnalysis/IL/ILEvmState.cs | 25 +++ .../CodeAnalysis/IL/ILExtensions.cs | 42 ++++++ .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 10 +- 4 files changed, 160 insertions(+), 59 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b8f242b2c21..1c2d1ce373b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -14,13 +14,14 @@ using System.Text; using System.Threading.Tasks; using Label = Sigil.Label; +using ProjectedEvmState = (Nethermind.Evm.EvmExceptionType, Nethermind.Evm.CodeAnalysis.IL.ILEvmState); namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func CompileSegment(string segmentName, OpcodeInfo[] code) + public static Func CompileSegment(string segmentName, OpcodeInfo[] code) { - Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); using Local address = method.DeclareLocal(typeof(Address)); @@ -29,10 +30,13 @@ public static Func CompileSegment(string segmentName, Op using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); + using Local returnState = method.DeclareLocal(typeof(EvmState)); using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local stack = method.DeclareLocal(typeof(Word*)); - using Local current = method.DeclareLocal(typeof(Word*)); + using Local currentSP = method.DeclareLocal(typeof(Word*)); + + using Local memory = method.DeclareLocal(typeof(byte*)); const int wordToAlignTo = 32; @@ -44,7 +48,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(stack); method.LoadLocal(stack); - method.StoreLocal(current); // copy to the current + method.StoreLocal(currentSP); // copy to the currentSP // gas method.LoadArgument(0); @@ -58,6 +62,8 @@ public static Func CompileSegment(string segmentName, Op Dictionary jumpDestinations = new(); + // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method + Dictionary gasCost = BuildCostLookup(code); for (int pc = 0; pc < code.Length; pc++) { @@ -85,7 +91,7 @@ public static Func CompileSegment(string segmentName, Op break; case Instruction.JUMPI: Label noJump = method.DefineLabel(); - method.StackLoadPrevious(current, 2); + method.StackLoadPrevious(currentSP, 2); method.Call(Word.GetIsZero, null); method.BranchIfTrue(noJump); @@ -95,7 +101,7 @@ public static Func CompileSegment(string segmentName, Op method.Branch(jumpTable); method.MarkLabel(noJump); - method.StackPop(current, 2); + method.StackPop(currentSP, 2); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -135,22 +141,22 @@ public static Func CompileSegment(string segmentName, Op method.LoadConstant(0); method.NewObject(typeof(UInt256), typeof(Span), typeof(bool)); - method.StackPush(current); + method.StackPush(currentSP); break; case Instruction.ADD: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.SUB: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.MUL: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.MOD: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel) => { Label label = il.DefineLabel(); @@ -169,7 +175,7 @@ public static Func CompileSegment(string segmentName, Op break; case Instruction.DIV: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel) => { Label label = il.DefineLabel(); @@ -188,31 +194,31 @@ public static Func CompileSegment(string segmentName, Op break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); break; case Instruction.LT: - EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.GT: - EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.EQ: - EmitComparaisonUInt256Method(method, uint256R, current, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.ISZERO: - method.StackLoadPrevious(current, 1); + method.StackLoadPrevious(currentSP, 1); method.Call(Word.GetIsZero); - method.StackPush(current); - method.StackPop(current, 1); + method.StackPush(currentSP); + method.StackPop(currentSP, 1); break; case Instruction.CODESIZE: method.LoadConstant(code.Length); method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); - method.StackPush(current); + method.StackPush(currentSP); break; case Instruction.POP: method.Pop(); - method.StackPop(current); + method.StackPop(currentSP); break; case Instruction.DUP1: case Instruction.DUP2: @@ -231,11 +237,11 @@ public static Func CompileSegment(string segmentName, Op case Instruction.DUP15: case Instruction.DUP16: count = (int)op.Operation - (int)Instruction.DUP1 + 1; - method.LoadLocal(current); - method.StackLoadPrevious(current, count); + method.LoadLocal(currentSP); + method.StackLoadPrevious(currentSP, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackPush(current); + method.StackPush(currentSP); break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -256,16 +262,16 @@ public static Func CompileSegment(string segmentName, Op count = (int)op.Operation - (int)Instruction.SWAP1 + 1; method.LoadLocalAddress(uint256R); - method.StackLoadPrevious(current, 1); + method.StackLoadPrevious(currentSP, 1); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackLoadPrevious(current, 1); - method.StackLoadPrevious(current, count); + method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(currentSP, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackLoadPrevious(current, count); + method.StackLoadPrevious(currentSP, count); method.LoadLocalAddress(uint256R); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); @@ -275,16 +281,42 @@ public static Func CompileSegment(string segmentName, Op } } + // prepare ILEvmState + method.MarkLabel(ret); + + method.LoadLocal(currentSP); + method.LoadLocal(stack); + method.Subtract(); + method.LoadConstant(Word.Size); + method.Divide(); + method.StoreLocal(uint256R); + + method.LoadLocal(uint256R); + method.NewArray(); + // pop the stack to the array + method.ForBranch(uint256R, (il, i) => + { + il.Duplicate(); + + il.LoadLocal(i); + il.StackLoadPrevious(currentSP); + il.LoadObject(typeof(Word)); + il.StoreElement(); + + il.StackPop(currentSP); + }); + + method.LoadConstant((int)EvmExceptionType.None); method.Branch(ret); method.MarkLabel(jumpTable); - method.StackPop(current); + method.StackPop(currentSP); // emit the jump table // if (jumpDest > uint.MaxValue) // ULong3 | Ulong2 | Ulong1 | Uint1 | Ushort1 - method.LoadLocal(current); + method.LoadLocal(currentSP); method.LoadConstant(uint.MaxValue); method.Call(Word.GetUInt256); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); @@ -299,7 +331,7 @@ public static Func CompileSegment(string segmentName, Op jumps[i] = method.DefineLabel(); } - method.Load(current, Word.Int0Field); + method.Load(currentSP, Word.Int0Field); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); @@ -336,19 +368,21 @@ public static Func CompileSegment(string segmentName, Op method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); method.Branch(ret); + // pop items from + + // return - method.MarkLabel(ret); method.Return(); - Func del = method.CreateDelegate(); + Func del = method.CreateDelegate(); return del; } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local current, MethodInfo operatin) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operatin) { - il.StackLoadPrevious(current, 1); + il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); - il.StackLoadPrevious(current, 2); + il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); // invoke op < on the uint256 il.Call(operatin, null); @@ -360,41 +394,41 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Convert(); il.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); il.StoreLocal(uint256R); - il.StackPop(current, 2); + il.StackPop(currentSP, 2); - il.LoadLocal(current); + il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(current); + il.StackPush(currentSP); } - private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local current, MethodInfo operation, Action, Label> customHandling = null) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label> customHandling = null) { Label label = il.DefineLabel(); - il.StackLoadPrevious(current, 1); + il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); - il.StackLoadPrevious(current, 2); + il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); customHandling.Invoke(il, label); il.Call(operation); il.StoreLocal(uint256R); - il.StackPop(current, 2); + il.StackPop(currentSP, 2); il.MarkLabel(label); - il.LoadLocal(current); + il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(current); + il.StackPush(currentSP); } private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); int costStart = 0; - long costCurrent = 0; + long costcurrentSP = 0; for (int pc = 0; pc < code.Length; pc++) { @@ -402,28 +436,28 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co switch (op.Operation) { case Instruction.JUMPDEST: - costs[costStart] = costCurrent; // remember the current chain of opcodes + costs[costStart] = costcurrentSP; // remember the currentSP chain of opcodes costStart = pc; - costCurrent = op.Metadata?.GasCost ?? 0; + costcurrentSP = op.Metadata?.GasCost ?? 0; break; case Instruction.JUMPI: case Instruction.JUMP: - costCurrent += op.Metadata?.GasCost ?? 0; - costs[costStart] = costCurrent; // remember the current chain of opcodes + costcurrentSP += op.Metadata?.GasCost ?? 0; + costs[costStart] = costcurrentSP; // remember the currentSP chain of opcodes costStart = pc + 1; // start with the next again - costCurrent = 0; + costcurrentSP = 0; break; default: - costCurrent += op.Metadata?.GasCost ?? 0; + costcurrentSP += op.Metadata?.GasCost ?? 0; break; } pc += op.Metadata?.AdditionalBytes ?? 0; } - if (costCurrent > 0) + if (costcurrentSP > 0) { - costs[costStart] = costCurrent; + costs[costStart] = costcurrentSP; } return costs; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs new file mode 100644 index 00000000000..2d17d5c26c4 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Int256; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.CodeAnalysis.IL; +internal class ILEvmState +{ + public byte[] bytes; + public UInt256[] Stack; + public EvmException EvmException; + public int ProgramCounter; + public int GasAvailable; + + public static FieldInfo GetFieldInfo(string name) + { + return typeof(ILEvmState).GetField(name); + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 53190b41fe1..820008ae12c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -80,6 +80,48 @@ public static void StackPop(this Emit il, Local local, Local count) il.StoreLocal(local); } + public static void WhileBranch(this Emit il, Local local, Action> action) + { + var start = il.DefineLabel(); + var end = il.DefineLabel(); + + il.MarkLabel(start); + il.LoadLocal(local); + il.BranchIfFalse(end); + + action(il); + + il.Branch(start); + il.MarkLabel(end); + } + + public static void ForBranch(this Emit il, Local count, Action, Local> action) + { + var start = il.DefineLabel(); + var end = il.DefineLabel(); + + // declare indexer + var i = il.DeclareLocal(); + il.LoadLocal(i); + il.LoadConstant(0); + il.StoreLocal(i); + + il.MarkLabel(start); + il.LoadLocal(i); + il.LoadLocal(count); + il.BranchIfGreater(end); + + action(il, i); + + il.LoadLocal(i); + il.LoadConstant(1); + il.Add(); + il.StoreLocal(i); + + il.MarkLabel(start); + il.Branch(start); + } + /// /// Loads the previous EVM stack value on top of .NET stack. /// diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 0ce108b3db7..d451889e8a5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -5,7 +5,7 @@ using Nethermind.Core.Specs; namespace Nethermind.Evm.CodeAnalysis.IL; - +using ProjectedEvmState = (EvmExceptionType, Nethermind.Evm.CodeAnalysis.IL.ILEvmState); /// /// Represents the IL-EVM information about the contract. /// @@ -35,7 +35,7 @@ private IlInfo() Chunks = FrozenDictionary.Empty; } - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) { Chunks = mappedOpcodes; Segments = segments; @@ -43,7 +43,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi // assumes small number of ILed public FrozenDictionary Chunks { get; init; } - public FrozenDictionary> Segments { get; init; } + public FrozenDictionary> Segments { get; init; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -64,11 +64,11 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec } case ILMode.SubsegmentsCompiling: { - if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) + if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) { return false; } - var exception = method.Invoke(gasAvailable); + var (exception, evmState) = method.Invoke(gasAvailable); // ToDo : Tidy up the exception handling // ToDo : Add context switch, migrate stack from IL to EVM and map memory // ToDo : Add context switch, prepare IL stack before executing the segment and map memory From 11768f60c3a2ec188cb86f71e68ce4c50f96b463 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 1 May 2024 16:29:52 +0100 Subject: [PATCH 021/146] minor changes --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 1c2d1ce373b..b1d63bf762d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -137,10 +137,14 @@ public static Func CompileSegment(string segmentName, O case Instruction.PUSH32: int count = (int)op.Operation - (int)Instruction.PUSH0; ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); + method.LoadLocal(currentSP); + method.InitializeObject(typeof(Word)); + method.LoadLocal(currentSP); + method.LoadArray(bytes.ToArray()); method.LoadConstant(0); method.NewObject(typeof(UInt256), typeof(Span), typeof(bool)); - + method.Call(Word.SetUInt256); method.StackPush(currentSP); break; case Instruction.ADD: @@ -207,17 +211,17 @@ public static Func CompileSegment(string segmentName, O break; case Instruction.ISZERO: method.StackLoadPrevious(currentSP, 1); + method.StackPop(currentSP, 1); method.Call(Word.GetIsZero); method.StackPush(currentSP); - method.StackPop(currentSP, 1); break; case Instruction.CODESIZE: method.LoadConstant(code.Length); method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + method.Call(Word.SetUInt256); method.StackPush(currentSP); break; case Instruction.POP: - method.Pop(); method.StackPop(currentSP); break; case Instruction.DUP1: From 5e5c0bb2eff0a823e63c0f7f4c4d9a3947ac7a81 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 6 May 2024 17:20:34 +0100 Subject: [PATCH 022/146] fix build issue : Signature mismatch in ILAnalyzer --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index e5699f3eed3..a9f7bd5c191 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -87,9 +87,9 @@ OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) return opcodes[..j]; } - FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) { - Dictionary> opcodeInfos = []; + Dictionary> opcodeInfos = []; List segment = []; foreach (var opcode in codeData) From 2313608c821d0c962f58929974db43a7e5edb488 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 7 May 2024 00:27:23 +0100 Subject: [PATCH 023/146] refactored code : to analyze in stages instead of at once --- .../CodeAnalysis/IlEvmTests.cs | 6 ++-- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 24 ++++--------- .../CodeAnalysis/IL/IlAnalyzer.cs | 34 +++++++++++-------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 27 +++++++++++---- .../Nethermind.Evm/VirtualMachine.cs | 4 +-- 5 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 2309d4e21f4..41970eb3074 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -66,7 +66,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -74,7 +74,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - P01P01ADD pattern = (P01P01ADD)IlAnalyzer.GetPatternHandler(P01P01ADD.Pattern); + P01P01ADD pattern = IlAnalyzer.GetPatternHandler(P01P01ADD.Pattern); byte[] bytecode = Prepare.EvmCode @@ -109,7 +109,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var address = receipts.TxReceipts[0].ContractAddress; */ - for(int i = 0; i < IlAnalyzer.IlAnalyzerThreshold * 2; i++) { + for(int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) { ExecuteBlock(new NullBlockTracer(), bytecode); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 8a5e20bddcb..88a1a57a3f4 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -16,20 +16,19 @@ public class CodeInfo : IThreadPoolWorkItem // IL-EVM - private volatile IlInfo? _il; private int _callCount; - public void NoticeExecution() + public async void NoticeExecution() { // IL-EVM info already created - if (_il != null) + if (_callCount > IlAnalyzer.IlCompilerThreshold) return; // use Interlocked just in case of concurrent execution to run it only once - if (Interlocked.Increment(ref _callCount) == IlAnalyzer.IlAnalyzerThreshold) - { - IlAnalyzer.StartAnalysis(this); - } + IlInfo.ILMode mode = Interlocked.Increment(ref _callCount) == IlAnalyzer.CompoundOpThreshold + ? IlInfo.ILMode.PatternMatching + : _callCount == IlAnalyzer.IlCompilerThreshold ? IlInfo.ILMode.SubsegmentsCompiling : IlInfo.ILMode.NoIlvm; + await IlAnalyzer.StartAnalysis(this, mode); } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); @@ -52,14 +51,7 @@ public CodeInfo(ReadOnlyMemory code) /// /// Gets information whether this code info has IL-EVM optimizations ready. /// - internal IlInfo? IlInfo - { - get - { - IlInfo? il = _il; - return il != null && !ReferenceEquals(il, IlInfo.NoIlEVM) ? il : null; - } - } + internal IlInfo? IlInfo { get; set; } = IlInfo.Empty; public CodeInfo(IPrecompile precompile) { @@ -77,7 +69,5 @@ void IThreadPoolWorkItem.Execute() { _analyzer.Execute(); } - - internal void SetIlInfo(IlInfo info) => _il = info; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index a9f7bd5c191..fb0f6586b84 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -43,30 +43,28 @@ public static Dictionary AddPattern(byte[] pattern, In } return Patterns; } - public static InstructionChunk GetPatternHandler(byte[] pattern) + public static T GetPatternHandler (byte[] pattern) where T : InstructionChunk { - return Patterns[pattern]; + return (T) Patterns[pattern]; } /// /// Starts the analyzing in a background task and outputs the value in the . - /// + /// thou /// The destination output. - public static Task StartAnalysis(CodeInfo codeInfo) + public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) { - return Task.Run(() => - { - IlInfo info = Analysis(codeInfo.MachineCode); - codeInfo.SetIlInfo(info); - }); + return Task.Run(() => Analysis(codeInfo, mode)); } /// /// For now, return null always to default to EVM. /// - private static IlInfo Analysis(ReadOnlyMemory machineCode) + private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) { + ReadOnlyMemory machineCode = codeInfo.MachineCode; + OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) { OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; @@ -114,9 +112,9 @@ OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) return opcodeInfos.ToFrozenDictionary(); } - FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode, out OpcodeInfo[] strippedBytecode) + FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode) { - strippedBytecode = StripByteCode(machineCode.Span); + var strippedBytecode = StripByteCode(machineCode.Span); var patternFound = new Dictionary(); foreach (var (pattern, mapping) in Patterns) { @@ -138,12 +136,20 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma return patternFound.ToFrozenDictionary(); } - return new IlInfo(CheckPatterns(machineCode, out OpcodeInfo[] strippedBytecode), SegmentCode(strippedBytecode)); + switch(mode) + { + case IlInfo.ILMode.PatternMatching: + codeInfo.IlInfo.WithChunks(CheckPatterns(machineCode)); + break; + case IlInfo.ILMode.SubsegmentsCompiling: + codeInfo.IlInfo.WithSegments(SegmentCode(StripByteCode(machineCode.Span))); + break; + } } /// /// How many execution a should perform before trying to get its opcodes optimized. /// - public const int IlAnalyzerThreshold = 23; + public const int CompoundOpThreshold = 23; public const int IlCompilerThreshold = 57; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index d451889e8a5..735084436c3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -2,6 +2,7 @@ using System.Collections.Frozen; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Microsoft.IdentityModel.Tokens; using Nethermind.Core.Specs; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -13,26 +14,40 @@ internal class IlInfo { public enum ILMode { - PatternMatching = 0, - SubsegmentsCompiling = 1 + NoIlvm = 0, + PatternMatching = 1, + SubsegmentsCompiling = 2 } /// /// Represents an information about IL-EVM being not able to optimize the given . /// - public static readonly IlInfo NoIlEVM = new(); + public static IlInfo Empty => new(); /// /// Represents what mode of IL-EVM is used. 0 is the default. [0 = Pattern matching, 1 = subsegments compiling] /// public static readonly ILMode Mode = ILMode.PatternMatching; - + public bool IsEmpty => Chunks.IsNullOrEmpty() && Segments.IsNullOrEmpty(); /// /// No overrides. /// private IlInfo() { Chunks = FrozenDictionary.Empty; + Segments = FrozenDictionary>.Empty; + } + + public IlInfo WithChunks(FrozenDictionary chunks) + { + Chunks = chunks; + return this; + } + + public IlInfo WithSegments(FrozenDictionary> segments) + { + Segments = segments; + return this; } public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) @@ -42,8 +57,8 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi } // assumes small number of ILed - public FrozenDictionary Chunks { get; init; } - public FrozenDictionary> Segments { get; init; } + public FrozenDictionary Chunks { get; set; } + public FrozenDictionary> Segments { get; set; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index e9a78e906e2..bb7190d0428 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -829,9 +829,7 @@ private CallResult ExecuteCode Date: Tue, 7 May 2024 23:04:56 +0100 Subject: [PATCH 024/146] refactors and fixes [WIP] --- .../CodeAnalysis/IlEvmTests.cs | 21 +++- .../CodeAnalysis/IL/ILCompiler.cs | 42 +++++-- .../CodeAnalysis/IL/ILEvmState.cs | 7 ++ .../CodeAnalysis/IL/IlAnalyzer.cs | 40 ++++--- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 108 +++++++++--------- src/Nethermind/Nethermind.Evm/Instruction.cs | 104 +++++++++++++++-- 6 files changed, 228 insertions(+), 94 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 41970eb3074..fa71ac9ee4e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -129,11 +129,11 @@ public void Pure_Opcode_Emition_Coveraga() foreach (var instruction in instructions) { string name = $"ILEVM_TEST_{instruction}"; - OpcodeInfo opcode = new OpcodeInfo(0, instruction, null, null); + OpcodeInfo opcode = new OpcodeInfo(0, instruction, null); try { ILCompiler.CompileSegment(name, [opcode]); - } catch (NotSupportedException) + } catch (Exception) { notYetImplemented.Add(instruction); } @@ -142,5 +142,22 @@ public void Pure_Opcode_Emition_Coveraga() Assert.That(notYetImplemented.Count, Is.EqualTo(0)); } + + [Test] + public void Ensure_Evm_ILvm_Compatibility() + { + byte[] bytecode = + Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .ADD() + .Done; + + + var function = ILCompiler.CompileSegment("ILEVM_TEST", IlAnalyzer.StripByteCode(bytecode)); + var result = function(100); + Assert.IsNotNull(result); + } + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b1d63bf762d..958e6068865 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -30,8 +30,9 @@ public static Func CompileSegment(string segmentName, O using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); - using Local returnState = method.DeclareLocal(typeof(EvmState)); - using Local gasAvailable = method.DeclareLocal(typeof(long)); + using Local returnState = method.DeclareLocal(typeof(ILEvmState)); + using Local gasAvailable = method.DeclareLocal(typeof(int)); + using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); @@ -52,6 +53,7 @@ public static Func CompileSegment(string segmentName, O // gas method.LoadArgument(0); + method.Convert(); method.StoreLocal(gasAvailable); Label outOfGas = method.DefineLabel("OutOfGas"); @@ -64,7 +66,7 @@ public static Func CompileSegment(string segmentName, O // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method - Dictionary gasCost = BuildCostLookup(code); + Dictionary gasCost = BuildCostLookup(code); for (int pc = 0; pc < code.Length; pc++) { OpcodeInfo op = code[pc]; @@ -77,8 +79,10 @@ public static Func CompileSegment(string segmentName, O method.Subtract(); method.Duplicate(); method.LoadConstant(0); - method.BranchIfLess(outOfGas); method.StoreLocal(gasAvailable); + method.Convert(); + method.BranchIfLess(outOfGas); + switch (op.Operation) { @@ -286,7 +290,6 @@ public static Func CompileSegment(string segmentName, O } // prepare ILEvmState - method.MarkLabel(ret); method.LoadLocal(currentSP); method.LoadLocal(stack); @@ -295,9 +298,10 @@ public static Func CompileSegment(string segmentName, O method.Divide(); method.StoreLocal(uint256R); + // set stack + method.LoadLocal(returnState); method.LoadLocal(uint256R); method.NewArray(); - // pop the stack to the array method.ForBranch(uint256R, (il, i) => { il.Duplicate(); @@ -309,9 +313,24 @@ public static Func CompileSegment(string segmentName, O il.StackPop(currentSP); }); + method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.Stack))); + + // set gas available + method.LoadLocal(returnState); + method.LoadLocal(gasAvailable); + method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.GasAvailable))); + // set program counter + method.LoadLocal(returnState); + method.LoadConstant(0); + method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.ProgramCounter))); + + method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.None); + method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.EvmException))); + + method.Branch(ret); method.MarkLabel(jumpTable); @@ -372,10 +391,9 @@ public static Func CompileSegment(string segmentName, O method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); method.Branch(ret); - // pop items from - // return + method.MarkLabel(ret); method.Return(); Func del = method.CreateDelegate(); @@ -415,7 +433,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); - customHandling.Invoke(il, label); + customHandling?.Invoke(il, label); il.Call(operation); il.StoreLocal(uint256R); @@ -428,11 +446,11 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.StackPush(currentSP); } - private static Dictionary BuildCostLookup(ReadOnlySpan code) + private static Dictionary BuildCostLookup(ReadOnlySpan code) { - Dictionary costs = new(); + Dictionary costs = new(); int costStart = 0; - long costcurrentSP = 0; + int costcurrentSP = 0; for (int pc = 0; pc < code.Length; pc++) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 2d17d5c26c4..75ae93e2950 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -14,10 +14,17 @@ internal class ILEvmState { public byte[] bytes; public UInt256[] Stack; + + // in case of exceptions public EvmException EvmException; + + // in case of jumps crossing section boundaries public int ProgramCounter; public int GasAvailable; + // in case STOP is executed + public bool StopExecution; + public static FieldInfo GetFieldInfo(string name) { return typeof(ILEvmState).GetField(name); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index fb0f6586b84..ea17e6a9d8a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -58,6 +58,26 @@ public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) return Task.Run(() => Analysis(codeInfo, mode)); } + public static OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) + { + OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; + int j = 0; + for (ushort i = 0; i < machineCode.Length; i++, j++) + { + Instruction opcode = (Instruction)machineCode[i]; + byte[] args = null; + ushort pc = i; + if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) + { + ushort immediatesCount = opcode - Instruction.PUSH0; + args = machineCode.Slice(i + 1, immediatesCount).ToArray(); + i += immediatesCount; + } + opcodes[j] = new OpcodeInfo(pc, opcode, args.AsMemory()); + } + return opcodes[..j]; + } + /// /// For now, return null always to default to EVM. /// @@ -65,25 +85,7 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) - { - OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; - int j = 0; - for (ushort i = 0; i < machineCode.Length; i++, j++) - { - Instruction opcode = (Instruction)machineCode[i]; - byte[] args = null; - ushort pc = i; - if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) - { - ushort immediatesCount = opcode - Instruction.PUSH0; - args = machineCode.Slice(i + 1, immediatesCount).ToArray(); - i += immediatesCount; - } - opcodes[j] = new OpcodeInfo(pc, opcode, args.AsMemory(), OpcodeMetadata.Operations.GetValueOrDefault(opcode)); - } - return opcodes[..j]; - } + FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) { diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 68dc8cae654..c49ec28d4ff 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -5,62 +5,62 @@ namespace Nethermind.Evm { public static class GasCostOf { - public const long Base = 2; - public const long VeryLow = 3; - public const long Low = 5; - public const long Mid = 8; - public const long High = 10; - public const long ExtCode = 20; - public const long ExtCodeEip150 = 700; - public const long Balance = 20; - public const long BalanceEip150 = 400; - public const long BalanceEip1884 = 700; - public const long SLoad = 50; - public const long SLoadEip150 = 200; - public const long SLoadEip1884 = 800; - public const long JumpDest = 1; - public const long SStoreNetMeteredEip1283 = 200; - public const long SStoreNetMeteredEip2200 = 800; - public const long SSet = 20000; - public const long SReset = 5000; - public const long Create = 32000; - public const long CodeDeposit = 200; - public const long Call = 40; - public const long CallEip150 = 700; - public const long CallValue = 9000; - public const long CallStipend = 2300; - public const long NewAccount = 25000; - public const long Exp = 10; - public const long ExpByte = 10; - public const long ExpByteEip160 = 50; - public const long Memory = 3; - public const long TxCreate = 32000; - public const long TxDataZero = 4; - public const long TxDataNonZero = 68; - public const long TxDataNonZeroEip2028 = 16; - public const long Transaction = 21000; - public const long BlobHash = 3; - public const long Log = 375; - public const long LogTopic = 375; - public const long LogData = 8; - public const long Sha3 = 30; - public const long Sha3Word = 6; - public const long BlockHash = 20; - public const long SelfDestruct = 0; - public const long SelfDestructEip150 = 5000; - public const long ExtCodeHash = 400; - public const long ExtCodeHashEip1884 = 700; - public const long SelfBalance = 5; - public const long InitCodeWord = 2; //eip-3860 gas per word cost for init code size + public const int Base = 2; + public const int VeryLow = 3; + public const int Low = 5; + public const int Mid = 8; + public const int High = 10; + public const int ExtCode = 20; + public const int ExtCodeEip150 = 700; + public const int Balance = 20; + public const int BalanceEip150 = 400; + public const int BalanceEip1884 = 700; + public const int SLoad = 50; + public const int SLoadEip150 = 200; + public const int SLoadEip1884 = 800; + public const int JumpDest = 1; + public const int SStoreNetMeteredEip1283 = 200; + public const int SStoreNetMeteredEip2200 = 800; + public const int SSet = 20000; + public const int SReset = 5000; + public const int Create = 32000; + public const int CodeDeposit = 200; + public const int Call = 40; + public const int CallEip150 = 700; + public const int CallValue = 9000; + public const int CallStipend = 2300; + public const int NewAccount = 25000; + public const int Exp = 10; + public const int ExpByte = 10; + public const int ExpByteEip160 = 50; + public const int Memory = 3; + public const int TxCreate = 32000; + public const int TxDataZero = 4; + public const int TxDataNonZero = 68; + public const int TxDataNonZeroEip2028 = 16; + public const int Transaction = 21000; + public const int BlobHash = 3; + public const int Log = 375; + public const int LogTopic = 375; + public const int LogData = 8; + public const int Sha3 = 30; + public const int Sha3Word = 6; + public const int BlockHash = 20; + public const int SelfDestruct = 0; + public const int SelfDestructEip150 = 5000; + public const int ExtCodeHash = 400; + public const int ExtCodeHashEip1884 = 700; + public const int SelfBalance = 5; + public const int InitCodeWord = 2; //eip-3860 gas per word cost for init code size - public const long ColdSLoad = 2100; // eip-2929 + public const int ColdSLoad = 2100; // eip-2929 - public const long ColdAccountAccess = 2600; // eip-2929 - public const long WarmStateRead = 100; // eip-2929 + public const int ColdAccountAccess = 2600; // eip-2929 + public const int WarmStateRead = 100; // eip-2929 - public const long AccessAccountListEntry = 2400; // eip-2930 - public const long AccessStorageListEntry = 1900; // eip-2930 - public const long TLoad = WarmStateRead; // eip-1153 - public const long TStore = WarmStateRead; // eip-1153 + public const int AccessAccountListEntry = 2400; // eip-2930 + public const int AccessStorageListEntry = 1900; // eip-2930 + public const int TLoad = WarmStateRead; // eip-1153 + public const int TStore = WarmStateRead; // eip-1153 } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 79aa1c27528..7027c7aa331 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -179,12 +179,12 @@ public enum Instruction : byte INVALID = 0xfe, SELFDESTRUCT = 0xff, } - public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) + public record struct OpcodeMetadata(int gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) { /// /// The gas cost. /// - public long GasCost { get; } = gasCost; + public int GasCost { get; } = gasCost; /// /// How many following bytes does this instruction have. @@ -206,22 +206,112 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta { [Instruction.POP ] = new(GasCostOf.Base, 0, 1, 0), [Instruction.PC ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.PUSH1 ] = new(GasCostOf.VeryLow, 1, 0, 1), [Instruction.PUSH2 ] = new(GasCostOf.VeryLow, 2, 0, 1), + [Instruction.PUSH3 ] = new(GasCostOf.VeryLow, 3, 0, 1), [Instruction.PUSH4 ] = new(GasCostOf.VeryLow, 4, 0, 1), + [Instruction.PUSH5 ] = new(GasCostOf.VeryLow, 5, 0, 1), + [Instruction.PUSH6 ] = new(GasCostOf.VeryLow, 6, 0, 1), + [Instruction.PUSH7 ] = new(GasCostOf.VeryLow, 7, 0, 1), + [Instruction.PUSH8 ] = new(GasCostOf.VeryLow, 8, 0, 1), + [Instruction.PUSH9 ] = new(GasCostOf.VeryLow, 9, 0, 1), + [Instruction.PUSH10 ] = new(GasCostOf.VeryLow, 10, 0, 1), + [Instruction.PUSH11 ] = new(GasCostOf.VeryLow, 11, 0, 1), + [Instruction.PUSH12 ] = new(GasCostOf.VeryLow, 12, 0, 1), + [Instruction.PUSH13 ] = new(GasCostOf.VeryLow, 13, 0, 1), + [Instruction.PUSH14 ] = new(GasCostOf.VeryLow, 14, 0, 1), + [Instruction.PUSH15 ] = new(GasCostOf.VeryLow, 15, 0, 1), + [Instruction.PUSH16 ] = new(GasCostOf.VeryLow, 16, 0, 1), + [Instruction.PUSH17 ] = new(GasCostOf.VeryLow, 17, 0, 1), + [Instruction.PUSH18 ] = new(GasCostOf.VeryLow, 18, 0, 1), + [Instruction.PUSH19 ] = new(GasCostOf.VeryLow, 19, 0, 1), + [Instruction.PUSH20 ] = new(GasCostOf.VeryLow, 20, 0, 1), + [Instruction.PUSH21 ] = new(GasCostOf.VeryLow, 21, 0, 1), + [Instruction.PUSH22 ] = new(GasCostOf.VeryLow, 22, 0, 1), + [Instruction.PUSH23 ] = new(GasCostOf.VeryLow, 23, 0, 1), + [Instruction.PUSH24 ] = new(GasCostOf.VeryLow, 24, 0, 1), + [Instruction.PUSH25 ] = new(GasCostOf.VeryLow, 25, 0, 1), + [Instruction.PUSH26 ] = new(GasCostOf.VeryLow, 26, 0, 1), + [Instruction.PUSH27 ] = new(GasCostOf.VeryLow, 27, 0, 1), + [Instruction.PUSH28 ] = new(GasCostOf.VeryLow, 28, 0, 1), + [Instruction.PUSH29 ] = new(GasCostOf.VeryLow, 29, 0, 1), + [Instruction.PUSH30 ] = new(GasCostOf.VeryLow, 30, 0, 1), + [Instruction.PUSH31 ] = new(GasCostOf.VeryLow, 31, 0, 1), + [Instruction.PUSH32 ] = new(GasCostOf.VeryLow, 32, 0, 1), + [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), [Instruction.JUMP ] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.JUMPI ] = new(GasCostOf.High, 0, 2, 0), [Instruction.SUB ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.DUP1 ] = new(GasCostOf.VeryLow, 0, 1, 2), - [Instruction.SWAP1 ] = new(GasCostOf.VeryLow, 0, 2, 2) + [Instruction.DUP2 ] = new(GasCostOf.VeryLow, 0, 2, 3), + [Instruction.DUP3 ] = new(GasCostOf.VeryLow, 0, 3, 4), + [Instruction.DUP4 ] = new(GasCostOf.VeryLow, 0, 4, 5), + [Instruction.DUP5 ] = new(GasCostOf.VeryLow, 0, 5, 6), + [Instruction.DUP6 ] = new(GasCostOf.VeryLow, 0, 6, 7), + [Instruction.DUP7 ] = new(GasCostOf.VeryLow, 0, 7, 8), + [Instruction.DUP8 ] = new(GasCostOf.VeryLow, 0, 8, 9), + [Instruction.DUP9 ] = new(GasCostOf.VeryLow, 0, 9, 10), + [Instruction.DUP10 ] = new(GasCostOf.VeryLow, 0, 10, 11), + [Instruction.DUP11 ] = new(GasCostOf.VeryLow, 0, 11, 12), + [Instruction.DUP12 ] = new(GasCostOf.VeryLow, 0, 12, 13), + [Instruction.DUP13 ] = new(GasCostOf.VeryLow, 0, 13, 14), + [Instruction.DUP14 ] = new(GasCostOf.VeryLow, 0, 14, 15), + [Instruction.DUP15 ] = new(GasCostOf.VeryLow, 0, 15, 16), + [Instruction.DUP16 ] = new(GasCostOf.VeryLow, 0, 16, 17), + + [Instruction.SWAP1 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP2 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP3 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP4 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP5 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP6 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP7 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP8 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP9 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP10 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP11 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP12 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP13 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP14 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP15 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP16 ] = new(GasCostOf.VeryLow, 0, 2, 2), + + [Instruction.ADD ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.MUL ] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SUB ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.DIV ] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SDIV ] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.MOD ] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SMOD ] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.EXP ] = new(GasCostOf.Exp, 0, 2, 1), + [Instruction.SIGNEXTEND] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.LT ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.GT ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SLT ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SGT ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.EQ ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.ISZERO ] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.AND ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.OR ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.XOR ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.NOT ] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.ADDMOD ] = new(GasCostOf.Mid, 0, 3, 1), + [Instruction.MULMOD ] = new(GasCostOf.Mid, 0, 3, 1), + [Instruction.SHL ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SHR ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SAR ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.BYTE ] = new(GasCostOf.VeryLow, 0, 2, 1), + }.ToFrozenDictionary(); } - public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments, OpcodeMetadata? metadata) + public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments) { - public OpcodeMetadata? Metadata { get; } = metadata; - public Instruction Operation { get; set; } = instruction; - public ushort ProgramCounter { get; set; } = pc; + public OpcodeMetadata? Metadata => OpcodeMetadata.Operations[instruction]; + public Instruction Operation => instruction; + public ushort ProgramCounter => pc; public ReadOnlyMemory? Arguments { get; set; } = arguments; } From d21a2f5660c374b9d509a11a03a0ea3f70a5667f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 9 May 2024 12:25:27 +0100 Subject: [PATCH 025/146] Refactors, and added some tests --- .../CodeAnalysis/IlEvmTests.cs | 46 ++++++++-- .../CodeAnalysis/IL/ILCompiler.cs | 88 +++++++++++++------ .../CodeAnalysis/IL/ILEvmState.cs | 2 +- .../CodeAnalysis/IL/ILExtensions.cs | 11 +-- .../CodeAnalysis/IL/IlAnalyzer.cs | 4 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 13 ++- src/Nethermind/Nethermind.Evm/Instruction.cs | 70 +++++++++++++++ 7 files changed, 183 insertions(+), 51 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index fa71ac9ee4e..84b42d948a0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -125,7 +125,7 @@ public void Pure_Opcode_Emition_Coveraga() .ToArray(); - List notYetImplemented = []; + List<(Instruction, Exception)> notYetImplemented = []; foreach (var instruction in instructions) { string name = $"ILEVM_TEST_{instruction}"; @@ -133,9 +133,9 @@ public void Pure_Opcode_Emition_Coveraga() try { ILCompiler.CompileSegment(name, [opcode]); - } catch (Exception) + } catch (Exception e) { - notYetImplemented.Add(instruction); + notYetImplemented.Add((instruction, e)); } } @@ -143,18 +143,50 @@ public void Pure_Opcode_Emition_Coveraga() } - [Test] - public void Ensure_Evm_ILvm_Compatibility() + public static IEnumerable GetBytecodes() { - byte[] bytecode = - Prepare.EvmCode + yield return Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .SUB() + .Done; + + yield return Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() .Done; + yield return Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .MUL() + .Done; + + yield return Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .EXP() + .Done; + + yield return Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .MOD() + .Done; + + yield return Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .DIV() + .Done; + } + [Test, TestCaseSource(nameof(GetBytecodes))] + public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) + { var function = ILCompiler.CompileSegment("ILEVM_TEST", IlAnalyzer.StripByteCode(bytecode)); + var body = function.Method.GetMethodBody(); var result = function(100); Assert.IsNotNull(result); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 958e6068865..876857b40cb 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -14,14 +14,13 @@ using System.Text; using System.Threading.Tasks; using Label = Sigil.Label; -using ProjectedEvmState = (Nethermind.Evm.EvmExceptionType, Nethermind.Evm.CodeAnalysis.IL.ILEvmState); namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func CompileSegment(string segmentName, OpcodeInfo[] code) + public static Func CompileSegment(string segmentName, OpcodeInfo[] code) { - Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); using Local address = method.DeclareLocal(typeof(Address)); @@ -30,9 +29,10 @@ public static Func CompileSegment(string segmentName, O using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); + using Local localArr = method.DeclareLocal(typeof(ReadOnlySpan)); using Local returnState = method.DeclareLocal(typeof(ILEvmState)); using Local gasAvailable = method.DeclareLocal(typeof(int)); - + using Local uint32A = method.DeclareLocal(typeof(uint)); using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); @@ -146,21 +146,23 @@ public static Func CompileSegment(string segmentName, O method.LoadLocal(currentSP); method.LoadArray(bytes.ToArray()); + method.StoreLocal(localArr); + method.LoadLocalAddress(localArr); method.LoadConstant(0); - method.NewObject(typeof(UInt256), typeof(Span), typeof(bool)); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; case Instruction.ADD: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.SUB: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.MUL: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.MOD: @@ -202,7 +204,7 @@ public static Func CompileSegment(string segmentName, O break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null); break; case Instruction.LT: EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); @@ -296,39 +298,40 @@ public static Func CompileSegment(string segmentName, O method.Subtract(); method.LoadConstant(Word.Size); method.Divide(); - method.StoreLocal(uint256R); + method.Convert(); + method.StoreLocal(uint32A); // set stack method.LoadLocal(returnState); - method.LoadLocal(uint256R); - method.NewArray(); - method.ForBranch(uint256R, (il, i) => + method.LoadLocal(uint32A); + method.NewArray(); + method.ForBranch(uint32A, (il, i) => { il.Duplicate(); il.LoadLocal(i); il.StackLoadPrevious(currentSP); - il.LoadObject(typeof(Word)); - il.StoreElement(); + il.Call(Word.GetUInt256); + il.StoreElement(); il.StackPop(currentSP); }); - method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.Stack))); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.Stack))); // set gas available method.LoadLocal(returnState); method.LoadLocal(gasAvailable); - method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.GasAvailable))); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.GasAvailable))); // set program counter method.LoadLocal(returnState); method.LoadConstant(0); - method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.ProgramCounter))); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.ProgramCounter))); method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.None); - method.StoreField(typeof(ILEvmState).GetField(nameof(ILEvmState.EvmException))); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); @@ -340,9 +343,11 @@ public static Func CompileSegment(string segmentName, O // if (jumpDest > uint.MaxValue) // ULong3 | Ulong2 | Ulong1 | Uint1 | Ushort1 method.LoadLocal(currentSP); - method.LoadConstant(uint.MaxValue); method.Call(Word.GetUInt256); - method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + method.StoreLocal(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadConstant(uint.MaxValue); + method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); method.BranchIfTrue(invalidAddress); @@ -383,31 +388,52 @@ public static Func CompileSegment(string segmentName, O // out of gas method.MarkLabel(outOfGas); + method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.OutOfGas); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); // invalid address return method.MarkLabel(invalidAddress); + method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); + method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); - // return method.MarkLabel(ret); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.Pop(); + method.LoadLocal(returnState); method.Return(); - Func del = method.CreateDelegate(); + Func del = method.CreateDelegate(); return del; } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operatin) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation) { il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); // invoke op < on the uint256 - il.Call(operatin, null); + il.Call(operation, null); // if true, push 1, else 0 il.LoadConstant(0); il.CompareEqual(); @@ -424,19 +450,24 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackPush(currentSP); } - private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label> customHandling = null) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label> customHandling, params Local[] locals) { Label label = il.DefineLabel(); il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); customHandling?.Invoke(il, label); + + il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(uint256R); il.Call(operation); - il.StoreLocal(uint256R); il.StackPop(currentSP, 2); il.MarkLabel(label); @@ -471,10 +502,9 @@ private static Dictionary BuildCostLookup(ReadOnlySpan cod break; default: costcurrentSP += op.Metadata?.GasCost ?? 0; + costs[pc] = costcurrentSP; break; } - - pc += op.Metadata?.AdditionalBytes ?? 0; } if (costcurrentSP > 0) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 75ae93e2950..9abc61e914a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -16,7 +16,7 @@ internal class ILEvmState public UInt256[] Stack; // in case of exceptions - public EvmException EvmException; + public EvmExceptionType EvmException; // in case of jumps crossing section boundaries public int ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 820008ae12c..c32d3c5e62d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -4,6 +4,7 @@ using Nethermind.Evm.CodeAnalysis.IL; using Sigil; using System; +using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -102,7 +103,6 @@ public static void ForBranch(this Emit il, Local count, Action, Lo // declare indexer var i = il.DeclareLocal(); - il.LoadLocal(i); il.LoadConstant(0); il.StoreLocal(i); @@ -118,8 +118,8 @@ public static void ForBranch(this Emit il, Local count, Action, Lo il.Add(); il.StoreLocal(i); - il.MarkLabel(start); il.Branch(start); + il.MarkLabel(end); } /// @@ -135,10 +135,12 @@ public static void StackLoadPrevious(this Emit il, Local local, int count public static void LoadArray(this Emit il, ReadOnlySpan value) { - il.LoadConstant(value.Length); il.NewArray(); + // get methodInfo of AsSpan from int[] it is a public instance method + var ArrToSpanMethod = typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) }); + for (int i = 0; i < value.Length; i++) { il.Duplicate(); @@ -147,8 +149,7 @@ public static void LoadArray(this Emit il, ReadOnlySpan value) il.StoreElement(); } - il.Call(typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), new[] { typeof(byte[]) })); - il.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); + il.Call(ArrToSpanMethod); } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index ea17e6a9d8a..722b61b4791 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -87,9 +87,9 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) - FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) { - Dictionary> opcodeInfos = []; + Dictionary> opcodeInfos = []; List segment = []; foreach (var opcode in codeData) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 735084436c3..6145a032961 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -6,7 +6,6 @@ using Nethermind.Core.Specs; namespace Nethermind.Evm.CodeAnalysis.IL; -using ProjectedEvmState = (EvmExceptionType, Nethermind.Evm.CodeAnalysis.IL.ILEvmState); /// /// Represents the IL-EVM information about the contract. /// @@ -35,7 +34,7 @@ public enum ILMode private IlInfo() { Chunks = FrozenDictionary.Empty; - Segments = FrozenDictionary>.Empty; + Segments = FrozenDictionary>.Empty; } public IlInfo WithChunks(FrozenDictionary chunks) @@ -44,13 +43,13 @@ public IlInfo WithChunks(FrozenDictionary chunks) return this; } - public IlInfo WithSegments(FrozenDictionary> segments) + public IlInfo WithSegments(FrozenDictionary> segments) { Segments = segments; return this; } - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) { Chunks = mappedOpcodes; Segments = segments; @@ -58,7 +57,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi // assumes small number of ILed public FrozenDictionary Chunks { get; set; } - public FrozenDictionary> Segments { get; set; } + public FrozenDictionary> Segments { get; set; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -79,11 +78,11 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec } case ILMode.SubsegmentsCompiling: { - if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) + if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) { return false; } - var (exception, evmState) = method.Invoke(gasAvailable); + var evmState = method.Invoke(gasAvailable); // ToDo : Tidy up the exception handling // ToDo : Add context switch, migrate stack from IL to EVM and map memory // ToDo : Add context switch, prepare IL stack before executing the segment and map memory diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 7027c7aa331..37f379ebecb 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -305,6 +305,75 @@ public record struct OpcodeMetadata(int gasCost, byte additionalBytes, byte stac [Instruction.SAR ] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.BYTE ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.KECCAK256] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.ADDRESS ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.BALANCE ] = new(GasCostOf.Balance, 0, 1, 1), + [Instruction.ORIGIN ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CALLER ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CALLVALUE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CALLDATALOAD] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.CALLDATASIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CALLDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.CODESIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CODECOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.GASPRICE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.EXTCODESIZE] = new(GasCostOf.ExtCode, 0, 1, 1), + [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), + [Instruction.RETURNDATASIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.RETURNDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.EXTCODEHASH] = new(GasCostOf.ExtCodeHash, 0, 1, 1), + [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), + + [Instruction.BLOCKHASH ] = new(GasCostOf.BlockHash, 0, 1, 1), + [Instruction.COINBASE ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.TIMESTAMP] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.NUMBER ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.PREVRANDAO] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.GASLIMIT ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CHAINID ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.SELFBALANCE] = new(GasCostOf.SelfBalance, 0, 0, 1), + [Instruction.BASEFEE ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.BLOBHASH ] = new(GasCostOf.BlobHash, 0, 1, 1), + [Instruction.BLOBBASEFEE] = new(GasCostOf.Base, 0, 0, 1), + + [Instruction.POP ] = new(GasCostOf.Base, 0, 1, 0), + [Instruction.MLOAD ] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.MSTORE ] = new(GasCostOf.VeryLow, 0, 2, 0), + [Instruction.MSTORE8 ] = new(GasCostOf.VeryLow, 0, 2, 0), + [Instruction.SLOAD ] = new(GasCostOf.SLoad, 0, 1, 1), + [Instruction.SSTORE ] = new(GasCostOf.SSet, 0, 2, 0), + [Instruction.JUMP ] = new(GasCostOf.Mid, 0, 1, 0), + [Instruction.JUMPI] = new(GasCostOf.Mid, 0, 2, 0), + [Instruction.PC ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.MSIZE ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.GAS ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.JUMPDEST ] = new(GasCostOf.JumpDest, 0, 0, 0), + [Instruction.MCOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + + [Instruction.LOG0] = new(GasCostOf.Log, 0, 2, 0), + [Instruction.LOG1] = new(GasCostOf.Log, 0, 3, 0), + [Instruction.LOG2] = new(GasCostOf.Log, 0, 4, 0), + [Instruction.LOG3] = new(GasCostOf.Log, 0, 5, 0), + [Instruction.LOG4] = new(GasCostOf.Log, 0, 6, 0), + + [Instruction.TLOAD] = new(GasCostOf.Base, 0, 1, 1), + [Instruction.TSTORE] = new(GasCostOf.Base, 0, 2, 0), + + [Instruction.CREATE] = new(GasCostOf.Create, 0, 3, 1), + [Instruction.CALL] = new(GasCostOf.Call, 0, 7, 1), + [Instruction.CALLCODE] = new(GasCostOf.Call, 0, 7, 1), + [Instruction.RETURN] = new(GasCostOf.Base, 0, 2, 0), + [Instruction.DELEGATECALL] = new(GasCostOf.Call, 0, 6, 1), + [Instruction.CREATE2] = new(GasCostOf.Create, 0, 4, 1), + [Instruction.STATICCALL] = new(GasCostOf.Call, 0, 6, 1), + [Instruction.REVERT] = new(GasCostOf.Base, 0, 2, 0), + [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), + [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct, 0, 1, 0), + + [Instruction.BEGINSUB] = new(GasCostOf.Base, 0, 0, 0), + [Instruction.RETURNSUB] = new(GasCostOf.Base, 0, 0, 0), + [Instruction.JUMPSUB] = new(GasCostOf.Base, 0, 1, 0), + }.ToFrozenDictionary(); } public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments) @@ -324,6 +393,7 @@ public static class InstructionExtensions Instruction.SLOAD or Instruction.SSTORE => true, Instruction.TLOAD or Instruction.TSTORE => true, Instruction.EXTCODESIZE or Instruction.EXTCODECOPY or Instruction.EXTCODEHASH => true, + Instruction.SELFDESTRUCT => true, _ => false, }; From c9e9bd7d688b56b83b7b7cd65f800162028890fc Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 12 May 2024 18:05:56 +0100 Subject: [PATCH 026/146] Added some comments --- .../CodeAnalysis/IlEvmTests.cs | 18 +--- .../CodeAnalysis/IL/ILCompiler.cs | 97 ++++++++++++------- .../CodeAnalysis/IL/ILExtensions.cs | 25 ++++- 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 84b42d948a0..7d96f4ee0f0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1,27 +1,17 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using System.Collections.Frozen; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using DotNetty.Common.Utilities; using FluentAssertions; -using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Core.Test.Blockchain; -using Nethermind.Core.Test.Builders; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Tracing; -using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Int256; -using NSubstitute; using NUnit.Framework; -using static Nethermind.Evm.VirtualMachine; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace Nethermind.Evm.Test.CodeAnalysis { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 876857b40cb..905f075e756 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -76,32 +76,45 @@ public static Func CompileSegment(string segmentName, OpcodeIn // get pc gas cost method.LoadConstant(gasCost[pc]); + + // subtract the gas cost method.Subtract(); + + // check if gas is available method.Duplicate(); method.LoadConstant(0); method.StoreLocal(gasAvailable); method.Convert(); - method.BranchIfLess(outOfGas); + // if gas is not available, branch to out of gas + method.BranchIfLess(outOfGas); + // else emit switch (op.Operation) { case Instruction.JUMPDEST: + // mark the jump destination jumpDestinations[pc] = method.DefineLabel(); method.MarkLabel(jumpDestinations[pc]); break; case Instruction.JUMP: + // we jump into the jump table method.Branch(jumpTable); break; case Instruction.JUMPI: + // consume the jump condition Label noJump = method.DefineLabel(); method.StackLoadPrevious(currentSP, 2); method.Call(Word.GetIsZero, null); + + // if the jump condition is false, we do not jump method.BranchIfTrue(noJump); // load the jump address method.LoadConstant(1); method.StoreLocal(consumeJumpCondition); + + // we jump into the jump table method.Branch(jumpTable); method.MarkLabel(noJump); @@ -139,17 +152,25 @@ public static Func CompileSegment(string segmentName, OpcodeIn case Instruction.PUSH30: case Instruction.PUSH31: case Instruction.PUSH32: + // we create a span of 32 bytes, and we copy the bytes from the arguments to the span int count = (int)op.Operation - (int)Instruction.PUSH0; ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); + + // we load the currentSP method.LoadLocal(currentSP); method.InitializeObject(typeof(Word)); method.LoadLocal(currentSP); + // we load the span of bytes method.LoadArray(bytes.ToArray()); method.StoreLocal(localArr); + + // we call UInt256 constructor taking a span of bytes and a bool method.LoadLocalAddress(localArr); method.LoadConstant(0); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + + // we store the UInt256 in the currentSP method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -167,11 +188,11 @@ public static Func CompileSegment(string segmentName, OpcodeIn case Instruction.MOD: EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, - (il, postInstructionLabel) => + (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); - il.Duplicate(); + il.LoadLocal(locals[1]); il.LoadConstant(0); il.CompareEqual(); @@ -186,11 +207,11 @@ public static Func CompileSegment(string segmentName, OpcodeIn case Instruction.DIV: EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, - (il, postInstructionLabel) => + (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); - il.Duplicate(); + il.LoadLocal(locals[1]); il.LoadConstant(0); il.CompareEqual(); @@ -216,9 +237,16 @@ public static Func CompileSegment(string segmentName, OpcodeIn EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.ISZERO: + // we load the currentSP method.StackLoadPrevious(currentSP, 1); method.StackPop(currentSP, 1); + + // we call the IsZero method on the UInt256 method.Call(Word.GetIsZero); + + // we convert the result to a Uint256 and store it in the currentSP + method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + method.Call(Word.SetUInt256); method.StackPush(currentSP); break; case Instruction.CODESIZE: @@ -293,6 +321,7 @@ public static Func CompileSegment(string segmentName, OpcodeIn // prepare ILEvmState + // we get stack size method.LoadLocal(currentSP); method.LoadLocal(stack); method.Subtract(); @@ -305,6 +334,8 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadLocal(returnState); method.LoadLocal(uint32A); method.NewArray(); + + // we iterate in IL over the stack and store the values method.ForBranch(uint32A, (il, i) => { il.Duplicate(); @@ -328,28 +359,33 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadConstant(0); method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.ProgramCounter))); - + // set exception method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.None); method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); - + // go to return method.Branch(ret); + + // jump table method.MarkLabel(jumpTable); method.StackPop(currentSP); // emit the jump table - // if (jumpDest > uint.MaxValue) - // ULong3 | Ulong2 | Ulong1 | Uint1 | Ushort1 + + // load the jump destination method.LoadLocal(currentSP); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); + + // if (jumpDest > uint.MaxValue) method.LoadLocalAddress(uint256B); method.LoadConstant(uint.MaxValue); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); - + // goto invalid address method.BranchIfTrue(invalidAddress); + // else const int jumpFanOutLog = 7; // 128 const int bitMask = (1 << jumpFanOutLog) - 1; @@ -359,6 +395,7 @@ public static Func CompileSegment(string segmentName, OpcodeIn jumps[i] = method.DefineLabel(); } + // we get first 32 bits of the jump destination since it is less than int.MaxValue method.Load(currentSP, Word.Int0Field); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); @@ -367,15 +404,15 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadConstant(bitMask); method.And(); + // switch on the first 7 bits method.Switch(jumps); - int[] destinations = jumpDestinations.Keys.ToArray(); for (int i = 0; i < jumpFanOutLog; i++) { method.MarkLabel(jumps[i]); // for each destination matching the bit mask emit check for the equality - foreach (int dest in destinations.Where(dest => (dest & bitMask) == i)) + foreach (int dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) { method.LoadLocal(jmpDestination); method.LoadConstant(dest); @@ -402,23 +439,6 @@ public static Func CompileSegment(string segmentName, OpcodeIn // return method.MarkLabel(ret); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); - method.Pop(); method.LoadLocal(returnState); method.Return(); @@ -428,15 +448,14 @@ public static Func CompileSegment(string segmentName, OpcodeIn private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation) { + // we the two uint256 from the stack il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); - // invoke op < on the uint256 + + // invoke op on the uint256 il.Call(operation, null); - // if true, push 1, else 0 - il.LoadConstant(0); - il.CompareEqual(); // convert to conv_i il.Convert(); @@ -444,16 +463,18 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StoreLocal(uint256R); il.StackPop(currentSP, 2); + // push the result to the stack il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(currentSP); } - private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label> customHandling, params Local[] locals) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { Label label = il.DefineLabel(); + // we the two uint256 from the stack il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); il.StoreLocal(locals[0]); @@ -461,16 +482,20 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - customHandling?.Invoke(il, label); - + // incase of custom handling, we branch to the label + customHandling?.Invoke(il, label, locals); + // invoke op on the uint256 il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(uint256R); il.Call(operation); il.StackPop(currentSP, 2); + // skip the main handling il.MarkLabel(label); + + // push the result to the stack il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index c32d3c5e62d..5e837bd1f8f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -81,18 +81,25 @@ public static void StackPop(this Emit il, Local local, Local count) il.StoreLocal(local); } - public static void WhileBranch(this Emit il, Local local, Action> action) + public static void WhileBranch(this Emit il, Local cond, Action, Local> action) { var start = il.DefineLabel(); var end = il.DefineLabel(); + // start of the loop il.MarkLabel(start); - il.LoadLocal(local); + + // if cond + il.LoadLocal(cond); il.BranchIfFalse(end); - action(il); + // emit body of loop + action(il, cond); + // jump to start of the loop il.Branch(start); + + // end of the loop il.MarkLabel(end); } @@ -101,24 +108,34 @@ public static void ForBranch(this Emit il, Local count, Action, Lo var start = il.DefineLabel(); var end = il.DefineLabel(); - // declare indexer + // declare i var i = il.DeclareLocal(); + + // we initialize i to 0 il.LoadConstant(0); il.StoreLocal(i); + // start of the loop il.MarkLabel(start); + + // i < count il.LoadLocal(i); il.LoadLocal(count); il.BranchIfGreater(end); + // emit body of loop action(il, i); + // i++ il.LoadLocal(i); il.LoadConstant(1); il.Add(); il.StoreLocal(i); + // jump to start of the loop il.Branch(start); + + // end of the loop il.MarkLabel(end); } From c5076fa9e215aedfc6aea99f162a8d6acb595889 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 12 May 2024 20:03:55 +0100 Subject: [PATCH 027/146] BoilerPlate for more opcodes --- .../CodeAnalysis/IL/ILCompiler.cs | 120 +++++++++++++++--- .../CodeAnalysis/IL/ILEvmState.cs | 11 +- .../CodeAnalysis/IL/ILExtensions.cs | 4 + .../CodeAnalysis/IL/IlAnalyzer.cs | 4 +- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 26 ++++ 5 files changed, 137 insertions(+), 28 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 905f075e756..12293565baf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -14,26 +14,30 @@ using System.Text; using System.Threading.Tasks; using Label = Sigil.Label; +using static Nethermind.Evm.IL.EmitExtensions; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func CompileSegment(string segmentName, OpcodeInfo[] code) + public static Func CompileSegment(string segmentName, OpcodeInfo[] code) { - Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); - using Local address = method.DeclareLocal(typeof(Address)); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); + + using Local address = method.DeclareLocal(typeof(Address)); using Local uint256A = method.DeclareLocal(typeof(UInt256)); using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); using Local localArr = method.DeclareLocal(typeof(ReadOnlySpan)); - using Local returnState = method.DeclareLocal(typeof(ILEvmState)); - using Local gasAvailable = method.DeclareLocal(typeof(int)); using Local uint32A = method.DeclareLocal(typeof(uint)); + using Local gasAvailable = method.DeclareLocal(typeof(int)); + using Local programCounter = method.DeclareLocal(typeof(ushort)); + using Local returnState = method.DeclareLocal(typeof(ILEvmState)); + using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); @@ -51,12 +55,18 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadLocal(stack); method.StoreLocal(currentSP); // copy to the currentSP - // gas + // set gas to local method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); method.Convert(); method.StoreLocal(gasAvailable); Label outOfGas = method.DefineLabel("OutOfGas"); + // set pc to local + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.ProgramCounter))); + method.StoreLocal(programCounter); + Label ret = method.DefineLabel("Return"); // the label just before return Label invalidAddress = method.DefineLabel("InvalidAddress"); // invalid jump address Label jumpTable = method.DefineLabel("Jumptable"); // jump table @@ -71,6 +81,10 @@ public static Func CompileSegment(string segmentName, OpcodeIn { OpcodeInfo op = code[pc]; + // set pc + method.LoadConstant(op.ProgramCounter); + method.StoreLocal(programCounter); + // load gasAvailable method.LoadLocal(gasAvailable); @@ -249,12 +263,6 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.Call(Word.SetUInt256); method.StackPush(currentSP); break; - case Instruction.CODESIZE: - method.LoadConstant(code.Length); - method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); - method.Call(Word.SetUInt256); - method.StackPush(currentSP); - break; case Instruction.POP: method.StackPop(currentSP); break; @@ -314,6 +322,72 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); break; + + // Note(Ayman): following opcode need double checking + // is pushing to stack happening correctly + case Instruction.CODESIZE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadConstant(code.Length); + method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.PC: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadLocal(programCounter); + method.StoreField(GetFieldInfo(nameof(Word.UInt0))); + method.StackPush(currentSP); + break; + case Instruction.COINBASE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(nameof(BlockHeader.GasBeneficiary))); + method.LoadObject(typeof(Address)); + method.StoreLocal(address); + method.LoadLocal(address); + method.Call(Word.SetAddress); + method.StackPush(currentSP); + break; + case Instruction.TIMESTAMP: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(nameof(BlockHeader.Timestamp))); + method.LoadObject(typeof(UInt256)); + method.StoreLocal(uint256A); + method.LoadLocal(uint256A); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.NUMBER: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(nameof(BlockHeader.Number))); + method.LoadObject(typeof(UInt256)); + method.StoreLocal(uint256A); + method.LoadLocal(uint256A); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.GASLIMIT: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(nameof(BlockHeader.GasLimit))); + method.LoadObject(typeof(UInt256)); + method.StoreLocal(uint256A); + method.LoadLocal(uint256A); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; default: throw new NotSupportedException(); } @@ -347,22 +421,28 @@ public static Func CompileSegment(string segmentName, OpcodeIn il.StackPop(currentSP); }); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.Stack))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.Stack))); + + // set header + method.LoadLocal(returnState); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.Header))); // set gas available method.LoadLocal(returnState); method.LoadLocal(gasAvailable); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.GasAvailable))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); // set program counter method.LoadLocal(returnState); - method.LoadConstant(0); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.ProgramCounter))); + method.LoadLocal(programCounter); + method.StoreField(GetFieldInfo(nameof(ILEvmState.ProgramCounter))); // set exception method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.None); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); // go to return method.Branch(ret); @@ -427,14 +507,14 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.MarkLabel(outOfGas); method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.OutOfGas); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); // invalid address return method.MarkLabel(invalidAddress); method.LoadLocal(returnState); method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); - method.StoreField(ILEvmState.GetFieldInfo(nameof(ILEvmState.EvmException))); + method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); // return @@ -442,7 +522,7 @@ public static Func CompileSegment(string segmentName, OpcodeIn method.LoadLocal(returnState); method.Return(); - Func del = method.CreateDelegate(); + Func del = method.CreateDelegate(); return del; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 9abc61e914a..94bb3eba18e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core; using Nethermind.Int256; using System; using System.Collections.Generic; @@ -12,6 +13,9 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILEvmState { + // static arguments + public BlockHeader Header; + public byte[] bytes; public UInt256[] Stack; @@ -19,14 +23,9 @@ internal class ILEvmState public EvmExceptionType EvmException; // in case of jumps crossing section boundaries - public int ProgramCounter; + public ushort ProgramCounter; public int GasAvailable; // in case STOP is executed public bool StopExecution; - - public static FieldInfo GetFieldInfo(string name) - { - return typeof(ILEvmState).GetField(name); - } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 5e837bd1f8f..fedcd924cc0 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -15,6 +15,10 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { + public static FieldInfo GetFieldInfo(string name) + { + return typeof(T).GetField(name); + } public static void Load(this Emit il, Local local, FieldInfo wordField) { if (local.LocalType != typeof(Word*)) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 722b61b4791..bd861cb9bbe 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -87,9 +87,9 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) - FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) { - Dictionary> opcodeInfos = []; + Dictionary> opcodeInfos = []; List segment = []; foreach (var opcode in codeData) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 29d4bbba8fc..36b791d3532 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core; using Nethermind.Int256; using System; using System.Buffers.Binary; @@ -46,6 +47,28 @@ internal struct Word public bool IsZero => (Ulong0 | Ulong1 | Ulong2 | Ulong3) == 0; + public unsafe Address Address + { + get + { + byte[] buffer = new byte[20]; + for (int i = 0; i < 20; i++) + { + buffer[i] = _buffer[i]; + } + + return new Address(buffer); + } + set + { + byte[] buffer = value.Bytes; + for (int i = 0; i < 20; i++) + { + _buffer[i] = buffer[i]; + } + } + } + public UInt256 UInt256 { get @@ -100,4 +123,7 @@ public UInt256 UInt256 public static readonly MethodInfo GetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.GetMethod; public static readonly MethodInfo SetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.SetMethod; + + public static readonly MethodInfo GetAddress = typeof(Word).GetProperty(nameof(Address))!.GetMethod; + public static readonly MethodInfo SetAddress = typeof(Word).GetProperty(nameof(Address))!.SetMethod; } From 545b0fab1f87e84f38cefb600674fe664f267ea9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 15 May 2024 00:20:12 +0100 Subject: [PATCH 028/146] dynamic emited method works, more bugs to hunt for --- .../CodeAnalysis/IlEvmTests.cs | 20 ++++++++++-- .../CodeAnalysis/IL/ILCompiler.cs | 18 +++++++---- .../CodeAnalysis/IL/ILEvmState.cs | 30 +++++++++++++++-- .../CodeAnalysis/IL/ILExtensions.cs | 2 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 32 ++++++++++++------- .../Nethermind.Evm/VirtualMachine.cs | 2 +- 6 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 7d96f4ee0f0..764c97ed9b5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -3,11 +3,14 @@ using FluentAssertions; using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Tracing; using Nethermind.Int256; +using Nethermind.Specs; using NUnit.Framework; +using Sigil; using System; using System.Collections.Generic; using System.Linq; @@ -135,6 +138,8 @@ public void Pure_Opcode_Emition_Coveraga() public static IEnumerable GetBytecodes() { + yield return Prepare.EvmCode + .Done; yield return Prepare.EvmCode .PushSingle(23) .PushSingle(7) @@ -175,11 +180,20 @@ public static IEnumerable GetBytecodes() [Test, TestCaseSource(nameof(GetBytecodes))] public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) { + ILEvmState iLEvmState = new ILEvmState + { + Stack = new UInt256[1024], + Header = BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header, + GasAvailable = 1000, + ProgramCounter = 0, + EvmException = EvmExceptionType.None, + StopExecution = false + }; var function = ILCompiler.CompileSegment("ILEVM_TEST", IlAnalyzer.StripByteCode(bytecode)); - var body = function.Method.GetMethodBody(); - var result = function(100); - Assert.IsNotNull(result); + var il_result = function(iLEvmState); + Assert.IsNotNull(il_result); } + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 12293565baf..9ac5a0d6890 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -45,6 +45,9 @@ public static Func CompileSegment(string segmentName, Op const int wordToAlignTo = 32; + // init ReturnState + method.LoadLocalAddress(returnState); + method.InitializeObject(); // allocate stack method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); @@ -394,6 +397,7 @@ public static Func CompileSegment(string segmentName, Op } // prepare ILEvmState + // check if returnState is null // we get stack size method.LoadLocal(currentSP); @@ -405,7 +409,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(uint32A); // set stack - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadLocal(uint32A); method.NewArray(); @@ -424,23 +428,23 @@ public static Func CompileSegment(string segmentName, Op method.StoreField(GetFieldInfo(nameof(ILEvmState.Stack))); // set header - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); method.StoreField(GetFieldInfo(nameof(ILEvmState.Header))); // set gas available - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadLocal(gasAvailable); method.StoreField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); // set program counter - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadLocal(programCounter); method.StoreField(GetFieldInfo(nameof(ILEvmState.ProgramCounter))); // set exception - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadConstant((int)EvmExceptionType.None); method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); @@ -505,14 +509,14 @@ public static Func CompileSegment(string segmentName, Op // out of gas method.MarkLabel(outOfGas); - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadConstant((int)EvmExceptionType.OutOfGas); method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); // invalid address return method.MarkLabel(invalidAddress); - method.LoadLocal(returnState); + method.LoadLocalAddress(returnState); method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); method.Branch(ret); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 94bb3eba18e..8cf88160d8e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -11,12 +11,38 @@ using System.Threading.Tasks; namespace Nethermind.Evm.CodeAnalysis.IL; -internal class ILEvmState +internal struct ILEvmState { // static arguments public BlockHeader Header; - public byte[] bytes; + public byte[] StackBytes + { + set + { + if(value.Length % 32 != 0) + { + throw new ArgumentException("Invalid byte array length"); + } + + Stack = new UInt256[value.Length / 32]; + for(int i = 0; i < value.Length; i += 32) + { + Stack[i / 32] = new UInt256(value[i..(i + 32)]); + } + } + + get + { + byte[] result = new byte[Stack.Length * 32]; + for(int i = 0; i < Stack.Length; i++) + { + Stack[i].PaddedBytes(32).CopyTo(result, i * 32); + } + return result; + } + } + public UInt256[] Stack; // in case of exceptions diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index fedcd924cc0..3ea61f14702 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -125,7 +125,7 @@ public static void ForBranch(this Emit il, Local count, Action, Lo // i < count il.LoadLocal(i); il.LoadLocal(count); - il.BranchIfGreater(end); + il.BranchIfEqual(end); // emit body of loop action(il, i); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 6145a032961..dcefbfbe49c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Microsoft.IdentityModel.Tokens; +using Nethermind.Core; using Nethermind.Core.Specs; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -34,7 +35,7 @@ public enum ILMode private IlInfo() { Chunks = FrozenDictionary.Empty; - Segments = FrozenDictionary>.Empty; + Segments = FrozenDictionary>.Empty; } public IlInfo WithChunks(FrozenDictionary chunks) @@ -43,13 +44,13 @@ public IlInfo WithChunks(FrozenDictionary chunks) return this; } - public IlInfo WithSegments(FrozenDictionary> segments) + public IlInfo WithSegments(FrozenDictionary> segments) { Segments = segments; return this; } - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) { Chunks = mappedOpcodes; Segments = segments; @@ -57,9 +58,9 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi // assumes small number of ILed public FrozenDictionary Chunks { get; set; } - public FrozenDictionary> Segments { get; set; } + public FrozenDictionary> Segments { get; set; } - public bool TryExecute(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) + public bool TryExecute(EvmState vmState, IReleaseSpec spec, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where TTracingInstructions : struct, VirtualMachine.IIsTracing { if (programCounter > ushort.MaxValue) @@ -78,15 +79,24 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec } case ILMode.SubsegmentsCompiling: { - if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) + if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) { return false; } - var evmState = method.Invoke(gasAvailable); - // ToDo : Tidy up the exception handling - // ToDo : Add context switch, migrate stack from IL to EVM and map memory - // ToDo : Add context switch, prepare IL stack before executing the segment and map memory - break; + + var ilvmState = new ILEvmState + { + GasAvailable = (int)gasAvailable, + StackBytes = vmState.DataStack, + Header = header, + ProgramCounter = (ushort)programCounter, + }; + + ilvmState = method.Invoke(ilvmState); + gasAvailable = ilvmState.GasAvailable; + vmState.DataStack = ilvmState.StackBytes; + programCounter = ilvmState.ProgramCounter; + break; } } return true; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index bb7190d0428..531b959c1ea 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -828,7 +828,7 @@ private CallResult ExecuteCode Date: Wed, 15 May 2024 00:24:26 +0100 Subject: [PATCH 029/146] notes --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 9ac5a0d6890..4f52215ddd7 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -21,6 +21,9 @@ internal class ILCompiler { public static Func CompileSegment(string segmentName, OpcodeInfo[] code) { + // code is optimistic assumes stack underflow and stack overflow to not occure (WE NEED EOF FOR THIS) + // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? + Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); @@ -507,6 +510,14 @@ public static Func CompileSegment(string segmentName, Op method.Branch(invalidAddress); } + // EvmExceptionType.StackUnderflow + method.MarkLabel(outOfGas); + method.LoadLocalAddress(returnState); + method.LoadConstant((int)EvmExceptionType.StackUnderflow); + method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); + method.Branch(ret); + + // out of gas method.MarkLabel(outOfGas); method.LoadLocalAddress(returnState); From 760f48194f83767f4fdfec5a181d8fe756bb01e1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 15 May 2024 19:00:27 +0100 Subject: [PATCH 030/146] exception code generation refactor --- .../CodeAnalysis/IlEvmTests.cs | 4 ++ .../CodeAnalysis/IL/ILCompiler.cs | 39 ++++++++----------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 764c97ed9b5..0db9cbfe3a9 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -140,6 +140,10 @@ public static IEnumerable GetBytecodes() { yield return Prepare.EvmCode .Done; + yield return Prepare.EvmCode + .PushSingle(7) + .ISZERO() + .Done; yield return Prepare.EvmCode .PushSingle(23) .PushSingle(7) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 4f52215ddd7..5e49af9e47f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -66,7 +66,13 @@ public static Func CompileSegment(string segmentName, Op method.LoadField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); method.Convert(); method.StoreLocal(gasAvailable); - Label outOfGas = method.DefineLabel("OutOfGas"); + + Dictionary labels = new(); + + foreach (var exception in Enum.GetValues()) + { + labels.Add(exception, method.DefineLabel(exception.ToString())); + } // set pc to local method.LoadArgument(0); @@ -107,7 +113,7 @@ public static Func CompileSegment(string segmentName, Op method.Convert(); // if gas is not available, branch to out of gas - method.BranchIfLess(outOfGas); + method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); // else emit switch (op.Operation) @@ -510,27 +516,14 @@ public static Func CompileSegment(string segmentName, Op method.Branch(invalidAddress); } - // EvmExceptionType.StackUnderflow - method.MarkLabel(outOfGas); - method.LoadLocalAddress(returnState); - method.LoadConstant((int)EvmExceptionType.StackUnderflow); - method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); - method.Branch(ret); - - - // out of gas - method.MarkLabel(outOfGas); - method.LoadLocalAddress(returnState); - method.LoadConstant((int)EvmExceptionType.OutOfGas); - method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); - method.Branch(ret); - - // invalid address return - method.MarkLabel(invalidAddress); - method.LoadLocalAddress(returnState); - method.LoadConstant((int)EvmExceptionType.InvalidJumpDestination); - method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); - method.Branch(ret); + foreach (var kvp in labels) + { + method.MarkLabel(kvp.Value); + method.LoadLocalAddress(returnState); + method.LoadConstant((int)kvp.Key); + method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); + method.Branch(ret); + } // return method.MarkLabel(ret); From a6aa0327c8148cac71517b7a88516bc2a63f782d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 16 May 2024 07:39:45 +0100 Subject: [PATCH 031/146] bug fixes and refactors --- .../CodeAnalysis/IL/ILCompiler.cs | 126 +++++++++++++----- .../CodeAnalysis/IL/ILEvmState.cs | 4 +- .../CodeAnalysis/IL/ILExtensions.cs | 6 + src/Nethermind/Nethermind.Evm/Instruction.cs | 1 + 4 files changed, 104 insertions(+), 33 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5e49af9e47f..0b8e722c546 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -49,8 +49,8 @@ public static Func CompileSegment(string segmentName, Op const int wordToAlignTo = 32; // init ReturnState - method.LoadLocalAddress(returnState); - method.InitializeObject(); + method.LoadArgument(0); + method.StoreLocal(returnState); // allocate stack method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); @@ -80,7 +80,6 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(programCounter); Label ret = method.DefineLabel("Return"); // the label just before return - Label invalidAddress = method.DefineLabel("InvalidAddress"); // invalid jump address Label jumpTable = method.DefineLabel("Jumptable"); // jump table Dictionary jumpDestinations = new(); @@ -118,6 +117,12 @@ public static Func CompileSegment(string segmentName, Op // else emit switch (op.Operation) { + case Instruction.STOP: + method.LoadLocalAddress(returnState); + method.LoadConstant(true); + method.StoreField(GetFieldInfo(nameof(ILEvmState.StopExecution))); + method.Branch(ret); + break; case Instruction.JUMPDEST: // mark the jump destination jumpDestinations[pc] = method.DefineLabel(); @@ -213,7 +218,7 @@ public static Func CompileSegment(string segmentName, Op break; case Instruction.MOD: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); @@ -228,7 +233,7 @@ public static Func CompileSegment(string segmentName, Op il.Branch(postInstructionLabel); il.MarkLabel(label); - }); + }, uint256A, uint256B); break; case Instruction.DIV: @@ -247,11 +252,11 @@ public static Func CompileSegment(string segmentName, Op il.Branch(postInstructionLabel); il.MarkLabel(label); - }); + }, uint256A, uint256B); break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null); + EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.LT: EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); @@ -263,6 +268,8 @@ public static Func CompileSegment(string segmentName, Op EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); break; case Instruction.ISZERO: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); // we load the currentSP method.StackLoadPrevious(currentSP, 1); method.StackPop(currentSP, 1); @@ -271,8 +278,7 @@ public static Func CompileSegment(string segmentName, Op method.Call(Word.GetIsZero); // we convert the result to a Uint256 and store it in the currentSP - method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); - method.Call(Word.SetUInt256); + method.StoreField(GetFieldInfo(nameof(Word.Byte0))); method.StackPush(currentSP); break; case Instruction.POP: @@ -341,8 +347,7 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadConstant(code.Length); - method.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); - method.Call(Word.SetUInt256); + method.StoreField(GetFieldInfo(nameof(Word.UInt0))); method.StackPush(currentSP); break; case Instruction.PC: @@ -358,9 +363,6 @@ public static Func CompileSegment(string segmentName, Op method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); method.LoadField(GetFieldInfo(nameof(BlockHeader.GasBeneficiary))); - method.LoadObject(typeof(Address)); - method.StoreLocal(address); - method.LoadLocal(address); method.Call(Word.SetAddress); method.StackPush(currentSP); break; @@ -370,9 +372,6 @@ public static Func CompileSegment(string segmentName, Op method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); method.LoadField(GetFieldInfo(nameof(BlockHeader.Timestamp))); - method.LoadObject(typeof(UInt256)); - method.StoreLocal(uint256A); - method.LoadLocal(uint256A); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -382,9 +381,6 @@ public static Func CompileSegment(string segmentName, Op method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); method.LoadField(GetFieldInfo(nameof(BlockHeader.Number))); - method.LoadObject(typeof(UInt256)); - method.StoreLocal(uint256A); - method.LoadLocal(uint256A); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -394,13 +390,85 @@ public static Func CompileSegment(string segmentName, Op method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); method.LoadField(GetFieldInfo(nameof(BlockHeader.GasLimit))); - method.LoadObject(typeof(UInt256)); - method.StoreLocal(uint256A); - method.LoadLocal(uint256A); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; - default: + case Instruction.CALLER: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.Caller))); + method.Call(Word.SetAddress); + method.StackPush(currentSP); + break; + case Instruction.ADDRESS: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.ExecutingAccount))); + method.Call(Word.SetAddress); + method.StackPush(currentSP); + break; + case Instruction.ORIGIN: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.TxCtx))); + method.LoadField(GetFieldInfo(nameof(TxExecutionContext.Origin))); + method.Call(Word.SetAddress); + method.StackPush(currentSP); + break; + case Instruction.CALLVALUE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.Value))); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.GASPRICE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(TxExecutionContext.GasPrice))); + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + + case Instruction.CALLDATALOAD: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.InputData))); + method.StackLoadPrevious(currentSP, 1); + method.LoadConstant(32); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); + method.Call(typeof(ZeroPaddedSpan).GetMethod(nameof(ZeroPaddedSpan.ToArray), BindingFlags.Instance | BindingFlags.Public)); + method.StoreLocal(localArr); + // we call UInt256 constructor taking a span of bytes and a bool + method.LoadLocalAddress(localArr); + method.LoadConstant(0); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.CALLDATASIZE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.InputData))); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + method.StoreField(GetFieldInfo(nameof(Word.Int0))); + method.StackPush(currentSP); + break; + default: throw new NotSupportedException(); } } @@ -436,12 +504,6 @@ public static Func CompileSegment(string segmentName, Op }); method.StoreField(GetFieldInfo(nameof(ILEvmState.Stack))); - // set header - method.LoadLocalAddress(returnState); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); - method.StoreField(GetFieldInfo(nameof(ILEvmState.Header))); - // set gas available method.LoadLocalAddress(returnState); method.LoadLocal(gasAvailable); @@ -477,7 +539,7 @@ public static Func CompileSegment(string segmentName, Op method.LoadConstant(uint.MaxValue); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); // goto invalid address - method.BranchIfTrue(invalidAddress); + method.BranchIfTrue(labels[EvmExceptionType.InvalidJumpDestination]); // else const int jumpFanOutLog = 7; // 128 @@ -513,7 +575,7 @@ public static Func CompileSegment(string segmentName, Op } // each bucket ends with a jump to invalid access to do not fall through to another one - method.Branch(invalidAddress); + method.Branch(labels[EvmExceptionType.InvalidJumpDestination]); } foreach (var kvp in labels) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 8cf88160d8e..74a8fe9a653 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -15,7 +15,9 @@ internal struct ILEvmState { // static arguments public BlockHeader Header; - + public ExecutionEnvironment Env; + public TxExecutionContext TxCtx; + public BlockExecutionContext BlkCtx; public byte[] StackBytes { set diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 3ea61f14702..b2e52e67e25 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -19,6 +19,12 @@ public static FieldInfo GetFieldInfo(string name) { return typeof(T).GetField(name); } + public static MethodInfo GetPropertyInfo(string name, bool getSetter, out PropertyInfo propInfo) + { + propInfo = typeof(T).GetProperty(name); + return getSetter? propInfo.GetSetMethod() : propInfo.GetGetMethod(); + } + public static void Load(this Emit il, Local local, FieldInfo wordField) { if (local.LocalType != typeof(Word*)) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 37f379ebecb..bb21660790d 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -394,6 +394,7 @@ public static class InstructionExtensions Instruction.TLOAD or Instruction.TSTORE => true, Instruction.EXTCODESIZE or Instruction.EXTCODECOPY or Instruction.EXTCODEHASH => true, Instruction.SELFDESTRUCT => true, + Instruction.BALANCE => true, _ => false, }; From 04ff54cab00d5520770459caa6f84f8fcb18a5b4 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 18 May 2024 17:40:52 +0100 Subject: [PATCH 032/146] Added memory opcodes initial implementation --- .../CodeAnalysis/IlEvmTests.cs | 17 +- .../CodeAnalysis/IL/ILCompiler.cs | 200 ++++++++++++++++-- .../CodeAnalysis/IL/ILEvmState.cs | 3 +- .../CodeAnalysis/IL/ILExtensions.cs | 3 +- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 108 +++++----- src/Nethermind/Nethermind.Evm/Instruction.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 36 ++-- 7 files changed, 272 insertions(+), 99 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 0db9cbfe3a9..bef7fb982c3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -179,6 +179,21 @@ public static IEnumerable GetBytecodes() .PushSingle(7) .DIV() .Done; + + yield return Prepare.EvmCode + .MSTORE(0, ((UInt256)23).PaddedBytes(32)) + .Done; + + yield return Prepare.EvmCode + .MSTORE(0, ((UInt256)23).PaddedBytes(32)) + .MLOAD(0) + .Done; + + yield return Prepare.EvmCode + .MSTORE(0, ((UInt256)23).PaddedBytes(32)) + .MCOPY(0, 32, 32) + .EQ() + .Done; } [Test, TestCaseSource(nameof(GetBytecodes))] @@ -188,7 +203,7 @@ public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) { Stack = new UInt256[1024], Header = BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header, - GasAvailable = 1000, + GasAvailable = 1000000, ProgramCounter = 0, EvmException = EvmExceptionType.None, StopExecution = false diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 0b8e722c546..51be62bf936 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; using Label = Sigil.Label; using static Nethermind.Evm.IL.EmitExtensions; +using static Nethermind.Evm.Tracing.GethStyle.JavaScript.Log; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler @@ -23,6 +24,7 @@ public static Func CompileSegment(string segmentName, Op { // code is optimistic assumes stack underflow and stack overflow to not occure (WE NEED EOF FOR THIS) // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? + // Note(Ayman) : verify all endianness arguments and bytes Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); @@ -34,17 +36,19 @@ public static Func CompileSegment(string segmentName, Op using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); - using Local localArr = method.DeclareLocal(typeof(ReadOnlySpan)); + using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); + using Local uint64A = method.DeclareLocal(typeof(ulong)); using Local uint32A = method.DeclareLocal(typeof(uint)); + using Local byte8A = method.DeclareLocal(typeof(byte)); - using Local gasAvailable = method.DeclareLocal(typeof(int)); + using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local programCounter = method.DeclareLocal(typeof(ushort)); using Local returnState = method.DeclareLocal(typeof(ILEvmState)); using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); - using Local memory = method.DeclareLocal(typeof(byte*)); + using Local memory = method.DeclareLocal(typeof(EvmPooledMemory)); const int wordToAlignTo = 32; @@ -52,6 +56,12 @@ public static Func CompileSegment(string segmentName, Op method.LoadArgument(0); method.StoreLocal(returnState); + // init memory + method.LoadArgument(0); + method.LoadField(GetFieldInfo(nameof(ILEvmState.Memory))); + method.StoreLocal(memory); + + // allocate stack method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); method.LocalAllocate(); @@ -64,7 +74,7 @@ public static Func CompileSegment(string segmentName, Op // set gas to local method.LoadArgument(0); method.LoadField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); - method.Convert(); + method.Convert(); method.StoreLocal(gasAvailable); Dictionary labels = new(); @@ -87,7 +97,7 @@ public static Func CompileSegment(string segmentName, Op // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method - Dictionary gasCost = BuildCostLookup(code); + Dictionary gasCost = BuildCostLookup(code); for (int pc = 0; pc < code.Length; pc++) { OpcodeInfo op = code[pc]; @@ -107,9 +117,8 @@ public static Func CompileSegment(string segmentName, Op // check if gas is available method.Duplicate(); - method.LoadConstant(0); method.StoreLocal(gasAvailable); - method.Convert(); + method.LoadConstant((long)0); // if gas is not available, branch to out of gas method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); @@ -194,10 +203,10 @@ public static Func CompileSegment(string segmentName, Op // we load the span of bytes method.LoadArray(bytes.ToArray()); - method.StoreLocal(localArr); + method.StoreLocal(localReadonOnlySpan); // we call UInt256 constructor taking a span of bytes and a bool - method.LoadLocalAddress(localArr); + method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(0); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); @@ -259,13 +268,13 @@ public static Func CompileSegment(string segmentName, Op EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.LT: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.GT: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.EQ: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256), typeof(UInt256) })); + EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.ISZERO: method.CleanWord(currentSP); @@ -449,9 +458,9 @@ public static Func CompileSegment(string segmentName, Op method.LoadConstant(32); method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); method.Call(typeof(ZeroPaddedSpan).GetMethod(nameof(ZeroPaddedSpan.ToArray), BindingFlags.Instance | BindingFlags.Public)); - method.StoreLocal(localArr); + method.StoreLocal(localReadonOnlySpan); // we call UInt256 constructor taking a span of bytes and a bool - method.LoadLocalAddress(localArr); + method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(0); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); @@ -468,7 +477,147 @@ public static Func CompileSegment(string segmentName, Op method.StoreField(GetFieldInfo(nameof(Word.Int0))); method.StackPush(currentSP); break; - default: + + case Instruction.MSIZE: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + + method.LoadLocal(memory); + method.LoadField(GetFieldInfo(nameof(EvmPooledMemory.Size))); + method.StoreField(GetFieldInfo(nameof(Word.Int0))); + method.StackPush(currentSP); + break; + case Instruction.MSTORE: + method.StackLoadPrevious(currentSP, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(currentSP, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(currentSP, 2); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(32); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadConstant(32); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); + break; + case Instruction.MSTORE8: + method.StackLoadPrevious(currentSP, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(currentSP, 2); + method.LoadField(Word.Byte0Field); + method.StoreLocal(byte8A); + method.StackPop(currentSP, 2); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(1); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadLocal(memory); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadConstant(32); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); + method.LoadConstant(0); + method.LoadElement(); + + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveByte))); + break; + case Instruction.MLOAD: + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + + method.StackLoadPrevious(currentSP, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(currentSP, 1); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(32); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(uint256A); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(0); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + + method.Call(Word.SetUInt256); + method.StackPush(currentSP); + break; + case Instruction.MCOPY: + method.StackLoadPrevious(currentSP, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + + method.StackLoadPrevious(currentSP, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + + method.StackLoadPrevious(currentSP, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + + method.StackPop(currentSP, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.VeryLow); + method.Multiply(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Max))); + method.StoreLocal(uint256R); + method.LoadLocalAddress(uint256R); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(memory); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(memory); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + break; + default: throw new NotSupportedException(); } } @@ -504,6 +653,11 @@ public static Func CompileSegment(string segmentName, Op }); method.StoreField(GetFieldInfo(nameof(ILEvmState.Stack))); + // set memory + method.LoadLocalAddress(returnState); + method.LoadLocal(memory); + method.StoreField(GetFieldInfo(nameof(ILEvmState.Memory))); + // set gas available method.LoadLocalAddress(returnState); method.LoadLocal(gasAvailable); @@ -596,20 +750,24 @@ public static Func CompileSegment(string segmentName, Op return del; } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, params Local[] locals) { // we the two uint256 from the stack il.StackLoadPrevious(currentSP, 1); il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); // invoke op on the uint256 + il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); il.Call(operation, null); // convert to conv_i il.Convert(); - il.Call(typeof(UInt256).GetMethod("op_Implicit", new[] { typeof(int) })); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); il.StoreLocal(uint256R); il.StackPop(currentSP, 2); @@ -622,7 +780,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { - Label label = il.DefineLabel(); + Label label = il.DefineLabel("SkipHandlingBinaryOp"); // we the two uint256 from the stack il.StackLoadPrevious(currentSP, 1); @@ -652,11 +810,11 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.StackPush(currentSP); } - private static Dictionary BuildCostLookup(ReadOnlySpan code) + private static Dictionary BuildCostLookup(ReadOnlySpan code) { - Dictionary costs = new(); + Dictionary costs = new(); int costStart = 0; - int costcurrentSP = 0; + long costcurrentSP = 0; for (int pc = 0; pc < code.Length; pc++) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 74a8fe9a653..3067bdbfb70 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -52,8 +52,9 @@ public byte[] StackBytes // in case of jumps crossing section boundaries public ushort ProgramCounter; - public int GasAvailable; + public long GasAvailable; // in case STOP is executed public bool StopExecution; + public EvmPooledMemory Memory; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index b2e52e67e25..07543ccf11a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -166,7 +166,6 @@ public static void LoadArray(this Emit il, ReadOnlySpan value) il.NewArray(); // get methodInfo of AsSpan from int[] it is a public instance method - var ArrToSpanMethod = typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) }); for (int i = 0; i < value.Length; i++) { @@ -176,7 +175,7 @@ public static void LoadArray(this Emit il, ReadOnlySpan value) il.StoreElement(); } - il.Call(ArrToSpanMethod); + il.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); } } diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index c49ec28d4ff..68dc8cae654 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -5,62 +5,62 @@ namespace Nethermind.Evm { public static class GasCostOf { - public const int Base = 2; - public const int VeryLow = 3; - public const int Low = 5; - public const int Mid = 8; - public const int High = 10; - public const int ExtCode = 20; - public const int ExtCodeEip150 = 700; - public const int Balance = 20; - public const int BalanceEip150 = 400; - public const int BalanceEip1884 = 700; - public const int SLoad = 50; - public const int SLoadEip150 = 200; - public const int SLoadEip1884 = 800; - public const int JumpDest = 1; - public const int SStoreNetMeteredEip1283 = 200; - public const int SStoreNetMeteredEip2200 = 800; - public const int SSet = 20000; - public const int SReset = 5000; - public const int Create = 32000; - public const int CodeDeposit = 200; - public const int Call = 40; - public const int CallEip150 = 700; - public const int CallValue = 9000; - public const int CallStipend = 2300; - public const int NewAccount = 25000; - public const int Exp = 10; - public const int ExpByte = 10; - public const int ExpByteEip160 = 50; - public const int Memory = 3; - public const int TxCreate = 32000; - public const int TxDataZero = 4; - public const int TxDataNonZero = 68; - public const int TxDataNonZeroEip2028 = 16; - public const int Transaction = 21000; - public const int BlobHash = 3; - public const int Log = 375; - public const int LogTopic = 375; - public const int LogData = 8; - public const int Sha3 = 30; - public const int Sha3Word = 6; - public const int BlockHash = 20; - public const int SelfDestruct = 0; - public const int SelfDestructEip150 = 5000; - public const int ExtCodeHash = 400; - public const int ExtCodeHashEip1884 = 700; - public const int SelfBalance = 5; - public const int InitCodeWord = 2; //eip-3860 gas per word cost for init code size + public const long Base = 2; + public const long VeryLow = 3; + public const long Low = 5; + public const long Mid = 8; + public const long High = 10; + public const long ExtCode = 20; + public const long ExtCodeEip150 = 700; + public const long Balance = 20; + public const long BalanceEip150 = 400; + public const long BalanceEip1884 = 700; + public const long SLoad = 50; + public const long SLoadEip150 = 200; + public const long SLoadEip1884 = 800; + public const long JumpDest = 1; + public const long SStoreNetMeteredEip1283 = 200; + public const long SStoreNetMeteredEip2200 = 800; + public const long SSet = 20000; + public const long SReset = 5000; + public const long Create = 32000; + public const long CodeDeposit = 200; + public const long Call = 40; + public const long CallEip150 = 700; + public const long CallValue = 9000; + public const long CallStipend = 2300; + public const long NewAccount = 25000; + public const long Exp = 10; + public const long ExpByte = 10; + public const long ExpByteEip160 = 50; + public const long Memory = 3; + public const long TxCreate = 32000; + public const long TxDataZero = 4; + public const long TxDataNonZero = 68; + public const long TxDataNonZeroEip2028 = 16; + public const long Transaction = 21000; + public const long BlobHash = 3; + public const long Log = 375; + public const long LogTopic = 375; + public const long LogData = 8; + public const long Sha3 = 30; + public const long Sha3Word = 6; + public const long BlockHash = 20; + public const long SelfDestruct = 0; + public const long SelfDestructEip150 = 5000; + public const long ExtCodeHash = 400; + public const long ExtCodeHashEip1884 = 700; + public const long SelfBalance = 5; + public const long InitCodeWord = 2; //eip-3860 gas per word cost for init code size - public const int ColdSLoad = 2100; // eip-2929 + public const long ColdSLoad = 2100; // eip-2929 - public const int ColdAccountAccess = 2600; // eip-2929 - public const int WarmStateRead = 100; // eip-2929 + public const long ColdAccountAccess = 2600; // eip-2929 + public const long WarmStateRead = 100; // eip-2929 - public const int AccessAccountListEntry = 2400; // eip-2930 - public const int AccessStorageListEntry = 1900; // eip-2930 - public const int TLoad = WarmStateRead; // eip-1153 - public const int TStore = WarmStateRead; // eip-1153 + public const long AccessAccountListEntry = 2400; // eip-2930 + public const long AccessStorageListEntry = 1900; // eip-2930 + public const long TLoad = WarmStateRead; // eip-1153 + public const long TStore = WarmStateRead; // eip-1153 } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index bb21660790d..4d6395c233b 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -179,12 +179,12 @@ public enum Instruction : byte INVALID = 0xfe, SELFDESTRUCT = 0xff, } - public record struct OpcodeMetadata(int gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) + public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) { /// /// The gas cost. /// - public int GasCost { get; } = gasCost; + public long GasCost { get; } = gasCost; /// /// How many following bytes does this instruction have. diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 531b959c1ea..06063009f6a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -759,7 +759,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM if (previousCallOutput.Length > 0) { UInt256 localPreviousDest = previousCallOutputDestination; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in localPreviousDest, (ulong)previousCallOutput.Length)) + if (!UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in localPreviousDest, (ulong)previousCallOutput.Length)) { goto OutOfGas; } @@ -1242,7 +1242,7 @@ private CallResult ExecuteCode externalCode = GetCachedCodeInfo(_worldState, address, spec).MachineCode; slice = externalCode.SliceWithZeroPadding(b, (int)result); @@ -1475,7 +1475,7 @@ private CallResult ExecuteCode( } if (!UpdateGas(spec.GetCallCost(), ref gasAvailable) || - !UpdateMemoryCost(vmState, ref gasAvailable, in dataOffset, dataLength) || - !UpdateMemoryCost(vmState, ref gasAvailable, in outputOffset, outputLength) || + !UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in dataOffset, dataLength) || + !UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in outputOffset, outputLength) || !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; if (spec.Use63Over64Rule) @@ -2359,7 +2359,7 @@ private static EvmExceptionType InstructionRevert(EvmState vmState, re !stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) + if (!UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in position, in length)) { return EvmExceptionType.OutOfGas; } @@ -2378,7 +2378,7 @@ private static EvmExceptionType InstructionReturn(EvmState vmState, re !stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, in length)) + if (!UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in position, in length)) { return EvmExceptionType.OutOfGas; } @@ -2469,7 +2469,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref if (!UpdateGas(gasCost, ref gasAvailable)) return (EvmExceptionType.OutOfGas, null); - if (!UpdateMemoryCost(vmState, ref gasAvailable, in memoryPositionOfInitCode, initCodeLength)) return (EvmExceptionType.OutOfGas, null); + if (!UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in memoryPositionOfInitCode, initCodeLength)) return (EvmExceptionType.OutOfGas, null); // TODO: copy pasted from CALL / DELEGATECALL, need to move it outside? if (env.CallDepth >= MaxCallDepth) // TODO: fragile ordering / potential vulnerability for different clients @@ -2581,7 +2581,7 @@ private static EvmExceptionType InstructionLog(EvmState vmState, ref E if (!stack.PopUInt256(out UInt256 position)) return EvmExceptionType.StackUnderflow; if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow; long topicsCount = instruction - Instruction.LOG0; - if (!UpdateMemoryCost(vmState, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; + if (!UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, in position, length)) return EvmExceptionType.OutOfGas; if (!UpdateGas( GasCostOf.Log + topicsCount * GasCostOf.LogTopic + (long)length * GasCostOf.LogData, ref gasAvailable)) return EvmExceptionType.OutOfGas; @@ -2801,9 +2801,9 @@ private static void UpdateCurrentState(EvmState state, int pc, long gas, int sta state.DataStackHead = stackHead; } - private static bool UpdateMemoryCost(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) + public static bool UpdateMemoryCost(ref EvmPooledMemory memory, ref long gasAvailable, in UInt256 position, in UInt256 length) { - long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length); + long memoryCost = memory.CalculateMemoryCost(in position, length); if (memoryCost != 0L) { if (!UpdateGas(memoryCost, ref gasAvailable)) From 251db2448f652702873563e77407fc3545228f19 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 21 May 2024 22:22:05 +0100 Subject: [PATCH 033/146] minor refactor and bug fixes --- .../CodeAnalysis/IlEvmTests.cs | 5 ++-- .../CodeAnalysis/IL/ILCompiler.cs | 29 ++++++++++--------- src/Nethermind/Nethermind.Evm/Instruction.cs | 5 ---- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index bef7fb982c3..9fc754f5979 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -141,8 +141,9 @@ public static IEnumerable GetBytecodes() yield return Prepare.EvmCode .Done; yield return Prepare.EvmCode - .PushSingle(7) - .ISZERO() + .ISZERO(7) + .ISZERO(7) + .ISZERO(7) .Done; yield return Prepare.EvmCode .PushSingle(23) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 51be62bf936..b4004d10dd9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -15,7 +15,6 @@ using System.Threading.Tasks; using Label = Sigil.Label; using static Nethermind.Evm.IL.EmitExtensions; -using static Nethermind.Evm.Tracing.GethStyle.JavaScript.Log; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler @@ -197,8 +196,7 @@ public static Func CompileSegment(string segmentName, Op ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); // we load the currentSP - method.LoadLocal(currentSP); - method.InitializeObject(typeof(Word)); + method.CleanWord(currentSP); method.LoadLocal(currentSP); // we load the span of bytes @@ -207,7 +205,7 @@ public static Func CompileSegment(string segmentName, Op // we call UInt256 constructor taking a span of bytes and a bool method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(0); + method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); // we store the UInt256 in the currentSP @@ -277,16 +275,17 @@ public static Func CompileSegment(string segmentName, Op EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.ISZERO: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + // we load the currentSP method.StackLoadPrevious(currentSP, 1); - method.StackPop(currentSP, 1); - - // we call the IsZero method on the UInt256 method.Call(Word.GetIsZero); + method.StackPop(currentSP, 1); + method.StoreLocal(byte8A); // we convert the result to a Uint256 and store it in the currentSP + method.CleanWord(currentSP); + method.LoadLocal(currentSP); + method.LoadLocal(byte8A); method.StoreField(GetFieldInfo(nameof(Word.Byte0))); method.StackPush(currentSP); break; @@ -461,7 +460,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(localReadonOnlySpan); // we call UInt256 constructor taking a span of bytes and a bool method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(0); + method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); @@ -569,7 +568,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(localReadonOnlySpan); method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(0); + method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); @@ -761,8 +760,8 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StoreLocal(locals[1]); // invoke op on the uint256 - il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[0]); il.Call(operation, null); // convert to conv_i @@ -772,6 +771,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackPop(currentSP, 2); // push the result to the stack + il.CleanWord(currentSP); il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); @@ -789,21 +789,22 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.StackLoadPrevious(currentSP, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); + il.StackPop(currentSP, 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); // invoke op on the uint256 - il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(uint256R); il.Call(operation); - il.StackPop(currentSP, 2); // skip the main handling il.MarkLabel(label); // push the result to the stack + il.CleanWord(currentSP); il.LoadLocal(currentSP); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index b60c316a90d..e681a0b0b6d 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -366,11 +366,6 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta [Instruction.REVERT] = new(GasCostOf.Base, 0, 2, 0), [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct, 0, 1, 0), - - [Instruction.BEGINSUB] = new(GasCostOf.Base, 0, 0, 0), - [Instruction.RETURNSUB] = new(GasCostOf.Base, 0, 0, 0), - [Instruction.JUMPSUB] = new(GasCostOf.Base, 0, 1, 0), - }.ToFrozenDictionary(); } public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments) From 1abc3ddbd90bb61233579c76038d9bcead60cbfd Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 23 May 2024 02:13:52 +0100 Subject: [PATCH 034/146] optimized away some flaws, refactored code, and more bug fixes --- .../CodeAnalysis/IlEvmTests.cs | 9 +- .../CodeAnalysis/IL/ILCompiler.cs | 148 ++++++++---------- .../CodeAnalysis/IL/ILEvmState.cs | 31 +--- .../CodeAnalysis/IL/ILExtensions.cs | 14 +- .../CodeAnalysis/IL/IlAnalyzer.cs | 7 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 21 +-- .../Nethermind.Evm/VirtualMachine.cs | 8 +- 7 files changed, 104 insertions(+), 134 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 9fc754f5979..314d9226877 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -142,7 +142,7 @@ public static IEnumerable GetBytecodes() .Done; yield return Prepare.EvmCode .ISZERO(7) - .ISZERO(7) + .ISZERO(0) .ISZERO(7) .Done; yield return Prepare.EvmCode @@ -202,16 +202,17 @@ public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) { ILEvmState iLEvmState = new ILEvmState { - Stack = new UInt256[1024], + Stack = new byte[1024], Header = BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header, GasAvailable = 1000000, ProgramCounter = 0, EvmException = EvmExceptionType.None, StopExecution = false }; + var memory = new EvmPooledMemory(); var function = ILCompiler.CompileSegment("ILEVM_TEST", IlAnalyzer.StripByteCode(bytecode)); - var il_result = function(iLEvmState); - Assert.IsNotNull(il_result); + var il_result = function(iLEvmState, ref memory); + Assert.IsTrue(il_result.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b4004d10dd9..fd3fa182d11 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -15,17 +15,20 @@ using System.Threading.Tasks; using Label = Sigil.Label; using static Nethermind.Evm.IL.EmitExtensions; +using MathGmp.Native; +using Nethermind.Evm.Tracing.GethStyle.Custom.Native.FourByte; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public static Func CompileSegment(string segmentName, OpcodeInfo[] code) + public delegate ILEvmState ExecuteSegment(ILEvmState state, ref EvmPooledMemory memory); + public static ExecuteSegment CompileSegment(string segmentName, OpcodeInfo[] code) { // code is optimistic assumes stack underflow and stack overflow to not occure (WE NEED EOF FOR THIS) // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? // Note(Ayman) : verify all endianness arguments and bytes - Emit> method = Emit>.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); @@ -47,20 +50,12 @@ public static Func CompileSegment(string segmentName, Op using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); - using Local memory = method.DeclareLocal(typeof(EvmPooledMemory)); - const int wordToAlignTo = 32; // init ReturnState method.LoadArgument(0); method.StoreLocal(returnState); - // init memory - method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Memory))); - method.StoreLocal(memory); - - // allocate stack method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); method.LocalAllocate(); @@ -72,7 +67,7 @@ public static Func CompileSegment(string segmentName, Op // set gas to local method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); method.Convert(); method.StoreLocal(gasAvailable); @@ -85,11 +80,12 @@ public static Func CompileSegment(string segmentName, Op // set pc to local method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.ProgramCounter))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.StoreLocal(programCounter); - Label ret = method.DefineLabel("Return"); // the label just before return + Label exit = method.DefineLabel("Return"); // the label just before return Label jumpTable = method.DefineLabel("Jumptable"); // jump table + Label ret = method.DefineLabel("return"); Dictionary jumpDestinations = new(); @@ -128,7 +124,7 @@ public static Func CompileSegment(string segmentName, Op case Instruction.STOP: method.LoadLocalAddress(returnState); method.LoadConstant(true); - method.StoreField(GetFieldInfo(nameof(ILEvmState.StopExecution))); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StopExecution))); method.Branch(ret); break; case Instruction.JUMPDEST: @@ -286,7 +282,7 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadLocal(byte8A); - method.StoreField(GetFieldInfo(nameof(Word.Byte0))); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Byte0))); method.StackPush(currentSP); break; case Instruction.POP: @@ -355,22 +351,22 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadConstant(code.Length); - method.StoreField(GetFieldInfo(nameof(Word.UInt0))); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); method.StackPush(currentSP); break; case Instruction.PC: method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadLocal(programCounter); - method.StoreField(GetFieldInfo(nameof(Word.UInt0))); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); method.StackPush(currentSP); break; case Instruction.COINBASE: method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); - method.LoadField(GetFieldInfo(nameof(BlockHeader.GasBeneficiary))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); method.Call(Word.SetAddress); method.StackPush(currentSP); break; @@ -378,8 +374,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); - method.LoadField(GetFieldInfo(nameof(BlockHeader.Timestamp))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -387,8 +383,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); - method.LoadField(GetFieldInfo(nameof(BlockHeader.Number))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -396,8 +392,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Header))); - method.LoadField(GetFieldInfo(nameof(BlockHeader.GasLimit))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -405,8 +401,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.Caller))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); method.StackPush(currentSP); break; @@ -414,8 +410,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); method.StackPush(currentSP); break; @@ -423,8 +419,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.TxCtx))); - method.LoadField(GetFieldInfo(nameof(TxExecutionContext.Origin))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin))); method.Call(Word.SetAddress); method.StackPush(currentSP); break; @@ -432,8 +428,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.Value))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -441,8 +437,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(TxExecutionContext.GasPrice))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice))); method.Call(Word.SetUInt256); method.StackPush(currentSP); break; @@ -451,8 +447,8 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); method.StackLoadPrevious(currentSP, 1); method.LoadConstant(32); method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); @@ -470,10 +466,10 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); method.LoadArgument(0); - method.LoadField(GetFieldInfo(nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.StoreField(GetFieldInfo(nameof(Word.Int0))); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); method.StackPush(currentSP); break; @@ -481,9 +477,9 @@ public static Func CompileSegment(string segmentName, Op method.CleanWord(currentSP); method.LoadLocal(currentSP); - method.LoadLocal(memory); - method.LoadField(GetFieldInfo(nameof(EvmPooledMemory.Size))); - method.StoreField(GetFieldInfo(nameof(Word.Int0))); + method.LoadArgumentAddress(1); + method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); method.StackPush(currentSP); break; case Instruction.MSTORE: @@ -495,7 +491,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(uint256B); method.StackPop(currentSP, 2); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(32); @@ -505,7 +501,7 @@ public static Func CompileSegment(string segmentName, Op method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.LoadConstant(32); @@ -522,7 +518,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(byte8A); method.StackPop(currentSP, 2); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(1); @@ -532,7 +528,7 @@ public static Func CompileSegment(string segmentName, Op method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadLocal(memory); + method.LoadArgumentAddress(1); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.LoadConstant(32); @@ -551,7 +547,7 @@ public static Func CompileSegment(string segmentName, Op method.StoreLocal(uint256A); method.StackPop(currentSP, 1); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(32); @@ -561,7 +557,7 @@ public static Func CompileSegment(string segmentName, Op method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(uint256A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); @@ -597,7 +593,7 @@ public static Func CompileSegment(string segmentName, Op method.Subtract(); method.StoreLocal(gasAvailable); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -608,9 +604,9 @@ public static Func CompileSegment(string segmentName, Op method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(memory); + method.LoadArgument(1); method.LoadLocalAddress(uint256B); method.LoadLocalAddress(uint256C); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); @@ -621,59 +617,43 @@ public static Func CompileSegment(string segmentName, Op } } + // prepare ILEvmState // check if returnState is null - + method.MarkLabel(ret); // we get stack size method.LoadLocal(currentSP); method.LoadLocal(stack); method.Subtract(); - method.LoadConstant(Word.Size); - method.Divide(); - method.Convert(); + method.Convert(); method.StoreLocal(uint32A); // set stack method.LoadLocalAddress(returnState); + method.LoadLocal(stack); + method.Convert(); + method.Call(typeof(nuint).GetMethods().Where(method => method.Name == "op_Explicit" && method.ReturnType == typeof(void*)).FirstOrDefault()); method.LoadLocal(uint32A); - method.NewArray(); - - // we iterate in IL over the stack and store the values - method.ForBranch(uint32A, (il, i) => - { - il.Duplicate(); - - il.LoadLocal(i); - il.StackLoadPrevious(currentSP); - il.Call(Word.GetUInt256); - il.StoreElement(); - - il.StackPop(currentSP); - }); - method.StoreField(GetFieldInfo(nameof(ILEvmState.Stack))); - - // set memory - method.LoadLocalAddress(returnState); - method.LoadLocal(memory); - method.StoreField(GetFieldInfo(nameof(ILEvmState.Memory))); + method.NewObject(typeof(Span).GetConstructor([typeof(void*), typeof(int)])); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); // set gas available method.LoadLocalAddress(returnState); method.LoadLocal(gasAvailable); - method.StoreField(GetFieldInfo(nameof(ILEvmState.GasAvailable))); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); // set program counter method.LoadLocalAddress(returnState); method.LoadLocal(programCounter); - method.StoreField(GetFieldInfo(nameof(ILEvmState.ProgramCounter))); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); // set exception method.LoadLocalAddress(returnState); method.LoadConstant((int)EvmExceptionType.None); - method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); // go to return - method.Branch(ret); + method.Branch(exit); // jump table @@ -736,16 +716,16 @@ public static Func CompileSegment(string segmentName, Op method.MarkLabel(kvp.Value); method.LoadLocalAddress(returnState); method.LoadConstant((int)kvp.Key); - method.StoreField(GetFieldInfo(nameof(ILEvmState.EvmException))); - method.Branch(ret); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); + method.Branch(exit); } // return - method.MarkLabel(ret); + method.MarkLabel(exit); method.LoadLocal(returnState); method.Return(); - Func del = method.CreateDelegate(); + ExecuteSegment del = method.CreateDelegate(); return del; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 3067bdbfb70..52dad1f05db 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -11,41 +11,15 @@ using System.Threading.Tasks; namespace Nethermind.Evm.CodeAnalysis.IL; -internal struct ILEvmState +internal ref struct ILEvmState { // static arguments public BlockHeader Header; public ExecutionEnvironment Env; public TxExecutionContext TxCtx; public BlockExecutionContext BlkCtx; - public byte[] StackBytes - { - set - { - if(value.Length % 32 != 0) - { - throw new ArgumentException("Invalid byte array length"); - } - Stack = new UInt256[value.Length / 32]; - for(int i = 0; i < value.Length; i += 32) - { - Stack[i / 32] = new UInt256(value[i..(i + 32)]); - } - } - - get - { - byte[] result = new byte[Stack.Length * 32]; - for(int i = 0; i < Stack.Length; i++) - { - Stack[i].PaddedBytes(32).CopyTo(result, i * 32); - } - return result; - } - } - - public UInt256[] Stack; + public Span Stack; // in case of exceptions public EvmExceptionType EvmException; @@ -56,5 +30,4 @@ public byte[] StackBytes // in case STOP is executed public bool StopExecution; - public EvmPooledMemory Memory; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 07543ccf11a..f89f56abff5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core; using Nethermind.Evm.CodeAnalysis.IL; using Sigil; using System; @@ -15,9 +16,18 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { - public static FieldInfo GetFieldInfo(string name) + unsafe static TResult ReinterpretCast(TOriginal original) + where TOriginal : struct + where TResult : struct { - return typeof(T).GetField(name); +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + return *(TResult*)(void*)&original; +#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + } + + public static FieldInfo GetFieldInfo(Type TypeInstance, string name) + { + return TypeInstance.GetField(name); } public static MethodInfo GetPropertyInfo(string name, bool getSetter, out PropertyInfo propInfo) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index bd861cb9bbe..03a1c92012a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; using static System.Net.Mime.MediaTypeNames; using static System.Runtime.InteropServices.JavaScript.JSType; @@ -85,11 +86,9 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - - - FrozenDictionary> SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary SegmentCode(OpcodeInfo[] codeData) { - Dictionary> opcodeInfos = []; + Dictionary opcodeInfos = []; List segment = []; foreach (var opcode in codeData) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index dcefbfbe49c..0c08b7842a2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -5,6 +5,7 @@ using Microsoft.IdentityModel.Tokens; using Nethermind.Core; using Nethermind.Core.Specs; +using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; namespace Nethermind.Evm.CodeAnalysis.IL; /// @@ -35,7 +36,7 @@ public enum ILMode private IlInfo() { Chunks = FrozenDictionary.Empty; - Segments = FrozenDictionary>.Empty; + Segments = FrozenDictionary.Empty; } public IlInfo WithChunks(FrozenDictionary chunks) @@ -44,13 +45,13 @@ public IlInfo WithChunks(FrozenDictionary chunks) return this; } - public IlInfo WithSegments(FrozenDictionary> segments) + public IlInfo WithSegments(FrozenDictionary segments) { Segments = segments; return this; } - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary> segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary segments) { Chunks = mappedOpcodes; Segments = segments; @@ -58,11 +59,12 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi // assumes small number of ILed public FrozenDictionary Chunks { get; set; } - public FrozenDictionary> Segments { get; set; } + public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, IReleaseSpec spec, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack) + public bool TryExecute(EvmState vmState, IReleaseSpec spec, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop) where TTracingInstructions : struct, VirtualMachine.IIsTracing { + shouldStop = false; if (programCounter > ushort.MaxValue) return false; @@ -79,7 +81,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec } case ILMode.SubsegmentsCompiling: { - if (Segments.TryGetValue((ushort)programCounter, out Func method) == false) + if (Segments.TryGetValue((ushort)programCounter, out ExecuteSegment method) == false) { return false; } @@ -87,15 +89,16 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec var ilvmState = new ILEvmState { GasAvailable = (int)gasAvailable, - StackBytes = vmState.DataStack, + Stack = vmState.DataStack, Header = header, ProgramCounter = (ushort)programCounter, }; - ilvmState = method.Invoke(ilvmState); + ilvmState = method.Invoke(ilvmState, ref vmState.Memory); gasAvailable = ilvmState.GasAvailable; - vmState.DataStack = ilvmState.StackBytes; + vmState.DataStack = ilvmState.Stack.ToArray(); programCounter = ilvmState.ProgramCounter; + shouldStop = ilvmState.StopExecution; break; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8e7dabb2c1d..6af48faf7be 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -821,6 +821,7 @@ private CallResult ExecuteCode Date: Thu, 23 May 2024 08:57:05 +0100 Subject: [PATCH 035/146] changed immediate data handling --- .../CodeAnalysis/IlEvmTests.cs | 7 ++--- .../CodeAnalysis/IL/ILCompiler.cs | 27 ++++++++++++------- .../CodeAnalysis/IL/IlAnalyzer.cs | 25 +++++++++-------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 12 ++++----- src/Nethermind/Nethermind.Evm/Instruction.cs | 4 +-- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 314d9226877..a3f35c26150 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -125,7 +125,7 @@ public void Pure_Opcode_Emition_Coveraga() OpcodeInfo opcode = new OpcodeInfo(0, instruction, null); try { - ILCompiler.CompileSegment(name, [opcode]); + ILCompiler.CompileSegment(name, [opcode], []); } catch (Exception e) { notYetImplemented.Add((instruction, e)); @@ -210,8 +210,9 @@ public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) StopExecution = false }; var memory = new EvmPooledMemory(); - var function = ILCompiler.CompileSegment("ILEVM_TEST", IlAnalyzer.StripByteCode(bytecode)); - var il_result = function(iLEvmState, ref memory); + var metadata = IlAnalyzer.StripByteCode(bytecode); + var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); + var il_result = ctx.Method(iLEvmState, ref memory, ctx.Data); Assert.IsTrue(il_result.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index fd3fa182d11..ba0907eb39f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -21,8 +21,12 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate ILEvmState ExecuteSegment(ILEvmState state, ref EvmPooledMemory memory); - public static ExecuteSegment CompileSegment(string segmentName, OpcodeInfo[] code) + public delegate ILEvmState ExecuteSegment(ILEvmState state, ref EvmPooledMemory memory, byte[][] immediatesData); + public class SegmentExecutionCtx { + public ExecuteSegment Method; + public byte[][] Data; + } + public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[] code, byte[][] data) { // code is optimistic assumes stack underflow and stack overflow to not occure (WE NEED EOF FOR THIS) // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? @@ -187,16 +191,15 @@ public static ExecuteSegment CompileSegment(string segmentName, OpcodeInfo[] cod case Instruction.PUSH30: case Instruction.PUSH31: case Instruction.PUSH32: - // we create a span of 32 bytes, and we copy the bytes from the arguments to the span - int count = (int)op.Operation - (int)Instruction.PUSH0; - ZeroPaddedSpan bytes = new ZeroPaddedSpan(op.Arguments.Value.Span, 32 - count, PadDirection.Left); - // we load the currentSP method.CleanWord(currentSP); method.LoadLocal(currentSP); // we load the span of bytes - method.LoadArray(bytes.ToArray()); + method.LoadArgument(2); + method.LoadConstant(op.Arguments.Value); + method.LoadElement(); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); method.StoreLocal(localReadonOnlySpan); // we call UInt256 constructor taking a span of bytes and a bool @@ -304,7 +307,7 @@ public static ExecuteSegment CompileSegment(string segmentName, OpcodeInfo[] cod case Instruction.DUP14: case Instruction.DUP15: case Instruction.DUP16: - count = (int)op.Operation - (int)Instruction.DUP1 + 1; + int count = (int)op.Operation - (int)Instruction.DUP1 + 1; method.LoadLocal(currentSP); method.StackLoadPrevious(currentSP, count); method.LoadObject(typeof(Word)); @@ -725,8 +728,12 @@ public static ExecuteSegment CompileSegment(string segmentName, OpcodeInfo[] cod method.LoadLocal(returnState); method.Return(); - ExecuteSegment del = method.CreateDelegate(); - return del; + ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); + return new SegmentExecutionCtx + { + Method = dynEmitedDelegate, + Data = data + }; } private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, params Local[] locals) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 03a1c92012a..8ebb2630b50 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Int256; using System; using System.Collections.Frozen; using System.Collections.Generic; @@ -59,24 +60,26 @@ public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) return Task.Run(() => Analysis(codeInfo, mode)); } - public static OpcodeInfo[] StripByteCode(ReadOnlySpan machineCode) + public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) { OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; + List data = new List(); int j = 0; for (ushort i = 0; i < machineCode.Length; i++, j++) { Instruction opcode = (Instruction)machineCode[i]; - byte[] args = null; + int? argsIndex = null; ushort pc = i; if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) { ushort immediatesCount = opcode - Instruction.PUSH0; - args = machineCode.Slice(i + 1, immediatesCount).ToArray(); + data.Add(machineCode.SliceWithZeroPadding((UInt256)i + 1, immediatesCount, PadDirection.Left).ToArray()); + argsIndex = data.Count - 1; i += immediatesCount; } - opcodes[j] = new OpcodeInfo(pc, opcode, args.AsMemory()); + opcodes[j] = new OpcodeInfo(pc, opcode, argsIndex); } - return opcodes[..j]; + return (opcodes[..j], data.ToArray()); } /// @@ -86,18 +89,18 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - FrozenDictionary SegmentCode(OpcodeInfo[] codeData) + FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData) { - Dictionary opcodeInfos = []; + Dictionary opcodeInfos = []; List segment = []; - foreach (var opcode in codeData) + foreach (var opcode in codeData.Item1) { if (opcode.Operation.IsStateful()) { if (segment.Count > 0) { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray())); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); segment.Clear(); } } @@ -108,14 +111,14 @@ FrozenDictionary SegmentCode(OpcodeInfo[] codeData) } if (segment.Count > 0) { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray())); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); } return opcodeInfos.ToFrozenDictionary(); } FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode) { - var strippedBytecode = StripByteCode(machineCode.Span); + var (strippedBytecode, data) = StripByteCode(machineCode.Span); var patternFound = new Dictionary(); foreach (var (pattern, mapping) in Patterns) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 0c08b7842a2..443e5c5ae28 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -36,7 +36,7 @@ public enum ILMode private IlInfo() { Chunks = FrozenDictionary.Empty; - Segments = FrozenDictionary.Empty; + Segments = FrozenDictionary.Empty; } public IlInfo WithChunks(FrozenDictionary chunks) @@ -45,13 +45,13 @@ public IlInfo WithChunks(FrozenDictionary chunks) return this; } - public IlInfo WithSegments(FrozenDictionary segments) + public IlInfo WithSegments(FrozenDictionary segments) { Segments = segments; return this; } - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary segments) + public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary segments) { Chunks = mappedOpcodes; Segments = segments; @@ -59,7 +59,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi // assumes small number of ILed public FrozenDictionary Chunks { get; set; } - public FrozenDictionary Segments { get; set; } + public FrozenDictionary Segments { get; set; } public bool TryExecute(EvmState vmState, IReleaseSpec spec, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop) where TTracingInstructions : struct, VirtualMachine.IIsTracing @@ -81,7 +81,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec } case ILMode.SubsegmentsCompiling: { - if (Segments.TryGetValue((ushort)programCounter, out ExecuteSegment method) == false) + if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx) == false) { return false; } @@ -94,7 +94,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec ProgramCounter = (ushort)programCounter, }; - ilvmState = method.Invoke(ilvmState, ref vmState.Memory); + ilvmState = ctx.Method.Invoke(ilvmState, ref vmState.Memory, ctx.Data); gasAvailable = ilvmState.GasAvailable; vmState.DataStack = ilvmState.Stack.ToArray(); programCounter = ilvmState.ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index e681a0b0b6d..74d1b4f7e77 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -368,12 +368,12 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct, 0, 1, 0), }.ToFrozenDictionary(); } - public struct OpcodeInfo(ushort pc, Instruction instruction, ReadOnlyMemory? arguments) + public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) { public OpcodeMetadata? Metadata => OpcodeMetadata.Operations[instruction]; public Instruction Operation => instruction; public ushort ProgramCounter => pc; - public ReadOnlyMemory? Arguments { get; set; } = arguments; + public int? Arguments { get; set; } = argumentIndex; } public static class InstructionExtensions From 32eabdc7f0e6892fa4b29418bd5065607f5b02d5 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 23 May 2024 10:45:57 +0100 Subject: [PATCH 036/146] minor refactors --- .../CodeAnalysis/IlEvmTests.cs | 4 ++-- .../CodeAnalysis/IL/ILCompiler.cs | 20 +++++++------------ .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index a3f35c26150..f600deb40df 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -212,8 +212,8 @@ public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) var memory = new EvmPooledMemory(); var metadata = IlAnalyzer.StripByteCode(bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - var il_result = ctx.Method(iLEvmState, ref memory, ctx.Data); - Assert.IsTrue(il_result.EvmException == EvmExceptionType.None); + ctx.Method(ref iLEvmState, ref memory, ctx.Data); + Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index ba0907eb39f..2d8e6ccf4e5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -21,7 +21,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate ILEvmState ExecuteSegment(ILEvmState state, ref EvmPooledMemory memory, byte[][] immediatesData); + public delegate void ExecuteSegment(ref ILEvmState state, ref EvmPooledMemory memory, byte[][] immediatesData); public class SegmentExecutionCtx { public ExecuteSegment Method; public byte[][] Data; @@ -49,17 +49,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local programCounter = method.DeclareLocal(typeof(ushort)); - using Local returnState = method.DeclareLocal(typeof(ILEvmState)); using Local stack = method.DeclareLocal(typeof(Word*)); using Local currentSP = method.DeclareLocal(typeof(Word*)); const int wordToAlignTo = 32; - // init ReturnState - method.LoadArgument(0); - method.StoreLocal(returnState); - // allocate stack method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); method.LocalAllocate(); @@ -126,7 +121,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ switch (op.Operation) { case Instruction.STOP: - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadConstant(true); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StopExecution))); method.Branch(ret); @@ -632,7 +627,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(uint32A); // set stack - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadLocal(stack); method.Convert(); method.Call(typeof(nuint).GetMethods().Where(method => method.Name == "op_Explicit" && method.ReturnType == typeof(void*)).FirstOrDefault()); @@ -641,17 +636,17 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); // set gas available - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadLocal(gasAvailable); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); // set program counter - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadLocal(programCounter); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); // set exception - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadConstant((int)EvmExceptionType.None); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); @@ -717,7 +712,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ foreach (var kvp in labels) { method.MarkLabel(kvp.Value); - method.LoadLocalAddress(returnState); + method.LoadArgument(0); method.LoadConstant((int)kvp.Key); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); method.Branch(exit); @@ -725,7 +720,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // return method.MarkLabel(exit); - method.LoadLocal(returnState); method.Return(); ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 443e5c5ae28..ad8edb6aa48 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -94,7 +94,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec ProgramCounter = (ushort)programCounter, }; - ilvmState = ctx.Method.Invoke(ilvmState, ref vmState.Memory, ctx.Data); + ctx.Method.Invoke(ref ilvmState, ref vmState.Memory, ctx.Data); gasAvailable = ilvmState.GasAvailable; vmState.DataStack = ilvmState.Stack.ToArray(); programCounter = ilvmState.ProgramCounter; From 84e9c15b221e46189575cecde0996843bf9864a8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 28 May 2024 16:26:03 +0100 Subject: [PATCH 037/146] - bug fixes in Memory opcodes - refactor stack handling --- .../CodeAnalysis/IlEvmTests.cs | 87 ++++-- .../CodeAnalysis/IL/ILCompiler.cs | 268 +++++++++--------- .../CodeAnalysis/IL/ILEvmState.cs | 7 +- .../CodeAnalysis/IL/ILExtensions.cs | 97 ++++--- 4 files changed, 249 insertions(+), 210 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index f600deb40df..9cf79e1a0bf 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -136,81 +136,112 @@ public void Pure_Opcode_Emition_Coveraga() } - public static IEnumerable GetBytecodes() + public static IEnumerable<(int,byte[])> GetBytecodes() { - yield return Prepare.EvmCode - .Done; - yield return Prepare.EvmCode + yield return (-1, Prepare.EvmCode + .Done); + yield return (0, Prepare.EvmCode + .PushSingle(1) + .PushSingle(2) + .PushSingle(3) + .PushSingle(4) + .Done); + yield return (1, Prepare.EvmCode .ISZERO(7) .ISZERO(0) .ISZERO(7) - .Done; - yield return Prepare.EvmCode + .Done); + yield return (2, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .SUB() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (3, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (4, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MUL() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (5, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EXP() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (6, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MOD() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (7, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .DIV() - .Done; + .Done); - yield return Prepare.EvmCode + yield return (8, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) - .Done; + .Done); - yield return Prepare.EvmCode + yield return (9, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) - .Done; + .Done); - yield return Prepare.EvmCode + yield return (10, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) - .MCOPY(0, 32, 32) + .MCOPY(32, 0, 32) + .Done); + + yield return (11, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) .EQ() - .Done; + .Done); + + yield return (12, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .GT() + .Done); + + yield return (13, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .LT() + .Done); + + yield return (14, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .EQ() + .NOT() + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] - public void Ensure_Evm_ILvm_Compatibility(byte[] bytecode) + public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) { ILEvmState iLEvmState = new ILEvmState { - Stack = new byte[1024], + Stack = new byte[1024 * 32], Header = BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header, GasAvailable = 1000000, ProgramCounter = 0, EvmException = EvmExceptionType.None, - StopExecution = false + StopExecution = false, + StackHead = 0 }; var memory = new EvmPooledMemory(); - var metadata = IlAnalyzer.StripByteCode(bytecode); + var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.Method(ref iLEvmState, ref memory, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2d8e6ccf4e5..d7eb5f6338d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -43,6 +43,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); + using Local localSpan = method.DeclareLocal(typeof(Span)); using Local uint64A = method.DeclareLocal(typeof(ulong)); using Local uint32A = method.DeclareLocal(typeof(uint)); using Local byte8A = method.DeclareLocal(typeof(byte)); @@ -50,19 +51,17 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local programCounter = method.DeclareLocal(typeof(ushort)); - using Local stack = method.DeclareLocal(typeof(Word*)); - using Local currentSP = method.DeclareLocal(typeof(Word*)); - - const int wordToAlignTo = 32; + using Local stack = method.DeclareLocal(typeof(Span)); + using Local head = method.DeclareLocal(typeof(int)); // allocate stack - method.LoadConstant(EvmStack.MaxStackSize * Word.Size + wordToAlignTo); - method.LocalAllocate(); - + method.LoadArgument(0); + method.Duplicate(); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); + method.Call(GetCastMethodInfo()); method.StoreLocal(stack); - - method.LoadLocal(stack); - method.StoreLocal(currentSP); // copy to the currentSP + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); + method.StoreLocal(head); // set gas to local method.LoadArgument(0); @@ -138,7 +137,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.JUMPI: // consume the jump condition Label noJump = method.DefineLabel(); - method.StackLoadPrevious(currentSP, 2); + method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetIsZero, null); // if the jump condition is false, we do not jump @@ -152,7 +151,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Branch(jumpTable); method.MarkLabel(noJump); - method.StackPop(currentSP, 2); + method.StackPop(head, 2); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -186,9 +185,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.PUSH30: case Instruction.PUSH31: case Instruction.PUSH32: - // we load the currentSP - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + // we load the stack + method.CleanWord(stack, head); + method.Load(stack, head); // we load the span of bytes method.LoadArgument(2); @@ -202,24 +201,24 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - // we store the UInt256 in the currentSP + // we store the UInt256 in the stack method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.ADD: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.SUB: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.MUL: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.MOD: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); @@ -238,7 +237,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ break; case Instruction.DIV: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); @@ -257,34 +256,33 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; case Instruction.LT: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.GT: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.EQ: - EmitComparaisonUInt256Method(method, uint256R, currentSP, typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.ISZERO: - - // we load the currentSP - method.StackLoadPrevious(currentSP, 1); + // we load the stack + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetIsZero); - method.StackPop(currentSP, 1); + method.StackPop(head, 1); method.StoreLocal(byte8A); - // we convert the result to a Uint256 and store it in the currentSP - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + // we convert the result to a Uint256 and store it in the stack + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadLocal(byte8A); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Byte0))); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.POP: - method.StackPop(currentSP); + method.StackPop(head); break; case Instruction.DUP1: case Instruction.DUP2: @@ -303,11 +301,11 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.DUP15: case Instruction.DUP16: int count = (int)op.Operation - (int)Instruction.DUP1 + 1; - method.LoadLocal(currentSP); - method.StackLoadPrevious(currentSP, count); + method.Load(stack, head); + method.StackLoadPrevious(stack, head, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -328,16 +326,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ count = (int)op.Operation - (int)Instruction.SWAP1 + 1; method.LoadLocalAddress(uint256R); - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackLoadPrevious(currentSP, 1); - method.StackLoadPrevious(currentSP, count); + method.StackLoadPrevious(stack, head, 1); + method.StackLoadPrevious(stack, head, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackLoadPrevious(currentSP, count); + method.StackLoadPrevious(stack, head, count); method.LoadLocalAddress(uint256R); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); @@ -346,108 +344,108 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // Note(Ayman): following opcode need double checking // is pushing to stack happening correctly case Instruction.CODESIZE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.LoadLocal(stack); method.LoadConstant(code.Length); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.PC: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadLocal(programCounter); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.COINBASE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); method.Call(Word.SetAddress); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.TIMESTAMP: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.NUMBER: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.GASLIMIT: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.CALLER: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.ADDRESS: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.ORIGIN: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin))); method.Call(Word.SetAddress); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.CALLVALUE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.GASPRICE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice))); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.CALLDATALOAD: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.LoadConstant(32); method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); method.Call(typeof(ZeroPaddedSpan).GetMethod(nameof(ZeroPaddedSpan.ToArray), BindingFlags.Instance | BindingFlags.Public)); @@ -458,36 +456,36 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.CALLDATASIZE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.MSIZE: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgumentAddress(1); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.MSTORE: - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackLoadPrevious(currentSP, 2); + method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(currentSP, 2); + method.StackPop(head, 2); method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); @@ -508,13 +506,13 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); break; case Instruction.MSTORE8: - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackLoadPrevious(currentSP, 2); + method.StackLoadPrevious(stack, head, 2); method.LoadField(Word.Byte0Field); method.StoreLocal(byte8A); - method.StackPop(currentSP, 2); + method.StackPop(head, 2); method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); @@ -537,13 +535,10 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveByte))); break; case Instruction.MLOAD: - method.CleanWord(currentSP); - method.LoadLocal(currentSP); - - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(currentSP, 1); + method.StackPop(head, 1); method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); @@ -561,31 +556,34 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); method.StoreLocal(localReadonOnlySpan); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.Call(Word.SetUInt256); - method.StackPush(currentSP); + method.StackPush(head); break; case Instruction.MCOPY: - method.StackLoadPrevious(currentSP, 1); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackLoadPrevious(currentSP, 2); + method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackLoadPrevious(currentSP, 3); + method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(currentSP, 3); + method.StackPop(head, 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant((long)1); + method.Add(); method.LoadConstant(GasCostOf.VeryLow); method.Multiply(); method.Subtract(); @@ -620,19 +618,14 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // check if returnState is null method.MarkLabel(ret); // we get stack size - method.LoadLocal(currentSP); - method.LoadLocal(stack); - method.Subtract(); - method.Convert(); - method.StoreLocal(uint32A); + method.LoadArgument(0); + method.LoadLocal(head); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); // set stack method.LoadArgument(0); method.LoadLocal(stack); - method.Convert(); - method.Call(typeof(nuint).GetMethods().Where(method => method.Name == "op_Explicit" && method.ReturnType == typeof(void*)).FirstOrDefault()); - method.LoadLocal(uint32A); - method.NewObject(typeof(Span).GetConstructor([typeof(void*), typeof(int)])); + method.Call(GetCastMethodInfo()); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); // set gas available @@ -656,12 +649,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // jump table method.MarkLabel(jumpTable); - method.StackPop(currentSP); + method.StackPop(head); // emit the jump table // load the jump destination - method.LoadLocal(currentSP); + method.Load(stack, head); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); @@ -682,7 +675,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } // we get first 32 bits of the jump destination since it is less than int.MaxValue - method.Load(currentSP, Word.Int0Field); + method.Load(stack, head, Word.Int0Field); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); @@ -730,15 +723,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ }; } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, params Local[] locals) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) { // we the two uint256 from the stack - il.StackLoadPrevious(currentSP, 1); + il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); il.StoreLocal(locals[0]); - il.StackLoadPrevious(currentSP, 2); + il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); + il.StackPop(stack.idx, 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -749,28 +743,26 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Convert(); il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); il.StoreLocal(uint256R); - il.StackPop(currentSP, 2); // push the result to the stack - il.CleanWord(currentSP); - il.LoadLocal(currentSP); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(currentSP); } - private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local currentSP, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { Label label = il.DefineLabel("SkipHandlingBinaryOp"); // we the two uint256 from the stack - il.StackLoadPrevious(currentSP, 1); + il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); il.StoreLocal(locals[0]); - il.StackLoadPrevious(currentSP, 2); + il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(currentSP, 2); + il.StackPop(stack.idx, 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -785,18 +777,18 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, Local il.MarkLabel(label); // push the result to the stack - il.CleanWord(currentSP); - il.LoadLocal(currentSP); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(currentSP); + il.StackPush(stack.idx, 1); } private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); int costStart = 0; - long costcurrentSP = 0; + long coststack = 0; for (int pc = 0; pc < code.Length; pc++) { @@ -804,27 +796,27 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co switch (op.Operation) { case Instruction.JUMPDEST: - costs[costStart] = costcurrentSP; // remember the currentSP chain of opcodes + costs[costStart] = coststack; // remember the stack chain of opcodes costStart = pc; - costcurrentSP = op.Metadata?.GasCost ?? 0; + coststack = op.Metadata?.GasCost ?? 0; break; case Instruction.JUMPI: case Instruction.JUMP: - costcurrentSP += op.Metadata?.GasCost ?? 0; - costs[costStart] = costcurrentSP; // remember the currentSP chain of opcodes + coststack += op.Metadata?.GasCost ?? 0; + costs[costStart] = coststack; // remember the stack chain of opcodes costStart = pc + 1; // start with the next again - costcurrentSP = 0; + coststack = 0; break; default: - costcurrentSP += op.Metadata?.GasCost ?? 0; - costs[pc] = costcurrentSP; + coststack += op.Metadata?.GasCost ?? 0; + costs[pc] = coststack; break; } } - if (costcurrentSP > 0) + if (coststack > 0) { - costs[costStart] = costcurrentSP; + costs[costStart] = coststack; } return costs; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 52dad1f05db..33de21fd690 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -18,16 +18,13 @@ internal ref struct ILEvmState public ExecutionEnvironment Env; public TxExecutionContext TxCtx; public BlockExecutionContext BlkCtx; - - public Span Stack; - // in case of exceptions public EvmExceptionType EvmException; - // in case of jumps crossing section boundaries public ushort ProgramCounter; public long GasAvailable; - // in case STOP is executed public bool StopExecution; + public int StackHead; + public Span Stack; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index f89f56abff5..37e7a1e5cf6 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; +using Nethermind.Db; using Nethermind.Evm.CodeAnalysis.IL; using Sigil; using System; @@ -16,75 +17,94 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { - unsafe static TResult ReinterpretCast(TOriginal original) +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + public unsafe static MethodInfo GetCastMethodInfo() where TResult : struct + { + MethodInfo method = typeof(EmitExtensions).GetMethod(nameof(EmitExtensions.ReinterpretCast)); + return method.MakeGenericMethod(typeof(TOriginal), typeof(TResult)); + } + public unsafe static Span ReinterpretCast(Span original) where TOriginal : struct where TResult : struct { -#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type - return *(TResult*)(void*)&original; -#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + Span result = Span.Empty; + fixed(TOriginal* ptr = original) + { + result = new Span(ptr, original.Length * sizeof(TOriginal) / sizeof(TResult)); + } + return result; } +#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + public static void PrintLocal(this Emit il, Local local) + { + if(local.LocalType.IsValueType) + { + il.LoadLocalAddress(local); + } else + { + il.LoadLocal(local); + } + il.Call(local.LocalType.GetMethods().Where(m => m.Name == "ToString" && m.GetParameters().Length == 0).First(), Type.EmptyTypes); + il.Call(typeof(Console).GetMethod(nameof(Console.WriteLine), [typeof(string)])); + } + public static FieldInfo GetFieldInfo(string name) => GetFieldInfo(typeof(T), name); public static FieldInfo GetFieldInfo(Type TypeInstance, string name) { return TypeInstance.GetField(name); } public static MethodInfo GetPropertyInfo(string name, bool getSetter, out PropertyInfo propInfo) + => GetPropertyInfo(typeof(T), name, getSetter, out propInfo); + public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool getSetter, out PropertyInfo propInfo) { - propInfo = typeof(T).GetProperty(name); + propInfo = typeInstance.GetProperty(name); return getSetter? propInfo.GetSetMethod() : propInfo.GetGetMethod(); } - public static void Load(this Emit il, Local local, FieldInfo wordField) + public static void Load(this Emit il, Local local, Local idx) { - if (local.LocalType != typeof(Word*)) - { - throw new ArgumentException($"Only Word* can be used. This variable is of type {local.LocalType}"); - } - - if (wordField.DeclaringType != typeof(Word)) - { - throw new ArgumentException($"Only Word fields can be used. This field is declared for {wordField.DeclaringType}"); - } + il.LoadLocalAddress(local); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); + } - il.LoadLocal(local); + public static void Load(this Emit il, Local local, Local idx, FieldInfo wordField) + { + il.LoadLocalAddress(local); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); il.LoadField(wordField); } - public static void CleanWord(this Emit il, Local local) + public static void CleanWord(this Emit il, Local local, Local idx) { - if (local.LocalType != typeof(Word*)) - { - throw new ArgumentException( - $"Only {nameof(Word)} pointers are supported. The passed local was type of {local.LocalType}."); - } + il.LoadLocalAddress(local); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); - il.LoadLocal(local); il.InitializeObject(typeof(Word)); } /// /// Advances the stack one word up. /// - public static void StackPush(this Emit il, Local local) + public static void StackPush(this Emit il, Local idx, int count = 1) { - il.LoadLocal(local); - il.LoadConstant(Word.Size); - il.Convert(); + il.LoadLocal(idx); + il.LoadConstant(count); il.Add(); - il.StoreLocal(local); + il.StoreLocal(idx); } /// /// Moves the stack words down. /// - public static void StackPop(this Emit il, Local local, int count = 1) + public static void StackPop(this Emit il, Local idx, int count = 1) { - il.LoadLocal(local); - il.LoadConstant(Word.Size * count); - il.Convert(); + il.LoadLocal(idx); + il.LoadConstant(count); il.Subtract(); - il.StoreLocal(local); + il.StoreLocal(idx); } /// @@ -93,10 +113,7 @@ public static void StackPop(this Emit il, Local local, int count = 1) public static void StackPop(this Emit il, Local local, Local count) { il.LoadLocal(local); - il.LoadConstant(Word.Size); il.LoadLocal(count); - il.Multiply(); - il.Convert(); il.Subtract(); il.StoreLocal(local); } @@ -162,12 +179,14 @@ public static void ForBranch(this Emit il, Local count, Action, Lo /// /// Loads the previous EVM stack value on top of .NET stack. /// - public static void StackLoadPrevious(this Emit il, Local local, int count = 1) + public static void StackLoadPrevious(this Emit il, Local src, Local idx, int count = 1) { - il.LoadLocal(local); - il.LoadConstant(Word.Size * count); + il.LoadLocalAddress(src); + il.LoadLocal(idx); + il.LoadConstant(count); il.Convert(); il.Subtract(); + il.Call(typeof(Span).GetMethod("get_Item")); } public static void LoadArray(this Emit il, ReadOnlySpan value) From 0117d214334a8b83b9fffae186e6d413e20f6300 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 29 May 2024 12:30:54 +0100 Subject: [PATCH 038/146] added draft implementation of more opcodes, needs verification --- .../CodeAnalysis/IlEvmTests.cs | 10 +- .../CodeAnalysis/IL/ILCompiler.cs | 145 +++++++++++++++++- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 28 ++++ 3 files changed, 172 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 9cf79e1a0bf..360b657c49a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -126,9 +126,11 @@ public void Pure_Opcode_Emition_Coveraga() try { ILCompiler.CompileSegment(name, [opcode], []); - } catch (Exception e) + } catch (NotSupportedException nse) { + notYetImplemented.Add((instruction, nse)); + } + catch (Exception) { - notYetImplemented.Add((instruction, e)); } } @@ -220,9 +222,7 @@ public void Pure_Opcode_Emition_Coveraga() .Done); yield return (14, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .EQ() + .PushSingle(1) .NOT() .Done); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index d7eb5f6338d..a60cae69d3d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -17,6 +17,8 @@ using static Nethermind.Evm.IL.EmitExtensions; using MathGmp.Native; using Nethermind.Evm.Tracing.GethStyle.Custom.Native.FourByte; +using System.Drawing; +using Nethermind.Core.Crypto; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler @@ -47,6 +49,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local uint64A = method.DeclareLocal(typeof(ulong)); using Local uint32A = method.DeclareLocal(typeof(uint)); using Local byte8A = method.DeclareLocal(typeof(byte)); + using Local buffer = method.DeclareLocal(typeof(byte*)); using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local programCounter = method.DeclareLocal(typeof(ushort)); @@ -125,6 +128,19 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StopExecution))); method.Branch(ret); break; + case Instruction.NOT: + method.Load(stack, head); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head); + + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256R); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Not))); + method.Load(stack, head); + method.LoadLocal(uint256R); + method.Call(Word.SetUInt256); + break; case Instruction.JUMPDEST: // mark the jump destination jumpDestinations[pc] = method.DefineLabel(); @@ -446,7 +462,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); method.StackLoadPrevious(stack, head, 1); - method.LoadConstant(32); + method.LoadConstant(Word.Size); method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); method.Call(typeof(ZeroPaddedSpan).GetMethod(nameof(ZeroPaddedSpan.ToArray), BindingFlags.Instance | BindingFlags.Public)); method.StoreLocal(localReadonOnlySpan); @@ -490,7 +506,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); - method.LoadConstant(32); + method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); @@ -500,7 +516,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(1); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); - method.LoadConstant(32); + method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(byte[]) })); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); @@ -527,7 +543,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgumentAddress(1); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); - method.LoadConstant(32); + method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); method.LoadConstant(0); method.LoadElement(); @@ -543,7 +559,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); - method.LoadConstant(32); + method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); @@ -608,6 +624,94 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); break; + case Instruction.AND: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; + case Instruction.OR: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; + case Instruction.XOR: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; + case Instruction.KECCAK256: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Sha3Word); + method.Multiply(); + method.LoadConstant(GasCostOf.Sha3); + method.Add(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadArgument(1); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); + method.Call(Word.SetKeccak); + method.StackPush(head); + break; + case Instruction.BYTE: + // load a + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.LoadField(GetFieldInfo(typeof(Word), nameof(Word._buffer))); + method.StoreLocal(buffer); + method.StackPop(head, 2); + + + Label pushZeroLabel = method.DefineLabel("PushZero"); + Label endOfInstructionImpl = method.DefineLabel("endOfByteOpcode"); + method.LoadLocalAddress(uint256A); + method.LoadConstant(Word.Size); + method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.LoadLocalAddress(uint256A); + method.LoadConstant(0); + method.Call(typeof(UInt256).GetMethod("op_LesserThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.Or(); + method.BranchIfTrue(pushZeroLabel); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(buffer); + method.Duplicate(); + method.LoadLocal(uint256A); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.Add(); + method.LoadObject(); + method.StoreField(Word.Byte0Field); + method.StackPush(head); + method.Branch(endOfInstructionImpl); + + method.MarkLabel(pushZeroLabel); + method.CleanWord(stack, head); + method.Load(stack, head); + method.Call(Word.SetToZero); + method.StackPush(head); + + method.MarkLabel(endOfInstructionImpl); + break; default: throw new NotSupportedException(); } @@ -674,7 +778,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ jumps[i] = method.DefineLabel(); } - // we get first 32 bits of the jump destination since it is less than int.MaxValue + // we get first Word.Size bits of the jump destination since it is less than int.MaxValue method.Load(stack, head, Word.Int0Field); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); @@ -723,6 +827,35 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ }; } + private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) + { + // Note: Use Vector256 directoly if UInt256 does not use it internally + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.StackPop(stack.idx, 2); + + // invoke op on the uint256 + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[0]); + il.Call(operation, null); + + // convert to conv_i + il.Convert(); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); + + // push the result to the stack + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + } + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) { // we the two uint256 from the stack diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 36b791d3532..4ccc6e216f2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Int256; using System; using System.Buffers.Binary; @@ -46,6 +47,29 @@ internal struct Word public ulong Ulong3; public bool IsZero => (Ulong0 | Ulong1 | Ulong2 | Ulong3) == 0; + public void ToZero() + { + Ulong0 = 0; Ulong1 = 0; + Ulong2 = 0; Ulong3 = 0; + } + + public unsafe ValueHash256 Keccak + { + get + { + fixed(byte* ptr = _buffer) { + return new ValueHash256(new Span(ptr, 32)); + } + } + set + { + ReadOnlySpan buffer = value.Bytes; + for (int i = 0; i < 20; i++) + { + _buffer[i] = buffer[i]; + } + } + } public unsafe Address Address { @@ -120,10 +144,14 @@ public UInt256 UInt256 public static readonly FieldInfo Ulong3Field = typeof(Word).GetField(nameof(Ulong3)); public static readonly MethodInfo GetIsZero = typeof(Word).GetProperty(nameof(IsZero))!.GetMethod; + public static readonly MethodInfo SetToZero = typeof(Word).GetProperty(nameof(ToZero))!.GetMethod; public static readonly MethodInfo GetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.GetMethod; public static readonly MethodInfo SetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.SetMethod; public static readonly MethodInfo GetAddress = typeof(Word).GetProperty(nameof(Address))!.GetMethod; public static readonly MethodInfo SetAddress = typeof(Word).GetProperty(nameof(Address))!.SetMethod; + + public static readonly MethodInfo GetKeccak = typeof(Word).GetProperty(nameof(Keccak))!.GetMethod; + public static readonly MethodInfo SetKeccak = typeof(Word).GetProperty(nameof(Keccak))!.SetMethod; } From 6990d824067d245d11f133cb9fc3a0b98ff51756 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 29 May 2024 17:23:01 +0100 Subject: [PATCH 039/146] added drafts for shift opcodes --- .../CodeAnalysis/IL/ILCompiler.cs | 76 ++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index a60cae69d3d..31affbaabd4 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -128,6 +128,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StopExecution))); method.Branch(ret); break; + case Instruction.INVALID: + method.Branch(labels[EvmExceptionType.InvalidCode]); + break; case Instruction.NOT: method.Load(stack, head); method.Call(Word.GetUInt256); @@ -169,6 +172,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(noJump); method.StackPop(head, 2); break; + case Instruction.PUSH0: + method.CleanWord(stack, head); + method.Load(stack, head); + method.Call(Word.SetToZero); + method.StackPush(head); + break; case Instruction.PUSH1: case Instruction.PUSH2: case Instruction.PUSH3: @@ -270,7 +279,21 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ il.MarkLabel(label); }, uint256A, uint256B); break; - + case Instruction.SHL: + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft:true, null, uint256A, uint256B); + break; + case Instruction.SHR: + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft:false, null, uint256A, uint256B); + break; + case Instruction.AND: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; + case Instruction.OR: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; + case Instruction.XOR: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + break; case Instruction.EXP: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; @@ -624,15 +647,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); break; - case Instruction.AND: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); - break; - case Instruction.OR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); - break; - case Instruction.XOR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); - break; case Instruction.KECCAK256: method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); @@ -717,6 +731,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } } + method.LoadLocal(gasAvailable); + method.LoadConstant(0); + method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); // prepare ILEvmState // check if returnState is null @@ -827,6 +844,45 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ }; } + private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, params Local[] locals) + { + MethodInfo shiftOp = typeof(UInt256).GetMethod(isLeft ? nameof(UInt256.LeftShift) : nameof(UInt256.RightShift)); + Label skipPop = il.DefineLabel("skipSecondPop"); + Label endOfOpcode = il.DefineLabel("endOfOpcode"); + + // Note: Use Vector256 directoly if UInt256 does not use it internally + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + + il.LoadLocalAddress(locals[0]); + il.LoadConstant(Word.Size * sizeof(byte)); + il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int)})); + il.BranchIfTrue(skipPop); + + il.LoadLocalAddress(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.LoadField(Word.Int0Field); + il.LoadLocalAddress(uint256R); + il.Call(shiftOp); + il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, 2); + il.CleanWord(stack.span, stack.idx); + il.LoadLocal(uint256R); + il.Call(Word.SetUInt256); + il.StackPush(stack.idx, 1); + il.BranchIfTrue(endOfOpcode); + + il.MarkLabel(skipPop); + il.StackPop(stack.idx, 2); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.Call(Word.SetToZero); + + il.MarkLabel(endOfOpcode); + } + private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) { // Note: Use Vector256 directoly if UInt256 does not use it internally From 4e8d008b509657b948018b7b4882c7139ecf4ae2 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 29 May 2024 17:30:24 +0100 Subject: [PATCH 040/146] minor fix --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 4ccc6e216f2..418330c7caf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -144,7 +144,7 @@ public UInt256 UInt256 public static readonly FieldInfo Ulong3Field = typeof(Word).GetField(nameof(Ulong3)); public static readonly MethodInfo GetIsZero = typeof(Word).GetProperty(nameof(IsZero))!.GetMethod; - public static readonly MethodInfo SetToZero = typeof(Word).GetProperty(nameof(ToZero))!.GetMethod; + public static readonly MethodInfo SetToZero = typeof(Word).GetMethod(nameof(ToZero))!; public static readonly MethodInfo GetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.GetMethod; public static readonly MethodInfo SetUInt256 = typeof(Word).GetProperty(nameof(UInt256))!.SetMethod; From 07864f768b42869781b3358ef4f03e134a8c8dc3 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 30 May 2024 04:03:48 +0100 Subject: [PATCH 041/146] added more opcode --- .../CodeAnalysis/IlEvmTests.cs | 3 +- .../CodeAnalysis/IL/ILCompiler.cs | 180 +++++++++++++++++- .../CodeAnalysis/IL/ILEvmState.cs | 3 + src/Nethermind/Nethermind.Evm/Instruction.cs | 1 + 4 files changed, 183 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 360b657c49a..97fc75c14ab 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -134,7 +134,8 @@ public void Pure_Opcode_Emition_Coveraga() } } - Assert.That(notYetImplemented.Count, Is.EqualTo(0)); + string missingOpcodes = String.Join("; ", notYetImplemented.Select(op => op.Item1.ToString())); + Assert.That(notYetImplemented.Count == 0, $"{notYetImplemented.Count} opcodes missing: [{missingOpcodes}]"); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 31affbaabd4..a0a4e80b61f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -384,7 +384,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // is pushing to stack happening correctly case Instruction.CODESIZE: method.CleanWord(stack, head); - method.LoadLocal(stack); + method.Load(stack, head); method.LoadConstant(code.Length); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); method.StackPush(head); @@ -478,12 +478,66 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StackPush(head); break; + case Instruction.CALLDATACOPY: + + Label endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.LoadConstant(GasCostOf.VeryLow); + method.Add(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(1); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); + method.StoreLocal(localReadonOnlySpan); + + method.LoadArgument(1); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + + method.MarkLabel(endOfOpcode); + break; + case Instruction.CALLDATALOAD: method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); method.StackLoadPrevious(stack, head, 1); method.LoadConstant(Word.Size); method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); @@ -502,7 +556,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); method.StackPush(head); @@ -726,6 +780,126 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(endOfInstructionImpl); break; + case Instruction.CODECOPY: + endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.LoadConstant(GasCostOf.VeryLow); + method.Add(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(1); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); + method.StoreLocal(localReadonOnlySpan); + + method.LoadArgument(1); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + + method.MarkLabel(endOfOpcode); + break; + case Instruction.GAS: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(gasAvailable); + method.StoreField(Word.Ulong0Field); + method.StackPush(head); + break; + case Instruction.RETURNDATASIZE: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + method.StoreField(Word.Int0Field); + method.StackPush(head); + break; + case Instruction.RETURNDATACOPY: + endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.LoadConstant(GasCostOf.VeryLow); + method.Add(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + // Note : check if c + b > returnData.Size + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(1); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.Call(GetPropertyInfo(typeof(ReadOnlyMemory), nameof(ReadOnlyMemory.Span), false, out _)); + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); + method.StoreLocal(localReadonOnlySpan); + + method.LoadArgument(1); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + + method.MarkLabel(endOfOpcode); + break; default: throw new NotSupportedException(); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 33de21fd690..a37aaee9305 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -13,6 +13,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal ref struct ILEvmState { + public byte[] MachineCode; // static arguments public BlockHeader Header; public ExecutionEnvironment Env; @@ -27,4 +28,6 @@ internal ref struct ILEvmState public bool StopExecution; public int StackHead; public Span Stack; + public ref EvmPooledMemory memory; + public ReadOnlyMemory ReturnBuffer; } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 74d1b4f7e77..8f261caa4d7 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -387,6 +387,7 @@ public static class InstructionExtensions Instruction.EXTCODESIZE or Instruction.EXTCODECOPY or Instruction.EXTCODEHASH => true, Instruction.SELFDESTRUCT => true, Instruction.BALANCE => true, + Instruction.SELFBALANCE => true, _ => false, }; From 8f1a98ebbb4709f0bb6dceac651e04848a16085b Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 30 May 2024 10:46:24 +0100 Subject: [PATCH 042/146] added support for the *MOD opcodes --- .../CodeAnalysis/IlEvmTests.cs | 18 +- .../CodeAnalysis/IL/ILCompiler.cs | 112 +++++++-- .../CodeAnalysis/IL/ILEvmState.cs | 4 +- .../CodeAnalysis/IL/ILExtensions.cs | 11 +- .../CodeAnalysis/IL/IlAnalyzer.cs | 8 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 9 +- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 3 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 232 +++++++++--------- .../Nethermind.Evm/VirtualMachine.cs | 2 +- 9 files changed, 245 insertions(+), 154 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 97fc75c14ab..dd17c6fba27 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using static Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript.Log; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -102,11 +103,12 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var address = receipts.TxReceipts[0].ContractAddress; */ - for(int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) { + for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) + { ExecuteBlock(new NullBlockTracer(), bytecode); } - Assert.Greater(pattern.CallCount, 0); + Assert.Greater(pattern.CallCount, 0); } [Test] @@ -126,7 +128,9 @@ public void Pure_Opcode_Emition_Coveraga() try { ILCompiler.CompileSegment(name, [opcode], []); - } catch (NotSupportedException nse) { + } + catch (NotSupportedException nse) + { notYetImplemented.Add((instruction, nse)); } catch (Exception) @@ -139,7 +143,7 @@ public void Pure_Opcode_Emition_Coveraga() } - public static IEnumerable<(int,byte[])> GetBytecodes() + public static IEnumerable<(int, byte[])> GetBytecodes() { yield return (-1, Prepare.EvmCode .Done); @@ -239,12 +243,12 @@ public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) ProgramCounter = 0, EvmException = EvmExceptionType.None, StopExecution = false, - StackHead = 0 + StackHead = 0, + Memory = new EvmPooledMemory() }; - var memory = new EvmPooledMemory(); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, ref memory, ctx.Data); + ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index a0a4e80b61f..e3063b52087 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -19,12 +19,15 @@ using Nethermind.Evm.Tracing.GethStyle.Custom.Native.FourByte; using System.Drawing; using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Specs; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate void ExecuteSegment(ref ILEvmState state, ref EvmPooledMemory memory, byte[][] immediatesData); - public class SegmentExecutionCtx { + public delegate void ExecuteSegment(ref ILEvmState state, ISpecProvider spec, byte[][] immediatesData); + public class SegmentExecutionCtx + { public ExecuteSegment Method; public byte[][] Data; } @@ -131,6 +134,14 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.INVALID: method.Branch(labels[EvmExceptionType.InvalidCode]); break; + case Instruction.CHAINID: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(1); + method.LoadField(GetFieldInfo(typeof(ISpecProvider), nameof(ISpecProvider.ChainId))); + method.StoreField(Word.Ulong0Field); + method.StackPush(head); + break; case Instruction.NOT: method.Load(stack, head); method.Call(Word.GetUInt256); @@ -233,17 +244,14 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.ADD: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; - case Instruction.SUB: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; - case Instruction.MUL: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; - case Instruction.MOD: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); @@ -260,7 +268,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ il.MarkLabel(label); }, uint256A, uint256B); break; - case Instruction.DIV: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => @@ -276,14 +283,50 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ il.LoadConstant(0); il.Branch(postInstructionLabel); + il.MarkLabel(label); + }, uint256A, uint256B); + break; + case Instruction.ADDMOD: + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel(); + + il.LoadLocal(locals[2]); + il.LoadConstant(0); + il.CompareEqual(); + + il.BranchIfFalse(label); + + il.LoadConstant(0); + il.Branch(postInstructionLabel); + + il.MarkLabel(label); + }, uint256A, uint256B); + break; + case Instruction.MULMOD: + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel(); + + il.LoadLocal(locals[2]); + il.LoadConstant(0); + il.CompareEqual(); + + il.BranchIfFalse(label); + + il.LoadConstant(0); + il.Branch(postInstructionLabel); + il.MarkLabel(label); }, uint256A, uint256B); break; case Instruction.SHL: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft:true, null, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, null, uint256A, uint256B); break; case Instruction.SHR: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft:false, null, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, null, uint256A, uint256B); break; case Instruction.AND: EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); @@ -507,7 +550,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256C); @@ -566,7 +610,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgumentAddress(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); method.StackPush(head); @@ -580,7 +625,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(uint256B); method.StackPop(head, 2); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); @@ -590,7 +636,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.LoadConstant(Word.Size); @@ -1032,7 +1079,7 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(locals[0]); il.LoadConstant(Word.Size * sizeof(byte)); - il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int)})); + il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); il.BranchIfTrue(skipPop); il.LoadLocalAddress(locals[0]); @@ -1147,6 +1194,43 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.StackPush(stack.idx, 1); } + private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + { + Label label = il.DefineLabel("SkipHandlingBinaryOp"); + + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.StackLoadPrevious(stack.span, stack.idx, 3); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[2]); + il.StackPop(stack.idx, 3); + + // incase of custom handling, we branch to the label + customHandling?.Invoke(il, label, locals); + + // invoke op on the uint256 + il.LoadLocalAddress(locals[2]); + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(uint256R); + il.Call(operation); + + // skip the main handling + il.MarkLabel(label); + + // push the result to the stack + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + il.StackPush(stack.idx, 1); + } + private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index a37aaee9305..3c0ecdf8ace 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -28,6 +28,6 @@ internal ref struct ILEvmState public bool StopExecution; public int StackHead; public Span Stack; - public ref EvmPooledMemory memory; - public ReadOnlyMemory ReturnBuffer; + public ref EvmPooledMemory Memory; + public ReadOnlyMemory ReturnBuffer; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 37e7a1e5cf6..4c9408d4613 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -28,9 +28,9 @@ public unsafe static Span ReinterpretCast(Span result = Span.Empty; - fixed(TOriginal* ptr = original) + fixed (TOriginal* ptr = original) { - result = new Span(ptr, original.Length * sizeof(TOriginal) / sizeof(TResult)); + result = new Span(ptr, original.Length * sizeof(TOriginal) / sizeof(TResult)); } return result; } @@ -38,10 +38,11 @@ public unsafe static Span ReinterpretCast(Span(this Emit il, Local local) { - if(local.LocalType.IsValueType) + if (local.LocalType.IsValueType) { il.LoadLocalAddress(local); - } else + } + else { il.LoadLocal(local); } @@ -58,7 +59,7 @@ public static MethodInfo GetPropertyInfo(string name, bool getSetter, out Pro public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool getSetter, out PropertyInfo propInfo) { propInfo = typeInstance.GetProperty(name); - return getSetter? propInfo.GetSetMethod() : propInfo.GetGetMethod(); + return getSetter ? propInfo.GetSetMethod() : propInfo.GetGetMethod(); } public static void Load(this Emit il, Local local, Local idx) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 8ebb2630b50..2d889c2e48f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -39,15 +39,15 @@ public int GetHashCode(byte[] key) private static Dictionary Patterns = new Dictionary(new ByteArrayComparer()); public static Dictionary AddPattern(byte[] pattern, InstructionChunk chunk) { - lock(Patterns) + lock (Patterns) { Patterns[pattern] = chunk; } return Patterns; } - public static T GetPatternHandler (byte[] pattern) where T : InstructionChunk + public static T GetPatternHandler(byte[] pattern) where T : InstructionChunk { - return (T) Patterns[pattern]; + return (T)Patterns[pattern]; } @@ -140,7 +140,7 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma return patternFound.ToFrozenDictionary(); } - switch(mode) + switch (mode) { case IlInfo.ILMode.PatternMatching: codeInfo.IlInfo.WithChunks(CheckPatterns(machineCode)); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index ad8edb6aa48..a7fd471e2fd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,14 +61,14 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, IReleaseSpec spec, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop) + public bool TryExecute(EvmState vmState, ISpecProvider specProvider, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldStop = false; if (programCounter > ushort.MaxValue) return false; - switch(Mode) + switch (Mode) { case ILMode.PatternMatching: { @@ -76,7 +76,7 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec { return false; } - chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + chunk.Invoke(vmState, specProvider.GetSpec(header.Number, header.Timestamp), ref programCounter, ref gasAvailable, ref stack); break; } case ILMode.SubsegmentsCompiling: @@ -92,9 +92,10 @@ public bool TryExecute(EvmState vmState, IReleaseSpec spec Stack = vmState.DataStack, Header = header, ProgramCounter = (ushort)programCounter, + Memory = ref vmState.Memory, }; - ctx.Method.Invoke(ref ilvmState, ref vmState.Memory, ctx.Data); + ctx.Method.Invoke(ref ilvmState, specProvider, ctx.Data); gasAvailable = ilvmState.GasAvailable; vmState.DataStack = ilvmState.Stack.ToArray(); programCounter = ilvmState.ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 418330c7caf..c5480b7f928 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -57,7 +57,8 @@ public unsafe ValueHash256 Keccak { get { - fixed(byte* ptr = _buffer) { + fixed (byte* ptr = _buffer) + { return new ValueHash256(new Span(ptr, 32)); } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 8f261caa4d7..1d9869c2633 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -201,112 +201,112 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta public static readonly IReadOnlyDictionary Operations = new Dictionary() { - [Instruction.POP ] = new(GasCostOf.Base, 0, 1, 0), - [Instruction.PC ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), + [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.PUSH1 ] = new(GasCostOf.VeryLow, 1, 0, 1), - [Instruction.PUSH2 ] = new(GasCostOf.VeryLow, 2, 0, 1), - [Instruction.PUSH3 ] = new(GasCostOf.VeryLow, 3, 0, 1), - [Instruction.PUSH4 ] = new(GasCostOf.VeryLow, 4, 0, 1), - [Instruction.PUSH5 ] = new(GasCostOf.VeryLow, 5, 0, 1), - [Instruction.PUSH6 ] = new(GasCostOf.VeryLow, 6, 0, 1), - [Instruction.PUSH7 ] = new(GasCostOf.VeryLow, 7, 0, 1), - [Instruction.PUSH8 ] = new(GasCostOf.VeryLow, 8, 0, 1), - [Instruction.PUSH9 ] = new(GasCostOf.VeryLow, 9, 0, 1), - [Instruction.PUSH10 ] = new(GasCostOf.VeryLow, 10, 0, 1), - [Instruction.PUSH11 ] = new(GasCostOf.VeryLow, 11, 0, 1), - [Instruction.PUSH12 ] = new(GasCostOf.VeryLow, 12, 0, 1), - [Instruction.PUSH13 ] = new(GasCostOf.VeryLow, 13, 0, 1), - [Instruction.PUSH14 ] = new(GasCostOf.VeryLow, 14, 0, 1), - [Instruction.PUSH15 ] = new(GasCostOf.VeryLow, 15, 0, 1), - [Instruction.PUSH16 ] = new(GasCostOf.VeryLow, 16, 0, 1), - [Instruction.PUSH17 ] = new(GasCostOf.VeryLow, 17, 0, 1), - [Instruction.PUSH18 ] = new(GasCostOf.VeryLow, 18, 0, 1), - [Instruction.PUSH19 ] = new(GasCostOf.VeryLow, 19, 0, 1), - [Instruction.PUSH20 ] = new(GasCostOf.VeryLow, 20, 0, 1), - [Instruction.PUSH21 ] = new(GasCostOf.VeryLow, 21, 0, 1), - [Instruction.PUSH22 ] = new(GasCostOf.VeryLow, 22, 0, 1), - [Instruction.PUSH23 ] = new(GasCostOf.VeryLow, 23, 0, 1), - [Instruction.PUSH24 ] = new(GasCostOf.VeryLow, 24, 0, 1), - [Instruction.PUSH25 ] = new(GasCostOf.VeryLow, 25, 0, 1), - [Instruction.PUSH26 ] = new(GasCostOf.VeryLow, 26, 0, 1), - [Instruction.PUSH27 ] = new(GasCostOf.VeryLow, 27, 0, 1), - [Instruction.PUSH28 ] = new(GasCostOf.VeryLow, 28, 0, 1), - [Instruction.PUSH29 ] = new(GasCostOf.VeryLow, 29, 0, 1), - [Instruction.PUSH30 ] = new(GasCostOf.VeryLow, 30, 0, 1), - [Instruction.PUSH31 ] = new(GasCostOf.VeryLow, 31, 0, 1), - [Instruction.PUSH32 ] = new(GasCostOf.VeryLow, 32, 0, 1), + [Instruction.PUSH1] = new(GasCostOf.VeryLow, 1, 0, 1), + [Instruction.PUSH2] = new(GasCostOf.VeryLow, 2, 0, 1), + [Instruction.PUSH3] = new(GasCostOf.VeryLow, 3, 0, 1), + [Instruction.PUSH4] = new(GasCostOf.VeryLow, 4, 0, 1), + [Instruction.PUSH5] = new(GasCostOf.VeryLow, 5, 0, 1), + [Instruction.PUSH6] = new(GasCostOf.VeryLow, 6, 0, 1), + [Instruction.PUSH7] = new(GasCostOf.VeryLow, 7, 0, 1), + [Instruction.PUSH8] = new(GasCostOf.VeryLow, 8, 0, 1), + [Instruction.PUSH9] = new(GasCostOf.VeryLow, 9, 0, 1), + [Instruction.PUSH10] = new(GasCostOf.VeryLow, 10, 0, 1), + [Instruction.PUSH11] = new(GasCostOf.VeryLow, 11, 0, 1), + [Instruction.PUSH12] = new(GasCostOf.VeryLow, 12, 0, 1), + [Instruction.PUSH13] = new(GasCostOf.VeryLow, 13, 0, 1), + [Instruction.PUSH14] = new(GasCostOf.VeryLow, 14, 0, 1), + [Instruction.PUSH15] = new(GasCostOf.VeryLow, 15, 0, 1), + [Instruction.PUSH16] = new(GasCostOf.VeryLow, 16, 0, 1), + [Instruction.PUSH17] = new(GasCostOf.VeryLow, 17, 0, 1), + [Instruction.PUSH18] = new(GasCostOf.VeryLow, 18, 0, 1), + [Instruction.PUSH19] = new(GasCostOf.VeryLow, 19, 0, 1), + [Instruction.PUSH20] = new(GasCostOf.VeryLow, 20, 0, 1), + [Instruction.PUSH21] = new(GasCostOf.VeryLow, 21, 0, 1), + [Instruction.PUSH22] = new(GasCostOf.VeryLow, 22, 0, 1), + [Instruction.PUSH23] = new(GasCostOf.VeryLow, 23, 0, 1), + [Instruction.PUSH24] = new(GasCostOf.VeryLow, 24, 0, 1), + [Instruction.PUSH25] = new(GasCostOf.VeryLow, 25, 0, 1), + [Instruction.PUSH26] = new(GasCostOf.VeryLow, 26, 0, 1), + [Instruction.PUSH27] = new(GasCostOf.VeryLow, 27, 0, 1), + [Instruction.PUSH28] = new(GasCostOf.VeryLow, 28, 0, 1), + [Instruction.PUSH29] = new(GasCostOf.VeryLow, 29, 0, 1), + [Instruction.PUSH30] = new(GasCostOf.VeryLow, 30, 0, 1), + [Instruction.PUSH31] = new(GasCostOf.VeryLow, 31, 0, 1), + [Instruction.PUSH32] = new(GasCostOf.VeryLow, 32, 0, 1), [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), - [Instruction.JUMP ] = new(GasCostOf.Mid, 0, 1, 0), - [Instruction.JUMPI ] = new(GasCostOf.High, 0, 2, 0), - [Instruction.SUB ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), + [Instruction.JUMPI] = new(GasCostOf.High, 0, 2, 0), + [Instruction.SUB] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.DUP1 ] = new(GasCostOf.VeryLow, 0, 1, 2), - [Instruction.DUP2 ] = new(GasCostOf.VeryLow, 0, 2, 3), - [Instruction.DUP3 ] = new(GasCostOf.VeryLow, 0, 3, 4), - [Instruction.DUP4 ] = new(GasCostOf.VeryLow, 0, 4, 5), - [Instruction.DUP5 ] = new(GasCostOf.VeryLow, 0, 5, 6), - [Instruction.DUP6 ] = new(GasCostOf.VeryLow, 0, 6, 7), - [Instruction.DUP7 ] = new(GasCostOf.VeryLow, 0, 7, 8), - [Instruction.DUP8 ] = new(GasCostOf.VeryLow, 0, 8, 9), - [Instruction.DUP9 ] = new(GasCostOf.VeryLow, 0, 9, 10), - [Instruction.DUP10 ] = new(GasCostOf.VeryLow, 0, 10, 11), - [Instruction.DUP11 ] = new(GasCostOf.VeryLow, 0, 11, 12), - [Instruction.DUP12 ] = new(GasCostOf.VeryLow, 0, 12, 13), - [Instruction.DUP13 ] = new(GasCostOf.VeryLow, 0, 13, 14), - [Instruction.DUP14 ] = new(GasCostOf.VeryLow, 0, 14, 15), - [Instruction.DUP15 ] = new(GasCostOf.VeryLow, 0, 15, 16), - [Instruction.DUP16 ] = new(GasCostOf.VeryLow, 0, 16, 17), + [Instruction.DUP1] = new(GasCostOf.VeryLow, 0, 1, 2), + [Instruction.DUP2] = new(GasCostOf.VeryLow, 0, 2, 3), + [Instruction.DUP3] = new(GasCostOf.VeryLow, 0, 3, 4), + [Instruction.DUP4] = new(GasCostOf.VeryLow, 0, 4, 5), + [Instruction.DUP5] = new(GasCostOf.VeryLow, 0, 5, 6), + [Instruction.DUP6] = new(GasCostOf.VeryLow, 0, 6, 7), + [Instruction.DUP7] = new(GasCostOf.VeryLow, 0, 7, 8), + [Instruction.DUP8] = new(GasCostOf.VeryLow, 0, 8, 9), + [Instruction.DUP9] = new(GasCostOf.VeryLow, 0, 9, 10), + [Instruction.DUP10] = new(GasCostOf.VeryLow, 0, 10, 11), + [Instruction.DUP11] = new(GasCostOf.VeryLow, 0, 11, 12), + [Instruction.DUP12] = new(GasCostOf.VeryLow, 0, 12, 13), + [Instruction.DUP13] = new(GasCostOf.VeryLow, 0, 13, 14), + [Instruction.DUP14] = new(GasCostOf.VeryLow, 0, 14, 15), + [Instruction.DUP15] = new(GasCostOf.VeryLow, 0, 15, 16), + [Instruction.DUP16] = new(GasCostOf.VeryLow, 0, 16, 17), - [Instruction.SWAP1 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP2 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP3 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP4 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP5 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP6 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP7 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP8 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP9 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP10 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP11 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP12 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP13 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP14 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP15 ] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP16 ] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP1] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP2] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP3] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP4] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP5] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP6] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP7] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP8] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP9] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP10] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP11] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP12] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP13] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP14] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP15] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP16] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.ADD ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.MUL ] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.SUB ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.DIV ] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.SDIV ] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.MOD ] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.SMOD ] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.EXP ] = new(GasCostOf.Exp, 0, 2, 1), + [Instruction.ADD] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.MUL] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SUB] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.DIV] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SDIV] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.MOD] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.SMOD] = new(GasCostOf.Low, 0, 2, 1), + [Instruction.EXP] = new(GasCostOf.Exp, 0, 2, 1), [Instruction.SIGNEXTEND] = new(GasCostOf.Low, 0, 2, 1), - [Instruction.LT ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.GT ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.SLT ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.SGT ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.EQ ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.ISZERO ] = new(GasCostOf.VeryLow, 0, 1, 1), - [Instruction.AND ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.OR ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.XOR ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.NOT ] = new(GasCostOf.VeryLow, 0, 1, 1), - [Instruction.ADDMOD ] = new(GasCostOf.Mid, 0, 3, 1), - [Instruction.MULMOD ] = new(GasCostOf.Mid, 0, 3, 1), - [Instruction.SHL ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.SHR ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.SAR ] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.BYTE ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.LT] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.GT] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SLT] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SGT] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.EQ] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.ISZERO] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.AND] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.OR] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.XOR] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.NOT] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.ADDMOD] = new(GasCostOf.Mid, 0, 3, 1), + [Instruction.MULMOD] = new(GasCostOf.Mid, 0, 3, 1), + [Instruction.SHL] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SHR] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.SAR] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.BYTE] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.KECCAK256] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.ADDRESS ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.BALANCE ] = new(GasCostOf.Balance, 0, 1, 1), - [Instruction.ORIGIN ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.CALLER ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.ADDRESS] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.BALANCE] = new(GasCostOf.Balance, 0, 1, 1), + [Instruction.ORIGIN] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CALLER] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLVALUE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLDATALOAD] = new(GasCostOf.VeryLow, 0, 1, 1), [Instruction.CALLDATASIZE] = new(GasCostOf.Base, 0, 0, 1), @@ -321,30 +321,30 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta [Instruction.EXTCODEHASH] = new(GasCostOf.ExtCodeHash, 0, 1, 1), [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), - [Instruction.BLOCKHASH ] = new(GasCostOf.BlockHash, 0, 1, 1), - [Instruction.COINBASE ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.BLOCKHASH] = new(GasCostOf.BlockHash, 0, 1, 1), + [Instruction.COINBASE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.TIMESTAMP] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.NUMBER ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.NUMBER] = new(GasCostOf.Base, 0, 0, 1), [Instruction.PREVRANDAO] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.GASLIMIT ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.CHAINID ] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.GASLIMIT] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CHAINID] = new(GasCostOf.Base, 0, 0, 1), [Instruction.SELFBALANCE] = new(GasCostOf.SelfBalance, 0, 0, 1), - [Instruction.BASEFEE ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.BLOBHASH ] = new(GasCostOf.BlobHash, 0, 1, 1), + [Instruction.BASEFEE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.BLOBHASH] = new(GasCostOf.BlobHash, 0, 1, 1), [Instruction.BLOBBASEFEE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.POP ] = new(GasCostOf.Base, 0, 1, 0), - [Instruction.MLOAD ] = new(GasCostOf.VeryLow, 0, 1, 1), - [Instruction.MSTORE ] = new(GasCostOf.VeryLow, 0, 2, 0), - [Instruction.MSTORE8 ] = new(GasCostOf.VeryLow, 0, 2, 0), - [Instruction.SLOAD ] = new(GasCostOf.SLoad, 0, 1, 1), - [Instruction.SSTORE ] = new(GasCostOf.SSet, 0, 2, 0), - [Instruction.JUMP ] = new(GasCostOf.Mid, 0, 1, 0), + [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), + [Instruction.MLOAD] = new(GasCostOf.VeryLow, 0, 1, 1), + [Instruction.MSTORE] = new(GasCostOf.VeryLow, 0, 2, 0), + [Instruction.MSTORE8] = new(GasCostOf.VeryLow, 0, 2, 0), + [Instruction.SLOAD] = new(GasCostOf.SLoad, 0, 1, 1), + [Instruction.SSTORE] = new(GasCostOf.SSet, 0, 2, 0), + [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.JUMPI] = new(GasCostOf.Mid, 0, 2, 0), - [Instruction.PC ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.MSIZE ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.GAS ] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.JUMPDEST ] = new(GasCostOf.JumpDest, 0, 0, 0), + [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.GAS] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), [Instruction.MCOPY] = new(GasCostOf.VeryLow, 0, 3, 0), [Instruction.LOG0] = new(GasCostOf.Log, 0, 2, 0), @@ -388,7 +388,7 @@ public static class InstructionExtensions Instruction.SELFDESTRUCT => true, Instruction.BALANCE => true, Instruction.SELFBALANCE => true, - _ => false, + _ => false, }; public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) => diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 6af48faf7be..b7b4618323b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -829,7 +829,7 @@ private CallResult ExecuteCode Date: Thu, 30 May 2024 21:42:43 +0100 Subject: [PATCH 043/146] Added more opcodes --- .../CodeAnalysis/IlEvmTests.cs | 9 +- .../CodeAnalysis/IL/ILCompiler.cs | 97 ++++++++++++++++++- .../CodeAnalysis/IL/ILEvmState.cs | 4 +- .../CodeAnalysis/IL/ILExtensions.cs | 7 ++ .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 10 +- .../Nethermind.Evm/VirtualMachine.cs | 8 +- 6 files changed, 125 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index dd17c6fba27..fee9fa0f172 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -242,9 +242,12 @@ public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) GasAvailable = 1000000, ProgramCounter = 0, EvmException = EvmExceptionType.None, - StopExecution = false, - StackHead = 0, - Memory = new EvmPooledMemory() + ShouldReturn = false, + ShouldRevert = false, + ShouldStop = false, + StackHead = 0, + Memory = new EvmPooledMemory(), + MachineCode = testcase.bytecode }; var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index e3063b52087..114c092865b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -43,10 +43,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); using Local address = method.DeclareLocal(typeof(Address)); + using Local uint256A = method.DeclareLocal(typeof(UInt256)); using Local uint256B = method.DeclareLocal(typeof(UInt256)); using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); + using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); using Local localSpan = method.DeclareLocal(typeof(Span)); using Local uint64A = method.DeclareLocal(typeof(ulong)); @@ -128,7 +130,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.STOP: method.LoadArgument(0); method.LoadConstant(true); - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StopExecution))); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); method.Branch(ret); break; case Instruction.INVALID: @@ -265,6 +267,22 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ il.LoadConstant(0); il.Branch(postInstructionLabel); + il.MarkLabel(label); + }, uint256A, uint256B); + break; + case Instruction.SMOD: + EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel(); + + il.LoadLocal(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.BranchIfFalse(label); + + il.LoadConstant(0); + il.Branch(postInstructionLabel); + il.MarkLabel(label); }, uint256A, uint256B); break; @@ -947,6 +965,47 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(endOfOpcode); break; + + case Instruction.RETURN or Instruction.REVERT: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.Duplicate(); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + + method.LoadArgument(0); + method.LoadConstant(true); + switch(op.Operation) + { + case Instruction.REVERT: + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldRevert))); + break; + case Instruction.RETURN: + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldReturn))); + break; + } + + break; default: throw new NotSupportedException(); } @@ -1194,6 +1253,42 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.StackPush(stack.idx, 1); } + private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + { + Label label = il.DefineLabel("SkipHandlingBinaryOp"); + + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.StackPop(stack.idx, 2); + + // incase of custom handling, we branch to the label + customHandling?.Invoke(il, label, locals); + + // invoke op on the uint256 + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[0]); + il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(uint256R); + il.Call(GetAsMethodInfo()); + il.Call(operation); + + // skip the main handling + il.MarkLabel(label); + + // push the result to the stack + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + il.StackPush(stack.idx, 1); + } + private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { Label label = il.DefineLabel("SkipHandlingBinaryOp"); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 3c0ecdf8ace..188c8928589 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -25,7 +25,9 @@ internal ref struct ILEvmState public ushort ProgramCounter; public long GasAvailable; // in case STOP is executed - public bool StopExecution; + public bool ShouldStop; + public bool ShouldRevert; + public bool ShouldReturn; public int StackHead; public Span Stack; public ref EvmPooledMemory Memory; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 4c9408d4613..e2e81d66d0a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; namespace Nethermind.Evm.IL; @@ -17,6 +18,12 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { + public static MethodInfo GetAsMethodInfo() where TResult : struct + { + MethodInfo method = typeof(Unsafe).GetMethod(nameof(Unsafe.As)); + return method.MakeGenericMethod(typeof(TOriginal), typeof(TResult)); + } + #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type public unsafe static MethodInfo GetCastMethodInfo() where TResult : struct { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index a7fd471e2fd..a08aadf5e11 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,10 +61,13 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, ISpecProvider specProvider, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop) + public bool TryExecute(EvmState vmState, ISpecProvider specProvider, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { + shouldReturn = false; + shouldRevert = false; shouldStop = false; + returnData = null; if (programCounter > ushort.MaxValue) return false; @@ -99,7 +102,10 @@ public bool TryExecute(EvmState vmState, ISpecProvider spe gasAvailable = ilvmState.GasAvailable; vmState.DataStack = ilvmState.Stack.ToArray(); programCounter = ilvmState.ProgramCounter; - shouldStop = ilvmState.StopExecution; + shouldStop = ilvmState.ShouldStop; + shouldReturn = ilvmState.ShouldReturn; + shouldRevert = ilvmState.ShouldRevert; + returnData = ilvmState.ReturnBuffer; break; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index e7423557439..7043746ef7b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -850,12 +850,13 @@ private CallResult ExecuteCode Date: Mon, 3 Jun 2024 20:58:15 +0100 Subject: [PATCH 044/146] - Added more opcodes - more bug fixes - more refactors --- .../CodeAnalysis/IlEvmTests.cs | 61 ++-- .../CodeAnalysis/IL/ILCompiler.cs | 303 +++++++++++++++--- .../CodeAnalysis/IL/ILEvmState.cs | 25 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 8 +- .../Nethermind.Evm/ILExecutionContext.cs | 15 + .../Nethermind.Evm/VirtualMachine.cs | 2 +- 6 files changed, 336 insertions(+), 78 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/ILExecutionContext.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index fee9fa0f172..e41db400b6e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -145,88 +145,96 @@ public void Pure_Opcode_Emition_Coveraga() public static IEnumerable<(int, byte[])> GetBytecodes() { - yield return (-1, Prepare.EvmCode + int i = 0; + yield return (i, Prepare.EvmCode .Done); - yield return (0, Prepare.EvmCode + yield return (++i, Prepare.EvmCode .PushSingle(1) .PushSingle(2) .PushSingle(3) .PushSingle(4) .Done); - yield return (1, Prepare.EvmCode + yield return (++i, Prepare.EvmCode .ISZERO(7) .ISZERO(0) .ISZERO(7) .Done); - yield return (2, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .SUB() .Done); - yield return (3, Prepare.EvmCode + yield return (++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() .Done); - yield return (4, Prepare.EvmCode + yield return (++i, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .PushSingle(5) + .ADDMOD() + .Done); + + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MUL() .Done); - yield return (5, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EXP() .Done); - yield return (6, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MOD() .Done); - yield return (7, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .DIV() .Done); - yield return (8, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .Done); - yield return (9, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) .Done); - yield return (10, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MCOPY(32, 0, 32) .Done); - yield return (11, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EQ() .Done); - yield return (12, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .GT() .Done); - yield return (13, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .LT() .Done); - yield return (14, Prepare.EvmCode + yield return(++i, Prepare.EvmCode .PushSingle(1) .NOT() .Done); @@ -235,20 +243,13 @@ public void Pure_Opcode_Emition_Coveraga() [Test, TestCaseSource(nameof(GetBytecodes))] public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) { - ILEvmState iLEvmState = new ILEvmState - { - Stack = new byte[1024 * 32], - Header = BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header, - GasAvailable = 1000000, - ProgramCounter = 0, - EvmException = EvmExceptionType.None, - ShouldReturn = false, - ShouldRevert = false, - ShouldStop = false, - StackHead = 0, - Memory = new EvmPooledMemory(), - MachineCode = testcase.bytecode - }; + var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); + var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, []); + var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, ReadOnlyMemory.Empty, txExCtx, 23, 7); + var stack = new byte[1024 * 32]; + var memory = new EvmPooledMemory(); + var returnBuffer = ReadOnlyMemory.Empty; + ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, envExCtx, txExCtx, blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, ctx.Data); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 114c092865b..67f27989f0d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -77,6 +77,11 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Convert(); method.StoreLocal(gasAvailable); + // set pc to local + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); + method.StoreLocal(programCounter); + Dictionary labels = new(); foreach (var exception in Enum.GetValues()) @@ -84,11 +89,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ labels.Add(exception, method.DefineLabel(exception.ToString())); } - // set pc to local - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); - method.StoreLocal(programCounter); - Label exit = method.DefineLabel("Return"); // the label just before return Label jumpTable = method.DefineLabel("Jumptable"); // jump table Label ret = method.DefineLabel("return"); @@ -258,29 +258,31 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ { Label label = il.DefineLabel(); - il.LoadLocal(locals[1]); - il.LoadConstant(0); - il.CompareEqual(); - + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); il.BranchIfFalse(label); il.LoadConstant(0); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.Branch(postInstructionLabel); il.MarkLabel(label); }, uint256A, uint256B); break; case Instruction.SMOD: - EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Mod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); - il.LoadLocal(locals[1]); + il.LoadLocalAddress(locals[1]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); il.BranchIfFalse(label); il.LoadConstant(0); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.Branch(postInstructionLabel); il.MarkLabel(label); @@ -292,53 +294,90 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ { Label label = il.DefineLabel(); - il.LoadLocal(locals[1]); - il.LoadConstant(0); - il.CompareEqual(); - + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); il.BranchIfFalse(label); il.LoadConstant(0); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.Branch(postInstructionLabel); il.MarkLabel(label); }, uint256A, uint256B); break; - case Instruction.ADDMOD: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, + + case Instruction.SDIV: + EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel(); + Label label1 = il.DefineLabel(); + Label label2 = il.DefineLabel(); + + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.BranchIfFalse(label1); - il.LoadLocal(locals[2]); il.LoadConstant(0); - il.CompareEqual(); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(label1); + + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + il.LoadField(typeof(Int256.Int256).GetField(nameof(Int256.Int256.MinusOne), BindingFlags.Static | BindingFlags.Public)); + il.Call(typeof(Int256.Int256).GetMethod("op_Equality")); + il.LoadLocalAddress(locals[0]); + il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static).GetMethod); + il.Call(typeof(Int256.Int256).GetMethod("op_Equality")); + il.And(); + il.BranchIfFalse(label2); + + il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static).GetMethod); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(label2); + }, uint256A, uint256B); + break; + case Instruction.ADDMOD: + EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel("failureCase"); + + il.LoadLocalAddress(locals[2]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); il.BranchIfFalse(label); il.LoadConstant(0); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B); + }, uint256A, uint256B, uint256C); break; case Instruction.MULMOD: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, + EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel(); - - il.LoadLocal(locals[2]); - il.LoadConstant(0); - il.CompareEqual(); + Label label = il.DefineLabel("failureCase"); + il.LoadLocalAddress(locals[2]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); il.BranchIfFalse(label); il.LoadConstant(0); + il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.StoreLocal(uint256R); il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B); + }, uint256A, uint256B, uint256C); break; case Instruction.SHL: EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, null, uint256A, uint256B); @@ -346,6 +385,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.SHR: EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, null, uint256A, uint256B); break; + case Instruction.SAR: + EmitShiftInt256Method(method, uint256R, (stack, head), null, uint256A, uint256B); + break; case Instruction.AND: EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); break; @@ -364,6 +406,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.GT: EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; + case Instruction.SLT: + EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256)}), false, uint256A, uint256B); + break; + case Instruction.SGT: + EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, uint256A, uint256B); + break; case Instruction.EQ: EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; @@ -461,7 +509,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); method.Call(Word.SetAddress); method.StackPush(head); @@ -470,7 +519,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); method.Call(Word.SetUInt256); method.StackPush(head); @@ -479,7 +529,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); method.Call(Word.SetUInt256); method.StackPush(head); @@ -488,7 +539,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Header))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); method.Call(Word.SetUInt256); method.StackPush(head); @@ -507,7 +559,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); method.StackPush(head); break; @@ -672,7 +724,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(byte8A); method.StackPop(head, 2); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(1); @@ -682,7 +735,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadArgumentAddress(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.LoadConstant(Word.Size); @@ -698,7 +752,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); @@ -708,7 +763,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); @@ -747,7 +803,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Subtract(); method.StoreLocal(gasAvailable); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -758,9 +815,11 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256B); method.LoadLocalAddress(uint256C); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); @@ -785,7 +844,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Subtract(); method.StoreLocal(gasAvailable); - method.LoadArgument(1); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -1006,13 +1064,72 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } break; + case Instruction.BASEFEE: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + break; + case Instruction.BLOBBASEFEE: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); + method.Call(GetPropertyInfo(typeof(UInt256?), nameof(Nullable.Value), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + break; + case Instruction.PREVRANDAO: + Label isPostMergeBranch = method.DefineLabel("PostMergeRandom"); + endOfOpcode = method.DefineLabel("EndOfOpcodePREVRANDAO"); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Duplicate(); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.IsPostMerge), false, out _)); + method.BranchIfTrue(isPostMergeBranch); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); + method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(BitConverter.IsLittleEndian); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.StackPush(head); + method.Branch(endOfOpcode); + + method.MarkLabel(isPostMergeBranch); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); + method.StackPush(head); + + method.MarkLabel(endOfOpcode); + break; + /*case Instruction.SIGNEXTEND: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + + method.LoadConstant(31); + method.LoadLocalAddress(uint256A); + method.LoadField(Word.Int0Field); + method.Subtract(); + method.Convert(); + method.StoreLocal(uint32A); + + + break;*/ default: throw new NotSupportedException(); } } method.LoadLocal(gasAvailable); - method.LoadConstant(0); + method.LoadConstant((long)0); method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); // prepare ILEvmState @@ -1147,6 +1264,46 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(uint256R); il.Call(shiftOp); il.StackPop(stack.idx, 2); + il.CleanWord(stack.span, stack.idx); + il.LoadLocal(uint256R); + il.Call(Word.SetUInt256); + il.StackPush(stack.idx, 1); + il.BranchIfTrue(endOfOpcode); + + il.MarkLabel(skipPop); + il.StackPop(stack.idx, 2); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.Call(Word.SetToZero); + il.StackPush(stack.idx, 1); + + il.MarkLabel(endOfOpcode); + } + + private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, params Local[] locals) + { + Label skipPop = il.DefineLabel("skipSecondPop"); + Label signIsNeg = il.DefineLabel("signIsNeg"); + Label endOfOpcode = il.DefineLabel("endOfOpcode"); + + // Note: Use Vector256 directoly if UInt256 does not use it internally + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + + il.LoadLocalAddress(locals[0]); + il.LoadConstant(Word.Size * sizeof(byte)); + il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + il.BranchIfTrue(skipPop); + + il.LoadLocalAddress(locals[0]); + il.Call(GetAsMethodInfo()); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.LoadField(Word.Int0Field); + il.LoadLocalAddress(uint256R); + il.Call(GetAsMethodInfo()); + il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.LoadLocal(uint256R); @@ -1158,7 +1315,24 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); + + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + il.Call(GetPropertyInfo(typeof(Int256.Int256), nameof(Int256.Int256.Sign), false, out _)); + il.LoadConstant(0); + il.BranchIfLess(signIsNeg); + il.Call(Word.SetToZero); + il.StackPush(stack.idx); + il.Branch(endOfOpcode); + + // sign + il.MarkLabel(signIsNeg); + il.LoadField(GetFieldInfo(typeof(Int256.Int256), nameof(Int256.Int256.MinusOne))); + il.Call(GetAsMethodInfo()); + il.Call(Word.SetUInt256); + il.StackPush(stack.idx); + il.Branch(endOfOpcode); il.MarkLabel(endOfOpcode); } @@ -1220,6 +1394,51 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Call(Word.SetUInt256); } + private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, params Local[] locals) + { + Label endOpcodeHandling = il.DefineLabel("endOfOpcodeHandling"); + Label pushZerohandling = il.DefineLabel("push1handling"); + // we the two uint256 from the stack + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.StackPop(stack.idx, 2); + + // invoke op on the uint256 + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[0]); + il.Call(GetAsMethodInfo()); + il.Call(operation, null); + il.LoadConstant(0); + if(isGreaterThan) + { + il.BranchIfLess(pushZerohandling); + } + else + { + il.BranchIfGreater(pushZerohandling); + } + + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.One))); + il.StoreLocal(uint256R); + il.Branch(endOpcodeHandling); + + il.MarkLabel(pushZerohandling); + + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.Zero))); + il.StoreLocal(uint256R); + il.MarkLabel(endOpcodeHandling); + // push the result to the stack + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + } + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { Label label = il.DefineLabel("SkipHandlingBinaryOp"); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 188c8928589..47a5e1d53a2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -9,13 +9,13 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; +using static Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript.Log; namespace Nethermind.Evm.CodeAnalysis.IL; internal ref struct ILEvmState { public byte[] MachineCode; // static arguments - public BlockHeader Header; public ExecutionEnvironment Env; public TxExecutionContext TxCtx; public BlockExecutionContext BlkCtx; @@ -28,8 +28,29 @@ internal ref struct ILEvmState public bool ShouldStop; public bool ShouldRevert; public bool ShouldReturn; + public int StackHead; public Span Stack; + public ref EvmPooledMemory Memory; - public ReadOnlyMemory ReturnBuffer; + + public ref ReadOnlyMemory ReturnBuffer; + + public ILEvmState(byte[] machineCode, ExecutionEnvironment env, TxExecutionContext txCtx, BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory returnBuffer) + { + MachineCode = machineCode; + Env = env; + TxCtx = txCtx; + BlkCtx = blkCtx; + EvmException = evmException; + ProgramCounter = programCounter; + GasAvailable = gasAvailable; + ShouldStop = shouldStop; + ShouldRevert = shouldRevert; + ShouldReturn = shouldReturn; + StackHead = stackHead; + Stack = stack; + Memory = ref memory; + ReturnBuffer = ref returnBuffer; + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index a08aadf5e11..a5bbf4aba36 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,7 +61,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, ISpecProvider specProvider, BlockHeader header, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; @@ -79,7 +79,7 @@ public bool TryExecute(EvmState vmState, ISpecProvider spe { return false; } - chunk.Invoke(vmState, specProvider.GetSpec(header.Number, header.Timestamp), ref programCounter, ref gasAvailable, ref stack); + chunk.Invoke(vmState, specProvider.GetSpec(blkCtx.Header.Number, blkCtx.Header.Timestamp), ref programCounter, ref gasAvailable, ref stack); break; } case ILMode.SubsegmentsCompiling: @@ -93,7 +93,9 @@ public bool TryExecute(EvmState vmState, ISpecProvider spe { GasAvailable = (int)gasAvailable, Stack = vmState.DataStack, - Header = header, + Env = vmState.Env, + BlkCtx = blkCtx, + TxCtx = txCtx, ProgramCounter = (ushort)programCounter, Memory = ref vmState.Memory, }; diff --git a/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs b/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs new file mode 100644 index 00000000000..67df80a55a7 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Int256; +using Nethermind.State; + +namespace Nethermind.Evm +{ + public readonly struct ILExecutionContext + { + public readonly IBlockhashProvider BlockhashProvider; + public readonly IWorldState WordState; + } +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 7043746ef7b..69af8a25eb6 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -864,7 +864,7 @@ private CallResult ExecuteCode Date: Tue, 4 Jun 2024 14:51:35 +0100 Subject: [PATCH 045/146] Minor fixes and refactor --- .../CodeAnalysis/IlEvmTests.cs | 17 +++- .../VirtualMachineTestsBase.cs | 5 +- .../CodeAnalysis/IL/ILCompiler.cs | 93 ++++++++++++++++++- .../CodeAnalysis/IL/ILEvmState.cs | 14 +-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 10 +- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 23 +++++ .../Nethermind.Evm/ILExecutionContext.cs | 15 --- .../Nethermind.Evm/VirtualMachine.cs | 6 +- 8 files changed, 146 insertions(+), 37 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Evm/ILExecutionContext.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index e41db400b6e..3e94520719a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -234,25 +234,34 @@ public void Pure_Opcode_Emition_Coveraga() .LT() .Done); - yield return(++i, Prepare.EvmCode + yield return (++i, Prepare.EvmCode .PushSingle(1) .NOT() .Done); + + yield return (++i, Prepare.EvmCode + .PushSingle(0) + .BLOBHASH() + .Done); + + yield return (++i, Prepare.EvmCode + .BLOCKHASH(0) + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) { var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); - var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, []); + var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()]); var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, ReadOnlyMemory.Empty, txExCtx, 23, 7); var stack = new byte[1024 * 32]; var memory = new EvmPooledMemory(); var returnBuffer = ReadOnlyMemory.Empty; - ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, envExCtx, txExCtx, blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref returnBuffer); + ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, ctx.Data); + ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 5985f35d3d5..d32e8f6462e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -31,6 +31,7 @@ public class VirtualMachineTestsBase protected const long DefaultBlockGasLimit = 8000000; private IEthereumEcdsa _ethereumEcdsa; + protected IBlockhashProvider _blockhashProvider; protected ITransactionProcessor _processor; private IDb _stateDb; @@ -66,8 +67,8 @@ public virtual void Setup() ITrieStore trieStore = new TrieStore(_stateDb, logManager); TestState = new WorldState(trieStore, codeDb, logManager); _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId, logManager); - IBlockhashProvider blockhashProvider = new TestBlockhashProvider(SpecProvider); - Machine = new VirtualMachine(blockhashProvider, SpecProvider, logManager); + _blockhashProvider = new TestBlockhashProvider(SpecProvider); + Machine = new VirtualMachine(_blockhashProvider, SpecProvider, logManager); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, logManager); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 67f27989f0d..c799e2a8baf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -25,7 +25,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate void ExecuteSegment(ref ILEvmState state, ISpecProvider spec, byte[][] immediatesData); + public delegate void ExecuteSegment(ref ILEvmState state, ISpecProvider spec, IBlockhashProvider BlockhashProvider, byte[][] immediatesData); public class SegmentExecutionCtx { public ExecuteSegment Method; @@ -43,6 +43,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); using Local address = method.DeclareLocal(typeof(Address)); + using Local hash256 = method.DeclareLocal(typeof(Hash256)); using Local uint256A = method.DeclareLocal(typeof(UInt256)); using Local uint256B = method.DeclareLocal(typeof(UInt256)); @@ -51,8 +52,10 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); using Local localSpan = method.DeclareLocal(typeof(Span)); + using Local localArray = method.DeclareLocal(typeof(byte[])); using Local uint64A = method.DeclareLocal(typeof(ulong)); using Local uint32A = method.DeclareLocal(typeof(uint)); + using Local int64A = method.DeclareLocal(typeof(long)); using Local byte8A = method.DeclareLocal(typeof(byte)); using Local buffer = method.DeclareLocal(typeof(byte*)); @@ -228,7 +231,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Load(stack, head); // we load the span of bytes - method.LoadArgument(2); + method.LoadArgument(3); method.LoadConstant(op.Arguments.Value); method.LoadElement(); method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); @@ -1109,6 +1112,92 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(endOfOpcode); break; + case Instruction.BLOBHASH: + Label blobVersionedHashNotFound = method.DefineLabel("BlobVersionedHashNotFoundDirectSection"); + endOfOpcode = method.DefineLabel("EndOfOpcodeImplementation"); + + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.LoadNull(); + method.BranchIfEqual(blobVersionedHashNotFound); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); + method.StackLoadPrevious(stack, head, 1); + method.LoadField(Word.Int0Field); + method.Duplicate(); + method.StoreLocal(uint32A); + method.StackPop(head, 1); + method.BranchIfLessOrEqual(blobVersionedHashNotFound); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.LoadLocal(uint32A); + method.LoadElement(); + method.StoreLocal(localArray); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(localArray); + method.Call(Word.SetArray); + method.Branch(endOfOpcode); + + method.MarkLabel(blobVersionedHashNotFound); + method.CleanWord(stack, head); + method.Load(stack, head); + method.Call(Word.SetToZero); + + method.MarkLabel(endOfOpcode); + method.StackPush(head, 1); + break; + case Instruction.BLOCKHASH: + Label blockHashReturnedNull = method.DefineLabel(); + Label pushToStackRegion = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.StackPop(head, 1); + method.LoadField(Word.Ulong0Field); + method.Convert(); + method.LoadConstant(long.MaxValue); + method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); + method.StoreLocal(int64A); + + method.LoadArgument(2); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.LoadLocalAddress(int64A); + method.Call(typeof(IBlockhashProvider).GetMethod(nameof(IBlockhashProvider.GetBlockhash), [typeof(BlockHeader), typeof(long).MakeByRefType()])); + + method.StoreLocal(hash256); + method.LoadNull(); + method.BranchIfEqual(blockHashReturnedNull); + // not equal + method.LoadLocal(hash256); + method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + method.Branch(pushToStackRegion); + // equal to null + method.MarkLabel(blockHashReturnedNull); + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.StoreLocal(localReadonOnlySpan); + + method.MarkLabel(pushToStackRegion); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(BitConverter.IsLittleEndian); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.Call(Word.SetUInt256); + method.StackPush(head); + break; /*case Instruction.SIGNEXTEND: method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 47a5e1d53a2..5701b88b8f3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -16,9 +16,9 @@ internal ref struct ILEvmState { public byte[] MachineCode; // static arguments - public ExecutionEnvironment Env; - public TxExecutionContext TxCtx; - public BlockExecutionContext BlkCtx; + public ref ExecutionEnvironment Env; + public ref TxExecutionContext TxCtx; + public ref BlockExecutionContext BlkCtx; // in case of exceptions public EvmExceptionType EvmException; // in case of jumps crossing section boundaries @@ -36,12 +36,12 @@ internal ref struct ILEvmState public ref ReadOnlyMemory ReturnBuffer; - public ILEvmState(byte[] machineCode, ExecutionEnvironment env, TxExecutionContext txCtx, BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory returnBuffer) + public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory returnBuffer) { MachineCode = machineCode; - Env = env; - TxCtx = txCtx; - BlkCtx = blkCtx; + Env = ref env; + TxCtx = ref txCtx; + BlkCtx = ref blkCtx; EvmException = evmException; ProgramCounter = programCounter; GasAvailable = gasAvailable; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index a5bbf4aba36..d24cbe59c28 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,7 +61,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; @@ -93,16 +93,18 @@ public bool TryExecute(EvmState vmState, BlockExecutionCon { GasAvailable = (int)gasAvailable, Stack = vmState.DataStack, + StackHead = vmState.DataStackHead, Env = vmState.Env, - BlkCtx = blkCtx, - TxCtx = txCtx, + BlkCtx = ref blkCtx, + TxCtx = ref txCtx, ProgramCounter = (ushort)programCounter, Memory = ref vmState.Memory, }; - ctx.Method.Invoke(ref ilvmState, specProvider, ctx.Data); + ctx.Method.Invoke(ref ilvmState, specProvider, blockHashProvider, ctx.Data); gasAvailable = ilvmState.GasAvailable; vmState.DataStack = ilvmState.Stack.ToArray(); + vmState.DataStackHead = ilvmState.StackHead; programCounter = ilvmState.ProgramCounter; shouldStop = ilvmState.ShouldStop; shouldReturn = ilvmState.ShouldReturn; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index c5480b7f928..9701336d8ae 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -53,6 +53,26 @@ public void ToZero() Ulong2 = 0; Ulong3 = 0; } + public unsafe byte[] Array + { + get + { + byte[] array = new byte[32]; + fixed(byte* src = _buffer, dest = array) + { + Buffer.MemoryCopy(src, dest, 32, 32); + } + return array; + } + set + { + fixed(byte* src = value, dest = _buffer) + { + Buffer.MemoryCopy(src, dest + (32 - value.Length), value.Length, value.Length); + } + } + } + public unsafe ValueHash256 Keccak { get @@ -155,4 +175,7 @@ public UInt256 UInt256 public static readonly MethodInfo GetKeccak = typeof(Word).GetProperty(nameof(Keccak))!.GetMethod; public static readonly MethodInfo SetKeccak = typeof(Word).GetProperty(nameof(Keccak))!.SetMethod; + + public static readonly MethodInfo GetArray = typeof(Word).GetProperty(nameof(Array))!.GetMethod; + public static readonly MethodInfo SetArray = typeof(Word).GetProperty(nameof(Array))!.SetMethod; } diff --git a/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs b/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs deleted file mode 100644 index 67df80a55a7..00000000000 --- a/src/Nethermind/Nethermind.Evm/ILExecutionContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core; -using Nethermind.Int256; -using Nethermind.State; - -namespace Nethermind.Evm -{ - public readonly struct ILExecutionContext - { - public readonly IBlockhashProvider BlockhashProvider; - public readonly IWorldState WordState; - } -} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 69af8a25eb6..62fa15a73ed 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -55,7 +55,7 @@ public class VirtualMachine : IVirtualMachine internal static readonly byte[] BytesZero = { 0 }; - internal static readonly byte[] BytesZero32 = + public static readonly byte[] BytesZero32 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -63,7 +63,7 @@ public class VirtualMachine : IVirtualMachine 0, 0, 0, 0, 0, 0, 0, 0 }; - internal static readonly byte[] BytesMax32 = + public static readonly byte[] BytesMax32 = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -864,7 +864,7 @@ private CallResult ExecuteCode Date: Sun, 9 Jun 2024 03:44:26 +0100 Subject: [PATCH 046/146] fix bug in JUMPI and jumpdest handling, plust extra fixes and refactors --- .../CodeAnalysis/IlEvmTests.cs | 155 ++++++++-- .../CodeAnalysis/IL/ILCompiler.cs | 272 ++++++++++-------- .../CodeAnalysis/IL/ILEvmState.cs | 4 +- .../CodeAnalysis/IL/ILExtensions.cs | 28 +- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 26 +- .../Nethermind.Evm/VirtualMachine.cs | 6 +- 6 files changed, 323 insertions(+), 168 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 3e94520719a..9d888cbae55 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -143,122 +143,223 @@ public void Pure_Opcode_Emition_Coveraga() } - public static IEnumerable<(int, byte[])> GetBytecodes() + public static IEnumerable<(Instruction?, byte[])> GetBytecodes() { - int i = 0; - yield return (i, Prepare.EvmCode + yield return (null, Prepare.EvmCode .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.PUSH32, Prepare.EvmCode .PushSingle(1) - .PushSingle(2) - .PushSingle(3) - .PushSingle(4) .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.ISZERO, Prepare.EvmCode .ISZERO(7) .ISZERO(0) .ISZERO(7) .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.SUB, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .SUB() .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.ADD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.ADDMOD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .PushSingle(5) .ADDMOD() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.MUL, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MUL() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.EXP, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EXP() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.MOD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MOD() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.DIV, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .DIV() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.MSTORE, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.MLOAD, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.MCOPY, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MCOPY(32, 0, 32) .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.EQ, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EQ() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.GT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .GT() .Done); - yield return(++i, Prepare.EvmCode + yield return(Instruction.LT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .LT() .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.NOT, Prepare.EvmCode .PushSingle(1) .NOT() .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.BLOBHASH, Prepare.EvmCode .PushSingle(0) .BLOBHASH() .Done); - yield return (++i, Prepare.EvmCode + yield return (Instruction.BLOCKHASH, Prepare.EvmCode .BLOCKHASH(0) .Done); + + yield return (Instruction.CALLDATACOPY, Prepare.EvmCode + .CALLDATACOPY(0, 0, 32) + .Done); + + yield return (Instruction.CALLDATALOAD, Prepare.EvmCode + .CALLDATALOAD(0) + .Done); + + yield return (Instruction.MSIZE, Prepare.EvmCode + .MSIZE() + .Done); + + yield return (Instruction.GASPRICE, Prepare.EvmCode + .GASPRICE() + .Done); + + yield return (Instruction.CODESIZE, Prepare.EvmCode + .CODESIZE() + .Done); + + yield return (Instruction.PC, Prepare.EvmCode + .PC() + .Done); + + yield return (Instruction.COINBASE, Prepare.EvmCode + .COINBASE() + .Done); + + yield return (Instruction.TIMESTAMP, Prepare.EvmCode + .TIMESTAMP() + .Done); + + yield return (Instruction.NUMBER, Prepare.EvmCode + .NUMBER() + .Done); + + yield return (Instruction.GASLIMIT, Prepare.EvmCode + .GASLIMIT() + .Done); + + yield return (Instruction.CALLER, Prepare.EvmCode + .CALLER() + .Done); + + yield return (Instruction.ADDRESS, Prepare.EvmCode + .ADDRESS() + .Done); + + yield return (Instruction.ORIGIN, Prepare.EvmCode + .ORIGIN() + .Done); + + yield return (Instruction.CALLVALUE, Prepare.EvmCode + .CALLVALUE() + .Done); + + yield return (Instruction.CHAINID, Prepare.EvmCode + .CHAINID() + .Done); + + yield return (Instruction.GAS,Prepare.EvmCode + .GAS() + .Done); + + yield return (Instruction.RETURNDATASIZE, Prepare.EvmCode + .RETURNDATASIZE() + .Done); + + yield return (Instruction.BASEFEE, Prepare.EvmCode + .BASEFEE() + .Done); + + yield return (Instruction.RETURN, Prepare.EvmCode + .RETURN(0, 32) + .Done); + + yield return (Instruction.REVERT, Prepare.EvmCode + .REVERT(0, 32) + .Done); + + yield return (Instruction.CALLDATASIZE, Prepare.EvmCode + .CALLDATASIZE() + .Done); + + yield return (Instruction.JUMPI, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .PushSingle(9) + .JUMPI() + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .Done); + + yield return (Instruction.JUMP, Prepare.EvmCode + .PushSingle(23) + .JUMP(7) + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] - public void Ensure_Evm_ILvm_Compatibility((int index, byte[] bytecode) testcase) + public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) testcase) { var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()]); - var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, ReadOnlyMemory.Empty, txExCtx, 23, 7); + var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); var stack = new byte[1024 * 32]; var memory = new EvmPooledMemory(); + var inputBuffer = envExCtx.InputData; var returnBuffer = ReadOnlyMemory.Empty; - ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref returnBuffer); + ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref inputBuffer, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, ctx.Data); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index c799e2a8baf..738eebddca2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -51,9 +51,11 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ using Local uint256R = method.DeclareLocal(typeof(UInt256)); using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); + using Local localZeroPaddedSpan = method.DeclareLocal(typeof(ZeroPaddedSpan)); using Local localSpan = method.DeclareLocal(typeof(Span)); using Local localArray = method.DeclareLocal(typeof(byte[])); using Local uint64A = method.DeclareLocal(typeof(ulong)); + using Local uint32A = method.DeclareLocal(typeof(uint)); using Local int64A = method.DeclareLocal(typeof(long)); using Local byte8A = method.DeclareLocal(typeof(byte)); @@ -85,16 +87,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.StoreLocal(programCounter); - Dictionary labels = new(); + Dictionary evmExceptionLabels = new(); foreach (var exception in Enum.GetValues()) { - labels.Add(exception, method.DefineLabel(exception.ToString())); + evmExceptionLabels.Add(exception, method.DefineLabel()); } - Label exit = method.DefineLabel("Return"); // the label just before return - Label jumpTable = method.DefineLabel("Jumptable"); // jump table - Label ret = method.DefineLabel("return"); + Label exit = method.DefineLabel(); // the label just before return + Label jumpTable = method.DefineLabel(); // jump table + Label ret = method.DefineLabel(); Dictionary jumpDestinations = new(); @@ -102,9 +104,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method Dictionary gasCost = BuildCostLookup(code); - for (int pc = 0; pc < code.Length; pc++) + for (int i = 0; i < code.Length; i++) { - OpcodeInfo op = code[pc]; + OpcodeInfo op = code[i]; // set pc method.LoadConstant(op.ProgramCounter); @@ -114,7 +116,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocal(gasAvailable); // get pc gas cost - method.LoadConstant(gasCost[pc]); + method.LoadConstant(op.Metadata?.GasCost ?? 0); // subtract the gas cost method.Subtract(); @@ -125,7 +127,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadConstant((long)0); // if gas is not available, branch to out of gas - method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); // else emit switch (op.Operation) @@ -137,13 +139,13 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Branch(ret); break; case Instruction.INVALID: - method.Branch(labels[EvmExceptionType.InvalidCode]); + method.Branch(evmExceptionLabels[EvmExceptionType.InvalidCode]); break; case Instruction.CHAINID: method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(1); - method.LoadField(GetFieldInfo(typeof(ISpecProvider), nameof(ISpecProvider.ChainId))); + method.CallVirtual(GetPropertyInfo(typeof(ISpecProvider), nameof(ISpecProvider.ChainId), false, out _)); method.StoreField(Word.Ulong0Field); method.StackPush(head); break; @@ -162,8 +164,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ break; case Instruction.JUMPDEST: // mark the jump destination - jumpDestinations[pc] = method.DefineLabel(); - method.MarkLabel(jumpDestinations[pc]); + jumpDestinations[op.ProgramCounter] = method.DefineLabel(); + method.MarkLabel(jumpDestinations[op.ProgramCounter]); break; case Instruction.JUMP: // we jump into the jump table @@ -173,7 +175,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // consume the jump condition Label noJump = method.DefineLabel(); method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetIsZero, null); + method.Call(Word.GetIsZero); // if the jump condition is false, we do not jump method.BranchIfTrue(noJump); @@ -309,7 +311,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ il.MarkLabel(label); }, uint256A, uint256B); break; - case Instruction.SDIV: EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => @@ -350,7 +351,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel("failureCase"); + Label label = il.DefineLabel(); il.LoadLocalAddress(locals[2]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); @@ -368,7 +369,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel("failureCase"); + Label label = il.DefineLabel(); il.LoadLocalAddress(locals[2]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); @@ -525,7 +526,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); - method.Call(Word.SetUInt256); + method.StoreField(Word.Ulong0Field); method.StackPush(head); break; case Instruction.NUMBER: @@ -535,7 +536,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); - method.Call(Word.SetUInt256); + method.StoreField(Word.Ulong0Field); method.StackPush(head); break; case Instruction.GASLIMIT: @@ -545,7 +546,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); - method.Call(Word.SetUInt256); + method.StoreField(Word.Ulong0Field); method.StackPush(head); break; case Instruction.CALLER: @@ -571,7 +572,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); method.Call(Word.SetAddress); method.StackPush(head); break; @@ -580,7 +581,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(ExecutionEnvironment.Value))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); method.StackPush(head); break; @@ -588,15 +589,13 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); method.Call(Word.SetUInt256); method.StackPush(head); break; - case Instruction.CALLDATACOPY: - - Label endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + Label endOfOpcode = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); @@ -629,56 +628,57 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); method.LoadLocalAddress(uint256B); method.LoadLocal(uint256C); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); - method.StoreLocal(localReadonOnlySpan); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localReadonOnlySpan); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); method.MarkLabel(endOfOpcode); break; - case Instruction.CALLDATALOAD: method.CleanWord(stack, head); method.Load(stack, head); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StackPop(head, 1); + method.StoreLocal(uint256A); + method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); - method.StackLoadPrevious(stack, head, 1); + + method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); - method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), BindingFlags.Static | BindingFlags.Public)); - method.Call(typeof(ZeroPaddedSpan).GetMethod(nameof(ZeroPaddedSpan.ToArray), BindingFlags.Instance | BindingFlags.Public)); - method.StoreLocal(localReadonOnlySpan); - // we call UInt256 constructor taking a span of bytes and a bool - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - - method.Call(Word.SetUInt256); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.LoadField(GetFieldInfo(typeof(ZeroPaddedSpan), nameof(ZeroPaddedSpan.Span))); + method.Call(Word.SetSpan); method.StackPush(head); break; case Instruction.CALLDATASIZE: method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); method.StackPush(head); break; - case Instruction.MSIZE: method.CleanWord(stack, head); method.Load(stack, head); @@ -686,7 +686,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); + method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Ulong0))); method.StackPush(head); break; case Instruction.MSTORE: @@ -707,7 +707,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -736,7 +736,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -759,12 +759,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); - method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.StoreLocal(uint256C); - method.LoadLocalAddress(uint256C); + method.LoadFieldAddress(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BigInt32))); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -816,7 +813,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocalAddress(uint256R); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -851,7 +848,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.CleanWord(stack, head); @@ -873,9 +870,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(buffer); method.StackPop(head, 2); - - Label pushZeroLabel = method.DefineLabel("PushZero"); - Label endOfInstructionImpl = method.DefineLabel("endOfByteOpcode"); + Label pushZeroLabel = method.DefineLabel(); + Label endOfInstructionImpl = method.DefineLabel(); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); @@ -907,7 +903,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(endOfInstructionImpl); break; case Instruction.CODECOPY: - endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + endOfOpcode = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); @@ -934,12 +930,13 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); @@ -950,7 +947,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); method.StoreLocal(localReadonOnlySpan); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localReadonOnlySpan); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); @@ -974,7 +972,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StackPush(head); break; case Instruction.RETURNDATACOPY: - endOfOpcode = method.DefineLabel($"endOf{op.Operation}"); + endOfOpcode = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); @@ -1003,12 +1001,13 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); @@ -1019,14 +1018,14 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); method.StoreLocal(localReadonOnlySpan); - method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localReadonOnlySpan); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); method.MarkLabel(endOfOpcode); break; - case Instruction.RETURN or Instruction.REVERT: method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); @@ -1042,17 +1041,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(labels[EvmExceptionType.OutOfGas]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadArgument(0); - method.Duplicate(); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.StoreObject>(); method.LoadArgument(0); method.LoadConstant(true); @@ -1088,8 +1086,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StackPush(head); break; case Instruction.PREVRANDAO: - Label isPostMergeBranch = method.DefineLabel("PostMergeRandom"); - endOfOpcode = method.DefineLabel("EndOfOpcodePREVRANDAO"); + Label isPostMergeBranch = method.DefineLabel(); + endOfOpcode = method.DefineLabel(); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -1113,8 +1111,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.MarkLabel(endOfOpcode); break; case Instruction.BLOBHASH: - Label blobVersionedHashNotFound = method.DefineLabel("BlobVersionedHashNotFoundDirectSection"); - endOfOpcode = method.DefineLabel("EndOfOpcodeImplementation"); + Label blobVersionedHashNotFound = method.DefineLabel(); + endOfOpcode = method.DefineLabel(); method.LoadArgument(0); @@ -1172,11 +1170,12 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.LoadLocalAddress(int64A); - method.Call(typeof(IBlockhashProvider).GetMethod(nameof(IBlockhashProvider.GetBlockhash), [typeof(BlockHeader), typeof(long).MakeByRefType()])); - + method.CallVirtual(typeof(IBlockhashProvider).GetMethod(nameof(IBlockhashProvider.GetBlockhash), [typeof(BlockHeader), typeof(long).MakeByRefType()])); + method.Duplicate(); method.StoreLocal(hash256); method.LoadNull(); method.BranchIfEqual(blockHashReturnedNull); + // not equal method.LoadLocal(hash256); method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); @@ -1184,7 +1183,9 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(localReadonOnlySpan); method.Branch(pushToStackRegion); // equal to null + method.MarkLabel(blockHashReturnedNull); + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); method.StoreLocal(localReadonOnlySpan); @@ -1198,20 +1199,44 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(Word.SetUInt256); method.StackPush(head); break; - /*case Instruction.SIGNEXTEND: + case Instruction.SIGNEXTEND: + Label signIsNegative = method.DefineLabel(); + Label endOfOpcodeHandling = method.DefineLabel(); + method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); + method.LoadField(Word.UInt0Field); + method.StoreLocal(uint32A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetSpan); + method.StoreLocal(localSpan); + method.StackPop(head, 2); - method.LoadConstant(31); - method.LoadLocalAddress(uint256A); - method.LoadField(Word.Int0Field); + method.LoadConstant((uint)31); + method.LoadLocal(uint32A); method.Subtract(); - method.Convert(); method.StoreLocal(uint32A); + method.LoadLocal(localArray); + method.LoadLocal(uint32A); + method.LoadElement(); + method.Convert(); + method.LoadConstant((sbyte)0); + method.BranchIfLess(signIsNegative); + + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); + method.Branch(endOfOpcodeHandling); - break;*/ + method.MarkLabel(signIsNegative); + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesMax32))); + + method.MarkLabel(endOfOpcodeHandling); + method.LoadConstant(0); + method.LoadLocal(uint32A); + method.Convert(); + method.Call(typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), [typeof(byte[]), typeof(int), typeof(int)])); + method.LoadLocalAddress(localSpan); + method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); + break; default: throw new NotSupportedException(); } @@ -1219,7 +1244,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocal(gasAvailable); method.LoadConstant((long)0); - method.BranchIfLess(labels[EvmExceptionType.OutOfGas]); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); // prepare ILEvmState // check if returnState is null @@ -1256,35 +1281,35 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // jump table method.MarkLabel(jumpTable); + method.StackLoadPrevious(stack, head, 1); + method.LoadField(Word.Int0Field); + method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); + method.StoreLocal(jmpDestination); method.StackPop(head); - // emit the jump table - - // load the jump destination - method.Load(stack, head); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); + method.StackPop(head, consumeJumpCondition); + method.LoadConstant(0); + method.StoreLocal(consumeJumpCondition); // if (jumpDest > uint.MaxValue) - method.LoadLocalAddress(uint256B); + + + method.LoadConstant(uint.MaxValue); - method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.LoadLocal(jmpDestination); // goto invalid address - method.BranchIfTrue(labels[EvmExceptionType.InvalidJumpDestination]); + method.BranchIfGreater(evmExceptionLabels[EvmExceptionType.InvalidJumpDestination]); // else - const int jumpFanOutLog = 7; // 128 - const int bitMask = (1 << jumpFanOutLog) - 1; - Label[] jumps = new Label[jumpFanOutLog]; - for (int i = 0; i < jumpFanOutLog; i++) + const int bitMask = (1 << 4) - 1; // 128 + Label[] jumps = new Label[bitMask]; + for (int i = 0; i < bitMask; i++) { jumps[i] = method.DefineLabel(); } // we get first Word.Size bits of the jump destination since it is less than int.MaxValue - method.Load(stack, head, Word.Int0Field); - method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); - method.StoreLocal(jmpDestination); + method.LoadLocal(jmpDestination); method.LoadConstant(bitMask); @@ -1293,23 +1318,26 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // switch on the first 7 bits method.Switch(jumps); - for (int i = 0; i < jumpFanOutLog; i++) + for (int i = 0; i < bitMask; i++) { method.MarkLabel(jumps[i]); - + method.Print(jmpDestination); // for each destination matching the bit mask emit check for the equality foreach (int dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) { method.LoadLocal(jmpDestination); method.LoadConstant(dest); + method.Duplicate(); + method.StoreLocal(uint32A); + method.Print(uint32A); method.BranchIfEqual(jumpDestinations[dest]); } - + method.Print(jmpDestination); // each bucket ends with a jump to invalid access to do not fall through to another one - method.Branch(labels[EvmExceptionType.InvalidJumpDestination]); + method.Branch(evmExceptionLabels[EvmExceptionType.InvalidCode]); } - foreach (var kvp in labels) + foreach (var kvp in evmExceptionLabels) { method.MarkLabel(kvp.Value); method.LoadArgument(0); @@ -1333,8 +1361,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, params Local[] locals) { MethodInfo shiftOp = typeof(UInt256).GetMethod(isLeft ? nameof(UInt256.LeftShift) : nameof(UInt256.RightShift)); - Label skipPop = il.DefineLabel("skipSecondPop"); - Label endOfOpcode = il.DefineLabel("endOfOpcode"); + Label skipPop = il.DefineLabel(); + Label endOfOpcode = il.DefineLabel(); // Note: Use Vector256 directoly if UInt256 does not use it internally // we the two uint256 from the stack @@ -1371,9 +1399,9 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, params Local[] locals) { - Label skipPop = il.DefineLabel("skipSecondPop"); - Label signIsNeg = il.DefineLabel("signIsNeg"); - Label endOfOpcode = il.DefineLabel("endOfOpcode"); + Label skipPop = il.DefineLabel(); + Label signIsNeg = il.DefineLabel(); + Label endOfOpcode = il.DefineLabel(); // Note: Use Vector256 directoly if UInt256 does not use it internally // we the two uint256 from the stack @@ -1485,8 +1513,8 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, params Local[] locals) { - Label endOpcodeHandling = il.DefineLabel("endOfOpcodeHandling"); - Label pushZerohandling = il.DefineLabel("push1handling"); + Label endOpcodeHandling = il.DefineLabel(); + Label pushZerohandling = il.DefineLabel(); // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); @@ -1530,7 +1558,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { - Label label = il.DefineLabel("SkipHandlingBinaryOp"); + Label label = il.DefineLabel(); // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); @@ -1563,7 +1591,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { - Label label = il.DefineLabel("SkipHandlingBinaryOp"); + Label label = il.DefineLabel(); // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); @@ -1599,7 +1627,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) { - Label label = il.DefineLabel("SkipHandlingBinaryOp"); + Label label = il.DefineLabel(); // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 5701b88b8f3..27f3dc9b297 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -34,9 +34,10 @@ internal ref struct ILEvmState public ref EvmPooledMemory Memory; + public ref ReadOnlyMemory InputBuffer; public ref ReadOnlyMemory ReturnBuffer; - public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory returnBuffer) + public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory inputBuffer, ref ReadOnlyMemory returnBuffer) { MachineCode = machineCode; Env = ref env; @@ -52,5 +53,6 @@ public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecut Stack = stack; Memory = ref memory; ReturnBuffer = ref returnBuffer; + InputBuffer = ref inputBuffer; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index e2e81d66d0a..d4df9448e13 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -6,6 +6,7 @@ using Nethermind.Evm.CodeAnalysis.IL; using Sigil; using System; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -43,19 +44,6 @@ public unsafe static Span ReinterpretCast(Span(this Emit il, Local local) - { - if (local.LocalType.IsValueType) - { - il.LoadLocalAddress(local); - } - else - { - il.LoadLocal(local); - } - il.Call(local.LocalType.GetMethods().Where(m => m.Name == "ToString" && m.GetParameters().Length == 0).First(), Type.EmptyTypes); - il.Call(typeof(Console).GetMethod(nameof(Console.WriteLine), [typeof(string)])); - } public static FieldInfo GetFieldInfo(string name) => GetFieldInfo(typeof(T), name); public static FieldInfo GetFieldInfo(Type TypeInstance, string name) { @@ -69,6 +57,20 @@ public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool ge return getSetter ? propInfo.GetSetMethod() : propInfo.GetGetMethod(); } + public static void Print(this Emit il, Local local) + { + if(local.LocalType.IsValueType) + { + il.LoadLocalAddress(local); + il.Call(local.LocalType.GetMethod("ToString", [])); + } + else + { + il.LoadLocal(local); + il.CallVirtual(local.LocalType.GetMethod("ToString", [])); + } + il.Call(typeof(Debug).GetMethod(nameof(Debug.WriteLine), [typeof(string)])); + } public static void Load(this Emit il, Local local, Local idx) { il.LoadLocalAddress(local); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 9701336d8ae..b4428dcea78 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -4,6 +4,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Int256; +using Nethermind.Trie; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -58,7 +59,7 @@ public unsafe byte[] Array get { byte[] array = new byte[32]; - fixed(byte* src = _buffer, dest = array) + fixed (byte* src = _buffer, dest = array) { Buffer.MemoryCopy(src, dest, 32, 32); } @@ -66,7 +67,25 @@ public unsafe byte[] Array } set { - fixed(byte* src = value, dest = _buffer) + fixed (byte* src = value, dest = _buffer) + { + Buffer.MemoryCopy(src, dest + (32 - value.Length), value.Length, value.Length); + } + } + } + + public unsafe ReadOnlySpan Span + { + get + { + fixed (byte* src = _buffer) + { + return new Span(src, 32); + } + } + set + { + fixed (byte* src = value, dest = _buffer) { Buffer.MemoryCopy(src, dest + (32 - value.Length), value.Length, value.Length); } @@ -178,4 +197,7 @@ public UInt256 UInt256 public static readonly MethodInfo GetArray = typeof(Word).GetProperty(nameof(Array))!.GetMethod; public static readonly MethodInfo SetArray = typeof(Word).GetProperty(nameof(Array))!.SetMethod; + + public static readonly MethodInfo GetSpan = typeof(Word).GetProperty(nameof(Span))!.GetMethod; + public static readonly MethodInfo SetSpan = typeof(Word).GetProperty(nameof(Span))!.SetMethod; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index b4de89fdf28..9d5e14268c0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -49,9 +49,9 @@ public class VirtualMachine : IVirtualMachine internal static CodeLruCache CodeCache { get; } = new(); private readonly static UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); - internal static ref readonly UInt256 P255 => ref P255Int; - internal static readonly UInt256 BigInt256 = 256; - internal static readonly UInt256 BigInt32 = 32; + public static ref readonly UInt256 P255 => ref P255Int; + public static readonly UInt256 BigInt256 = 256; + public static readonly UInt256 BigInt32 = 32; internal static readonly byte[] BytesZero = { 0 }; From 26b6287b583b51fb8107f2d80064f261c766fbe4 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 10 Jun 2024 10:34:07 +0100 Subject: [PATCH 047/146] fix jumpdest emition --- .../CodeAnalysis/IlEvmTests.cs | 85 +++++++++++++++---- .../CodeAnalysis/IL/ILCompiler.cs | 73 ++++++++-------- .../CodeAnalysis/IL/ILExtensions.cs | 4 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 1 + 4 files changed, 108 insertions(+), 55 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 9d888cbae55..9c22cc7490b 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -329,24 +329,77 @@ public void Pure_Opcode_Emition_Coveraga() .Done); yield return (Instruction.JUMPI, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .PushSingle(9) - .JUMPI() - .PushSingle(3) - .JUMPDEST() - .PushSingle(0) - .MUL() - .Done); + .PushSingle(23) + .PushSingle(1) + .JUMPI(9) + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .Done); yield return (Instruction.JUMP, Prepare.EvmCode - .PushSingle(23) - .JUMP(7) - .PushSingle(3) - .JUMPDEST() - .PushSingle(0) - .MUL() - .Done); + .PushSingle(23) + .JUMP(10) + .JUMPDEST() + .PushSingle(3) + .MUL() + .STOP() + .JUMPDEST() + .JUMP(5) + .Done); + + yield return (Instruction.SHL, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SHL() + .Done); + + yield return (Instruction.SHR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SHR() + .Done); + + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SAR() + .Done); + + yield return (Instruction.AND, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .AND() + .Done); + + yield return (Instruction.OR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .OR() + .Done); + + yield return (Instruction.XOR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .XOR() + .Done); + + yield return (Instruction.SLT, Prepare.EvmCode + .PushData(23) + .PushSingle(4) + .SLT() + .Done); + + yield return (Instruction.SGT, Prepare.EvmCode + .PushData(23) + .PushData(1) + .SGT() + .Done); + + yield return (Instruction.BYTE, Prepare.EvmCode + .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 738eebddca2..fed40ca8fde 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -108,6 +108,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ { OpcodeInfo op = code[i]; + + if(op.Operation is Instruction.JUMPDEST) + { + // mark the jump destination + jumpDestinations[op.ProgramCounter] = method.DefineLabel(); + method.MarkLabel(jumpDestinations[op.ProgramCounter]); + method.LoadConstant(op.ProgramCounter); + method.StoreLocal(programCounter); + continue; + } // set pc method.LoadConstant(op.ProgramCounter); method.StoreLocal(programCounter); @@ -120,7 +130,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // subtract the gas cost method.Subtract(); - // check if gas is available method.Duplicate(); method.StoreLocal(gasAvailable); @@ -162,11 +171,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadLocal(uint256R); method.Call(Word.SetUInt256); break; - case Instruction.JUMPDEST: - // mark the jump destination - jumpDestinations[op.ProgramCounter] = method.DefineLabel(); - method.MarkLabel(jumpDestinations[op.ProgramCounter]); - break; case Instruction.JUMP: // we jump into the jump table method.Branch(jumpTable); @@ -384,22 +388,22 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ }, uint256A, uint256B, uint256C); break; case Instruction.SHL: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, null, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, uint256A, uint256B); break; case Instruction.SHR: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, null, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, uint256A, uint256B); break; case Instruction.SAR: - EmitShiftInt256Method(method, uint256R, (stack, head), null, uint256A, uint256B); + EmitShiftInt256Method(method, uint256R, (stack, head), uint256A, uint256B); break; case Instruction.AND: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); break; case Instruction.OR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); break; case Instruction.XOR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); break; case Instruction.EXP: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); @@ -866,30 +870,29 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.Call(Word.GetUInt256); method.StoreLocal(uint256A); method.StackLoadPrevious(stack, head, 2); - method.LoadField(GetFieldInfo(typeof(Word), nameof(Word._buffer))); - method.StoreLocal(buffer); + method.Call(Word.GetSpan); + method.StoreLocal(localReadonOnlySpan); method.StackPop(head, 2); Label pushZeroLabel = method.DefineLabel(); Label endOfInstructionImpl = method.DefineLabel(); method.LoadLocalAddress(uint256A); - method.LoadConstant(Word.Size); + method.LoadConstant(Word.Size - 1); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); method.LoadLocalAddress(uint256A); method.LoadConstant(0); - method.Call(typeof(UInt256).GetMethod("op_LesserThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); method.Or(); method.BranchIfTrue(pushZeroLabel); method.CleanWord(stack, head); method.Load(stack, head); - method.LoadLocal(buffer); - method.Duplicate(); + method.LoadLocalAddress(localReadonOnlySpan); method.LoadLocal(uint256A); method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); method.Convert(); - method.Add(); - method.LoadObject(); + method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); + method.LoadIndirect(); method.StoreField(Word.Byte0Field); method.StackPush(head); method.Branch(endOfInstructionImpl); @@ -1242,10 +1245,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } } - method.LoadLocal(gasAvailable); - method.LoadConstant((long)0); - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - // prepare ILEvmState // check if returnState is null method.MarkLabel(ret); @@ -1382,16 +1381,15 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.Call(shiftOp); il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); il.StackPush(stack.idx, 1); - il.BranchIfTrue(endOfOpcode); + il.Branch(endOfOpcode); il.MarkLabel(skipPop); il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); - il.Call(Word.SetToZero); il.StackPush(stack.idx, 1); il.MarkLabel(endOfOpcode); @@ -1423,30 +1421,34 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); il.StackPush(stack.idx, 1); - il.BranchIfTrue(endOfOpcode); + il.Branch(endOfOpcode); il.MarkLabel(skipPop); il.StackPop(stack.idx, 2); - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); - il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); il.Call(GetPropertyInfo(typeof(Int256.Int256), nameof(Int256.Int256.Sign), false, out _)); il.LoadConstant(0); il.BranchIfLess(signIsNeg); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); il.Call(Word.SetToZero); il.StackPush(stack.idx); il.Branch(endOfOpcode); // sign il.MarkLabel(signIsNeg); - il.LoadField(GetFieldInfo(typeof(Int256.Int256), nameof(Int256.Int256.MinusOne))); + il.CleanWord(stack.span, stack.idx); + il.Load(stack.span, stack.idx); + il.LoadFieldAddress(GetFieldInfo(typeof(Int256.Int256), nameof(Int256.Int256.MinusOne))); il.Call(GetAsMethodInfo()); + il.LoadObject(); il.Call(Word.SetUInt256); il.StackPush(stack.idx); il.Branch(endOfOpcode); @@ -1469,13 +1471,9 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc // invoke op on the uint256 il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(uint256R); il.Call(operation, null); - // convert to conv_i - il.Convert(); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - il.StoreLocal(uint256R); - // push the result to the stack il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); @@ -1529,6 +1527,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.Call(GetAsMethodInfo()); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); + il.LoadObject(); il.Call(operation, null); il.LoadConstant(0); if(isGreaterThan) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index d4df9448e13..4dc8a49350a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -19,9 +19,9 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { - public static MethodInfo GetAsMethodInfo() where TResult : struct + public static MethodInfo GetAsMethodInfo() { - MethodInfo method = typeof(Unsafe).GetMethod(nameof(Unsafe.As)); + MethodInfo method = typeof(Unsafe).GetMethods().First((m) => m.Name == nameof(Unsafe.As) && m.ReturnType.IsByRef); return method.MakeGenericMethod(typeof(TOriginal), typeof(TResult)); } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 1d9869c2633..c2c0c79d316 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -202,6 +202,7 @@ public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte sta new Dictionary() { [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), + [Instruction.STOP] = new(0, 0, 0, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.PUSH1] = new(GasCostOf.VeryLow, 1, 0, 1), From 4922f1b25486677b6dfeeb92aabcb11324a2cfae Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 21 Jun 2024 14:02:55 +0100 Subject: [PATCH 048/146] Added a work-around to cross segment jumps inside IL --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 12 ++++++++++++ .../Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs | 10 ++++++---- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 4 +++- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 4 +++- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index fed40ca8fde..2afcfa63cee 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -1286,6 +1286,18 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreLocal(jmpDestination); method.StackPop(head); + //check if jump crosses segment boundaies + Label jumpIsLocal = method.DefineLabel(); + method.LoadLocal(jmpDestination); + method.LoadConstant(code[^1].ProgramCounter + code[^1].Metadata?.AdditionalBytes ?? 0); + method.BranchIfLessOrEqual(jumpIsLocal); + + method.LoadArgument(0); + method.LoadConstant(true); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldJump))); + method.Branch(ret); + + method.MarkLabel(jumpIsLocal); method.StackPop(head, consumeJumpCondition); method.LoadConstant(0); method.StoreLocal(consumeJumpCondition); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 27f3dc9b297..5a7fb78059d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -28,6 +28,7 @@ internal ref struct ILEvmState public bool ShouldStop; public bool ShouldRevert; public bool ShouldReturn; + public bool ShouldJump; public int StackHead; public Span Stack; @@ -37,7 +38,7 @@ internal ref struct ILEvmState public ref ReadOnlyMemory InputBuffer; public ref ReadOnlyMemory ReturnBuffer; - public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, bool shouldStop, bool shouldRevert, bool shouldReturn, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory inputBuffer, ref ReadOnlyMemory returnBuffer) + public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory inputBuffer, ref ReadOnlyMemory returnBuffer) { MachineCode = machineCode; Env = ref env; @@ -46,9 +47,10 @@ public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecut EvmException = evmException; ProgramCounter = programCounter; GasAvailable = gasAvailable; - ShouldStop = shouldStop; - ShouldRevert = shouldRevert; - ShouldReturn = shouldReturn; + ShouldStop = false; + ShouldRevert = false; + ShouldReturn = false; + ShouldJump = false; StackHead = stackHead; Stack = stack; Memory = ref memory; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index d24cbe59c28..d70e7cebe1c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,12 +61,13 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; shouldRevert = false; shouldStop = false; + shouldJump = false; returnData = null; if (programCounter > ushort.MaxValue) return false; @@ -110,6 +111,7 @@ public bool TryExecute(EvmState vmState, BlockExecutionCon shouldReturn = ilvmState.ShouldReturn; shouldRevert = ilvmState.ShouldRevert; returnData = ilvmState.ReturnBuffer; + shouldJump = ilvmState.ShouldJump; break; } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index c2c0c79d316..0d43c1940b0 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -176,7 +176,7 @@ public enum Instruction : byte INVALID = 0xfe, SELFDESTRUCT = 0xff, } - public record struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) + public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) { /// /// The gas cost. diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9d5e14268c0..cbbbba07dbd 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -857,6 +857,7 @@ private CallResult ExecuteCode Date: Sat, 22 Jun 2024 00:41:32 +0100 Subject: [PATCH 049/146] fix build issue fix ephemeralSegmentJump program counter setting --- .../CodeAnalysis/IlEvmTests.cs | 6 +- .../CodeAnalysis/IL/ILCompiler.cs | 56 ++++++++++++++----- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 9c22cc7490b..55173b97cc0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -400,6 +400,10 @@ public void Pure_Opcode_Emition_Coveraga() yield return (Instruction.BYTE, Prepare.EvmCode .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) .Done); + + yield return (Instruction.JUMP, Prepare.EvmCode + .JUMP(31) + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] @@ -412,7 +416,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) var memory = new EvmPooledMemory(); var inputBuffer = envExCtx.InputData; var returnBuffer = ReadOnlyMemory.Empty; - ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, false, false, false, 0, stack, ref memory, ref inputBuffer, ref returnBuffer); + ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, 0, stack, ref memory, ref inputBuffer, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, ctx.Data); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2afcfa63cee..70617785258 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -39,6 +39,26 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + if (code.Length == 0) + { + method.Return(); + } + else + { + EmitSegmentBody(method, code); + } + + ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); + return new SegmentExecutionCtx + { + Method = dynEmitedDelegate, + Data = data + }; + } + + private static void EmitSegmentBody(Emit method, OpcodeInfo[] code) + { + using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); @@ -109,7 +129,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ OpcodeInfo op = code[i]; - if(op.Operation is Instruction.JUMPDEST) + if (op.Operation is Instruction.JUMPDEST) { // mark the jump destination jumpDestinations[op.ProgramCounter] = method.DefineLabel(); @@ -415,7 +435,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); break; case Instruction.SLT: - EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256)}), false, uint256A, uint256B); + EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), false, uint256A, uint256B); break; case Instruction.SGT: EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, uint256A, uint256B); @@ -665,7 +685,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); - + method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); method.LoadConstant((int)PadDirection.Right); @@ -1057,7 +1077,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.LoadArgument(0); method.LoadConstant(true); - switch(op.Operation) + switch (op.Operation) { case Instruction.REVERT: method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldRevert))); @@ -1159,7 +1179,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ case Instruction.BLOCKHASH: Label blockHashReturnedNull = method.DefineLabel(); Label pushToStackRegion = method.DefineLabel(); - + method.StackLoadPrevious(stack, head, 1); method.StackPop(head, 1); method.LoadField(Word.Ulong0Field); @@ -1245,6 +1265,8 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } } + Label skipProgramCounterSetting = method.DefineLabel(); + Local isEphemeralJump = method.DeclareLocal(); // prepare ILEvmState // check if returnState is null method.MarkLabel(ret); @@ -1265,10 +1287,16 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); // set program counter + method.LoadLocal(isEphemeralJump); + method.BranchIfTrue(skipProgramCounterSetting); + method.LoadArgument(0); method.LoadLocal(programCounter); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); + method.MarkLabel(skipProgramCounterSetting); + + // set exception method.LoadArgument(0); method.LoadConstant((int)EvmExceptionType.None); @@ -1277,7 +1305,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // go to return method.Branch(exit); - // jump table method.MarkLabel(jumpTable); method.StackLoadPrevious(stack, head, 1); @@ -1289,12 +1316,18 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); method.LoadLocal(jmpDestination); - method.LoadConstant(code[^1].ProgramCounter + code[^1].Metadata?.AdditionalBytes ?? 0); + method.LoadConstant(code[code.Length - 1].ProgramCounter + code[code.Length - 1].Metadata?.AdditionalBytes ?? 0); method.BranchIfLessOrEqual(jumpIsLocal); method.LoadArgument(0); + method.Duplicate(); + method.LoadConstant(true); + method.StoreLocal(isEphemeralJump); method.LoadConstant(true); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldJump))); + method.LoadLocal(jmpDestination); + method.Convert(); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.Branch(ret); method.MarkLabel(jumpIsLocal); @@ -1320,7 +1353,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } // we get first Word.Size bits of the jump destination since it is less than int.MaxValue - + method.LoadLocal(jmpDestination); method.LoadConstant(bitMask); @@ -1360,13 +1393,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // return method.MarkLabel(exit); method.Return(); - - ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); - return new SegmentExecutionCtx - { - Method = dynEmitedDelegate, - Data = data - }; } private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, params Local[] locals) From 05a0fd4717cc076bbbccd96d2fe3c38acca417b3 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 24 Jul 2024 15:17:28 +0100 Subject: [PATCH 050/146] * Expose EvmState in ILEvmState struct --- .../CodeAnalysis/IlEvmTests.cs | 16 ++++++- .../CodeAnalysis/IL/ILCompiler.cs | 8 ++-- .../CodeAnalysis/IL/ILEvmState.cs | 44 ++++++++++++------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 20 +++------ .../Nethermind.Evm/ExecutionEnvironment.cs | 1 + .../Nethermind.Evm/VirtualMachine.cs | 2 +- 6 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 55173b97cc0..18b7781a3f6 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using FluentAssertions; +using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Evm.CodeAnalysis; @@ -9,6 +10,7 @@ using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Specs; +using Nethermind.State; using NUnit.Framework; using Sigil; using System; @@ -413,10 +415,20 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()]); var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); var stack = new byte[1024 * 32]; - var memory = new EvmPooledMemory(); var inputBuffer = envExCtx.InputData; var returnBuffer = ReadOnlyMemory.Empty; - ILEvmState iLEvmState = new ILEvmState(testcase.bytecode, ref envExCtx, ref txExCtx, ref blkExCtx, EvmExceptionType.None, 0, 100000, 0, stack, ref memory, ref inputBuffer, ref returnBuffer); + + var state = new EvmState( + 1_000_000, + new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), + ExecutionType.CALL, + isTopLevel: false, + Snapshot.Empty, + isContinuation: false); + + state.InitStacks(); + + ILEvmState iLEvmState = new ILEvmState(state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, ctx.Data); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 70617785258..7933ce36916 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -654,9 +654,9 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadLocalAddress(uint256B); method.LoadLocal(uint256C); method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); @@ -682,9 +682,9 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPop(head, 1); method.StoreLocal(uint256A); + method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.InputData))); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 5a7fb78059d..652e2592195 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -14,11 +14,15 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal ref struct ILEvmState { - public byte[] MachineCode; + public ReadOnlyMemory MachineCode; + public EvmState EvmState; // static arguments - public ref ExecutionEnvironment Env; - public ref TxExecutionContext TxCtx; - public ref BlockExecutionContext BlkCtx; + // * vmState.Env : + public ref readonly ExecutionEnvironment Env; + // * vmState.Env.TxCtx : + public ref readonly TxExecutionContext TxCtx; + // * vmState.Env.TxCtx.BlkCtx : + public ref readonly BlockExecutionContext BlkCtx; // in case of exceptions public EvmExceptionType EvmException; // in case of jumps crossing section boundaries @@ -30,31 +34,39 @@ internal ref struct ILEvmState public bool ShouldReturn; public bool ShouldJump; - public int StackHead; + // * vmState.DataStackHead : + public ref int StackHead; + // * vmState.DataStack : public Span Stack; + // * vmState.Memory : public ref EvmPooledMemory Memory; - public ref ReadOnlyMemory InputBuffer; + public ref readonly ReadOnlyMemory InputBuffer; public ref ReadOnlyMemory ReturnBuffer; - public ILEvmState(byte[] machineCode, ref ExecutionEnvironment env, ref TxExecutionContext txCtx, ref BlockExecutionContext blkCtx, EvmExceptionType evmException, ushort programCounter, long gasAvailable, int stackHead, Span stack, ref EvmPooledMemory memory, ref ReadOnlyMemory inputBuffer, ref ReadOnlyMemory returnBuffer) + public ILEvmState(EvmState evmState, EvmExceptionType evmException, ushort programCounter, long gasAvailable, ref ReadOnlyMemory returnBuffer) { - MachineCode = machineCode; - Env = ref env; - TxCtx = ref txCtx; - BlkCtx = ref blkCtx; + // locals for ease of access + EvmState = evmState; + MachineCode = evmState.Env.CodeInfo.MachineCode; + Env = ref evmState.Env; + TxCtx = ref evmState.Env.TxExecutionContext; + BlkCtx = ref evmState.Env.TxExecutionContext.BlockExecutionContext; + StackHead = ref evmState.DataStackHead; + Stack = evmState.DataStack; + Memory = ref evmState.Memory; + EvmException = evmException; ProgramCounter = programCounter; GasAvailable = gasAvailable; + + InputBuffer = ref evmState.Env.InputData; + ReturnBuffer = ref returnBuffer; + ShouldStop = false; ShouldRevert = false; ShouldReturn = false; ShouldJump = false; - StackHead = stackHead; - Stack = stack; - Memory = ref memory; - ReturnBuffer = ref returnBuffer; - InputBuffer = ref inputBuffer; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index b3e7f657874..baa2e11cf9f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,7 +61,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, BlockExecutionContext blkCtx, TxExecutionContext txCtx, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, ref ReadOnlyMemory outputBuffer, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; @@ -80,6 +80,7 @@ public bool TryExecute(EvmState vmState, BlockExecutionCon { return false; } + var blkCtx = vmState.Env.TxExecutionContext.BlockExecutionContext; chunk.Invoke(vmState, specProvider.GetSpec(blkCtx.Header.Number, blkCtx.Header.Timestamp), ref programCounter, ref gasAvailable, ref stack); break; } @@ -90,28 +91,19 @@ public bool TryExecute(EvmState vmState, BlockExecutionCon return false; } - var ilvmState = new ILEvmState - { - GasAvailable = (int)gasAvailable, - Stack = vmState.DataStack, - StackHead = vmState.DataStackHead, - Env = vmState.Env, - BlkCtx = ref blkCtx, - TxCtx = ref txCtx, - ProgramCounter = (ushort)programCounter, - Memory = ref vmState.Memory, - }; + var ilvmState = new ILEvmState(vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); ctx.Method.Invoke(ref ilvmState, specProvider, blockHashProvider, ctx.Data); + gasAvailable = ilvmState.GasAvailable; - vmState.DataStack = ilvmState.Stack.ToArray(); - vmState.DataStackHead = ilvmState.StackHead; programCounter = ilvmState.ProgramCounter; shouldStop = ilvmState.ShouldStop; shouldReturn = ilvmState.ShouldReturn; shouldRevert = ilvmState.ShouldRevert; + returnData = ilvmState.ReturnBuffer; shouldJump = ilvmState.ShouldJump; + break; } } diff --git a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs index ec6600fef8e..15c57382a0d 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs @@ -58,6 +58,7 @@ public ExecutionEnvironment /// public readonly ReadOnlyMemory InputData; + /// /// Transaction originator /// diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 39ea93f0a57..d5d4b768abd 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -728,7 +728,7 @@ private CallResult ExecuteCode Date: Fri, 26 Jul 2024 13:02:55 +0100 Subject: [PATCH 051/146] * Expose IWorldState to ILEVM --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 4 +++- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 10 +++++++++- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 5 +++-- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs | 11 +++++++++++ src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 18b7781a3f6..f04cc1331bf 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -11,6 +11,8 @@ using Nethermind.Int256; using Nethermind.Specs; using Nethermind.State; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; using NUnit.Framework; using Sigil; using System; @@ -431,7 +433,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) ILEvmState iLEvmState = new ILEvmState(state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, ctx.Data); + ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, TestState, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 7933ce36916..fe459625e14 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -21,11 +21,12 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Specs; +using Nethermind.State; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate void ExecuteSegment(ref ILEvmState state, ISpecProvider spec, IBlockhashProvider BlockhashProvider, byte[][] immediatesData); + public delegate void ExecuteSegment(ref ILEvmState vmstate, ISpecProvider spec, IBlockhashProvider blockhashProvider, IWorldState worldState, byte[][] immediatesData); public class SegmentExecutionCtx { public ExecuteSegment Method; @@ -1260,6 +1261,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(localSpan); method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); break; + + case Instruction.SLOAD: + // load vmState from argument + method.LoadArgument(0); + // load storage from vmState + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + break; default: throw new NotSupportedException(); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index baa2e11cf9f..1644ec137a5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -5,6 +5,7 @@ using Microsoft.IdentityModel.Tokens; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.State; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -61,7 +62,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, ref ReadOnlyMemory outputBuffer, ISpecProvider specProvider, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, ref ReadOnlyMemory outputBuffer, ISpecProvider specProvider, IWorldState worldState, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; @@ -93,7 +94,7 @@ public bool TryExecute(EvmState vmState, ref ReadOnlyMemor var ilvmState = new ILEvmState(vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - ctx.Method.Invoke(ref ilvmState, specProvider, blockHashProvider, ctx.Data); + ctx.Method.Invoke(ref ilvmState, specProvider, blockHashProvider, worldState, ctx.Data); gasAvailable = ilvmState.GasAvailable; programCounter = ilvmState.ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index b4428dcea78..a3768e7d690 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -5,6 +5,7 @@ using Nethermind.Core.Crypto; using Nethermind.Int256; using Nethermind.Trie; +using Newtonsoft.Json.Linq; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -200,4 +201,14 @@ public UInt256 UInt256 public static readonly MethodInfo GetSpan = typeof(Word).GetProperty(nameof(Span))!.GetMethod; public static readonly MethodInfo SetSpan = typeof(Word).GetProperty(nameof(Span))!.SetMethod; + + public static explicit operator Word(Span span) + { + unsafe + { + var result = new Word(); + result.Span = span; + return result; + } + } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d5d4b768abd..62bfd58cd8e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -728,7 +728,7 @@ private CallResult ExecuteCode Date: Fri, 26 Jul 2024 23:31:02 +0100 Subject: [PATCH 052/146] * draft implementation for TSTORE/TLOAD --- .../CodeAnalysis/IL/ILCompiler.cs | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index fe459625e14..2cb1c2beab9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -82,6 +82,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local byte8A = method.DeclareLocal(typeof(byte)); using Local buffer = method.DeclareLocal(typeof(byte*)); + using Local storageCell = method.DeclareLocal(typeof(StorageCell)); + using Local gasAvailable = method.DeclareLocal(typeof(long)); using Local programCounter = method.DeclareLocal(typeof(ushort)); @@ -1262,11 +1264,57 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); break; - case Instruction.SLOAD: - // load vmState from argument + case Instruction.TSTORE: method.LoadArgument(0); - // load storage from vmState method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), true, out _)); + method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetArray); + method.StoreLocal(localArray); + + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.Call(GetPropertyInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount), false, out _)); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.LoadLocal(localArray); + method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); + break; + case Instruction.TLOAD: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.Call(GetPropertyInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount), false, out _)); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(localReadonOnlySpan); + method.Call(Word.SetSpan); + method.StackPush(head); break; default: throw new NotSupportedException(); From 65821c6456f8d72c5c5dc275e170082f4fd777aa Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 27 Jul 2024 09:12:08 +0100 Subject: [PATCH 053/146] * fix some failing tests --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 10 ++++++++++ .../Nethermind.Evm.Test/VirtualMachineTestsBase.cs | 4 ++-- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 14 ++++++++------ .../Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs | 4 ++-- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index f04cc1331bf..469381c9ffb 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -408,6 +408,16 @@ public void Pure_Opcode_Emition_Coveraga() yield return (Instruction.JUMP, Prepare.EvmCode .JUMP(31) .Done); + + yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode + .PushData(0) + .PushData(23) + .TSTORE() + .PushData(0) + .TLOAD() + .PushData(23) + .EQ() + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 1b36ba47c2c..2d9f69dced0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -68,9 +68,9 @@ public virtual void Setup() ITrieStore trieStore = new TrieStore(_stateDb, logManager); TestState = new WorldState(trieStore, codeDb, logManager); _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId, logManager); - IBlockhashProvider blockhashProvider = new TestBlockhashProvider(SpecProvider); + _blockhashProvider = new TestBlockhashProvider(SpecProvider); CodeInfoRepository = new CodeInfoRepository(); - Machine = new VirtualMachine(blockhashProvider, SpecProvider, CodeInfoRepository, logManager); + Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2cb1c2beab9..6dce8e63403 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -260,7 +260,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); // we load the span of bytes - method.LoadArgument(3); + method.LoadArgument(4); method.LoadConstant(op.Arguments.Value); method.LoadElement(); method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); @@ -660,6 +660,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); + method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256B); method.LoadLocal(uint256C); method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); @@ -688,6 +689,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); + method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); @@ -1267,7 +1269,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.TSTORE: method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), true, out _)); + method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); method.StackLoadPrevious(stack, head, 1); @@ -1282,12 +1284,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.Call(GetPropertyInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount), false, out _)); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); method.StoreLocal(storageCell); - method.LoadArgument(2); + method.LoadArgument(3); method.LoadLocalAddress(storageCell); method.LoadLocal(localArray); method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); @@ -1300,12 +1302,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.Call(GetPropertyInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount), false, out _)); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); method.StoreLocal(storageCell); - method.LoadArgument(2); + method.LoadArgument(3); method.LoadLocalAddress(storageCell); method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); method.StoreLocal(localReadonOnlySpan); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 652e2592195..4891cf669df 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -35,7 +35,7 @@ internal ref struct ILEvmState public bool ShouldJump; // * vmState.DataStackHead : - public ref int StackHead; + public int StackHead; // * vmState.DataStack : public Span Stack; @@ -53,7 +53,7 @@ public ILEvmState(EvmState evmState, EvmExceptionType evmException, ushort progr Env = ref evmState.Env; TxCtx = ref evmState.Env.TxExecutionContext; BlkCtx = ref evmState.Env.TxExecutionContext.BlockExecutionContext; - StackHead = ref evmState.DataStackHead; + StackHead = evmState.DataStackHead; Stack = evmState.DataStack; Memory = ref evmState.Memory; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 62bfd58cd8e..679f9ec7fd9 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -37,7 +37,7 @@ public class VirtualMachine : IVirtualMachine private static readonly UInt256 P255Int = (UInt256)System.Numerics.BigInteger.Pow(2, 255); internal static ref readonly UInt256 P255 => ref P255Int; internal static readonly UInt256 BigInt256 = 256; - internal static readonly UInt256 BigInt32 = 32; + public static readonly UInt256 BigInt32 = 32; internal static readonly byte[] BytesZero = [0]; From 44c1f4ad5a2097cc67736461f9b44669213ab653 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 27 Jul 2024 23:35:45 +0100 Subject: [PATCH 054/146] * fix TSTORE/TLOAD --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 6 ++---- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 469381c9ffb..dbf3ea9161f 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -410,13 +410,11 @@ public void Pure_Opcode_Emition_Coveraga() .Done); yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode - .PushData(0) .PushData(23) + .PushData(7) .TSTORE() - .PushData(0) + .PushData(7) .TLOAD() - .PushData(23) - .EQ() .Done); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 6dce8e63403..807894ae4ca 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -1292,7 +1292,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(3); method.LoadLocalAddress(storageCell); method.LoadLocal(localArray); - method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); break; case Instruction.TLOAD: method.StackLoadPrevious(stack, head, 1); @@ -1309,12 +1309,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(3); method.LoadLocalAddress(storageCell); - method.CallVirtual(typeof(StorageCell).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); method.StoreLocal(localReadonOnlySpan); method.CleanWord(stack, head); method.Load(stack, head); - method.LoadLocalAddress(localReadonOnlySpan); + method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); method.StackPush(head); break; From aa05c83a10020a47df9d4b79d18fa7c76ccbf385 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 28 Jul 2024 22:57:58 +0100 Subject: [PATCH 055/146] * fix EXTCODE* opcodes --- .../CodeAnalysis/IlEvmTests.cs | 27 ++- .../CodeAnalysis/IL/ILCompiler.cs | 178 +++++++++++++++++- .../CodeAnalysis/IL/ILEvmState.cs | 5 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 8 +- .../Nethermind.Evm/VirtualMachine.cs | 20 +- 5 files changed, 213 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index dbf3ea9161f..b468dfc1e25 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -10,6 +10,7 @@ using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Specs; +using Nethermind.Specs.Forks; using Nethermind.State; using Nethermind.Trie; using Nethermind.Trie.Pruning; @@ -18,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using static Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript.Log; @@ -416,6 +418,21 @@ public void Pure_Opcode_Emition_Coveraga() .PushData(7) .TLOAD() .Done); + + yield return (Instruction.EXTCODESIZE, Prepare.EvmCode + .EXTCODESIZE(Address.FromNumber(1)) + .Done); + + yield return (Instruction.EXTCODEHASH, Prepare.EvmCode + .EXTCODEHASH(Address.FromNumber(1)) + .Done); + + yield return (Instruction.EXTCODECOPY, Prepare.EvmCode + .PushData(0) + .PushData(0) + .PushData(0) + .EXTCODECOPY(Address.FromNumber(1)) + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] @@ -428,6 +445,9 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) var inputBuffer = envExCtx.InputData; var returnBuffer = ReadOnlyMemory.Empty; + TestState.CreateAccount(Address.FromNumber(1), 1000000); + TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); + var state = new EvmState( 1_000_000, new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), @@ -436,12 +456,15 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) Snapshot.Empty, isContinuation: false); + IVirtualMachine evm = typeof(VirtualMachine).GetField("_evm", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Machine) as IVirtualMachine; + ICodeInfoRepository codeInfoRepository = typeof(VirtualMachine).GetField("_codeInfoRepository", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(evm) as ICodeInfoRepository; + state.InitStacks(); - ILEvmState iLEvmState = new ILEvmState(state, EvmExceptionType.None, 0, 100000, ref returnBuffer); + ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, MainnetSpecProvider.Instance, _blockhashProvider, TestState, ctx.Data); + ctx.Method(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance , ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 807894ae4ca..b2eef2eb824 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -22,11 +22,12 @@ using Nethermind.Core.Specs; using Nethermind.Specs; using Nethermind.State; +using Nethermind.Evm.Tracing; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate void ExecuteSegment(ref ILEvmState vmstate, ISpecProvider spec, IBlockhashProvider blockhashProvider, IWorldState worldState, byte[][] immediatesData); + public delegate void ExecuteSegment(ref ILEvmState vmstate, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, byte[][] immediatesData); public class SegmentExecutionCtx { public ExecuteSegment Method; @@ -71,9 +72,11 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local uint256C = method.DeclareLocal(typeof(UInt256)); using Local uint256R = method.DeclareLocal(typeof(UInt256)); + using Local localReadOnlyMemory = method.DeclareLocal(typeof(ReadOnlyMemory)); using Local localReadonOnlySpan = method.DeclareLocal(typeof(ReadOnlySpan)); using Local localZeroPaddedSpan = method.DeclareLocal(typeof(ZeroPaddedSpan)); using Local localSpan = method.DeclareLocal(typeof(Span)); + using Local localMemory = method.DeclareLocal(typeof(Memory)); using Local localArray = method.DeclareLocal(typeof(byte[])); using Local uint64A = method.DeclareLocal(typeof(ulong)); @@ -176,8 +179,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.CHAINID: method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(1); - method.CallVirtual(GetPropertyInfo(typeof(ISpecProvider), nameof(ISpecProvider.ChainId), false, out _)); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.StoreField(Word.Ulong0Field); method.StackPush(head); break; @@ -260,7 +263,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); // we load the span of bytes - method.LoadArgument(4); + method.LoadArgument(5); method.LoadConstant(op.Arguments.Value); method.LoadElement(); method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); @@ -1193,7 +1196,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); method.StoreLocal(int64A); - method.LoadArgument(2); + method.LoadArgument(1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -1289,7 +1292,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); method.StoreLocal(storageCell); - method.LoadArgument(3); + method.LoadArgument(2); method.LoadLocalAddress(storageCell); method.LoadLocal(localArray); method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); @@ -1307,7 +1310,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); method.StoreLocal(storageCell); - method.LoadArgument(3); + method.LoadArgument(2); method.LoadLocalAddress(storageCell); method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); method.StoreLocal(localReadonOnlySpan); @@ -1318,12 +1321,171 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.SetSpan); method.StackPush(head); break; + case Instruction.EXTCODESIZE: + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.CleanWord(stack, head); + method.Load(stack, head); + + method.LoadArgument(3); + method.LoadArgument(2); + method.LoadLocal(address); + method.LoadArgument(4); + method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); + method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); + method.StoreLocal(localReadOnlyMemory); + method.LoadLocalAddress(localReadOnlyMemory); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + + method.StoreField(Word.Int0Field); + method.StackPush(head); + break; + + case Instruction.EXTCODECOPY: + endOfOpcode = method.DefineLabel(); + + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 4); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 4); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(3); + method.LoadArgument(2); + method.LoadLocal(address); + method.LoadArgument(4); + method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); + method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); + + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); + + method.MarkLabel(endOfOpcode); + break; + case Instruction.EXTCODEHASH: + endOfOpcode = method.DefineLabel(); + + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeHashCost))); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.AccountExists))); + method.LoadConstant(false); + method.CompareEqual(); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.IsDeadAccount))); + method.Or(); + method.BranchIfTrue(endOfOpcode); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); + method.Call(Word.SetKeccak); + method.MarkLabel(endOfOpcode); + method.StackPush(head); + break; default: throw new NotSupportedException(); } } - Label skipProgramCounterSetting = method.DefineLabel(); + Label skipProgramCounterSetting = method .DefineLabel(); Local isEphemeralJump = method.DeclareLocal(); // prepare ILEvmState // check if returnState is null diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 4891cf669df..8b754a5fe7f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -14,6 +14,8 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal ref struct ILEvmState { + public ulong ChainId; + public ReadOnlyMemory MachineCode; public EvmState EvmState; // static arguments @@ -45,8 +47,9 @@ internal ref struct ILEvmState public ref readonly ReadOnlyMemory InputBuffer; public ref ReadOnlyMemory ReturnBuffer; - public ILEvmState(EvmState evmState, EvmExceptionType evmException, ushort programCounter, long gasAvailable, ref ReadOnlyMemory returnBuffer) + public ILEvmState(ulong chainId, EvmState evmState, EvmExceptionType evmException, ushort programCounter, long gasAvailable, ref ReadOnlyMemory returnBuffer) { + ChainId = chainId; // locals for ease of access EvmState = evmState; MachineCode = evmState.Env.CodeInfo.MachineCode; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 1644ec137a5..e0e1a1bb27c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -62,7 +62,7 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, ref ReadOnlyMemory outputBuffer, ISpecProvider specProvider, IWorldState worldState, IBlockhashProvider blockHashProvider, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) where TTracingInstructions : struct, VirtualMachine.IIsTracing { shouldReturn = false; @@ -82,7 +82,7 @@ public bool TryExecute(EvmState vmState, ref ReadOnlyMemor return false; } var blkCtx = vmState.Env.TxExecutionContext.BlockExecutionContext; - chunk.Invoke(vmState, specProvider.GetSpec(blkCtx.Header.Number, blkCtx.Header.Timestamp), ref programCounter, ref gasAvailable, ref stack); + chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); break; } case ILMode.SubsegmentsCompiling: @@ -92,9 +92,9 @@ public bool TryExecute(EvmState vmState, ref ReadOnlyMemor return false; } - var ilvmState = new ILEvmState(vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); + var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - ctx.Method.Invoke(ref ilvmState, specProvider, blockHashProvider, worldState, ctx.Data); + ctx.Method.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); gasAvailable = ilvmState.GasAvailable; programCounter = ilvmState.ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 679f9ec7fd9..82ef34b211e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -476,14 +476,14 @@ private static void UpdateGasUp(long refund, ref long gasAvailable) gasAvailable += refund; } - private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec, bool chargeForWarm = true) + public static bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec, ITxTracer tracer, bool chargeForWarm = true) { // Console.WriteLine($"Accessing {address}"); bool result = true; if (spec.UseHotAndColdStorage) { - if (_txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + if (tracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list { vmState.WarmUp(address); } @@ -728,7 +728,7 @@ private CallResult ExecuteCode( Address codeSource = stack.PopAddress(); if (codeSource is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, spec)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, spec, _txTracer)) return EvmExceptionType.OutOfGas; UInt256 callValue; switch (instruction) @@ -2300,7 +2300,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref Address inheritor = stack.PopAddress(); if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, spec, false)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, spec, _txTracer, false)) return EvmExceptionType.OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.CreateList.Contains(executingAccount); From e5a2e648ddde7b6e77cdc5ab961a3d96100ffce8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 29 Jul 2024 01:06:18 +0100 Subject: [PATCH 056/146] * Added Balance and SELFBALANCE opcodes --- .../CodeAnalysis/IlEvmTests.cs | 8 +++++ .../CodeAnalysis/IL/ILCompiler.cs | 35 +++++++++++++++++++ src/Nethermind/Nethermind.Evm/Instruction.cs | 11 ++---- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index b468dfc1e25..c3f3492bb80 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -433,6 +433,14 @@ public void Pure_Opcode_Emition_Coveraga() .PushData(0) .EXTCODECOPY(Address.FromNumber(1)) .Done); + + yield return (Instruction.BALANCE, Prepare.EvmCode + .BALANCE(Address.FromNumber(1)) + .Done); + + yield return (Instruction.SELFBALANCE, Prepare.EvmCode + .SELFBALANCE() + .Done); } [Test, TestCaseSource(nameof(GetBytecodes))] diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b2eef2eb824..38108066b91 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -1480,6 +1480,41 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.MarkLabel(endOfOpcode); method.StackPush(head); break; + case Instruction.SELFBALANCE: + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); + method.Call(Word.SetUInt256); + method.StackPush(head); + break; + case Instruction.BALANCE: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); + method.Call(Word.SetUInt256); + method.StackPush(head); + break; default: throw new NotSupportedException(); } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 0d43c1940b0..26836319aa6 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -315,11 +315,11 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.CODESIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CODECOPY] = new(GasCostOf.VeryLow, 0, 3, 0), [Instruction.GASPRICE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.EXTCODESIZE] = new(GasCostOf.ExtCode, 0, 1, 1), - [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), + [Instruction.EXTCODESIZE] = new(0, 0, 1, 1), + [Instruction.EXTCODECOPY] = new(0, 0, 4, 0), [Instruction.RETURNDATASIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.RETURNDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), - [Instruction.EXTCODEHASH] = new(GasCostOf.ExtCodeHash, 0, 1, 1), + [Instruction.EXTCODEHASH] = new(0, 0, 1, 1), [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), [Instruction.BLOCKHASH] = new(GasCostOf.BlockHash, 0, 1, 1), @@ -383,12 +383,7 @@ public static class InstructionExtensions { Instruction.CREATE or Instruction.CREATE2 => true, Instruction.CALL or Instruction.CALLCODE or Instruction.DELEGATECALL or Instruction.STATICCALL => true, - Instruction.SLOAD or Instruction.SSTORE => true, - Instruction.TLOAD or Instruction.TSTORE => true, - Instruction.EXTCODESIZE or Instruction.EXTCODECOPY or Instruction.EXTCODEHASH => true, Instruction.SELFDESTRUCT => true, - Instruction.BALANCE => true, - Instruction.SELFBALANCE => true, _ => false, }; From 2660e2899a33f35be67cef53731ae3880cfde2fe Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 31 Jul 2024 00:05:48 +0100 Subject: [PATCH 057/146] * Add stack underflow / overflow checks * Add SSTORE opcode (Experimental) * Add SLOAD opcode --- .../CodeAnalysis/IlEvmTests.cs | 8 + .../CodeAnalysis/IL/ILCompiler.cs | 310 +++++++++++------- .../CodeAnalysis/IL/ILExtensions.cs | 18 +- .../Nethermind.Evm/VirtualMachine.cs | 45 +-- 4 files changed, 241 insertions(+), 140 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index c3f3492bb80..4d114988648 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -419,6 +419,14 @@ public void Pure_Opcode_Emition_Coveraga() .TLOAD() .Done); + yield return (Instruction.SSTORE | Instruction.SLOAD, Prepare.EvmCode + .PushData(23) + .PushData(7) + .SSTORE() + .PushData(7) + .SLOAD() + .Done); + yield return (Instruction.EXTCODESIZE, Prepare.EvmCode .EXTCODESIZE(Address.FromNumber(1)) .Done); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 38108066b91..1c7b5423943 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -182,13 +182,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.StoreField(Word.Ulong0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.NOT: method.Load(stack, head); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256R); @@ -218,13 +218,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(jumpTable); method.MarkLabel(noJump); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); break; case Instruction.PUSH0: method.CleanWord(stack, head); method.Load(stack, head); method.Call(Word.SetToZero); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -276,16 +276,16 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // we store the UInt256 in the stack method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.ADD: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SUB: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Subtract), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); break; case Instruction.MUL: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Multiply), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); break; case Instruction.MOD: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, @@ -303,7 +303,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B); + }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SMOD: EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Mod), BindingFlags.Public | BindingFlags.Static)!, @@ -321,7 +321,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B); + }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.DIV: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, @@ -339,7 +339,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B); + }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SDIV: EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static)!, @@ -375,7 +375,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label2); - }, uint256A, uint256B); + }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.ADDMOD: EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, @@ -393,7 +393,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B, uint256C); + }, evmExceptionLabels, uint256A, uint256B, uint256C); break; case Instruction.MULMOD: EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, @@ -411,60 +411,60 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.Branch(postInstructionLabel); il.MarkLabel(label); - }, uint256A, uint256B, uint256C); + }, evmExceptionLabels, uint256A, uint256B, uint256C); break; case Instruction.SHL: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SHR: - EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, uint256A, uint256B); + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SAR: - EmitShiftInt256Method(method, uint256R, (stack, head), uint256A, uint256B); + EmitShiftInt256Method(method, uint256R, (stack, head), evmExceptionLabels, uint256A, uint256B); break; case Instruction.AND: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); break; case Instruction.OR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); break; case Instruction.XOR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, uint256A, uint256B); + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); break; case Instruction.LT: - EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); break; case Instruction.GT: - EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); break; case Instruction.SLT: - EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), false, uint256A, uint256B); + EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), false, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SGT: - EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, uint256A, uint256B); + EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EQ: - EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), uint256A, uint256B); + EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); break; case Instruction.ISZERO: // we load the stack method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetIsZero); - method.StackPop(head, 1); method.StoreLocal(byte8A); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); // we convert the result to a Uint256 and store it in the stack method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(byte8A); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Byte0))); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.POP: - method.StackPop(head); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); break; case Instruction.DUP1: case Instruction.DUP2: @@ -487,7 +487,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -530,14 +530,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadConstant(code.Length); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.PC: method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(programCounter); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.COINBASE: method.CleanWord(stack, head); @@ -547,7 +547,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); method.Call(Word.SetAddress); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.TIMESTAMP: method.CleanWord(stack, head); @@ -557,7 +557,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); method.StoreField(Word.Ulong0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.NUMBER: method.CleanWord(stack, head); @@ -567,7 +567,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); method.StoreField(Word.Ulong0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.GASLIMIT: method.CleanWord(stack, head); @@ -577,7 +577,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); method.StoreField(Word.Ulong0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.CALLER: method.CleanWord(stack, head); @@ -586,7 +586,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.ADDRESS: method.CleanWord(stack, head); @@ -595,7 +595,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.ORIGIN: method.CleanWord(stack, head); @@ -604,7 +604,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); method.Call(Word.SetAddress); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.CALLVALUE: method.CleanWord(stack, head); @@ -613,7 +613,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.GASPRICE: method.CleanWord(stack, head); @@ -622,7 +622,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.CALLDATACOPY: Label endOfOpcode = method.DefineLabel(); @@ -636,7 +636,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, 3); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -681,14 +681,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.MarkLabel(endOfOpcode); break; case Instruction.CALLDATALOAD: - method.CleanWord(stack, head); - method.Load(stack, head); - method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); - method.StackPop(head, 1); method.StoreLocal(uint256A); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.CleanWord(stack, head); + method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); @@ -700,7 +699,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.LoadField(GetFieldInfo(typeof(ZeroPaddedSpan), nameof(ZeroPaddedSpan.Span))); method.Call(Word.SetSpan); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.CALLDATASIZE: method.CleanWord(stack, head); @@ -709,7 +708,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.MSIZE: method.CleanWord(stack, head); @@ -719,7 +718,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Ulong0))); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.MSTORE: method.StackLoadPrevious(stack, head, 1); @@ -728,7 +727,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -757,7 +756,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.LoadField(Word.Byte0Field); method.StoreLocal(byte8A); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -785,7 +784,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, 1); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -808,7 +807,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.MCOPY: method.StackLoadPrevious(stack, head, 1); @@ -823,7 +822,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, 3); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -864,7 +863,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256B); @@ -890,7 +889,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); method.Call(Word.SetKeccak); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.BYTE: // load a @@ -900,7 +899,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localReadonOnlySpan); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); Label pushZeroLabel = method.DefineLabel(); Label endOfInstructionImpl = method.DefineLabel(); @@ -922,14 +921,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); method.LoadIndirect(); method.StoreField(Word.Byte0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); method.Branch(endOfInstructionImpl); method.MarkLabel(pushZeroLabel); method.CleanWord(stack, head); method.Load(stack, head); method.Call(Word.SetToZero); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); method.MarkLabel(endOfInstructionImpl); break; @@ -945,7 +944,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, 3); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -991,7 +990,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadLocal(gasAvailable); method.StoreField(Word.Ulong0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.RETURNDATASIZE: method.CleanWord(stack, head); @@ -1000,7 +999,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(Word.Int0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.RETURNDATACOPY: endOfOpcode = method.DefineLabel(); @@ -1014,7 +1013,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, 3); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -1064,7 +1063,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -1104,7 +1103,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.BLOBBASEFEE: method.CleanWord(stack, head); @@ -1114,7 +1113,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); method.Call(GetPropertyInfo(typeof(UInt256?), nameof(Nullable.Value), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.PREVRANDAO: Label isPostMergeBranch = method.DefineLabel(); @@ -1132,12 +1131,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); method.Branch(endOfOpcode); method.MarkLabel(isPostMergeBranch); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); method.MarkLabel(endOfOpcode); break; @@ -1145,6 +1144,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Label blobVersionedHashNotFound = method.DefineLabel(); endOfOpcode = method.DefineLabel(); + method.StackLoadPrevious(stack, head, 1); + method.LoadField(Word.Int0Field); + method.StoreLocal(uint32A); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); @@ -1156,11 +1159,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); - method.StackLoadPrevious(stack, head, 1); - method.LoadField(Word.Int0Field); - method.Duplicate(); - method.StoreLocal(uint32A); - method.StackPop(head, 1); + method.LoadLocal(uint32A); method.BranchIfLessOrEqual(blobVersionedHashNotFound); method.LoadArgument(0); @@ -1182,19 +1181,19 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.SetToZero); method.MarkLabel(endOfOpcode); - method.StackPush(head, 1); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.BLOCKHASH: Label blockHashReturnedNull = method.DefineLabel(); Label pushToStackRegion = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); - method.StackPop(head, 1); method.LoadField(Word.Ulong0Field); method.Convert(); method.LoadConstant(long.MaxValue); method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); method.StoreLocal(int64A); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadArgument(1); method.LoadArgument(0); @@ -1228,7 +1227,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.SIGNEXTEND: Label signIsNegative = method.DefineLabel(); @@ -1240,7 +1239,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localSpan); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadConstant((uint)31); method.LoadLocal(uint32A); @@ -1283,7 +1282,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.GetArray); method.StoreLocal(localArray); - method.StackPop(head, 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1301,7 +1300,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, 1); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1319,8 +1318,87 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + break; + + case Instruction.SSTORE: + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetSpan); + method.StoreLocal(localReadonOnlySpan); + method.StackPop(head,evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadArgument(2); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + + MethodInfo sstoreMethod = + typeof(VirtualMachine) + .GetMethod(nameof(VirtualMachine.InstructionSStore), BindingFlags.Static | BindingFlags.NonPublic) + .MakeGenericMethod(typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing)); + method.Call(sstoreMethod); + + endOfOpcode = method.DefineLabel(); + method.Duplicate(); + method.StoreLocal(uint32A); + method.LoadConstant((int)EvmExceptionType.None); + method.BranchIfEqual(endOfOpcode); + + method.LoadArgument(0); + method.LoadLocal(uint32A); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); + method.Branch(exit); + + method.MarkLabel(endOfOpcode); + break; + case Instruction.SLOAD: + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetSLoadCost))); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(storageCell); + method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeStorageAccessGas), BindingFlags.Static | BindingFlags.NonPublic)); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.Get), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(localReadonOnlySpan); + method.Call(Word.SetSpan); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; + case Instruction.EXTCODESIZE: method.LoadLocal(gasAvailable); method.LoadArgument(4); @@ -1331,7 +1409,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, 1); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1357,7 +1435,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.StoreField(Word.Int0Field); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.EXTCODECOPY: @@ -1381,7 +1459,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 4); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, 4); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 4); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -1448,7 +1526,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, 1); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1478,7 +1556,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); method.Call(Word.SetKeccak); method.MarkLabel(endOfOpcode); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.SELFBALANCE: method.CleanWord(stack, head); @@ -1489,13 +1567,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.BALANCE: method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, 1); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1513,7 +1591,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocal(address); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); method.Call(Word.SetUInt256); - method.StackPush(head); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; default: throw new NotSupportedException(); @@ -1566,7 +1644,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(Word.Int0Field); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); - method.StackPop(head); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); @@ -1586,7 +1664,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(ret); method.MarkLabel(jumpIsLocal); - method.StackPop(head, consumeJumpCondition); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], consumeJumpCondition); method.LoadConstant(0); method.StoreLocal(consumeJumpCondition); @@ -1650,7 +1728,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Return(); } - private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, params Local[] locals) + private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, Dictionary exceptions, params Local[] locals) { MethodInfo shiftOp = typeof(UInt256).GetMethod(isLeft ? nameof(UInt256.LeftShift) : nameof(UInt256.RightShift)); Label skipPop = il.DefineLabel(); @@ -1672,23 +1750,23 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.LoadField(Word.Int0Field); il.LoadLocalAddress(uint256R); il.Call(shiftOp); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.CleanWord(stack.span, stack.idx); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); il.MarkLabel(endOfOpcode); } - private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, params Local[] locals) + private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, Dictionary exceptions, params Local[] locals) { Label skipPop = il.DefineLabel(); Label signIsNeg = il.DefineLabel(); @@ -1712,16 +1790,16 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); @@ -1732,7 +1810,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.Call(Word.SetToZero); - il.StackPush(stack.idx); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); il.Branch(endOfOpcode); // sign @@ -1743,13 +1821,13 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(GetAsMethodInfo()); il.LoadObject(); il.Call(Word.SetUInt256); - il.StackPush(stack.idx); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); il.Branch(endOfOpcode); il.MarkLabel(endOfOpcode); } - private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) + private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) { // Note: Use Vector256 directoly if UInt256 does not use it internally // we the two uint256 from the stack @@ -1759,7 +1837,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1774,7 +1852,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.Call(Word.SetUInt256); } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, params Local[] locals) + private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) { // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); @@ -1783,7 +1861,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1802,7 +1880,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Call(Word.SetUInt256); } - private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, params Local[] locals) + private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, Dictionary exceptions, params Local[] locals) { Label endOpcodeHandling = il.DefineLabel(); Label pushZerohandling = il.DefineLabel(); @@ -1813,7 +1891,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1848,7 +1926,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.Call(Word.SetUInt256); } - private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) { Label label = il.DefineLabel(); @@ -1859,7 +1937,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -1878,10 +1956,10 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); } - private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) { Label label = il.DefineLabel(); @@ -1892,7 +1970,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -1914,10 +1992,10 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); } - private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, params Local[] locals) + private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) { Label label = il.DefineLabel(); @@ -1931,7 +2009,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 3); il.Call(Word.GetUInt256); il.StoreLocal(locals[2]); - il.StackPop(stack.idx, 3); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 3); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -1951,7 +2029,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); } private static Dictionary BuildCostLookup(ReadOnlySpan code) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 4dc8a49350a..8c4efdda0e5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -98,34 +98,46 @@ public static void CleanWord(this Emit il, Local local, Local idx) /// /// Advances the stack one word up. /// - public static void StackPush(this Emit il, Local idx, int count = 1) + public static void StackPush(this Emit il, Local idx, Sigil.Label stackOverflowLabel, int count = 1) { il.LoadLocal(idx); il.LoadConstant(count); il.Add(); il.StoreLocal(idx); + + il.LoadLocal(idx); + il.LoadConstant(1024); + il.BranchIfGreater(stackOverflowLabel); } /// /// Moves the stack words down. /// - public static void StackPop(this Emit il, Local idx, int count = 1) + public static void StackPop(this Emit il, Local idx, Sigil.Label stackUnderflowLabel, int count = 1) { il.LoadLocal(idx); il.LoadConstant(count); il.Subtract(); il.StoreLocal(idx); + + il.LoadLocal(idx); + il.LoadConstant(0); + il.BranchIfLess(stackUnderflowLabel); } /// /// Moves the stack words down. /// - public static void StackPop(this Emit il, Local local, Local count) + public static void StackPop(this Emit il, Local local, Sigil.Label stackUnderflowLabel, Local count) { il.LoadLocal(local); il.LoadLocal(count); il.Subtract(); il.StoreLocal(local); + + il.LoadLocal(local); + il.LoadConstant(0); + il.BranchIfLess(stackUnderflowLabel); } public static void WhileBranch(this Emit il, Local cond, Action, Local> action) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 82ef34b211e..8953b51cd43 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -502,25 +502,26 @@ public static bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmStat return result; } - private enum StorageAccessType + internal enum StorageAccessType { SLOAD, SSTORE } - private bool ChargeStorageAccessGas( + static internal bool ChargeStorageAccessGas( ref long gasAvailable, EvmState vmState, in StorageCell storageCell, StorageAccessType storageAccessType, - IReleaseSpec spec) + IReleaseSpec spec, + ITxTracer txTracer) { // Console.WriteLine($"Accessing {storageCell} {storageAccessType}"); bool result = true; if (spec.UseHotAndColdStorage) { - if (_txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list + if (txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list { vmState.WarmUp(in storageCell); } @@ -1576,7 +1577,9 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec); + if(stack.PopUInt256(out result)) goto OutOfGas; + ReadOnlySpan bytesSpan = stack.PopWord256(); + exceptionType = InstructionSStore(vmState, _state, ref gasAvailable, ref result, ref bytesSpan, spec, _txTracer); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; break; @@ -2523,7 +2526,8 @@ private EvmExceptionType InstructionSLoad vmState, in storageCell, StorageAccessType.SLOAD, - spec)) return EvmExceptionType.OutOfGas; + spec, + _txTracer)) return EvmExceptionType.OutOfGas; ReadOnlySpan value = _state.Get(in storageCell); stack.PushBytes(value); @@ -2536,7 +2540,7 @@ private EvmExceptionType InstructionSLoad } [SkipLocalsInit] - private EvmExceptionType InstructionSStore(EvmState vmState, ref EvmStack stack, ref long gasAvailable, IReleaseSpec spec) + static internal EvmExceptionType InstructionSStore(EvmState vmState, IWorldState state, ref long gasAvailable, ref UInt256 result, ref ReadOnlySpan bytes, IReleaseSpec spec, ITxTracer txTracer) where TTracingInstructions : struct, IIsTracing where TTracingRefunds : struct, IIsTracing where TTracingStorage : struct, IIsTracing @@ -2550,12 +2554,10 @@ private EvmExceptionType InstructionSStore bytes = stack.PopWord256(); bool newIsZero = bytes.IsZero(); bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; @@ -2566,9 +2568,10 @@ private EvmExceptionType InstructionSStore currentValue = _state.Get(in storageCell); + ReadOnlySpan currentValue = state.Get(in storageCell); // Console.WriteLine($"current: {currentValue.ToHexString()} newValue {newValue.ToHexString()}"); bool currentIsZero = currentValue.IsZero(); @@ -2582,7 +2585,7 @@ private EvmExceptionType InstructionSStore originalValue = _state.GetOriginal(in storageCell); + Span originalValue = state.GetOriginal(in storageCell); bool originalIsZero = originalValue.IsZero(); bool currentSameAsOriginal = Bytes.AreEqual(originalValue, currentValue); @@ -2615,7 +2618,7 @@ private EvmExceptionType InstructionSStore valueToStore = newIsZero ? BytesZero.AsSpan() : bytes; byte[] storageBytes = new byte[32]; // do not stackalloc here storageCell.Index.ToBigEndian(storageBytes); - _txTracer.ReportStorageChange(storageBytes, valueToStore); + txTracer.ReportStorageChange(storageBytes, valueToStore); } if (typeof(TTracingStorage) == typeof(IsTracing)) { - _txTracer.SetOperationStorage(storageCell.Address, result, bytes, currentValue); + txTracer.SetOperationStorage(storageCell.Address, result, bytes, currentValue); } return EvmExceptionType.None; From e399afa71d247e2896be95e194306e13a926d138 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 12:23:31 +0100 Subject: [PATCH 058/146] * Added opcode Enabled check --- .../VirtualMachineTestsBase.cs | 2 +- .../CodeAnalysis/IL/ILCompiler.cs | 5 ++ src/Nethermind/Nethermind.Evm/Instruction.cs | 88 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 2d9f69dced0..c005ef21996 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -67,7 +67,7 @@ public virtual void Setup() _stateDb = new MemDb(); ITrieStore trieStore = new TrieStore(_stateDb, logManager); TestState = new WorldState(trieStore, codeDb, logManager); - _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId, logManager); + _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId); _blockhashProvider = new TestBlockhashProvider(SpecProvider); CodeInfoRepository = new CodeInfoRepository(); Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 1c7b5423943..725280355bd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -134,6 +134,11 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { OpcodeInfo op = code[i]; + // check if opcode is activated in current spec + method.LoadConstant((byte)op.Operation); + method.LoadArgument(4); + method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.InvalidCode]); if (op.Operation is Instruction.JUMPDEST) { diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 26836319aa6..ec89a11a770 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -379,6 +379,94 @@ public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) public static class InstructionExtensions { + public static bool IsEnabled(this Instruction instruction, IReleaseSpec? spec = null) => instruction switch + { + Instruction.STOP => true, + Instruction.ADD => true, + Instruction.MUL => true, + Instruction.SUB => true, + Instruction.DIV => true, + Instruction.SDIV => true, + Instruction.MOD => true, + Instruction.SMOD => true, + Instruction.ADDMOD => true, + Instruction.MULMOD => true, + Instruction.EXP => true, + Instruction.SIGNEXTEND => true, + Instruction.LT => true, + Instruction.GT => true, + Instruction.SLT => true, + Instruction.SGT => true, + Instruction.EQ => true, + Instruction.ISZERO => true, + Instruction.AND => true, + Instruction.OR => true, + Instruction.XOR => true, + Instruction.NOT => true, + Instruction.BYTE => true, + Instruction.KECCAK256 => true, + Instruction.ADDRESS => true, + Instruction.BALANCE => true, + Instruction.CALLER => true, + Instruction.CALLVALUE => true, + Instruction.ORIGIN => true, + Instruction.CALLDATALOAD => true, + Instruction.CALLDATASIZE => true, + Instruction.CALLDATACOPY => true, + Instruction.CODESIZE => true, + Instruction.CODECOPY => true, + Instruction.GASPRICE => true, + Instruction.EXTCODESIZE => true, + Instruction.EXTCODECOPY => true, + Instruction.RETURNDATASIZE => spec.ReturnDataOpcodesEnabled, + Instruction.RETURNDATACOPY => spec.ReturnDataOpcodesEnabled, + Instruction.BLOCKHASH => true, + Instruction.COINBASE => true, + Instruction.PREVRANDAO => true, + Instruction.TIMESTAMP => true, + Instruction.NUMBER => true, + Instruction.GASLIMIT => true, + Instruction.CHAINID => spec.ChainIdOpcodeEnabled, + Instruction.SELFBALANCE => spec.SelfBalanceOpcodeEnabled, + Instruction.BASEFEE => spec.BaseFeeEnabled, + Instruction.BLOBHASH => spec.IsEip4844Enabled, + Instruction.BLOBBASEFEE => spec.BlobBaseFeeEnabled, + Instruction.POP => true, + Instruction.MLOAD => true, + Instruction.MSTORE => true, + Instruction.MSTORE8 => true, + Instruction.SLOAD => true, + Instruction.SSTORE => true, + Instruction.JUMP => true, + Instruction.JUMPI => true, + Instruction.PC => true, + Instruction.MSIZE => true, + Instruction.GAS => true, + Instruction.JUMPDEST => true, + Instruction.PUSH0 => spec.IncludePush0Instruction, + >= Instruction.PUSH1 and <= Instruction.PUSH32 => true, + >= Instruction.DUP1 and <= Instruction.DUP16 => true, + >= Instruction.SWAP1 and <= Instruction.SWAP16 => true, + >= Instruction.LOG0 and <= Instruction.LOG4 => true, + Instruction.CREATE => true, + Instruction.CREATE2 => spec.Create2OpcodeEnabled, + Instruction.RETURN => true, + Instruction.DELEGATECALL => spec.DelegateCallEnabled, + Instruction.CALL => true, + Instruction.CALLCODE => true, + Instruction.STATICCALL => spec.StaticCallEnabled, + Instruction.REVERT => spec.RevertOpcodeEnabled, + Instruction.INVALID => true, + Instruction.SELFDESTRUCT => true, + Instruction.SHL => spec.ShiftOpcodesEnabled, + Instruction.SHR => spec.ShiftOpcodesEnabled, + Instruction.SAR => spec.ShiftOpcodesEnabled, + Instruction.EXTCODEHASH => spec.ExtCodeHashOpcodeEnabled, + Instruction.TLOAD => spec.TransientStorageEnabled, + Instruction.TSTORE => spec.TransientStorageEnabled, + Instruction.MCOPY => spec.MCopyIncluded, + _ => false + }; public static bool IsStateful(this Instruction instruction) => instruction switch { Instruction.CREATE or Instruction.CREATE2 => true, From 3e86010716ed3a38942d6c80987be5a3857f99b2 Mon Sep 17 00:00:00 2001 From: Siddharth Vaderaa Date: Mon, 26 Aug 2024 21:39:17 +0200 Subject: [PATCH 059/146] LOG Instructions (#7290) Co-authored-by: Demuirgos --- .../CodeAnalysis/IlEvmTests.cs | 75 ++++++-- .../CodeAnalysis/IL/ILCompiler.cs | 182 +++++++++++++++--- .../CodeAnalysis/IL/ILEvmState.cs | 2 +- .../CodeAnalysis/IL/ILExtensions.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 4 +- 5 files changed, 217 insertions(+), 48 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 4d114988648..2097d457590 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -161,7 +161,7 @@ public void Pure_Opcode_Emition_Coveraga() .ISZERO(0) .ISZERO(7) .Done); - yield return(Instruction.SUB, Prepare.EvmCode + yield return (Instruction.SUB, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .SUB() @@ -180,57 +180,57 @@ public void Pure_Opcode_Emition_Coveraga() .ADDMOD() .Done); - yield return(Instruction.MUL, Prepare.EvmCode + yield return (Instruction.MUL, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MUL() .Done); - yield return(Instruction.EXP, Prepare.EvmCode + yield return (Instruction.EXP, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EXP() .Done); - yield return(Instruction.MOD, Prepare.EvmCode + yield return (Instruction.MOD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MOD() .Done); - yield return(Instruction.DIV, Prepare.EvmCode + yield return (Instruction.DIV, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .DIV() .Done); - yield return(Instruction.MSTORE, Prepare.EvmCode + yield return (Instruction.MSTORE, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .Done); - yield return(Instruction.MLOAD, Prepare.EvmCode + yield return (Instruction.MLOAD, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) .Done); - yield return(Instruction.MCOPY, Prepare.EvmCode + yield return (Instruction.MCOPY, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MCOPY(32, 0, 32) .Done); - yield return(Instruction.EQ, Prepare.EvmCode + yield return (Instruction.EQ, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EQ() .Done); - yield return(Instruction.GT, Prepare.EvmCode + yield return (Instruction.GT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .GT() .Done); - yield return(Instruction.LT, Prepare.EvmCode + yield return (Instruction.LT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .LT() @@ -310,7 +310,7 @@ public void Pure_Opcode_Emition_Coveraga() .CHAINID() .Done); - yield return (Instruction.GAS,Prepare.EvmCode + yield return (Instruction.GAS, Prepare.EvmCode .GAS() .Done); @@ -411,6 +411,55 @@ public void Pure_Opcode_Emition_Coveraga() .JUMP(31) .Done); + yield return (Instruction.LOG0, Prepare.EvmCode + .Log(0, 0) + .Done); + + yield return (Instruction.LOG1, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .Log(1, 0, [TestItem.KeccakA]) + .Done); + + yield return (Instruction.LOG2, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(64) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(96) + .Op(Instruction.MSTORE) + .Log(4, 0, [TestItem.KeccakA, TestItem.KeccakB]) + .Done); + + yield return (Instruction.LOG3, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .Log(2, 0, [TestItem.KeccakA, TestItem.KeccakA, TestItem.KeccakB]) + .Done); + + yield return (Instruction.LOG4, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(64) + .Op(Instruction.MSTORE) + .Log(3, 0, [TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakA, TestItem.KeccakB]) + .Done); + yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode .PushData(23) .PushData(7) @@ -480,7 +529,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance , ctx.Data); + ctx.Method(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 725280355bd..b0480610e48 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2,27 +2,20 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; using Nethermind.Evm.IL; +using Nethermind.Evm.Tracing; using Nethermind.Int256; +using Nethermind.State; using Sigil; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using System.Threading.Tasks; -using Label = Sigil.Label; using static Nethermind.Evm.IL.EmitExtensions; -using MathGmp.Native; -using Nethermind.Evm.Tracing.GethStyle.Custom.Native.FourByte; -using System.Drawing; -using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; -using Nethermind.Specs; -using Nethermind.State; -using Nethermind.Evm.Tracing; +using Label = Sigil.Label; namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler @@ -65,6 +58,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); using Local address = method.DeclareLocal(typeof(Address)); + using Local hash256 = method.DeclareLocal(typeof(Hash256)); using Local uint256A = method.DeclareLocal(typeof(UInt256)); @@ -124,6 +118,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Label jumpTable = method.DefineLabel(); // jump table Label ret = method.DefineLabel(); + Dictionary jumpDestinations = new(); @@ -169,7 +164,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // if gas is not available, branch to out of gas method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - // else emit + // else emit switch (op.Operation) { case Instruction.STOP: @@ -1272,7 +1267,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(localSpan); method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); break; - + case Instruction.LOG0: + case Instruction.LOG1: + case Instruction.LOG2: + case Instruction.LOG3: + case Instruction.LOG4: + sbyte topicsCount = (sbyte)(op.Operation - Instruction.LOG0); + EmitLogMethod(method, (stack, head), topicsCount, evmExceptionLabels, uint256A, uint256B, int64A, gasAvailable, hash256, localReadOnlyMemory); + break; case Instruction.TSTORE: method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); @@ -1333,7 +1335,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localReadonOnlySpan); - method.StackPop(head,evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); @@ -1603,7 +1605,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co } } - Label skipProgramCounterSetting = method .DefineLabel(); + Label skipProgramCounterSetting = method.DefineLabel(); Local isEphemeralJump = method.DeclareLocal(); // prepare ILEvmState // check if returnState is null @@ -1760,13 +1762,13 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.CleanWord(stack.span, stack.idx); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); il.MarkLabel(endOfOpcode); } @@ -1795,16 +1797,16 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); @@ -1842,7 +1844,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1866,7 +1868,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1896,7 +1898,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 il.LoadLocalAddress(locals[1]); @@ -1906,7 +1908,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.LoadObject(); il.Call(operation, null); il.LoadConstant(0); - if(isGreaterThan) + if (isGreaterThan) { il.BranchIfLess(pushZerohandling); } @@ -1942,7 +1944,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -1961,7 +1963,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); } private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -1975,7 +1977,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -1997,7 +1999,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow],1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); } private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -2014,7 +2016,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 3); il.Call(Word.GetUInt256); il.StoreLocal(locals[2]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 3); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 3); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -2037,6 +2039,124 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); } + + private static void EmitLogMethod( + Emit il, + (Local span, Local idx) stack, + sbyte topicsCount, + Dictionary exceptions, + Local uint256A, Local uint256B, Local int64A, Local gasAvailable, Local hash256, Local localReadOnlyMemory + ) + { + using Local logEntry = il.DeclareLocal(); + Action loadExecutingAccount = () => + { + // Executing account + il.LoadArgument(0); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + il.LoadField( + GetFieldInfo( + typeof(ExecutionEnvironment), + nameof(ExecutionEnvironment.ExecutingAccount) + ) + ); + }; + + Action loadMemoryIntoByteArray = () => + { + // memory load + il.LoadArgument(0); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + il.LoadLocalAddress(uint256A); // position + il.LoadLocalAddress(uint256B); // length + il.Call( + typeof(EvmPooledMemory).GetMethod( + nameof(EvmPooledMemory.Load), + [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()] + ) + ); + il.StoreLocal(localReadOnlyMemory); + il.LoadLocalAddress(localReadOnlyMemory); + il.Call(typeof(ReadOnlyMemory).GetMethod(nameof(ReadOnlyMemory.ToArray))); + }; + + // Pop an item off the Stack, create a Hash256 object, store it in a local + Action storeLocalHash256AtStackIndex = (int index) => + { + using (var keccak = il.DeclareLocal(typeof(ValueHash256))) + { + il.StackLoadPrevious(stack.span, stack.idx, index); + il.Call(Word.GetKeccak); + il.StoreLocal(keccak); + il.LoadLocalAddress(keccak); + il.NewObject(typeof(Hash256), typeof(ValueHash256).MakeByRefType()); + } + }; + + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(uint256A); // position + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(uint256B); // length + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + // UpdateMemoryCost + il.LoadArgument(0); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + il.LoadLocalAddress(gasAvailable); + il.LoadLocalAddress(uint256A); // position + il.LoadLocalAddress(uint256B); // length + il.Call( + typeof(VirtualMachine).GetMethod( + nameof(VirtualMachine.UpdateMemoryCost) + ) + ); + il.BranchIfFalse(exceptions[EvmExceptionType.OutOfGas]); + + // update gasAvailable + il.LoadLocal(gasAvailable); + il.LoadConstant(topicsCount * GasCostOf.LogTopic + GasCostOf.Log); + il.Convert(); + il.LoadLocal(uint256B); // length + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + il.Convert(); + il.LoadConstant(GasCostOf.LogData); + il.Multiply(); + il.Add(); + il.Subtract(); + il.Duplicate(); + il.StoreLocal(gasAvailable); // gasAvailable -= gasCost + + il.LoadConstant((ulong)0); + il.BranchIfLess(exceptions[EvmExceptionType.OutOfGas]); + + loadExecutingAccount(); + loadMemoryIntoByteArray(); + + il.LoadConstant(topicsCount); + il.NewArray(); + for (int i = 0; i < topicsCount; i++) + { + il.Duplicate(); + il.LoadConstant(i); + storeLocalHash256AtStackIndex(i); + il.StoreElement(); + } + // Creat an LogEntry Object from Items on the Stack + il.NewObject(typeof(LogEntry), typeof(Address), typeof(byte[]), typeof(Hash256[])); + il.StoreLocal(logEntry); + il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], topicsCount); + + il.LoadArgument(0); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + il.CallVirtual(GetPropertyInfo(typeof(EvmState), nameof(EvmState.Logs), getSetter: false, out _)); + il.LoadLocal(logEntry); + il.CallVirtual( + typeof(ICollection).GetMethod(nameof(ICollection.Add)) + ); + + } + private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 8b754a5fe7f..35a3c6e5198 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -51,7 +51,7 @@ public ILEvmState(ulong chainId, EvmState evmState, EvmExceptionType evmExceptio { ChainId = chainId; // locals for ease of access - EvmState = evmState; + EvmState = evmState; MachineCode = evmState.Env.CodeInfo.MachineCode; Env = ref evmState.Env; TxCtx = ref evmState.Env.TxExecutionContext; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 8c4efdda0e5..c39039d1bcb 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -59,7 +59,7 @@ public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool ge public static void Print(this Emit il, Local local) { - if(local.LocalType.IsValueType) + if (local.LocalType.IsValueType) { il.LoadLocalAddress(local); il.Call(local.LocalType.GetMethod("ToString", [])); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 8953b51cd43..f39f364b3f8 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -1273,7 +1273,7 @@ private CallResult ExecuteCode bytesSpan = stack.PopWord256(); exceptionType = InstructionSStore(vmState, _state, ref gasAvailable, ref result, ref bytesSpan, spec, _txTracer); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; From 7dab2ea161c55fdbab523db636b4ee6fa7bd8dc6 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 26 Aug 2024 23:18:08 +0100 Subject: [PATCH 060/146] * adding IConfig to VirtualMachine --- .../CodeAnalysis/IlEvmTests.cs | 62 +++++++++++++++++++ .../VirtualMachineTestsBase.cs | 10 +-- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../CodeAnalysis/IL/ILCompiler.cs | 14 ++--- .../CodeAnalysis/IL/IlAnalyzer.cs | 4 +- .../Nethermind.Evm/Config/IVMConfig.cs | 35 +++++++++++ .../Nethermind.Evm/Config/VMConfig.cs | 18 ++++++ .../Nethermind.Evm/VirtualMachine.cs | 23 +++++-- 8 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs create mode 100644 src/Nethermind/Nethermind.Evm/Config/VMConfig.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 2097d457590..90a1dbbeff9 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -3,12 +3,16 @@ using FluentAssertions; using Nethermind.Core; +using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.CodeAnalysis.IL; +using Nethermind.Evm.Config; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; +using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.State; @@ -36,19 +40,77 @@ public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounte UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; stack.PushUInt256(lhs + rhs); + + programCounter += 5; } } + public class P02JMP : InstructionChunk + { + public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; + public byte CallCount { get; set; } = 0; + + public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; + if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpdestionation; + } + else + { + throw new Exception(EvmExceptionType.InvalidJumpDestination.ToString()); + } + } + + } + public class P02CJMP : InstructionChunk + { + public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; + public byte CallCount { get; set; } = 0; + + public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + stack.PopUInt256(out UInt256 condition); + int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; + if (condition.u0 != 0 && jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpdestionation; + } + else + { + throw new Exception(EvmExceptionType.InvalidJumpDestination.ToString()); + } + } + } [TestFixture] public class IlEvmTests : VirtualMachineTestsBase { private const string AnalyzerField = "_analyzer"; + private readonly IVMConfig _vmConfig = new VMConfig() + { + IsJitEnabled = true, + IsPatternMatchingEnabled = true, + + EnablePatternMatchingThreshold = 4, + EnableJittingThreshold = 8, + }; [SetUp] public override void Setup() { base.Setup(); + ILogManager logManager = GetLogManager(); + + _blockhashProvider = new TestBlockhashProvider(SpecProvider); + Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, _vmConfig); + _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); + IlAnalyzer.AddPattern(P01P01ADD.Pattern, new P01P01ADD()); + IlAnalyzer.AddPattern(P02JMP.Pattern, new P02JMP()); + IlAnalyzer.AddPattern(P02CJMP.Pattern, new P02CJMP()); } [Test] diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index c005ef21996..0e3e240a03a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -30,14 +30,14 @@ public class VirtualMachineTestsBase protected const string HexZero = "00"; protected const long DefaultBlockGasLimit = 8000000; - private IEthereumEcdsa _ethereumEcdsa; + protected IEthereumEcdsa _ethereumEcdsa; protected IBlockhashProvider _blockhashProvider; protected ITransactionProcessor _processor; - private IDb _stateDb; + protected IDb _stateDb; - protected VirtualMachine Machine { get; private set; } - protected CodeInfoRepository CodeInfoRepository { get; private set; } - protected IWorldState TestState { get; private set; } + protected VirtualMachine Machine { get; set; } + protected CodeInfoRepository CodeInfoRepository { get; set; } + protected IWorldState TestState { get; set; } protected static Address Contract { get; } = new("0xd75a3a95360e44a3874e691fb48d77855f127069"); protected static Address Sender { get; } = TestItem.AddressA; protected static Address Recipient { get; } = TestItem.AddressB; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index fced3d14ff7..817a4bfe433 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -21,7 +21,7 @@ public class CodeInfo : IThreadPoolWorkItem public async void NoticeExecution() { // IL-EVM info already created - if (_callCount > IlAnalyzer.IlCompilerThreshold) + if (_callCount > Math.Max(IlAnalyzer.IlCompilerThreshold, IlAnalyzer.CompoundOpThreshold)) return; // use Interlocked just in case of concurrent execution to run it only once diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b0480610e48..66815377096 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -128,13 +128,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co for (int i = 0; i < code.Length; i++) { OpcodeInfo op = code[i]; - - // check if opcode is activated in current spec - method.LoadConstant((byte)op.Operation); - method.LoadArgument(4); - method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.InvalidCode]); - if (op.Operation is Instruction.JUMPDEST) { // mark the jump destination @@ -144,6 +137,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(programCounter); continue; } + + // check if opcode is activated in current spec + method.LoadConstant((byte)op.Operation); + method.LoadArgument(4); + method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.InvalidCode]); + // set pc method.LoadConstant(op.ProgramCounter); method.StoreLocal(programCounter); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 2d889c2e48f..9c542372dfc 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -154,6 +154,6 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma /// /// How many execution a should perform before trying to get its opcodes optimized. /// - public const int CompoundOpThreshold = 23; - public const int IlCompilerThreshold = 57; + public static int CompoundOpThreshold = 32; + public static int IlCompilerThreshold = 128; } diff --git a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs new file mode 100644 index 00000000000..bb4646c94d8 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.Config; +public interface IVMConfig : IConfig +{ + [ConfigItem( + Description = "Activates or Deactivates n-gram pattern optimizations", + DefaultValue = "false")] + public bool IsPatternMatchingEnabled { get; set; } + + [ConfigItem( + Description = "Activates or Deactivates JIT optimizations", + DefaultValue = "false")] + public bool IsJitEnabled { get; set; } + + [ConfigItem( + Description = "Threshold for enabling JIT optimizations", + DefaultValue = "128")] + public int EnableJittingThreshold { get; set; } + + [ConfigItem( + Description = "Threshold for enabling n-gram pattern optimizations", + DefaultValue = "32")] + public int EnablePatternMatchingThreshold { get; set; } + + public bool IsVmOptimizationEnabled => IsPatternMatchingEnabled || IsJitEnabled; +} diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs new file mode 100644 index 00000000000..82d40cf4d66 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.Config; +internal class VMConfig : IVMConfig +{ + public bool IsPatternMatchingEnabled { get; set; } = false; + public bool IsJitEnabled { get; set; } = false; + public int EnablePatternMatchingThreshold { get; set; } = 32; + public int EnableJittingThreshold { get; set; } = 128; +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f39f364b3f8..541ae7e878a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -30,6 +30,7 @@ namespace Nethermind.Evm; using Int256; +using Nethermind.Evm.Config; public class VirtualMachine : IVirtualMachine { @@ -59,16 +60,22 @@ public class VirtualMachine : IVirtualMachine private readonly IVirtualMachine _evm; + private IVMConfig _config; public VirtualMachine( IBlockhashProvider? blockhashProvider, ISpecProvider? specProvider, ICodeInfoRepository codeInfoRepository, - ILogManager? logManager) + ILogManager? logManager, + IVMConfig vmConfig = null) { ILogger logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + _config = vmConfig ?? new VMConfig(); _evm = logger.IsTrace - ? new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, logger) - : new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, logger); + ? new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger) + : new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger); + + IlAnalyzer.CompoundOpThreshold = _config.EnablePatternMatchingThreshold; + IlAnalyzer.IlCompilerThreshold = _config.EnableJittingThreshold; } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -144,11 +151,13 @@ internal sealed class VirtualMachine : IVirtualMachine where TLogger : private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; private readonly ICodeInfoRepository _codeInfoRepository; + private readonly IVMConfig _vmConfig; public VirtualMachine( IBlockhashProvider? blockhashProvider, ISpecProvider? specProvider, ICodeInfoRepository codeInfoRepository, + IVMConfig vmConfig, ILogger? logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -156,6 +165,7 @@ public VirtualMachine( _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); + _vmConfig = vmConfig; } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -641,7 +651,10 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM goto Empty; } - vmState.Env.CodeInfo.NoticeExecution(); + if(_vmConfig.IsVmOptimizationEnabled) + { + vmState.Env.CodeInfo.NoticeExecution(); + } vmState.InitStacks(); EvmStack stack = new(vmState.DataStack.AsSpan(), vmState.DataStackHead, _txTracer); @@ -729,7 +742,7 @@ private CallResult ExecuteCode Date: Mon, 26 Aug 2024 23:45:50 +0100 Subject: [PATCH 061/146] * adding IVmConfig to INethermindApi Interface --- src/Nethermind/Nethermind.Api/IBasicApi.cs | 2 ++ src/Nethermind/Nethermind.Api/NethermindApi.cs | 2 ++ src/Nethermind/Nethermind.Evm/Config/VMConfig.cs | 2 +- src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs | 4 +++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IBasicApi.cs b/src/Nethermind/Nethermind.Api/IBasicApi.cs index 3b78f16a865..2ef4f4e4eda 100644 --- a/src/Nethermind/Nethermind.Api/IBasicApi.cs +++ b/src/Nethermind/Nethermind.Api/IBasicApi.cs @@ -12,6 +12,7 @@ using Nethermind.Core.Timers; using Nethermind.Crypto; using Nethermind.Db; +using Nethermind.Evm.Config; using Nethermind.KeyStore; using Nethermind.Logging; using Nethermind.Serialization.Json; @@ -31,6 +32,7 @@ public interface IBasicApi ICryptoRandom CryptoRandom { get; } IDbProvider? DbProvider { get; set; } IDbFactory? DbFactory { get; set; } + IVMConfig? VMConfig { get; set; } IEthereumEcdsa? EthereumEcdsa { get; set; } IJsonSerializer EthereumJsonSerializer { get; set; } IFileSystem FileSystem { get; set; } diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index e07aa13ba9e..b7a284cb49d 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -56,6 +56,7 @@ using Nethermind.Wallet; using Nethermind.Sockets; using Nethermind.Trie; +using Nethermind.Evm.Config; namespace Nethermind.Api { @@ -110,6 +111,7 @@ public IBlockchainBridge CreateBlockchainBridge() } public IAbiEncoder AbiEncoder { get; } = Nethermind.Abi.AbiEncoder.Instance; + public IVMConfig? VMConfig { get; set; } public IBlobTxStorage? BlobTxStorage { get; set; } public IBlockchainProcessor? BlockchainProcessor { get; set; } public CompositeBlockPreprocessorStep BlockPreprocessor { get; } = new(); diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs index 82d40cf4d66..e0a3984a0c4 100644 --- a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace Nethermind.Evm.Config; -internal class VMConfig : IVMConfig +public class VMConfig : IVMConfig { public bool IsPatternMatchingEnabled { get; set; } = false; public bool IsJitEnabled { get; set; } = false; diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 21189802d53..5b9193190cd 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -29,6 +29,7 @@ using Nethermind.Db; using Nethermind.Db.FullPruning; using Nethermind.Evm; +using Nethermind.Evm.Config; using Nethermind.Evm.TransactionProcessing; using Nethermind.JsonRpc.Converters; using Nethermind.JsonRpc.Modules.DebugModule; @@ -189,7 +190,8 @@ protected VirtualMachine CreateVirtualMachine(CodeInfoRepository codeInfoReposit blockhashProvider, _api.SpecProvider, codeInfoRepository, - _api.LogManager); + _api.LogManager, + _api.VMConfig!); return virtualMachine; } From af48bc909a5a57c929577f50cc6ce153351852fd Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 27 Aug 2024 20:58:28 +0100 Subject: [PATCH 062/146] * fix pattern dictionary having wrong key * fix SSTORE --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 9 +++++---- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 2 +- src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs | 4 ++-- src/Nethermind/Nethermind.Evm/Config/VMConfig.cs | 4 ++-- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 6 +++--- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 90a1dbbeff9..aa89ede71ca 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -94,8 +94,8 @@ public class IlEvmTests : VirtualMachineTestsBase IsJitEnabled = true, IsPatternMatchingEnabled = true, - EnablePatternMatchingThreshold = 4, - EnableJittingThreshold = 8, + PatternMatchingThreshold = 4, + JittingThreshold = 8, }; [SetUp] @@ -147,7 +147,8 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .PushSingle(42) .PushSingle(5) .ADD() - .JUMP(0) + .PUSHx([0, 0]) + .JUMP() .Done; /* @@ -171,7 +172,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var address = receipts.TxReceipts[0].ContractAddress; */ - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) + for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 32; i++) { ExecuteBlock(new NullBlockTracer(), bytecode); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 9c542372dfc..051f6ccb3bd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -132,7 +132,7 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma if (found) { - patternFound.Add((ushort)i, mapping); + patternFound.Add((ushort)strippedBytecode[i].ProgramCounter, mapping); i += pattern.Length - 1; } } diff --git a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs index bb4646c94d8..b72cb08d0d2 100644 --- a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -24,12 +24,12 @@ public interface IVMConfig : IConfig [ConfigItem( Description = "Threshold for enabling JIT optimizations", DefaultValue = "128")] - public int EnableJittingThreshold { get; set; } + public int JittingThreshold { get; set; } [ConfigItem( Description = "Threshold for enabling n-gram pattern optimizations", DefaultValue = "32")] - public int EnablePatternMatchingThreshold { get; set; } + public int PatternMatchingThreshold { get; set; } public bool IsVmOptimizationEnabled => IsPatternMatchingEnabled || IsJitEnabled; } diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs index e0a3984a0c4..373aad22b22 100644 --- a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -13,6 +13,6 @@ public class VMConfig : IVMConfig { public bool IsPatternMatchingEnabled { get; set; } = false; public bool IsJitEnabled { get; set; } = false; - public int EnablePatternMatchingThreshold { get; set; } = 32; - public int EnableJittingThreshold { get; set; } = 128; + public int PatternMatchingThreshold { get; set; } = 32; + public int JittingThreshold { get; set; } = 128; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 541ae7e878a..450d2ac33e0 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -74,8 +74,8 @@ public VirtualMachine( ? new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger) : new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger); - IlAnalyzer.CompoundOpThreshold = _config.EnablePatternMatchingThreshold; - IlAnalyzer.IlCompilerThreshold = _config.EnableJittingThreshold; + IlAnalyzer.CompoundOpThreshold = _config.PatternMatchingThreshold; + IlAnalyzer.IlCompilerThreshold = _config.JittingThreshold; } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -1590,7 +1590,7 @@ private CallResult ExecuteCode bytesSpan = stack.PopWord256(); exceptionType = InstructionSStore(vmState, _state, ref gasAvailable, ref result, ref bytesSpan, spec, _txTracer); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; From 152383aaa7ceb2c4ccbad8eff509a0965f460f4c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 27 Aug 2024 21:51:04 +0100 Subject: [PATCH 063/146] * fix whitespace --- src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 2 +- src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index aa89ede71ca..6433f90d45d 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -107,7 +107,7 @@ public override void Setup() _blockhashProvider = new TestBlockhashProvider(SpecProvider); Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, _vmConfig); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); - + IlAnalyzer.AddPattern(P01P01ADD.Pattern, new P01P01ADD()); IlAnalyzer.AddPattern(P02JMP.Pattern, new P02JMP()); IlAnalyzer.AddPattern(P02CJMP.Pattern, new P02CJMP()); diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 0e3e240a03a..02994876845 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -36,7 +36,7 @@ public class VirtualMachineTestsBase protected IDb _stateDb; protected VirtualMachine Machine { get; set; } - protected CodeInfoRepository CodeInfoRepository { get; set; } + protected CodeInfoRepository CodeInfoRepository { get; set; } protected IWorldState TestState { get; set; } protected static Address Contract { get; } = new("0xd75a3a95360e44a3874e691fb48d77855f127069"); protected static Address Sender { get; } = TestItem.AddressA; diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 450d2ac33e0..54b6fad49f2 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -651,7 +651,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM goto Empty; } - if(_vmConfig.IsVmOptimizationEnabled) + if (_vmConfig.IsVmOptimizationEnabled) { vmState.Env.CodeInfo.NoticeExecution(); } From b54f4b2ec415c3d6fe7af25f4a4d8b76365b4266 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 28 Aug 2024 04:08:13 +0100 Subject: [PATCH 064/146] * Added InvalidJump exception --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 4 ++-- .../Nethermind.Evm/InvalidJumpDestinationException.cs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/InvalidJumpDestinationException.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 6433f90d45d..ee27e33c197 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -60,7 +60,7 @@ public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounte } else { - throw new Exception(EvmExceptionType.InvalidJumpDestination.ToString()); + throw new InvalidJumpDestinationException(); } } @@ -81,7 +81,7 @@ public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounte } else { - throw new Exception(EvmExceptionType.InvalidJumpDestination.ToString()); + throw new InvalidJumpDestinationException(); } } } diff --git a/src/Nethermind/Nethermind.Evm/InvalidJumpDestinationException.cs b/src/Nethermind/Nethermind.Evm/InvalidJumpDestinationException.cs new file mode 100644 index 00000000000..be9d15fa1b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/InvalidJumpDestinationException.cs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Evm +{ + public class InvalidJumpDestinationException : EvmException + { + public override EvmExceptionType ExceptionType => EvmExceptionType.InvalidJumpDestination; + } +} From b1158624f00eb3f340db76aecab21c11913e16e0 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 28 Aug 2024 06:04:31 +0100 Subject: [PATCH 065/146] * exposed IWorldState inside of chunkcs interface --- .../CodeAnalysis/IlEvmTests.cs | 40 +++++++++++++++---- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- .../CodeAnalysis/IL/InstructionChunk.cs | 3 +- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index ee27e33c197..a76ec5990f2 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -34,7 +34,7 @@ public class P01P01ADD : InstructionChunk public static byte[] Pattern => [96, 96, 01]; public byte CallCount { get; set; } = 0; - public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; @@ -45,12 +45,37 @@ public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounte } } - public class P02JMP : InstructionChunk + public class IsContractCheck : InstructionChunk + { + public static byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; + public byte CallCount { get; set; } = 0; + + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + Address address = stack.PopAddress(); + int contractCodeSize = worldState.GetCode(address).Length; + stack.PushUInt32(contractCodeSize); + if(contractCodeSize == 0) + { + stack.PushOne(); + } + else + { + stack.PushZero(); + } + + programCounter += 3; + } + + } + public class EmulatedStaticJump : InstructionChunk { public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; public byte CallCount { get; set; } = 0; - public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; @@ -65,12 +90,12 @@ public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounte } } - public class P02CJMP : InstructionChunk + public class EmulatedStaticCJump : InstructionChunk { public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; public byte CallCount { get; set; } = 0; - public void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; stack.PopUInt256(out UInt256 condition); @@ -109,8 +134,9 @@ public override void Setup() _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); IlAnalyzer.AddPattern(P01P01ADD.Pattern, new P01P01ADD()); - IlAnalyzer.AddPattern(P02JMP.Pattern, new P02JMP()); - IlAnalyzer.AddPattern(P02CJMP.Pattern, new P02CJMP()); + IlAnalyzer.AddPattern(EmulatedStaticCJump.Pattern, new EmulatedStaticCJump()); + IlAnalyzer.AddPattern(EmulatedStaticJump.Pattern, new EmulatedStaticJump()); + IlAnalyzer.AddPattern(IsContractCheck.Pattern, new IsContractCheck()); } [Test] diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index e0e1a1bb27c..168799d903e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -82,7 +82,7 @@ public bool TryExecute(EvmState vmState, ulong chainId, re return false; } var blkCtx = vmState.Env.TxExecutionContext.BlockExecutionContext; - chunk.Invoke(vmState, spec, ref programCounter, ref gasAvailable, ref stack); + chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); break; } case ILMode.SubsegmentsCompiling: diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index a180b22e5c4..473f2cf11d2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Specs; +using Nethermind.State; using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -13,7 +14,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; interface InstructionChunk { static byte[] Pattern { get; } - void Invoke(EvmState vmState, IReleaseSpec spec, ref int programCounter, + void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; } From b76544e920290b9fd3e2e4d9643424cd40042130 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 28 Aug 2024 06:25:17 +0100 Subject: [PATCH 066/146] * fix whitespace format --- src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index a76ec5990f2..68772e20263 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -55,9 +55,9 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe CallCount++; Address address = stack.PopAddress(); - int contractCodeSize = worldState.GetCode(address).Length; + int contractCodeSize = worldState.GetCode(address).Length; stack.PushUInt32(contractCodeSize); - if(contractCodeSize == 0) + if (contractCodeSize == 0) { stack.PushOne(); } From 0628912a29fec218518e4db8eb9298ccce0c0f4c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 29 Aug 2024 02:17:53 +0100 Subject: [PATCH 067/146] * Added gas cost to chunks --- .../CodeAnalysis/IlEvmTests.cs | 75 ++++++++++++++++++- .../CodeAnalysis/IL/InstructionChunk.cs | 2 + .../Nethermind.Evm/VirtualMachine.cs | 4 +- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 68772e20263..13e2918430a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -34,9 +34,18 @@ public class P01P01ADD : InstructionChunk public static byte[] Pattern => [96, 96, 01]; public byte CallCount { get; set; } = 0; + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.Base; + return gasCost; + } + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; stack.PushUInt256(lhs + rhs); @@ -45,15 +54,51 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } } + public class MethodSelector : InstructionChunk + { + public static byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.Base + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + + byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; + byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; + VirtualMachine.UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, 0, 32); + vmState.Memory.SaveByte(location, value); + stack.PushUInt256(vmState.Env.Value); + stack.PushUInt256(vmState.Env.Value); + + programCounter += 2 + 2 + 1 + 1 + 1; + } + } + public class IsContractCheck : InstructionChunk { public static byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; public byte CallCount { get; set; } = 0; + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = spec.GetExtCodeCost() + GasCostOf.VeryLow + GasCostOf.Base; + return gasCost; + } + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + Address address = stack.PopAddress(); int contractCodeSize = worldState.GetCode(address).Length; stack.PushUInt32(contractCodeSize); @@ -75,9 +120,18 @@ public class EmulatedStaticJump : InstructionChunk public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; public byte CallCount { get; set; } = 0; + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.Mid; + return gasCost; + } + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) { @@ -95,9 +149,18 @@ public class EmulatedStaticCJump : InstructionChunk public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; public byte CallCount { get; set; } = 0; + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.High; + return gasCost; + } + public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + stack.PopUInt256(out UInt256 condition); int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; if (condition.u0 != 0 && jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) @@ -119,8 +182,8 @@ public class IlEvmTests : VirtualMachineTestsBase IsJitEnabled = true, IsPatternMatchingEnabled = true, - PatternMatchingThreshold = 4, - JittingThreshold = 8, + PatternMatchingThreshold = 128, + JittingThreshold = 512, }; [SetUp] @@ -167,6 +230,10 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() byte[] bytecode = Prepare.EvmCode .JUMPDEST() + .GAS() + .PushSingle(1000) + .EQ() + .JUMPI(22) .PushSingle(23) .PushSingle(7) .ADD() @@ -175,6 +242,8 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .ADD() .PUSHx([0, 0]) .JUMP() + .JUMPDEST() + .STOP() .Done; /* @@ -198,7 +267,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var address = receipts.TxReceipts[0].ContractAddress; */ - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 32; i++) + for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 1024; i++) { ExecuteBlock(new NullBlockTracer(), bytecode); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index 473f2cf11d2..cda98d0f25a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -17,4 +17,6 @@ interface InstructionChunk void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; + + long GasCost(EvmState vmState, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 54b6fad49f2..62572235b51 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -470,7 +470,7 @@ private void RevertParityTouchBugAccount(IReleaseSpec spec) } } - private static bool UpdateGas(long gasCost, ref long gasAvailable) + public static bool UpdateGas(long gasCost, ref long gasAvailable) { if (gasAvailable < gasCost) { @@ -481,7 +481,7 @@ private static bool UpdateGas(long gasCost, ref long gasAvailable) return true; } - private static void UpdateGasUp(long refund, ref long gasAvailable) + public static void UpdateGasUp(long refund, ref long gasAvailable) { gasAvailable += refund; } From fa2e265cdfb88484281c4b26c0a0bdd079a68b54 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 30 Aug 2024 11:21:43 +0100 Subject: [PATCH 068/146] * refactors and more tests --- .../CodeAnalysis/IlEvmTests.cs | 77 +++++++++++-------- .../CodeAnalysis/IL/IlAnalyzer.cs | 47 +++++------ .../CodeAnalysis/IL/InstructionChunk.cs | 2 +- 3 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 13e2918430a..9706a9fcbc7 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -31,7 +31,7 @@ namespace Nethermind.Evm.Test.CodeAnalysis { public class P01P01ADD : InstructionChunk { - public static byte[] Pattern => [96, 96, 01]; + public byte[] Pattern => [96, 96, 01]; public byte CallCount { get; set; } = 0; public long GasCost(EvmState vmState, IReleaseSpec spec) @@ -56,7 +56,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe public class MethodSelector : InstructionChunk { - public static byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; public byte CallCount { get; set; } = 0; public long GasCost(EvmState vmState, IReleaseSpec spec) @@ -84,7 +84,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe public class IsContractCheck : InstructionChunk { - public static byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; + public byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; public byte CallCount { get; set; } = 0; public long GasCost(EvmState vmState, IReleaseSpec spec) @@ -117,7 +117,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } public class EmulatedStaticJump : InstructionChunk { - public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; + public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; public byte CallCount { get; set; } = 0; public long GasCost(EvmState vmState, IReleaseSpec spec) @@ -146,7 +146,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } public class EmulatedStaticCJump : InstructionChunk { - public static byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; + public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; public byte CallCount { get; set; } = 0; public long GasCost(EvmState vmState, IReleaseSpec spec) @@ -173,6 +173,9 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } } } + + + [TestFixture] public class IlEvmTests : VirtualMachineTestsBase { @@ -196,10 +199,14 @@ public override void Setup() Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, _vmConfig); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); - IlAnalyzer.AddPattern(P01P01ADD.Pattern, new P01P01ADD()); - IlAnalyzer.AddPattern(EmulatedStaticCJump.Pattern, new EmulatedStaticCJump()); - IlAnalyzer.AddPattern(EmulatedStaticJump.Pattern, new EmulatedStaticJump()); - IlAnalyzer.AddPattern(IsContractCheck.Pattern, new IsContractCheck()); + var code = Prepare.EvmCode.STOP().Done; + TestState.CreateAccount(Address.FromNumber(23), 1000000); + TestState.InsertCode(Address.FromNumber(23), code, SpecProvider.GenesisSpec); + + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); } [Test] @@ -222,10 +229,39 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() codeInfo.IlInfo.Chunks.Count.Should().Be(2); } + + [Test] + public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() + { + byte[] bytecode = + Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .Call(Address.FromNumber(23), 10000) + .PushSingle(23) + .PushSingle(7) + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .STOP() + .Done; + + CodeInfo codeInfo = new CodeInfo(bytecode); + + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); + + codeInfo.IlInfo.Segments.Count.Should().Be(2); + } + [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - P01P01ADD pattern = IlAnalyzer.GetPatternHandler(P01P01ADD.Pattern); + P01P01ADD pattern = IlAnalyzer.GetPatternHandler(); byte[] bytecode = Prepare.EvmCode @@ -246,27 +282,6 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .STOP() .Done; - /* - byte[] initcode = - Prepare.EvmCode - .StoreDataInMemory(0, bytecode) - .Return(bytecode.Length, 0) - .Done; - - byte[] code = - Prepare.EvmCode - .PushData(0) - .PushData(0) - .PushData(0) - .PushData(0) - .PushData(0) - .Create(initcode, 1) - .PushData(1000) - .CALL() - .Done; - var address = receipts.TxReceipts[0].ContractAddress; - */ - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 1024; i++) { ExecuteBlock(new NullBlockTracer(), bytecode); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 051f6ccb3bd..eb20b772485 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Int256; using System; using System.Collections.Frozen; @@ -18,36 +20,27 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal static class IlAnalyzer { - public class ByteArrayComparer : IEqualityComparer + private static Dictionary Patterns = new Dictionary(); + public static void AddPattern(InstructionChunk handler) { - public bool Equals(byte[] left, byte[] right) - { - if (left == null || right == null) - { - return left == right; - } - return left.SequenceEqual(right); - } - public int GetHashCode(byte[] key) - { - if (key == null) - throw new ArgumentNullException("key"); - return key.Sum(b => b); + lock (Patterns) { + Patterns[handler.GetType()] = handler; } } - - private static Dictionary Patterns = new Dictionary(new ByteArrayComparer()); - public static Dictionary AddPattern(byte[] pattern, InstructionChunk chunk) + public static void AddPattern() where T : InstructionChunk { + var handler = Activator.CreateInstance(); lock (Patterns) { - Patterns[pattern] = chunk; + Patterns[typeof(T)] = handler; } - return Patterns; } - public static T GetPatternHandler(byte[] pattern) where T : InstructionChunk + public static T GetPatternHandler() where T : InstructionChunk { - return (T)Patterns[pattern]; + lock (Patterns) + { + return (T)Patterns[typeof(T)]; + } } @@ -120,20 +113,20 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma { var (strippedBytecode, data) = StripByteCode(machineCode.Span); var patternFound = new Dictionary(); - foreach (var (pattern, mapping) in Patterns) + foreach (var (_, chunkHandler) in Patterns) { - for (int i = 0; i < strippedBytecode.Length - pattern.Length + 1; i++) + for (int i = 0; i < strippedBytecode.Length - chunkHandler.Pattern.Length + 1; i++) { bool found = true; - for (int j = 0; j < pattern.Length && found; j++) + for (int j = 0; j < chunkHandler.Pattern.Length && found; j++) { - found = ((byte)strippedBytecode[i + j].Operation == pattern[j]); + found = ((byte)strippedBytecode[i + j].Operation == chunkHandler.Pattern[j]); } if (found) { - patternFound.Add((ushort)strippedBytecode[i].ProgramCounter, mapping); - i += pattern.Length - 1; + patternFound.Add((ushort)strippedBytecode[i].ProgramCounter, chunkHandler); + i += chunkHandler.Pattern.Length - 1; } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index cda98d0f25a..a7d821b31ae 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -13,7 +13,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// interface InstructionChunk { - static byte[] Pattern { get; } + byte[] Pattern { get; } void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; From de174e3baddfe2c993752f45405827e8625c2dee Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 2 Sep 2024 23:11:06 +0100 Subject: [PATCH 069/146] Added more tests, fixed indexing bugs, fixed stack size inconsistencies --- .../CodeAnalysis/IlEvmTests.cs | 114 ++++++++++++++---- .../EvmPooledMemoryTests.cs | 29 +++++ .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 5 +- .../CodeAnalysis/IL/ILCompiler.cs | 50 +++----- .../CodeAnalysis/IL/ILVMTracer.cs | 91 ++++++++++++++ .../CodeAnalysis/IL/IlAnalyzer.cs | 19 +-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 75 ++++++------ .../CodeAnalysis/IL/InstructionChunk.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- .../Tracing/AlwaysCancelTxTracer.cs | 25 ++++ .../Tracing/BlockReceiptsTracer.cs | 38 ++++++ .../Tracing/CancellationTxTracer.cs | 81 +++++++++++++ .../Tracing/CompositeTxTracer.cs | 76 ++++++++++++ .../Tracing/Debugger/DebugTracer.cs | 30 ++++- .../JavaScript/GethLikeJavaScriptTxTracer.cs | 2 +- .../Nethermind.Evm/Tracing/ITxILVMTracer.cs | 64 ++++++++++ .../Nethermind.Evm/Tracing/ITxTracer.cs | 2 +- .../Nethermind.Evm/Tracing/TxTracer.cs | 15 +++ .../Nethermind.Evm/VirtualMachine.cs | 51 ++++++-- 19 files changed, 652 insertions(+), 119 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs create mode 100644 src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 9706a9fcbc7..586f685e923 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -40,17 +40,18 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; stack.PushUInt256(lhs + rhs); programCounter += 5; + return EvmExceptionType.None; } } @@ -65,11 +66,11 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; @@ -79,6 +80,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe stack.PushUInt256(vmState.Env.Value); programCounter += 2 + 2 + 1 + 1 + 1; + return EvmExceptionType.None; } } @@ -93,11 +95,11 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; Address address = stack.PopAddress(); int contractCodeSize = worldState.GetCode(address).Length; @@ -112,6 +114,7 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } programCounter += 3; + return EvmExceptionType.None; } } @@ -126,11 +129,11 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) @@ -139,8 +142,9 @@ public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spe } else { - throw new InvalidJumpDestinationException(); + return EvmExceptionType.InvalidJumpDestination; } + return EvmExceptionType.None; } } @@ -155,22 +159,30 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) throw new Exception(); + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; stack.PopUInt256(out UInt256 condition); int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; - if (condition.u0 != 0 && jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + if (!condition.IsZero) { - programCounter = jumpdestionation; - } - else + if(jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpdestionation; + } + else + { + return EvmExceptionType.InvalidJumpDestination; + } + } else { - throw new InvalidJumpDestinationException(); + programCounter += 4; } + + return EvmExceptionType.None; } } @@ -185,8 +197,8 @@ public class IlEvmTests : VirtualMachineTestsBase IsJitEnabled = true, IsPatternMatchingEnabled = true, - PatternMatchingThreshold = 128, - JittingThreshold = 512, + PatternMatchingThreshold = 4, + JittingThreshold = 256, }; [SetUp] @@ -224,7 +236,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching, NullTxTracer.Instance); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -253,7 +265,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling, NullTxTracer.Instance); codeInfo.IlInfo.Segments.Count.Should().Be(2); } @@ -261,33 +273,83 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - P01P01ADD pattern = IlAnalyzer.GetPatternHandler(); + var pattern1 = IlAnalyzer.GetPatternHandler(); + var pattern2 = IlAnalyzer.GetPatternHandler(); + var pattern3 = IlAnalyzer.GetPatternHandler(); byte[] bytecode = Prepare.EvmCode .JUMPDEST() + .PushSingle(1000) .GAS() + .LT() + .PUSHx([0, 26]) + .JUMPI() + .PushSingle(23) + .PushSingle(7) + .ADD() + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .PUSHx([0, 0]) + .JUMP() + .JUMPDEST() + .STOP() + .Done; + + + var accumulatedTraces = new List(); + for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2 ; i++) + { + var tracer = new IlvmBlockTracer(); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + accumulatedTraces.AddRange(traces); + } + + Assert.Greater(accumulatedTraces.Count, 0); + } + + + + [Test] + public void Execution_Swap_Happens_When_Segments_are_compiled() + { + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() .PushSingle(1000) - .EQ() - .JUMPI(22) + .GAS() + .LT() + .PUSHx([0, 26]) + .JUMPI() .PushSingle(23) .PushSingle(7) .ADD() + .POP() .PushSingle(42) .PushSingle(5) .ADD() + .POP() .PUSHx([0, 0]) .JUMP() .JUMPDEST() .STOP() .Done; - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 1024; i++) + var accumulatedTraces = new List(); + for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 2; i++) { - ExecuteBlock(new NullBlockTracer(), bytecode); + var tracer = new IlvmBlockTracer(); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + accumulatedTraces.AddRange(traces); } - Assert.Greater(pattern.CallCount, 0); + + Assert.Greater(accumulatedTraces.Count, 0); } [Test] diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 69e75399a8f..97071b3cd1e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -221,6 +221,10 @@ public class MyTracer : ITxTracer, IDisposable public bool IsTracingAccess { get; } = false; public bool IsTracingFees => false; public bool IsTracingLogs => false; + public bool IsTracingEvmChunks => false; + public bool IsTracingEvmSegments => false; + public bool IsTracingPatternsAnalysis => false; + public bool IsTracingPrecompilationAnalysis => false; public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage @@ -234,6 +238,7 @@ public class MyTracer : ITxTracer, IDisposable || IsTracingFees || IsTracingLogs; + public string lastmemline; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256 stateRoot = null) @@ -391,5 +396,29 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) } public void Dispose() { } + + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + } + + public void ReportChunkAnalysisStart() + { + } + + public void ReportChunkAnalysisEnd() + { + } + + public void ReportSegmentAnalysisStart() + { + } + + public void ReportSegmentAnalysisEnd() + { + } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 817a4bfe433..7c71974a175 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -6,6 +6,7 @@ using Nethermind.Evm.CodeAnalysis.IL; using System.Runtime.CompilerServices; using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Tracing; namespace Nethermind.Evm.CodeAnalysis { @@ -18,7 +19,7 @@ public class CodeInfo : IThreadPoolWorkItem // IL-EVM private int _callCount; - public async void NoticeExecution() + public async void NoticeExecution(ITxTracer tracer) { // IL-EVM info already created if (_callCount > Math.Max(IlAnalyzer.IlCompilerThreshold, IlAnalyzer.CompoundOpThreshold)) @@ -28,7 +29,7 @@ public async void NoticeExecution() IlInfo.ILMode mode = Interlocked.Increment(ref _callCount) == IlAnalyzer.CompoundOpThreshold ? IlInfo.ILMode.PatternMatching : _callCount == IlAnalyzer.IlCompilerThreshold ? IlInfo.ILMode.SubsegmentsCompiling : IlInfo.ILMode.NoIlvm; - await IlAnalyzer.StartAnalysis(this, mode); + await IlAnalyzer.StartAnalysis(this, mode, tracer); } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 66815377096..5194d2cf8c2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -53,7 +53,6 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ private static void EmitSegmentBody(Emit method, OpcodeInfo[] code) { - using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); @@ -139,8 +138,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co } // check if opcode is activated in current spec - method.LoadConstant((byte)op.Operation); method.LoadArgument(4); + method.LoadConstant((byte)op.Operation); method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.InvalidCode]); @@ -206,7 +205,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Label noJump = method.DefineLabel(); method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetIsZero); - // if the jump condition is false, we do not jump method.BranchIfTrue(noJump); @@ -222,8 +220,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co break; case Instruction.PUSH0: method.CleanWord(stack, head); - method.Load(stack, head); - method.Call(Word.SetToZero); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.PUSH1: @@ -266,16 +262,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(5); method.LoadConstant(op.Arguments.Value); method.LoadElement(); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); - method.StoreLocal(localReadonOnlySpan); - - // we call UInt256 constructor taking a span of bytes and a bool - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - - // we store the UInt256 in the stack - method.Call(Word.SetUInt256); + method.Call(Word.SetArray); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.ADD: @@ -1653,6 +1640,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(jmpDestination); method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], consumeJumpCondition); + method.LoadConstant(0); + method.StoreLocal(consumeJumpCondition); + //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); method.LoadLocal(jmpDestination); @@ -1671,41 +1662,35 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(ret); method.MarkLabel(jumpIsLocal); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], consumeJumpCondition); - method.LoadConstant(0); - method.StoreLocal(consumeJumpCondition); // if (jumpDest > uint.MaxValue) - - - method.LoadConstant(uint.MaxValue); method.LoadLocal(jmpDestination); // goto invalid address method.BranchIfGreater(evmExceptionLabels[EvmExceptionType.InvalidJumpDestination]); // else - const int bitMask = (1 << 4) - 1; // 128 - Label[] jumps = new Label[bitMask]; - for (int i = 0; i < bitMask; i++) + const int length = 1 << 8; + const int bitMask = length - 1; // 128 + Label[] jumps = new Label[length]; + for (int i = 0; i < length; i++) { jumps[i] = method.DefineLabel(); } // we get first Word.Size bits of the jump destination since it is less than int.MaxValue - method.LoadLocal(jmpDestination); method.LoadConstant(bitMask); method.And(); + // switch on the first 7 bits method.Switch(jumps); - for (int i = 0; i < bitMask; i++) + for (int i = 0; i < length; i++) { method.MarkLabel(jumps[i]); - method.Print(jmpDestination); // for each destination matching the bit mask emit check for the equality foreach (int dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) { @@ -1713,10 +1698,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(dest); method.Duplicate(); method.StoreLocal(uint32A); - method.Print(uint32A); method.BranchIfEqual(jumpDestinations[dest]); } - method.Print(jmpDestination); // each bucket ends with a jump to invalid access to do not fall through to another one method.Branch(evmExceptionLabels[EvmExceptionType.InvalidCode]); } @@ -1857,6 +1840,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) @@ -1885,6 +1869,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, Dictionary exceptions, params Local[] locals) @@ -1931,6 +1916,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -1963,7 +1949,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -1999,7 +1985,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -2036,7 +2022,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs new file mode 100644 index 00000000000..89473789025 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Evm.Tracing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.CodeAnalysis.IL; + +internal class IlvmTxTrace(List ilvmTraceEntries) +{ + public List IlvmTrace { get; set; } = ilvmTraceEntries; +} + +internal abstract class IlvmTrace; +internal class ChunkTrace : IlvmTrace +{ + public int Start { get; set; } + public int End { get; set; } + public int Gas { get; set; } + public int PC { get; set; } + public string SegmentID { get; set; } +} + +internal class SegmentTrace : IlvmTrace +{ + public int Gas { get; set; } + public int PC { get; set; } + public string SegmentID { get; set; } +} + +internal class AnalysisTrace : IlvmTrace +{ + public IlInfo.ILMode Mode { get; set; } + public bool IsStart { get; set; } + public bool IsEnd => !IsStart; + +} + +internal class IlvmBlockTracer : BlockTracerBase +{ + protected override IlvmTxTrace OnEnd(ILVMTxTracer txTracer) + { + return new IlvmTxTrace(txTracer.IlvmTraceEntries); + } + + protected override ILVMTxTracer OnStart(Transaction? tx) + { + return new ILVMTxTracer(); + } +} + +internal class ILVMTxTracer : TxTracer +{ + public List IlvmTraceEntries { get; set; } = new List(); + + public override void ReportChunkAnalysisEnd() + { + IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.PatternMatching, IsStart = false }); + } + + public override void ReportChunkAnalysisStart() + { + IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.PatternMatching, IsStart = true }); + } + + public override void ReportChunkExecution(long gas, int pc, string segmentID) + { + IlvmTraceEntries.Add(new ChunkTrace { Gas = (int)gas, PC = pc, SegmentID = segmentID }); + } + + public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + IlvmTraceEntries.Add(new SegmentTrace { Gas = (int)gas, PC = pc, SegmentID = segmentId }); + } + + public override void ReportSegmentAnalysisEnd() + { + IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.SubsegmentsCompiling, IsStart = false }); + } + + public override void ReportSegmentAnalysisStart() + { + IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.SubsegmentsCompiling, IsStart = true }); + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index eb20b772485..d7c6f3e3d85 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -3,6 +3,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Evm.Tracing; using Nethermind.Int256; using System; using System.Collections.Frozen; @@ -48,9 +49,9 @@ public static T GetPatternHandler() where T : InstructionChunk /// Starts the analyzing in a background task and outputs the value in the . /// thou /// The destination output. - public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) + public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tracer) { - return Task.Run(() => Analysis(codeInfo, mode)); + return Task.Run(() => Analysis(codeInfo, mode, tracer)); } public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) @@ -78,12 +79,13 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) + private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tracer) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData) + static FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) { + tracer.ReportChunkAnalysisStart(); Dictionary opcodeInfos = []; List segment = []; @@ -106,11 +108,13 @@ FrozenDictionary SegmentCode((OpcodeInfo[], byte[][ { opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); } + tracer.ReportChunkAnalysisEnd(); return opcodeInfos.ToFrozenDictionary(); } - FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode) + static FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode, ITxTracer tracer) { + tracer.ReportChunkAnalysisStart(); var (strippedBytecode, data) = StripByteCode(machineCode.Span); var patternFound = new Dictionary(); foreach (var (_, chunkHandler) in Patterns) @@ -130,16 +134,17 @@ FrozenDictionary CheckPatterns(ReadOnlyMemory ma } } } + tracer.ReportChunkAnalysisEnd(); return patternFound.ToFrozenDictionary(); } switch (mode) { case IlInfo.ILMode.PatternMatching: - codeInfo.IlInfo.WithChunks(CheckPatterns(machineCode)); + codeInfo.IlInfo.WithChunks(CheckPatterns(machineCode, tracer)); break; case IlInfo.ILMode.SubsegmentsCompiling: - codeInfo.IlInfo.WithSegments(SegmentCode(StripByteCode(machineCode.Span))); + codeInfo.IlInfo.WithSegments(SegmentCode(StripByteCode(machineCode.Span), tracer)); break; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 168799d903e..75f1d23a3eb 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -5,6 +5,7 @@ using Microsoft.IdentityModel.Tokens; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Evm.Tracing; using Nethermind.State; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; @@ -14,6 +15,17 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// internal class IlInfo { + internal struct ILChunkExecutionResult + { + public bool ShouldAbort; + public bool ShouldJump; + public bool ShouldStop; + public bool ShouldRevert; + public bool ShouldReturn; + public object ReturnData; + public EvmExceptionType ExceptionType; + } + public enum ILMode { NoIlvm = 0, @@ -62,52 +74,43 @@ public IlInfo(FrozenDictionary mappedOpcodes, FrozenDi public FrozenDictionary Chunks { get; set; } public FrozenDictionary Segments { get; set; } - public bool TryExecute(EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out bool shouldJump, out bool shouldStop, out bool shouldRevert, out bool shouldReturn, out object returnData) + public bool TryExecute(EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ITxTracer tracer, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out ILChunkExecutionResult? result) where TTracingInstructions : struct, VirtualMachine.IIsTracing { - shouldReturn = false; - shouldRevert = false; - shouldStop = false; - shouldJump = false; - returnData = null; + result = null; if (programCounter > ushort.MaxValue) return false; - switch (Mode) + var executionResult = new ILChunkExecutionResult(); + if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { - case ILMode.PatternMatching: - { - if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk) == false) - { - return false; - } - var blkCtx = vmState.Env.TxExecutionContext.BlockExecutionContext; - chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); - break; - } - case ILMode.SubsegmentsCompiling: - { - if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx) == false) - { - return false; - } - - var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - - ctx.Method.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); + tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Method.Method.Name); + var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - gasAvailable = ilvmState.GasAvailable; - programCounter = ilvmState.ProgramCounter; - shouldStop = ilvmState.ShouldStop; - shouldReturn = ilvmState.ShouldReturn; - shouldRevert = ilvmState.ShouldRevert; + ctx.Method.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); - returnData = ilvmState.ReturnBuffer; - shouldJump = ilvmState.ShouldJump; + gasAvailable = ilvmState.GasAvailable; + programCounter = ilvmState.ProgramCounter; - break; - } + executionResult.ShouldReturn = ilvmState.ShouldReturn; + executionResult.ShouldRevert = ilvmState.ShouldRevert; + executionResult.ShouldStop = ilvmState.ShouldStop; + executionResult.ShouldAbort = ilvmState.EvmException != EvmExceptionType.None; + executionResult.ShouldJump = ilvmState.ShouldJump; + executionResult.ExceptionType = ilvmState.EvmException; + executionResult.ReturnData = ilvmState.ReturnBuffer; + } else if(Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) + { + tracer.ReportChunkExecution(gasAvailable, programCounter, chunk.GetType().Name); + var evmException = chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); + executionResult.ShouldAbort = evmException != EvmExceptionType.None; + executionResult.ExceptionType = evmException; + } else + { + return false; } + + result = executionResult; return true; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index a7d821b31ae..77dcb335ee3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -14,7 +14,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; interface InstructionChunk { byte[] Pattern { get; } - void Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, + EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, IIsTracing; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index ec89a11a770..9c6de3c579d 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -379,7 +379,7 @@ public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) public static class InstructionExtensions { - public static bool IsEnabled(this Instruction instruction, IReleaseSpec? spec = null) => instruction switch + public static bool IsEnabled(this IReleaseSpec? spec, Instruction instruction) => instruction switch { Instruction.STOP => true, Instruction.ADD => true, diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index 603cec0f6e2..d436b1c8e43 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -42,6 +42,10 @@ public static AlwaysCancelTxTracer Instance public bool IsTracingAccess => true; public bool IsTracingFees => true; public bool IsTracingLogs => true; + public bool IsTracingEvmChunks => true; + public bool IsTracingEvmSegments => true; + public bool IsTracingPatternsAnalysis => true; + public bool IsTracingPrecompilationAnalysis => true; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) => throw new OperationCanceledException(ErrorMessage); @@ -100,4 +104,25 @@ public static AlwaysCancelTxTracer Instance public void ReportAccess(IReadOnlySet
accessedAddresses, IReadOnlySet accessedStorageCells) => throw new OperationCanceledException(ErrorMessage); public void ReportFees(UInt256 fees, UInt256 burntFees) => throw new OperationCanceledException(ErrorMessage); public void Dispose() { } + + public void ReportChunkExecutionStart(long gas, int pc, Type segmentID) => throw new OperationCanceledException(ErrorMessage); + + public void ReportChunkExecutionEnd(long gas, int pc, Type segmentID) => throw new OperationCanceledException(ErrorMessage); + + public void ReportSegmentExecutionStart(long gas, int pc, int segmentId) => throw new OperationCanceledException(ErrorMessage); + public void ReportSegmentExecutionEnd(long gas, int pc, int segmentId) => throw new OperationCanceledException(ErrorMessage); + public void ReportChunkAnalysisStart() => throw new OperationCanceledException(ErrorMessage); + public void ReportChunkAnalysisEnd() => throw new OperationCanceledException(ErrorMessage); + public void ReportSegmentAnalysisStart() => throw new OperationCanceledException(ErrorMessage); + public void ReportSegmentAnalysisEnd() => throw new OperationCanceledException(ErrorMessage); + + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + throw new NotImplementedException(); + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + throw new NotImplementedException(); + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs index 4a8587a7690..b1807d7d9d3 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -204,6 +204,14 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public ITxTracer InnerTracer => _currentTxTracer; + public bool IsTracingEvmChunks => _currentTxTracer.IsTracingEvmChunks; + + public bool IsTracingEvmSegments => _currentTxTracer.IsTracingEvmSegments; + + public bool IsTracingPatternsAnalysis => _currentTxTracer.IsTracingPatternsAnalysis; + + public bool IsTracingPrecompilationAnalysis => throw new NotImplementedException(); + public int TakeSnapshot() => _txReceipts.Count; public void Restore(int snapshot) @@ -272,4 +280,34 @@ public void Dispose() { _currentTxTracer.Dispose(); } + + public void ReportChunkAnalysisStart() + { + _currentTxTracer.ReportChunkAnalysisStart(); + } + + public void ReportChunkAnalysisEnd() + { + _currentTxTracer.ReportChunkAnalysisEnd(); + } + + public void ReportSegmentAnalysisStart() + { + _currentTxTracer.ReportSegmentAnalysisStart(); + } + + public void ReportSegmentAnalysisEnd() + { + _currentTxTracer.ReportSegmentAnalysisEnd(); + } + + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + _currentTxTracer.ReportChunkExecution(gas, pc, segmentID); + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + _currentTxTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index 5bfd05b9d4a..cdcec8e8ab4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -27,6 +27,10 @@ public class CancellationTxTracer(ITxTracer innerTracer, CancellationToken token private readonly bool _isTracingBlockAccess; private readonly bool _isTracingFees; private readonly bool _isTracingOpLevelLogs; + private readonly bool _isTracingEvmChunks; + private readonly bool _isTracingEvmSegments; + private readonly bool _isTracingPatternsAnalysis; + private readonly bool _isTracingPrecompilationAnalysis; public ITxTracer InnerTracer => innerTracer; @@ -117,6 +121,29 @@ public bool IsTracingLogs init => _isTracingOpLevelLogs = value; } + public bool IsTracingEvmChunks + { + get => _isTracingEvmChunks || innerTracer.IsTracingEvmChunks; + init => _isTracingEvmChunks = value; + } + + public bool IsTracingEvmSegments + { + get => _isTracingEvmSegments || innerTracer.IsTracingEvmSegments; + init => _isTracingEvmSegments = value; + } + + public bool IsTracingPatternsAnalysis + { + get => _isTracingPatternsAnalysis || innerTracer.IsTracingPatternsAnalysis; + init => _isTracingPatternsAnalysis = value; + } + + public bool IsTracingPrecompilationAnalysis + { + get => _isTracingPrecompilationAnalysis || innerTracer.IsTracingPrecompilationAnalysis; + init => _isTracingPrecompilationAnalysis = value; + } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -455,4 +482,58 @@ public void Dispose() { innerTracer.Dispose(); } + + public void ReportChunkAnalysisStart() + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportChunkAnalysisStart(); + } + } + + public void ReportChunkAnalysisEnd() + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportChunkAnalysisEnd(); + } + } + + public void ReportSegmentAnalysisStart() + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportSegmentAnalysisStart(); + } + } + + public void ReportSegmentAnalysisEnd() + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportSegmentAnalysisEnd(); + } + } + + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportChunkExecution(gas, pc, segmentID); + } + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + } + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index 336766c6650..843b18a9869 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -54,6 +54,10 @@ public CompositeTxTracer(IList txTracers) public bool IsTracingAccess { get; } public bool IsTracingFees { get; } public bool IsTracingLogs { get; } + public bool IsTracingEvmChunks { get; } + public bool IsTracingEvmSegments { get; } + public bool IsTracingPatternsAnalysis { get; } + public bool IsTracingPrecompilationAnalysis { get; } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -518,4 +522,76 @@ public void Dispose() _txTracers[index].Dispose(); } } + + public void ReportChunkAnalysisStart() + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingFees) + { + innerTracer.ReportChunkAnalysisStart(); + } + } + } + + public void ReportChunkAnalysisEnd() + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingFees) + { + innerTracer.ReportChunkAnalysisEnd(); + } + } + } + + public void ReportSegmentAnalysisStart() + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingFees) + { + innerTracer.ReportSegmentAnalysisStart(); + } + } + } + + public void ReportSegmentAnalysisEnd() + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingFees) + { + innerTracer.ReportSegmentAnalysisEnd(); + } + } + } + + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingEvmChunks) + { + innerTracer.ReportChunkExecution(gas, pc, segmentID); + } + } + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + for (int index = 0; index < _txTracers.Count; index++) + { + ITxTracer innerTracer = _txTracers[index]; + if (innerTracer.IsTracingEvmSegments) + { + innerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + } + } + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index e37a5ce52b5..8a48e4dc8db 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -61,6 +61,14 @@ public DebugTracer(ITxTracer tracer) public bool IsTracingLogs => InnerTracer.IsTracingLogs; + public bool IsTracingEvmChunks => InnerTracer.IsTracingEvmChunks; + + public bool IsTracingEvmSegments => InnerTracer.IsTracingEvmSegments; + + public bool IsTracingPatternsAnalysis => InnerTracer.IsTracingPatternsAnalysis; + + public bool IsTracingPrecompilationAnalysis => InnerTracer.IsTracingPrecompilationAnalysis; + public bool IsBreakpoitnSet(int depth, int programCounter) => _breakPoints.ContainsKey((depth, programCounter)); public void SetBreakPoint((int depth, int pc) point, Func condition = null) @@ -287,8 +295,24 @@ public void ReportStorageRead(in StorageCell storageCell) => InnerTracer.ReportStorageRead(storageCell); public void Dispose() - { - _autoResetEvent.Dispose(); - } + => _autoResetEvent.Dispose(); + + public void ReportChunkAnalysisStart() + => InnerTracer.ReportChunkAnalysisStart(); + + public void ReportChunkAnalysisEnd() + => InnerTracer.ReportChunkAnalysisEnd(); + + public void ReportSegmentAnalysisStart() + => InnerTracer.ReportSegmentAnalysisStart(); + + public void ReportSegmentAnalysisEnd() + => InnerTracer.ReportSegmentAnalysisEnd(); + + public void ReportChunkExecution(long gas, int pc, string segmentID) + => InnerTracer.ReportChunkExecution(gas, pc, segmentID); + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + => InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); } #endif diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs index dde143ac7e3..18121413ee7 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/JavaScript/GethLikeJavaScriptTxTracer.cs @@ -11,7 +11,7 @@ namespace Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; -public sealed class GethLikeJavaScriptTxTracer : GethLikeTxTracer, ITxTracer +public sealed class GethLikeJavaScriptTxTracer : GethLikeTxTracer { private readonly dynamic _tracer; private readonly Log _log = new(); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs new file mode 100644 index 00000000000..ac2f31e4ffd --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Int256; +using Nethermind.State.Tracing; + +namespace Nethermind.Evm.Tracing; + +public interface IILVMTracer +{ + + /// + /// Traces EVM chunks + /// + /// + /// Controls + /// - + /// + bool IsTracingEvmChunks { get; } + + /// + /// Traces EVM precompiled segments + /// + /// + /// Controls + /// - + /// + bool IsTracingEvmSegments { get; } + + /// + /// Traces Bytecode compound patterns analysis + /// + /// + /// Controls + /// - + /// - + /// + bool IsTracingPatternsAnalysis { get; } + + /// + /// Traces Bytecode Precompilation analysis + /// + /// + /// Controls + /// - + /// - + /// + /// + bool IsTracingPrecompilationAnalysis { get; } + + void ReportChunkExecution(long gas, int pc, string segmentID); + void ReportCompiledSegmentExecution(long gas, int pc, string segmentId); + + void ReportChunkAnalysisStart(); + void ReportChunkAnalysisEnd(); + + void ReportSegmentAnalysisStart(); + void ReportSegmentAnalysisEnd(); + +} diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index 4552b43b2d1..35c14c5413b 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm.Tracing; -public interface ITxTracer : IWorldStateTracer, IDisposable +public interface ITxTracer : IWorldStateTracer, IDisposable, IILVMTracer { bool IsCancelable => false; bool IsCancelled => false; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 439427fb1c2..0d3ca33632f 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -43,6 +43,15 @@ protected TxTracer() public virtual bool IsTracingFees { get; protected set; } public virtual bool IsTracingStorage { get; protected set; } public virtual bool IsTracingLogs { get; protected set; } + + public bool IsTracingEvmChunks => throw new NotImplementedException(); + + public bool IsTracingEvmSegments => throw new NotImplementedException(); + + public bool IsTracingPatternsAnalysis => throw new NotImplementedException(); + + public bool IsTracingPrecompilationAnalysis => throw new NotImplementedException(); + public virtual void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { } public virtual void ReportCodeChange(Address address, byte[]? before, byte[]? after) { } public virtual void ReportNonceChange(Address address, UInt256? before, UInt256? after) { } @@ -76,5 +85,11 @@ public virtual void ReportRefund(long refund) { } public virtual void ReportExtraGasPressure(long extraGasPressure) { } public virtual void ReportAccess(IReadOnlySet
accessedAddresses, IReadOnlySet accessedStorageCells) { } public virtual void ReportFees(UInt256 fees, UInt256 burntFees) { } + public virtual void ReportChunkExecution(long gas, int pc, string segmentID) { } + public virtual void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { } + public virtual void ReportChunkAnalysisStart() { } + public virtual void ReportChunkAnalysisEnd() { } + public virtual void ReportSegmentAnalysisStart() { } + public virtual void ReportSegmentAnalysisEnd() { } public virtual void Dispose() { } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 62572235b51..81c61bebe8e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -653,7 +653,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM if (_vmConfig.IsVmOptimizationEnabled) { - vmState.Env.CodeInfo.NoticeExecution(); + vmState.Env.CodeInfo.NoticeExecution(_txTracer); } vmState.InitStacks(); @@ -728,13 +728,10 @@ private CallResult ExecuteCode Date: Tue, 3 Sep 2024 11:28:43 +0100 Subject: [PATCH 070/146] fix build issue --- .../StateTestTxTracer.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 2d00f475f49..c34f6f45626 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -33,8 +33,11 @@ public class StateTestTxTracer : ITxTracer, IDisposable public bool IsTracingAccess { get; } = false; public bool IsTracingFees => false; public bool IsTracingLogs => false; - public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage || IsTracingMemory || IsTracingInstructions || IsTracingRefunds || IsTracingCode || IsTracingStack || IsTracingBlockHash || IsTracingAccess || IsTracingFees || IsTracingLogs; - + public bool IsTracingEvmChunks => false; + public bool IsTracingEvmSegments => false; + public bool IsTracingPatternsAnalysis => false; + public bool IsTracingPrecompilationAnalysis => false; + public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage || IsTracingMemory || IsTracingInstructions || IsTracingRefunds || IsTracingCode || IsTracingStack || IsTracingBlockHash || IsTracingAccess || IsTracingFees || IsTracingLogs || IsTracingEvmChunks || IsTracingEvmSegments || IsTracingPatternsAnalysis || IsTracingPrecompilationAnalysis; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256 stateRoot = null) { @@ -273,5 +276,35 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) throw new NotImplementedException(); } + public void ReportChunkExecution(long gas, int pc, string segmentID) + { + throw new NotSupportedException(); + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + { + throw new NotSupportedException(); + } + + public void ReportChunkAnalysisStart() + { + throw new NotSupportedException(); + } + + public void ReportChunkAnalysisEnd() + { + throw new NotSupportedException(); + } + + public void ReportSegmentAnalysisStart() + { + throw new NotSupportedException(); + } + + public void ReportSegmentAnalysisEnd() + { + throw new NotSupportedException(); + } public void Dispose() { } + } From 7b265d9c33d7d412bbb81dfb9751ad5b116bb1fe Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 3 Sep 2024 14:13:26 +0100 Subject: [PATCH 071/146] minor changes to ILVMTracer interface --- .../CodeAnalysis/IlEvmTests.cs | 7 +-- .../EvmPooledMemoryTests.cs | 22 ++------ .../CodeAnalysis/IL/ILVMTracer.cs | 21 -------- .../CodeAnalysis/IL/IlAnalyzer.cs | 7 +-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 6 ++- .../Tracing/AlwaysCancelTxTracer.cs | 19 +------ .../Tracing/BlockReceiptsTracer.cs | 25 --------- .../Tracing/CancellationTxTracer.cs | 50 ------------------ .../Tracing/CompositeTxTracer.cs | 51 ------------------- .../Tracing/Debugger/DebugTracer.cs | 16 ------ .../Nethermind.Evm/Tracing/ITxILVMTracer.cs | 27 ---------- .../Nethermind.Evm/Tracing/TxTracer.cs | 18 ++----- .../Nethermind.Evm/VirtualMachine.cs | 12 ++--- .../StateTestTxTracer.cs | 24 +-------- 14 files changed, 27 insertions(+), 278 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 586f685e923..c43448cf0c3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -169,7 +169,7 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; if (!condition.IsZero) { - if(jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) { programCounter = jumpdestionation; } @@ -177,7 +177,8 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel { return EvmExceptionType.InvalidJumpDestination; } - } else + } + else { programCounter += 4; } @@ -301,7 +302,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var accumulatedTraces = new List(); - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2 ; i++) + for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) { var tracer = new IlvmBlockTracer(); ExecuteBlock(tracer, bytecode); diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 97071b3cd1e..61a4697db2d 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -223,8 +223,6 @@ public class MyTracer : ITxTracer, IDisposable public bool IsTracingLogs => false; public bool IsTracingEvmChunks => false; public bool IsTracingEvmSegments => false; - public bool IsTracingPatternsAnalysis => false; - public bool IsTracingPrecompilationAnalysis => false; public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage @@ -236,7 +234,9 @@ public class MyTracer : ITxTracer, IDisposable || IsTracingBlockHash || IsTracingAccess || IsTracingFees - || IsTracingLogs; + || IsTracingLogs + || IsTracingEvmChunks + || IsTracingEvmSegments; public string lastmemline; @@ -404,21 +404,5 @@ public void ReportChunkExecution(long gas, int pc, string segmentID) public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { } - - public void ReportChunkAnalysisStart() - { - } - - public void ReportChunkAnalysisEnd() - { - } - - public void ReportSegmentAnalysisStart() - { - } - - public void ReportSegmentAnalysisEnd() - { - } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs index 89473789025..64d766d4d76 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs @@ -58,17 +58,6 @@ protected override ILVMTxTracer OnStart(Transaction? tx) internal class ILVMTxTracer : TxTracer { public List IlvmTraceEntries { get; set; } = new List(); - - public override void ReportChunkAnalysisEnd() - { - IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.PatternMatching, IsStart = false }); - } - - public override void ReportChunkAnalysisStart() - { - IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.PatternMatching, IsStart = true }); - } - public override void ReportChunkExecution(long gas, int pc, string segmentID) { IlvmTraceEntries.Add(new ChunkTrace { Gas = (int)gas, PC = pc, SegmentID = segmentID }); @@ -78,14 +67,4 @@ public override void ReportCompiledSegmentExecution(long gas, int pc, string seg { IlvmTraceEntries.Add(new SegmentTrace { Gas = (int)gas, PC = pc, SegmentID = segmentId }); } - - public override void ReportSegmentAnalysisEnd() - { - IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.SubsegmentsCompiling, IsStart = false }); - } - - public override void ReportSegmentAnalysisStart() - { - IlvmTraceEntries.Add(new AnalysisTrace { Mode = IlInfo.ILMode.SubsegmentsCompiling, IsStart = true }); - } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index d7c6f3e3d85..55499f34fac 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -24,7 +24,8 @@ internal static class IlAnalyzer private static Dictionary Patterns = new Dictionary(); public static void AddPattern(InstructionChunk handler) { - lock (Patterns) { + lock (Patterns) + { Patterns[handler.GetType()] = handler; } } @@ -85,7 +86,6 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tr static FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) { - tracer.ReportChunkAnalysisStart(); Dictionary opcodeInfos = []; List segment = []; @@ -108,13 +108,11 @@ static FrozenDictionary SegmentCode((OpcodeInfo[], { opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); } - tracer.ReportChunkAnalysisEnd(); return opcodeInfos.ToFrozenDictionary(); } static FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode, ITxTracer tracer) { - tracer.ReportChunkAnalysisStart(); var (strippedBytecode, data) = StripByteCode(machineCode.Span); var patternFound = new Dictionary(); foreach (var (_, chunkHandler) in Patterns) @@ -134,7 +132,6 @@ static FrozenDictionary CheckPatterns(ReadOnlyMemory(EvmState vmState, ulong chainId, re executionResult.ShouldJump = ilvmState.ShouldJump; executionResult.ExceptionType = ilvmState.EvmException; executionResult.ReturnData = ilvmState.ReturnBuffer; - } else if(Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) + } + else if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { tracer.ReportChunkExecution(gasAvailable, programCounter, chunk.GetType().Name); var evmException = chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); executionResult.ShouldAbort = evmException != EvmExceptionType.None; executionResult.ExceptionType = evmException; - } else + } + else { return false; } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index d436b1c8e43..8e0e5582e2f 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -44,8 +44,6 @@ public static AlwaysCancelTxTracer Instance public bool IsTracingLogs => true; public bool IsTracingEvmChunks => true; public bool IsTracingEvmSegments => true; - public bool IsTracingPatternsAnalysis => true; - public bool IsTracingPrecompilationAnalysis => true; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) => throw new OperationCanceledException(ErrorMessage); @@ -109,20 +107,7 @@ public void Dispose() { } public void ReportChunkExecutionEnd(long gas, int pc, Type segmentID) => throw new OperationCanceledException(ErrorMessage); - public void ReportSegmentExecutionStart(long gas, int pc, int segmentId) => throw new OperationCanceledException(ErrorMessage); - public void ReportSegmentExecutionEnd(long gas, int pc, int segmentId) => throw new OperationCanceledException(ErrorMessage); - public void ReportChunkAnalysisStart() => throw new OperationCanceledException(ErrorMessage); - public void ReportChunkAnalysisEnd() => throw new OperationCanceledException(ErrorMessage); - public void ReportSegmentAnalysisStart() => throw new OperationCanceledException(ErrorMessage); - public void ReportSegmentAnalysisEnd() => throw new OperationCanceledException(ErrorMessage); + public void ReportChunkExecution(long gas, int pc, string segmentID) => throw new OperationCanceledException(ErrorMessage); - public void ReportChunkExecution(long gas, int pc, string segmentID) - { - throw new NotImplementedException(); - } - - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) - { - throw new NotImplementedException(); - } + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) => throw new OperationCanceledException(ErrorMessage); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs index b1807d7d9d3..3822c0d5cf6 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -207,11 +207,6 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public bool IsTracingEvmChunks => _currentTxTracer.IsTracingEvmChunks; public bool IsTracingEvmSegments => _currentTxTracer.IsTracingEvmSegments; - - public bool IsTracingPatternsAnalysis => _currentTxTracer.IsTracingPatternsAnalysis; - - public bool IsTracingPrecompilationAnalysis => throw new NotImplementedException(); - public int TakeSnapshot() => _txReceipts.Count; public void Restore(int snapshot) @@ -281,26 +276,6 @@ public void Dispose() _currentTxTracer.Dispose(); } - public void ReportChunkAnalysisStart() - { - _currentTxTracer.ReportChunkAnalysisStart(); - } - - public void ReportChunkAnalysisEnd() - { - _currentTxTracer.ReportChunkAnalysisEnd(); - } - - public void ReportSegmentAnalysisStart() - { - _currentTxTracer.ReportSegmentAnalysisStart(); - } - - public void ReportSegmentAnalysisEnd() - { - _currentTxTracer.ReportSegmentAnalysisEnd(); - } - public void ReportChunkExecution(long gas, int pc, string segmentID) { _currentTxTracer.ReportChunkExecution(gas, pc, segmentID); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index cdcec8e8ab4..ff420a3203c 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -29,8 +29,6 @@ public class CancellationTxTracer(ITxTracer innerTracer, CancellationToken token private readonly bool _isTracingOpLevelLogs; private readonly bool _isTracingEvmChunks; private readonly bool _isTracingEvmSegments; - private readonly bool _isTracingPatternsAnalysis; - private readonly bool _isTracingPrecompilationAnalysis; public ITxTracer InnerTracer => innerTracer; @@ -133,18 +131,6 @@ public bool IsTracingEvmSegments init => _isTracingEvmSegments = value; } - public bool IsTracingPatternsAnalysis - { - get => _isTracingPatternsAnalysis || innerTracer.IsTracingPatternsAnalysis; - init => _isTracingPatternsAnalysis = value; - } - - public bool IsTracingPrecompilationAnalysis - { - get => _isTracingPrecompilationAnalysis || innerTracer.IsTracingPrecompilationAnalysis; - init => _isTracingPrecompilationAnalysis = value; - } - public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { token.ThrowIfCancellationRequested(); @@ -483,42 +469,6 @@ public void Dispose() innerTracer.Dispose(); } - public void ReportChunkAnalysisStart() - { - token.ThrowIfCancellationRequested(); - if (innerTracer.IsTracingFees) - { - InnerTracer.ReportChunkAnalysisStart(); - } - } - - public void ReportChunkAnalysisEnd() - { - token.ThrowIfCancellationRequested(); - if (innerTracer.IsTracingFees) - { - InnerTracer.ReportChunkAnalysisEnd(); - } - } - - public void ReportSegmentAnalysisStart() - { - token.ThrowIfCancellationRequested(); - if (innerTracer.IsTracingFees) - { - InnerTracer.ReportSegmentAnalysisStart(); - } - } - - public void ReportSegmentAnalysisEnd() - { - token.ThrowIfCancellationRequested(); - if (innerTracer.IsTracingFees) - { - InnerTracer.ReportSegmentAnalysisEnd(); - } - } - public void ReportChunkExecution(long gas, int pc, string segmentID) { token.ThrowIfCancellationRequested(); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index 843b18a9869..4a8dd7f43aa 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -56,8 +56,6 @@ public CompositeTxTracer(IList txTracers) public bool IsTracingLogs { get; } public bool IsTracingEvmChunks { get; } public bool IsTracingEvmSegments { get; } - public bool IsTracingPatternsAnalysis { get; } - public bool IsTracingPrecompilationAnalysis { get; } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -522,55 +520,6 @@ public void Dispose() _txTracers[index].Dispose(); } } - - public void ReportChunkAnalysisStart() - { - for (int index = 0; index < _txTracers.Count; index++) - { - ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingFees) - { - innerTracer.ReportChunkAnalysisStart(); - } - } - } - - public void ReportChunkAnalysisEnd() - { - for (int index = 0; index < _txTracers.Count; index++) - { - ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingFees) - { - innerTracer.ReportChunkAnalysisEnd(); - } - } - } - - public void ReportSegmentAnalysisStart() - { - for (int index = 0; index < _txTracers.Count; index++) - { - ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingFees) - { - innerTracer.ReportSegmentAnalysisStart(); - } - } - } - - public void ReportSegmentAnalysisEnd() - { - for (int index = 0; index < _txTracers.Count; index++) - { - ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingFees) - { - innerTracer.ReportSegmentAnalysisEnd(); - } - } - } - public void ReportChunkExecution(long gas, int pc, string segmentID) { for (int index = 0; index < _txTracers.Count; index++) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index 8a48e4dc8db..4587fc44842 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -65,10 +65,6 @@ public DebugTracer(ITxTracer tracer) public bool IsTracingEvmSegments => InnerTracer.IsTracingEvmSegments; - public bool IsTracingPatternsAnalysis => InnerTracer.IsTracingPatternsAnalysis; - - public bool IsTracingPrecompilationAnalysis => InnerTracer.IsTracingPrecompilationAnalysis; - public bool IsBreakpoitnSet(int depth, int programCounter) => _breakPoints.ContainsKey((depth, programCounter)); public void SetBreakPoint((int depth, int pc) point, Func condition = null) @@ -297,18 +293,6 @@ public void ReportStorageRead(in StorageCell storageCell) public void Dispose() => _autoResetEvent.Dispose(); - public void ReportChunkAnalysisStart() - => InnerTracer.ReportChunkAnalysisStart(); - - public void ReportChunkAnalysisEnd() - => InnerTracer.ReportChunkAnalysisEnd(); - - public void ReportSegmentAnalysisStart() - => InnerTracer.ReportSegmentAnalysisStart(); - - public void ReportSegmentAnalysisEnd() - => InnerTracer.ReportSegmentAnalysisEnd(); - public void ReportChunkExecution(long gas, int pc, string segmentID) => InnerTracer.ReportChunkExecution(gas, pc, segmentID); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs index ac2f31e4ffd..afd575479f2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs @@ -31,34 +31,7 @@ public interface IILVMTracer /// bool IsTracingEvmSegments { get; } - /// - /// Traces Bytecode compound patterns analysis - /// - /// - /// Controls - /// - - /// - - /// - bool IsTracingPatternsAnalysis { get; } - - /// - /// Traces Bytecode Precompilation analysis - /// - /// - /// Controls - /// - - /// - - /// - /// - bool IsTracingPrecompilationAnalysis { get; } - void ReportChunkExecution(long gas, int pc, string segmentID); void ReportCompiledSegmentExecution(long gas, int pc, string segmentId); - void ReportChunkAnalysisStart(); - void ReportChunkAnalysisEnd(); - - void ReportSegmentAnalysisStart(); - void ReportSegmentAnalysisEnd(); - } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 0d3ca33632f..804bfdaac3d 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -26,7 +26,9 @@ protected TxTracer() || IsTracingBlockHash || IsTracingAccess || IsTracingFees - || IsTracingLogs; + || IsTracingLogs + || IsTracingEvmChunks + || IsTracingEvmSegments; } public bool IsTracing { get; protected set; } public virtual bool IsTracingState { get; protected set; } @@ -43,14 +45,8 @@ protected TxTracer() public virtual bool IsTracingFees { get; protected set; } public virtual bool IsTracingStorage { get; protected set; } public virtual bool IsTracingLogs { get; protected set; } - - public bool IsTracingEvmChunks => throw new NotImplementedException(); - - public bool IsTracingEvmSegments => throw new NotImplementedException(); - - public bool IsTracingPatternsAnalysis => throw new NotImplementedException(); - - public bool IsTracingPrecompilationAnalysis => throw new NotImplementedException(); + public virtual bool IsTracingEvmChunks { get; protected set; } + public virtual bool IsTracingEvmSegments { get; protected set; } public virtual void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { } public virtual void ReportCodeChange(Address address, byte[]? before, byte[]? after) { } @@ -87,9 +83,5 @@ public virtual void ReportAccess(IReadOnlySet
accessedAddresses, IReadO public virtual void ReportFees(UInt256 fees, UInt256 burntFees) { } public virtual void ReportChunkExecution(long gas, int pc, string segmentID) { } public virtual void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { } - public virtual void ReportChunkAnalysisStart() { } - public virtual void ReportChunkAnalysisEnd() { } - public virtual void ReportSegmentAnalysisStart() { } - public virtual void ReportSegmentAnalysisEnd() { } public virtual void Dispose() { } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 81c61bebe8e..9855e6cd734 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -739,7 +739,7 @@ private CallResult ExecuteCode false; public bool IsTracingEvmChunks => false; public bool IsTracingEvmSegments => false; - public bool IsTracingPatternsAnalysis => false; - public bool IsTracingPrecompilationAnalysis => false; - public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage || IsTracingMemory || IsTracingInstructions || IsTracingRefunds || IsTracingCode || IsTracingStack || IsTracingBlockHash || IsTracingAccess || IsTracingFees || IsTracingLogs || IsTracingEvmChunks || IsTracingEvmSegments || IsTracingPatternsAnalysis || IsTracingPrecompilationAnalysis; + public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage || IsTracingMemory || IsTracingInstructions || IsTracingRefunds || IsTracingCode || IsTracingStack || IsTracingBlockHash || IsTracingAccess || IsTracingFees || IsTracingLogs || IsTracingEvmChunks || IsTracingEvmSegments; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256 stateRoot = null) { @@ -285,26 +283,6 @@ public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { throw new NotSupportedException(); } - - public void ReportChunkAnalysisStart() - { - throw new NotSupportedException(); - } - - public void ReportChunkAnalysisEnd() - { - throw new NotSupportedException(); - } - - public void ReportSegmentAnalysisStart() - { - throw new NotSupportedException(); - } - - public void ReportSegmentAnalysisEnd() - { - throw new NotSupportedException(); - } public void Dispose() { } } From 06f4bbffee01028df9a8e8931c9e6dcf0e91cbd0 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 5 Sep 2024 00:20:58 +0100 Subject: [PATCH 072/146] * fix cross segment jumps --- .../CodeAnalysis/IlEvmTests.cs | 49 +++++++++++++++-- .../EvmPooledMemoryTests.cs | 10 ++-- .../CodeAnalysis/IL/ILCompiler.cs | 55 ++++++++++++++----- .../CodeAnalysis/IL/ILVMTracer.cs | 34 +++--------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 9 +-- .../Tracing/AlwaysCancelTxTracer.cs | 6 +- .../Tracing/BlockReceiptsTracer.cs | 8 +-- .../Tracing/CancellationTxTracer.cs | 12 ++-- .../Tracing/CompositeTxTracer.cs | 12 ++-- .../Tracing/Debugger/DebugTracer.cs | 8 +-- .../Nethermind.Evm/Tracing/ITxILVMTracer.cs | 8 +-- .../Nethermind.Evm/Tracing/TxTracer.cs | 10 ++-- .../Nethermind.Evm/VirtualMachine.cs | 2 +- 13 files changed, 136 insertions(+), 87 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index c43448cf0c3..d595a5aa465 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -301,20 +301,61 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) { var tracer = new IlvmBlockTracer(); ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).Where(tr => !tr.IsPrecompiled).ToList(); accumulatedTraces.AddRange(traces); } Assert.Greater(accumulatedTraces.Count, 0); } + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() + { + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .JUMPI(59) + .PushSingle(23) + .PushSingle(7) + .ADD() + .Call(Address.FromNumber(23), 10000) + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .JUMP(0) + .JUMPDEST() + .STOP() + .Done; + + var accumulatedTraces = new List(); + for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) + { + var tracer = new IlvmBlockTracer(); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + accumulatedTraces.AddRange(traces); + } + // check if these patterns occur in the traces + // segment 0 + // pattern p1p1padd + + // segment 0 + // segment 1 + Assert.Greater(accumulatedTraces.GroupBy(tr => tr.IsPrecompiled).Count(), 0); + } + [Test] public void Execution_Swap_Happens_When_Segments_are_compiled() { @@ -340,12 +381,12 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .STOP() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 2; i++) { var tracer = new IlvmBlockTracer(); ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).Where(tr => tr.IsPrecompiled).ToList(); accumulatedTraces.AddRange(traces); } diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 61a4697db2d..0d6322286f3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -221,8 +221,8 @@ public class MyTracer : ITxTracer, IDisposable public bool IsTracingAccess { get; } = false; public bool IsTracingFees => false; public bool IsTracingLogs => false; - public bool IsTracingEvmChunks => false; - public bool IsTracingEvmSegments => false; + public bool IsTracingPredefinedPatterns => false; + public bool IsTracingCompiledSegments => false; public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage @@ -235,8 +235,8 @@ public class MyTracer : ITxTracer, IDisposable || IsTracingAccess || IsTracingFees || IsTracingLogs - || IsTracingEvmChunks - || IsTracingEvmSegments; + || IsTracingPredefinedPatterns + || IsTracingCompiledSegments; public string lastmemline; @@ -397,7 +397,7 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public void Dispose() { } - public void ReportChunkExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5194d2cf8c2..3d8d1fb87bf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -141,10 +141,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(4); method.LoadConstant((byte)op.Operation); method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.InvalidCode]); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.BadInstruction]); // set pc - method.LoadConstant(op.ProgramCounter); + method.LoadConstant(op.ProgramCounter + (op.Metadata?.AdditionalBytes ?? 0)); method.StoreLocal(programCounter); // load gasAvailable @@ -173,7 +173,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(ret); break; case Instruction.INVALID: - method.Branch(evmExceptionLabels[EvmExceptionType.InvalidCode]); + method.Branch(evmExceptionLabels[EvmExceptionType.BadInstruction]); break; case Instruction.CHAINID: method.CleanWord(stack, head); @@ -1619,6 +1619,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadLocal(programCounter); + method.LoadConstant(1); + method.Add(); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.MarkLabel(skipProgramCounterSetting); @@ -1646,9 +1648,23 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); + + int maxJump = code[^1].ProgramCounter + (code[^1].Metadata?.AdditionalBytes ?? 0); + int minJump = code[0].ProgramCounter; + + // if (jumpDest <= maxJump) + method.LoadLocal(jmpDestination); + method.LoadConstant(maxJump); + method.CompareLessThan(); + + // if (jumpDest >= minJump) method.LoadLocal(jmpDestination); - method.LoadConstant(code[code.Length - 1].ProgramCounter + code[code.Length - 1].Metadata?.AdditionalBytes ?? 0); - method.BranchIfLessOrEqual(jumpIsLocal); + method.LoadConstant(minJump); + method.CompareGreaterThan(); + + method.And(); + + method.BranchIfTrue(jumpIsLocal); method.LoadArgument(0); method.Duplicate(); @@ -1701,7 +1717,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfEqual(jumpDestinations[dest]); } // each bucket ends with a jump to invalid access to do not fall through to another one - method.Branch(evmExceptionLabels[EvmExceptionType.InvalidCode]); + method.Branch(evmExceptionLabels[EvmExceptionType.InvalidJumpDestination]); } foreach (var kvp in evmExceptionLabels) @@ -1830,8 +1846,8 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 - il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(uint256R); il.Call(operation, null); @@ -1855,8 +1871,8 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 - il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); il.Call(operation, null); // convert to conv_i @@ -1886,10 +1902,10 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); // invoke op on the uint256 - il.LoadLocalAddress(locals[1]); - il.Call(GetAsMethodInfo()); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); il.LoadObject(); il.Call(operation, null); il.LoadConstant(0); @@ -1936,8 +1952,8 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca customHandling?.Invoke(il, label, locals); // invoke op on the uint256 - il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(uint256R); il.Call(operation); @@ -1969,10 +1985,10 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local customHandling?.Invoke(il, label, locals); // invoke op on the uint256 - il.LoadLocalAddress(locals[1]); - il.Call(GetAsMethodInfo()); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(operation); @@ -2008,9 +2024,9 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc customHandling?.Invoke(il, label, locals); // invoke op on the uint256 - il.LoadLocalAddress(locals[2]); - il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[2]); il.LoadLocalAddress(uint256R); il.Call(operation); @@ -2143,6 +2159,15 @@ private static void EmitLogMethod( } + private static void EmitGasAvailabilityCheck( + Emit il, + Local gasAvailable, + Label outOfGasLabel) + { + il.LoadLocal(gasAvailable); + il.LoadConstant(0); + il.BranchIfLess(outOfGasLabel); + } private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs index 64d766d4d76..c30d7ee1b12 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs @@ -12,36 +12,18 @@ namespace Nethermind.Evm.CodeAnalysis.IL; -internal class IlvmTxTrace(List ilvmTraceEntries) +internal class IlvmTxTrace(List ilvmTraceEntries) { - public List IlvmTrace { get; set; } = ilvmTraceEntries; + public List IlvmTrace { get; set; } = ilvmTraceEntries; } -internal abstract class IlvmTrace; -internal class ChunkTrace : IlvmTrace +internal class ChunkTraceEntry { - public int Start { get; set; } - public int End { get; set; } + public bool IsPrecompiled { get; set; } public int Gas { get; set; } public int PC { get; set; } public string SegmentID { get; set; } } - -internal class SegmentTrace : IlvmTrace -{ - public int Gas { get; set; } - public int PC { get; set; } - public string SegmentID { get; set; } -} - -internal class AnalysisTrace : IlvmTrace -{ - public IlInfo.ILMode Mode { get; set; } - public bool IsStart { get; set; } - public bool IsEnd => !IsStart; - -} - internal class IlvmBlockTracer : BlockTracerBase { protected override IlvmTxTrace OnEnd(ILVMTxTracer txTracer) @@ -57,14 +39,14 @@ protected override ILVMTxTracer OnStart(Transaction? tx) internal class ILVMTxTracer : TxTracer { - public List IlvmTraceEntries { get; set; } = new List(); - public override void ReportChunkExecution(long gas, int pc, string segmentID) + public List IlvmTraceEntries { get; set; } = new List(); + public override void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { - IlvmTraceEntries.Add(new ChunkTrace { Gas = (int)gas, PC = pc, SegmentID = segmentID }); + IlvmTraceEntries.Add(new ChunkTraceEntry { Gas = (int)gas, PC = pc, SegmentID = segmentID, IsPrecompiled = false }); } public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { - IlvmTraceEntries.Add(new SegmentTrace { Gas = (int)gas, PC = pc, SegmentID = segmentId }); + IlvmTraceEntries.Add(new ChunkTraceEntry { Gas = (int)gas, PC = pc, SegmentID = segmentId, IsPrecompiled = true }); } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index a372328583f..d52bc1fc865 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -17,7 +17,7 @@ internal class IlInfo { internal struct ILChunkExecutionResult { - public bool ShouldAbort; + public readonly bool ShouldFail => ExceptionType != EvmExceptionType.None; public bool ShouldJump; public bool ShouldStop; public bool ShouldRevert; @@ -95,16 +95,17 @@ public bool TryExecute(EvmState vmState, ulong chainId, re executionResult.ShouldReturn = ilvmState.ShouldReturn; executionResult.ShouldRevert = ilvmState.ShouldRevert; executionResult.ShouldStop = ilvmState.ShouldStop; - executionResult.ShouldAbort = ilvmState.EvmException != EvmExceptionType.None; executionResult.ShouldJump = ilvmState.ShouldJump; executionResult.ExceptionType = ilvmState.EvmException; executionResult.ReturnData = ilvmState.ReturnBuffer; + + vmState.DataStackHead = ilvmState.StackHead; + stack.Head = ilvmState.StackHead; } else if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { - tracer.ReportChunkExecution(gasAvailable, programCounter, chunk.GetType().Name); + tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.GetType().Name); var evmException = chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); - executionResult.ShouldAbort = evmException != EvmExceptionType.None; executionResult.ExceptionType = evmException; } else diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index 8e0e5582e2f..dd1e44ef466 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -42,8 +42,8 @@ public static AlwaysCancelTxTracer Instance public bool IsTracingAccess => true; public bool IsTracingFees => true; public bool IsTracingLogs => true; - public bool IsTracingEvmChunks => true; - public bool IsTracingEvmSegments => true; + public bool IsTracingPredefinedPatterns => true; + public bool IsTracingCompiledSegments => true; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) => throw new OperationCanceledException(ErrorMessage); @@ -107,7 +107,7 @@ public void Dispose() { } public void ReportChunkExecutionEnd(long gas, int pc, Type segmentID) => throw new OperationCanceledException(ErrorMessage); - public void ReportChunkExecution(long gas, int pc, string segmentID) => throw new OperationCanceledException(ErrorMessage); + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) => throw new OperationCanceledException(ErrorMessage); public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) => throw new OperationCanceledException(ErrorMessage); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs index 3822c0d5cf6..563e3e2b482 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -204,9 +204,9 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public ITxTracer InnerTracer => _currentTxTracer; - public bool IsTracingEvmChunks => _currentTxTracer.IsTracingEvmChunks; + public bool IsTracingPredefinedPatterns => _currentTxTracer.IsTracingPredefinedPatterns; - public bool IsTracingEvmSegments => _currentTxTracer.IsTracingEvmSegments; + public bool IsTracingCompiledSegments => _currentTxTracer.IsTracingCompiledSegments; public int TakeSnapshot() => _txReceipts.Count; public void Restore(int snapshot) @@ -276,9 +276,9 @@ public void Dispose() _currentTxTracer.Dispose(); } - public void ReportChunkExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { - _currentTxTracer.ReportChunkExecution(gas, pc, segmentID); + _currentTxTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); } public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index ff420a3203c..eea6f909b08 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -119,15 +119,15 @@ public bool IsTracingLogs init => _isTracingOpLevelLogs = value; } - public bool IsTracingEvmChunks + public bool IsTracingPredefinedPatterns { - get => _isTracingEvmChunks || innerTracer.IsTracingEvmChunks; + get => _isTracingEvmChunks || innerTracer.IsTracingPredefinedPatterns; init => _isTracingEvmChunks = value; } - public bool IsTracingEvmSegments + public bool IsTracingCompiledSegments { - get => _isTracingEvmSegments || innerTracer.IsTracingEvmSegments; + get => _isTracingEvmSegments || innerTracer.IsTracingCompiledSegments; init => _isTracingEvmSegments = value; } @@ -469,12 +469,12 @@ public void Dispose() innerTracer.Dispose(); } - public void ReportChunkExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { token.ThrowIfCancellationRequested(); if (innerTracer.IsTracingFees) { - InnerTracer.ReportChunkExecution(gas, pc, segmentID); + InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index 4a8dd7f43aa..c0ba65b06bc 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -54,8 +54,8 @@ public CompositeTxTracer(IList txTracers) public bool IsTracingAccess { get; } public bool IsTracingFees { get; } public bool IsTracingLogs { get; } - public bool IsTracingEvmChunks { get; } - public bool IsTracingEvmSegments { get; } + public bool IsTracingPredefinedPatterns { get; } + public bool IsTracingCompiledSegments { get; } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -520,14 +520,14 @@ public void Dispose() _txTracers[index].Dispose(); } } - public void ReportChunkExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingEvmChunks) + if (innerTracer.IsTracingPredefinedPatterns) { - innerTracer.ReportChunkExecution(gas, pc, segmentID); + innerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); } } } @@ -537,7 +537,7 @@ public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; - if (innerTracer.IsTracingEvmSegments) + if (innerTracer.IsTracingCompiledSegments) { innerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index 4587fc44842..bb8f305bdc2 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -61,9 +61,9 @@ public DebugTracer(ITxTracer tracer) public bool IsTracingLogs => InnerTracer.IsTracingLogs; - public bool IsTracingEvmChunks => InnerTracer.IsTracingEvmChunks; + public bool IsTracingPredefinedPatterns => InnerTracer.IsTracingPredefinedPatterns; - public bool IsTracingEvmSegments => InnerTracer.IsTracingEvmSegments; + public bool IsTracingCompiledSegments => InnerTracer.IsTracingCompiledSegments; public bool IsBreakpoitnSet(int depth, int programCounter) => _breakPoints.ContainsKey((depth, programCounter)); @@ -293,8 +293,8 @@ public void ReportStorageRead(in StorageCell storageCell) public void Dispose() => _autoResetEvent.Dispose(); - public void ReportChunkExecution(long gas, int pc, string segmentID) - => InnerTracer.ReportChunkExecution(gas, pc, segmentID); + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + => InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) => InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs index afd575479f2..f8c0a07f6f7 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs @@ -18,9 +18,9 @@ public interface IILVMTracer /// /// /// Controls - /// - + /// - /// - bool IsTracingEvmChunks { get; } + bool IsTracingPredefinedPatterns { get; } /// /// Traces EVM precompiled segments @@ -29,9 +29,9 @@ public interface IILVMTracer /// Controls /// - /// - bool IsTracingEvmSegments { get; } + bool IsTracingCompiledSegments { get; } - void ReportChunkExecution(long gas, int pc, string segmentID); + void ReportPredefinedPatternExecution(long gas, int pc, string segmentID); void ReportCompiledSegmentExecution(long gas, int pc, string segmentId); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 804bfdaac3d..9d877299aaf 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -27,8 +27,8 @@ protected TxTracer() || IsTracingAccess || IsTracingFees || IsTracingLogs - || IsTracingEvmChunks - || IsTracingEvmSegments; + || IsTracingPredefinedPatterns + || IsTracingCompiledSegments; } public bool IsTracing { get; protected set; } public virtual bool IsTracingState { get; protected set; } @@ -45,8 +45,8 @@ protected TxTracer() public virtual bool IsTracingFees { get; protected set; } public virtual bool IsTracingStorage { get; protected set; } public virtual bool IsTracingLogs { get; protected set; } - public virtual bool IsTracingEvmChunks { get; protected set; } - public virtual bool IsTracingEvmSegments { get; protected set; } + public virtual bool IsTracingPredefinedPatterns { get; protected set; } + public virtual bool IsTracingCompiledSegments { get; protected set; } public virtual void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { } public virtual void ReportCodeChange(Address address, byte[]? before, byte[]? after) { } @@ -81,7 +81,7 @@ public virtual void ReportRefund(long refund) { } public virtual void ReportExtraGasPressure(long extraGasPressure) { } public virtual void ReportAccess(IReadOnlySet
accessedAddresses, IReadOnlySet accessedStorageCells) { } public virtual void ReportFees(UInt256 fees, UInt256 burntFees) { } - public virtual void ReportChunkExecution(long gas, int pc, string segmentID) { } + public virtual void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { } public virtual void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { } public virtual void Dispose() { } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 9855e6cd734..3123671c24a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -762,7 +762,7 @@ private CallResult ExecuteCode Date: Fri, 6 Sep 2024 01:13:23 +0100 Subject: [PATCH 073/146] fix endianness issue in StoreField --- .../CodeAnalysis/IlEvmTests.cs | 158 ++++++++++++++---- .../ByteCodeBuilderExtensions.cs | 4 +- .../CodeAnalysis/IL/ILCompiler.cs | 54 +++--- .../CodeAnalysis/IL/IlAnalyzer.cs | 6 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 9 +- .../CodeAnalysis/IL/InstructionChunk.cs | 10 +- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 119 +++++++++---- src/Nethermind/Nethermind.Evm/Instruction.cs | 3 +- 8 files changed, 263 insertions(+), 100 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index d595a5aa465..fde1e5d0ea2 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -25,12 +25,39 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; +using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; using static Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript.Log; namespace Nethermind.Evm.Test.CodeAnalysis { - public class P01P01ADD : InstructionChunk + internal class AbortDestinationPattern : InstructionChunk // for testing { + public string Name => nameof(AbortDestinationPattern); + public byte[] Pattern => [(byte)Instruction.JUMPDEST, (byte)Instruction.STOP]; + public byte CallCount { get; set; } = 0; + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + return GasCostOf.JumpDest; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + programCounter += 2; + result.ShouldStop = true; + } + } + internal class SomeAfterTwoPush : InstructionChunk + { + public string Name => nameof(SomeAfterTwoPush); public byte[] Pattern => [96, 96, 01]; public byte CallCount { get; set; } = 0; @@ -40,23 +67,28 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; stack.PushUInt256(lhs + rhs); programCounter += 5; - return EvmExceptionType.None; } } - public class MethodSelector : InstructionChunk + internal class MethodSelector : InstructionChunk { + public string Name => nameof(MethodSelector); public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; public byte CallCount { get; set; } = 0; @@ -66,11 +98,16 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; @@ -80,12 +117,12 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel stack.PushUInt256(vmState.Env.Value); programCounter += 2 + 2 + 1 + 1 + 1; - return EvmExceptionType.None; } } - public class IsContractCheck : InstructionChunk + internal class IsContractCheck : InstructionChunk { + public string Name => nameof(IsContractCheck); public byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; public byte CallCount { get; set; } = 0; @@ -95,11 +132,16 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; Address address = stack.PopAddress(); int contractCodeSize = worldState.GetCode(address).Length; @@ -114,12 +156,12 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel } programCounter += 3; - return EvmExceptionType.None; } } - public class EmulatedStaticJump : InstructionChunk + internal class EmulatedStaticJump : InstructionChunk { + public string Name => nameof(EmulatedStaticJump); public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; public byte CallCount { get; set; } = 0; @@ -129,11 +171,16 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) @@ -142,14 +189,14 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel } else { - return EvmExceptionType.InvalidJumpDestination; + result.ExceptionType = EvmExceptionType.InvalidJumpDestination; } - return EvmExceptionType.None; } } - public class EmulatedStaticCJump : InstructionChunk + internal class EmulatedStaticCJump : InstructionChunk { + public string Name => nameof(EmulatedStaticCJump); public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; public byte CallCount { get; set; } = 0; @@ -159,11 +206,16 @@ public long GasCost(EvmState vmState, IReleaseSpec spec) return gasCost; } - public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, ref long gasAvailable, ref EvmStack stack) where T : struct, VirtualMachine.IIsTracing + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing { CallCount++; - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) return EvmExceptionType.OutOfGas; + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; stack.PopUInt256(out UInt256 condition); int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; @@ -175,22 +227,20 @@ public EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IRel } else { - return EvmExceptionType.InvalidJumpDestination; + result.ExceptionType = EvmExceptionType.InvalidJumpDestination; } } else { programCounter += 4; } - - return EvmExceptionType.None; } } [TestFixture] - public class IlEvmTests : VirtualMachineTestsBase + internal class IlEvmTests : VirtualMachineTestsBase { private const string AnalyzerField = "_analyzer"; private readonly IVMConfig _vmConfig = new VMConfig() @@ -212,14 +262,20 @@ public override void Setup() Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, _vmConfig); _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); - var code = Prepare.EvmCode.STOP().Done; + var code = Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done; TestState.CreateAccount(Address.FromNumber(23), 1000000); TestState.InsertCode(Address.FromNumber(23), code, SpecProvider.GenesisSpec); - IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); IlAnalyzer.AddPattern(); IlAnalyzer.AddPattern(); IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); } [Test] @@ -274,7 +330,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - var pattern1 = IlAnalyzer.GetPatternHandler(); + var pattern1 = IlAnalyzer.GetPatternHandler(); var pattern2 = IlAnalyzer.GetPatternHandler(); var pattern3 = IlAnalyzer.GetPatternHandler(); @@ -322,11 +378,11 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() .PushSingle(1000) .GAS() .LT() - .JUMPI(59) + .JUMPI(58) .PushSingle(23) .PushSingle(7) .ADD() - .Call(Address.FromNumber(23), 10000) + .Call(Address.FromNumber(23), 100) .POP() .PushSingle(42) .PushSingle(5) @@ -344,6 +400,48 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() ExecuteBlock(tracer, bytecode); var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); accumulatedTraces.AddRange(traces); + + } + + // in the last stint gas is almost below 1000 + // it executes segment 0 (0..46) + // then calls address 23 (segment 0..5 since it is precompiled as well) + // then it executes segment 48..59 which ends in jump back to pc = 0 + // then it executes segment 0..46 again but this time gas is below 1000 + // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) + // so the last segment executed is AbortDestinationPattern + + string[] desiredTracePattern = new[] + { + "ILEVM_PRECOMPILED_[0..46]", + "ILEVM_PRECOMPILED_[0..5]", + "ILEVM_PRECOMPILED_[48..59]", + "ILEVM_PRECOMPILED_[0..46]", + "AbortDestinationPattern" + }; + + string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); + } + + + [Test] + public void JIT_invalid_opcode_resultd_in_failure() + { + byte[] bytecode = + Prepare.EvmCode + .PUSHx() // PUSH0 + .POP() + .STOP() + .Done; + + var accumulatedTraces = new List(); + for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) + { + var tracer = new IlvmBlockTracer(); + ExecuteBlock(tracer, bytecode, (1024, null)); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + accumulatedTraces.AddRange(traces); } @@ -806,7 +904,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.Method(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance, ctx.Data); + ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance, ctx.Data); Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilderExtensions.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilderExtensions.cs index b94b33475f3..10b9889fd4d 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilderExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilderExtensions.cs @@ -112,8 +112,8 @@ public static Prepare SELFDESTRUCT(this Prepare @this, Address? address = null) public static Prepare EXTCODEHASH(this Prepare @this, Address? address = null) => @this.PushSingle(address) .Op(Instruction.EXTCODEHASH); - public static Prepare PUSHx(this Prepare @this, byte[] args) - => @this.PushData(args); + public static Prepare PUSHx(this Prepare @this, byte[] args = null) + => args is null ? @this.Op(Instruction.PUSH0) : @this.PushData(args); public static Prepare MLOAD(this Prepare @this, UInt256? pos = null) => @this.PushSingle(pos) .Op(Instruction.MLOAD); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 3d8d1fb87bf..5e475fa83f9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -23,7 +23,8 @@ internal class ILCompiler public delegate void ExecuteSegment(ref ILEvmState vmstate, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, byte[][] immediatesData); public class SegmentExecutionCtx { - public ExecuteSegment Method; + public string Name => PrecompiledSegment.Method.Name; + public ExecuteSegment PrecompiledSegment; public byte[][] Data; } public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[] code, byte[][] data) @@ -46,14 +47,14 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); return new SegmentExecutionCtx { - Method = dynEmitedDelegate, + PrecompiledSegment = dynEmitedDelegate, Data = data }; } private static void EmitSegmentBody(Emit method, OpcodeInfo[] code) { - using Local jmpDestination = method.DeclareLocal(Word.Int0Field.FieldType); + using Local jmpDestination = method.DeclareLocal(typeof(int)); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); using Local address = method.DeclareLocal(typeof(Address)); @@ -144,14 +145,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.BadInstruction]); // set pc - method.LoadConstant(op.ProgramCounter + (op.Metadata?.AdditionalBytes ?? 0)); + method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); method.StoreLocal(programCounter); // load gasAvailable method.LoadLocal(gasAvailable); // get pc gas cost - method.LoadConstant(op.Metadata?.GasCost ?? 0); + method.LoadConstant(op.Metadata.GasCost); // subtract the gas cost method.Subtract(); @@ -180,7 +181,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); - method.StoreField(Word.Ulong0Field); + method.Call(Word.SetULong0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.NOT: @@ -447,7 +448,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(byte8A); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Byte0))); + method.StoreField(Word.Byte0Field); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.POP: @@ -516,14 +517,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); method.LoadConstant(code.Length); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); + method.Call(Word.SetInt0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.PC: method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(programCounter); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.UInt0))); + method.Call(Word.SetUInt0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.COINBASE: @@ -543,7 +544,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); - method.StoreField(Word.Ulong0Field); + method.Call(Word.SetULong0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.NUMBER: @@ -553,7 +554,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); - method.StoreField(Word.Ulong0Field); + method.Call(Word.SetULong0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.GASLIMIT: @@ -563,7 +564,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); - method.StoreField(Word.Ulong0Field); + method.Call(Word.SetULong0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.CALLER: @@ -704,7 +705,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Ulong0))); + method.Call(Word.SetULong0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.MSTORE: @@ -976,7 +977,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(gasAvailable); - method.StoreField(Word.Ulong0Field); + method.Call(Word.SetULong0); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.RETURNDATASIZE: @@ -985,7 +987,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.StoreField(Word.Int0Field); + method.Call(Word.SetInt0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.RETURNDATACOPY: @@ -1132,7 +1134,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co endOfOpcode = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); - method.LoadField(Word.Int0Field); + method.Call(Word.GetInt0); method.StoreLocal(uint32A); method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); @@ -1175,7 +1177,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Label pushToStackRegion = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); - method.LoadField(Word.Ulong0Field); + method.Call(Word.GetUInt0); method.Convert(); method.LoadConstant(long.MaxValue); method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); @@ -1221,7 +1223,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Label endOfOpcodeHandling = method.DefineLabel(); method.StackLoadPrevious(stack, head, 1); - method.LoadField(Word.UInt0Field); + method.Call(Word.GetUInt0); method.StoreLocal(uint32A); method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); @@ -1428,7 +1430,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(localReadOnlyMemory); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.StoreField(Word.Int0Field); + method.Call(Word.SetInt0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; @@ -1637,7 +1639,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // jump table method.MarkLabel(jumpTable); method.StackLoadPrevious(stack, head, 1); - method.LoadField(Word.Int0Field); + method.Call(Word.GetInt0); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); @@ -1649,7 +1651,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); - int maxJump = code[^1].ProgramCounter + (code[^1].Metadata?.AdditionalBytes ?? 0); + int maxJump = code[^1].ProgramCounter + code[^1].Metadata.AdditionalBytes; int minJump = code[0].ProgramCounter; // if (jumpDest <= maxJump) @@ -1753,7 +1755,7 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(locals[0]); il.StackLoadPrevious(stack.span, stack.idx, 2); - il.LoadField(Word.Int0Field); + il.Call(Word.GetInt0); il.LoadLocalAddress(uint256R); il.Call(shiftOp); il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); @@ -1792,7 +1794,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); il.StackLoadPrevious(stack.span, stack.idx, 2); - il.LoadField(Word.Int0Field); + il.Call(Word.GetInt0); il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); @@ -2182,17 +2184,17 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co case Instruction.JUMPDEST: costs[costStart] = coststack; // remember the stack chain of opcodes costStart = pc; - coststack = op.Metadata?.GasCost ?? 0; + coststack = op.Metadata.GasCost; break; case Instruction.JUMPI: case Instruction.JUMP: - coststack += op.Metadata?.GasCost ?? 0; + coststack += op.Metadata.GasCost; costs[costStart] = coststack; // remember the stack chain of opcodes costStart = pc + 1; // start with the next again coststack = 0; break; default: - coststack += op.Metadata?.GasCost ?? 0; + coststack += op.Metadata.GasCost; costs[pc] = coststack; break; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 55499f34fac..06143d4b982 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -86,6 +86,8 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tr static FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) { + string GenerateName(List segment) => $"ILEVM_PRECOMPILED_[{segment[0].ProgramCounter}..{segment[^1].ProgramCounter + segment[^1].Metadata.AdditionalBytes}]"; + Dictionary opcodeInfos = []; List segment = []; @@ -95,7 +97,7 @@ static FrozenDictionary SegmentCode((OpcodeInfo[], { if (segment.Count > 0) { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment(GenerateName(segment), segment.ToArray(), codeData.Item2)); segment.Clear(); } } @@ -106,7 +108,7 @@ static FrozenDictionary SegmentCode((OpcodeInfo[], } if (segment.Count > 0) { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment($"ILEVM_{Guid.NewGuid()}", segment.ToArray(), codeData.Item2)); + opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment(GenerateName(segment), segment.ToArray(), codeData.Item2)); } return opcodeInfos.ToFrozenDictionary(); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index d52bc1fc865..a776009326a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -84,10 +84,10 @@ public bool TryExecute(EvmState vmState, ulong chainId, re var executionResult = new ILChunkExecutionResult(); if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { - tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Method.Method.Name); + tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Name); var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - ctx.Method.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); + ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); gasAvailable = ilvmState.GasAvailable; programCounter = ilvmState.ProgramCounter; @@ -104,9 +104,8 @@ public bool TryExecute(EvmState vmState, ulong chainId, re } else if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { - tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.GetType().Name); - var evmException = chunk.Invoke(vmState, worldState, spec, ref programCounter, ref gasAvailable, ref stack); - executionResult.ExceptionType = evmException; + tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.Name); + chunk.Invoke(vmState, blockHashProvider, worldState, codeinfoRepository, spec, ref programCounter, ref gasAvailable, ref stack, ref executionResult); } else { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index 77dcb335ee3..a6bf35a390b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -3,6 +3,7 @@ using Nethermind.Core.Specs; using Nethermind.State; +using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -13,10 +14,13 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// interface InstructionChunk { + string Name { get; } byte[] Pattern { get; } - EvmExceptionType Invoke(EvmState vmState, IWorldState worldState, IReleaseSpec spec, ref int programCounter, - ref long gasAvailable, - ref EvmStack stack) where T : struct, IIsTracing; + void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing; long GasCost(EvmState vmState, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index a3768e7d690..0aae551c2c0 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -6,6 +6,7 @@ using Nethermind.Int256; using Nethermind.Trie; using Newtonsoft.Json.Linq; +using Org.BouncyCastle.Utilities; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -25,34 +26,34 @@ internal struct Word [FieldOffset(0)] public unsafe fixed byte _buffer[Size]; [FieldOffset(Size - sizeof(byte))] - public byte Byte0; + public byte _uByte0; [FieldOffset(Size - sizeof(int))] - public int Int0; + private int _sInt0; [FieldOffset(Size - sizeof(int))] - public uint UInt0; + private uint _uInt0; [FieldOffset(Size - 2 * sizeof(int))] - public uint UInt1; + private uint _uInt1; [FieldOffset(Size - 1 * sizeof(ulong))] - public ulong Ulong0; + private ulong _ulong0; [FieldOffset(Size - 2 * sizeof(ulong))] - public ulong Ulong1; + private ulong _ulong1; [FieldOffset(Size - 3 * sizeof(ulong))] - public ulong Ulong2; + private ulong _ulong2; [FieldOffset(Size - 4 * sizeof(ulong))] - public ulong Ulong3; + private ulong _ulong3; - public bool IsZero => (Ulong0 | Ulong1 | Ulong2 | Ulong3) == 0; + public bool IsZero => (_ulong0 | _ulong1 | _ulong2 | _ulong3) == 0; public void ToZero() { - Ulong0 = 0; Ulong1 = 0; - Ulong2 = 0; Ulong3 = 0; + _ulong0 = 0; _ulong1 = 0; + _ulong2 = 0; _ulong3 = 0; } public unsafe byte[] Array @@ -138,10 +139,10 @@ public UInt256 UInt256 { get { - ulong u3 = Ulong3; - ulong u2 = Ulong2; - ulong u1 = Ulong1; - ulong u0 = Ulong0; + ulong u3 = _ulong3; + ulong u2 = _ulong2; + ulong u1 = _ulong1; + ulong u0 = _ulong0; if (BitConverter.IsLittleEndian) { @@ -157,32 +158,88 @@ public UInt256 UInt256 { if (BitConverter.IsLittleEndian) { - Ulong3 = BinaryPrimitives.ReverseEndianness(value.u3); - Ulong2 = BinaryPrimitives.ReverseEndianness(value.u2); - Ulong1 = BinaryPrimitives.ReverseEndianness(value.u1); - Ulong0 = BinaryPrimitives.ReverseEndianness(value.u0); + _ulong3 = BinaryPrimitives.ReverseEndianness(value.u3); + _ulong2 = BinaryPrimitives.ReverseEndianness(value.u2); + _ulong1 = BinaryPrimitives.ReverseEndianness(value.u1); + _ulong0 = BinaryPrimitives.ReverseEndianness(value.u0); } else { - Ulong3 = value.u3; - Ulong2 = value.u2; - Ulong1 = value.u1; - Ulong0 = value.u0; + _ulong3 = value.u3; + _ulong2 = value.u2; + _ulong1 = value.u1; + _ulong0 = value.u0; } } } - public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(Byte0)); + public uint UInt0 + { + get + { + return _uInt0; + } + set + { + if (BitConverter.IsLittleEndian) + { + _uInt0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _uInt0 = value; + } + } + } + + public int Int0 + { + get + { + return _sInt0; + } + set + { + if (BitConverter.IsLittleEndian) + { + _sInt0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _sInt0 = value; + } + } + } + + public ulong ULong0 + { + get + { + return _ulong0; + } + set + { + if (BitConverter.IsLittleEndian) + { + _ulong0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _ulong0 = value; + } + } + } + + public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(_uByte0)); - public static readonly FieldInfo Int0Field = typeof(Word).GetField(nameof(Int0)); + public static readonly MethodInfo GetInt0 = typeof(Word).GetProperty(nameof(Int0))!.GetMethod; + public static readonly MethodInfo SetInt0 = typeof(Word).GetProperty(nameof(Int0))!.SetMethod; - public static readonly FieldInfo UInt0Field = typeof(Word).GetField(nameof(UInt0)); - public static readonly FieldInfo UInt1Field = typeof(Word).GetField(nameof(UInt1)); + public static readonly MethodInfo GetUInt0 = typeof(Word).GetProperty(nameof(UInt0))!.GetMethod; + public static readonly MethodInfo SetUInt0 = typeof(Word).GetProperty(nameof(UInt0))!.SetMethod; - public static readonly FieldInfo Ulong0Field = typeof(Word).GetField(nameof(Ulong0)); - public static readonly FieldInfo Ulong1Field = typeof(Word).GetField(nameof(Ulong1)); - public static readonly FieldInfo Ulong2Field = typeof(Word).GetField(nameof(Ulong2)); - public static readonly FieldInfo Ulong3Field = typeof(Word).GetField(nameof(Ulong3)); + public static readonly MethodInfo GetULong0 = typeof(Word).GetProperty(nameof(ULong0))!.GetMethod; + public static readonly MethodInfo SetULong0 = typeof(Word).GetProperty(nameof(ULong0))!.SetMethod; public static readonly MethodInfo GetIsZero = typeof(Word).GetProperty(nameof(IsZero))!.GetMethod; public static readonly MethodInfo SetToZero = typeof(Word).GetMethod(nameof(ToZero))!; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 9c6de3c579d..ada25990693 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -205,6 +205,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.STOP] = new(0, 0, 0, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.PUSH0] = new(GasCostOf.VeryLow, 0, 0, 1), [Instruction.PUSH1] = new(GasCostOf.VeryLow, 1, 0, 1), [Instruction.PUSH2] = new(GasCostOf.VeryLow, 2, 0, 1), [Instruction.PUSH3] = new(GasCostOf.VeryLow, 3, 0, 1), @@ -371,7 +372,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav } public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) { - public OpcodeMetadata? Metadata => OpcodeMetadata.Operations[instruction]; + public OpcodeMetadata Metadata => OpcodeMetadata.Operations[instruction]; public Instruction Operation => instruction; public ushort ProgramCounter => pc; public int? Arguments { get; set; } = argumentIndex; From 0f3292429bccfb1a2e3b3d8b332be6e9bfaea9b3 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 6 Sep 2024 01:43:17 +0100 Subject: [PATCH 074/146] fix build issue --- .../Nethermind.Test.Runner/StateTestTxTracer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 98b71183d15..e63bcc7d33b 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -37,6 +37,10 @@ public class StateTestTxTracer : ITxTracer, IDisposable public bool IsTracingEvmSegments => false; public bool IsTracing => IsTracingReceipt || IsTracingActions || IsTracingOpLevelStorage || IsTracingMemory || IsTracingInstructions || IsTracingRefunds || IsTracingCode || IsTracingStack || IsTracingBlockHash || IsTracingAccess || IsTracingFees || IsTracingLogs || IsTracingEvmChunks || IsTracingEvmSegments; + public bool IsTracingPredefinedPatterns => throw new NotImplementedException(); + + public bool IsTracingCompiledSegments => throw new NotImplementedException(); + public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256 stateRoot = null) { _trace.Result.Output = output; @@ -274,15 +278,14 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) throw new NotImplementedException(); } - public void ReportChunkExecution(long gas, int pc, string segmentID) - { - throw new NotSupportedException(); - } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { throw new NotSupportedException(); } public void Dispose() { } + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + { + throw new NotImplementedException(); + } } From 9a8a73bcae8f6ccdb41e8f33d8f6b1752943b438 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 6 Sep 2024 01:55:30 +0100 Subject: [PATCH 075/146] fix nullptr exception error --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5e475fa83f9..5c635ae2d7c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -695,7 +695,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.StoreField(GetFieldInfo(typeof(Word), nameof(Word.Int0))); + method.Call(Word.SetInt0); method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.MSIZE: From bd9c05fb0f4da6569d3212fca43870f408529493 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 6 Sep 2024 01:59:49 +0100 Subject: [PATCH 076/146] fix failing tests --- src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index fde1e5d0ea2..40defaa07b8 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -426,7 +426,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() [Test] - public void JIT_invalid_opcode_resultd_in_failure() + public void JIT_invalid_opcode_results_in_failure() { byte[] bytecode = Prepare.EvmCode @@ -480,7 +480,7 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 2; i++) + for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) { var tracer = new IlvmBlockTracer(); ExecuteBlock(tracer, bytecode); From 187df410a98d3f1b155111a238dfbafd133946fe Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 6 Sep 2024 02:34:28 +0100 Subject: [PATCH 077/146] * Adding Hash as Id to segment names (may revert) --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 8 ++++---- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 11 ++++++++--- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 6 +++--- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 9 ++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 40defaa07b8..4ff6ed6aac3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -413,10 +413,10 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_[0..46]", - "ILEVM_PRECOMPILED_[0..5]", - "ILEVM_PRECOMPILED_[48..59]", - "ILEVM_PRECOMPILED_[0..46]", + "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[0..46]", + "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", + "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[48..59]", + "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[0..46]", "AbortDestinationPattern" }; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 7c71974a175..8fac330990d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -7,11 +7,13 @@ using System.Runtime.CompilerServices; using Nethermind.Evm.Precompiles; using Nethermind.Evm.Tracing; +using Nethermind.Core.Crypto; namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo : IThreadPoolWorkItem { + public Hash256 CodeHash { get; init; } public ReadOnlyMemory MachineCode { get; } public IPrecompile? Precompile { get; set; } @@ -33,16 +35,18 @@ public async void NoticeExecution(ITxTracer tracer) } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); - public static CodeInfo Empty { get; } = new CodeInfo(Array.Empty()); + public static CodeInfo Empty { get; } = new CodeInfo(Array.Empty(), Keccak.OfAnEmptyString); - public CodeInfo(byte[] code) + public CodeInfo(byte[] code, Hash256 codeHash = null) { + CodeHash = codeHash ?? Keccak.Compute(code.AsSpan()); MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } - public CodeInfo(ReadOnlyMemory code) + public CodeInfo(ReadOnlyMemory code, Hash256 codeHash = null) { + CodeHash = codeHash ?? Keccak.Compute(code.Span); MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } @@ -60,6 +64,7 @@ public CodeInfo(IPrecompile precompile) Precompile = precompile; MachineCode = Array.Empty(); _analyzer = _emptyAnalyzer; + CodeHash = Keccak.OfAnEmptyString; } public bool ValidateJump(int destination) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 06143d4b982..6fd3db37217 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -84,9 +84,9 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tr { ReadOnlyMemory machineCode = codeInfo.MachineCode; - static FrozenDictionary SegmentCode((OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) + static FrozenDictionary SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) { - string GenerateName(List segment) => $"ILEVM_PRECOMPILED_[{segment[0].ProgramCounter}..{segment[^1].ProgramCounter + segment[^1].Metadata.AdditionalBytes}]"; + string GenerateName(List segment) => $"ILEVM_PRECOMPILED_({codeInfo.CodeHash.ToShortString()})[{segment[0].ProgramCounter}..{segment[^1].ProgramCounter + segment[^1].Metadata.AdditionalBytes}]"; Dictionary opcodeInfos = []; @@ -143,7 +143,7 @@ static FrozenDictionary CheckPatterns(ReadOnlyMemory initCode) { if (!_codeCache.TryGet(codeHash, out CodeInfo? codeInfo)) { - codeInfo = new(initCode.ToArray()); + codeInfo = new(initCode.ToArray(), codeHash); // Prime the code cache as likely to be used by more txs _codeCache.Set(codeHash, codeInfo); @@ -161,10 +161,9 @@ public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - CodeInfo codeInfo = new(code); - codeInfo.AnalyseInBackgroundIfRequired(); - Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); + CodeInfo codeInfo = new(code, codeHash); + codeInfo.AnalyseInBackgroundIfRequired(); state.InsertCode(codeOwner, codeHash, code, spec); _codeCache.Set(codeHash, codeInfo); } From 19b0063b4d6c16d52908acf0f007d4a24036a386 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 9 Sep 2024 13:40:37 +0100 Subject: [PATCH 078/146] Aletered tracing mechanism --- .../CodeAnalysis/IlEvmTests.cs | 38 ++++++------ .../EvmPooledMemoryTests.cs | 4 +- .../CodeAnalysis/IL/ILVMTracer.cs | 52 ---------------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 7 ++- .../Tracing/AlwaysCancelTxTracer.cs | 4 +- .../Tracing/BlockReceiptsTracer.cs | 8 +-- .../Tracing/CancellationTxTracer.cs | 8 +-- .../Tracing/CompositeTxTracer.cs | 8 +-- .../Tracing/Debugger/DebugTracer.cs | 8 +-- .../GethStyle/GethLikeTxMemoryTracer.cs | 59 +++++++++++++++++++ .../Tracing/GethStyle/GethLikeTxTracer.cs | 32 ++++++++++ .../Tracing/GethStyle/GethTxTraceEntry.cs | 7 +++ .../Nethermind.Evm/Tracing/ITxILVMTracer.cs | 4 +- .../Nethermind.Evm/Tracing/TxTracer.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 7 +-- 15 files changed, 146 insertions(+), 104 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 4ff6ed6aac3..3ab209d3aa4 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -10,6 +10,7 @@ using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Config; using Nethermind.Evm.Tracing; +using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; using Nethermind.Logging; @@ -357,12 +358,12 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) { - var tracer = new IlvmBlockTracer(); + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).Where(tr => !tr.IsPrecompiled).ToList(); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && !tr.IsPrecompiled.Value).ToList(); accumulatedTraces.AddRange(traces); } @@ -393,12 +394,12 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() .STOP() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) { - var tracer = new IlvmBlockTracer(); + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.SegmentID is not null).ToList(); accumulatedTraces.AddRange(traces); } @@ -435,23 +436,18 @@ public void JIT_invalid_opcode_results_in_failure() .STOP() .Done; - var accumulatedTraces = new List(); - for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) + var accumulatedTraces = new List(); + var numberOfRuns = IlAnalyzer.IlCompilerThreshold * 1024; + for (int i = 0; i < numberOfRuns; i++) { - var tracer = new IlvmBlockTracer(); + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode, (1024, null)); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).ToList(); + var traces = tracer.BuildResult().ToList(); accumulatedTraces.AddRange(traces); } - - // check if these patterns occur in the traces - // segment 0 - // pattern p1p1padd - - // segment 0 - // segment 1 - Assert.Greater(accumulatedTraces.GroupBy(tr => tr.IsPrecompiled).Count(), 0); + var failedTraces = accumulatedTraces.Where(tr => tr.Failed && tr.Entries.Any(subtr => subtr.Error == EvmExceptionType.BadInstruction.ToString())).ToList(); + Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); } [Test] @@ -479,12 +475,12 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .STOP() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) { - var tracer = new IlvmBlockTracer(); + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.IlvmTrace.OfType()).Where(tr => tr.IsPrecompiled).ToList(); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); accumulatedTraces.AddRange(traces); } diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 0d6322286f3..ecb84c1d0c5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -397,11 +397,11 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public void Dispose() { } - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs deleted file mode 100644 index c30d7ee1b12..00000000000 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILVMTracer.cs +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Evm.Tracing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Nethermind.Evm.CodeAnalysis.IL; - -internal class IlvmTxTrace(List ilvmTraceEntries) -{ - public List IlvmTrace { get; set; } = ilvmTraceEntries; -} - -internal class ChunkTraceEntry -{ - public bool IsPrecompiled { get; set; } - public int Gas { get; set; } - public int PC { get; set; } - public string SegmentID { get; set; } -} -internal class IlvmBlockTracer : BlockTracerBase -{ - protected override IlvmTxTrace OnEnd(ILVMTxTracer txTracer) - { - return new IlvmTxTrace(txTracer.IlvmTraceEntries); - } - - protected override ILVMTxTracer OnStart(Transaction? tx) - { - return new ILVMTxTracer(); - } -} - -internal class ILVMTxTracer : TxTracer -{ - public List IlvmTraceEntries { get; set; } = new List(); - public override void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) - { - IlvmTraceEntries.Add(new ChunkTraceEntry { Gas = (int)gas, PC = pc, SegmentID = segmentID, IsPrecompiled = false }); - } - - public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) - { - IlvmTraceEntries.Add(new ChunkTraceEntry { Gas = (int)gas, PC = pc, SegmentID = segmentId, IsPrecompiled = true }); - } -} diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index a776009326a..c70fbb267b5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -8,6 +8,7 @@ using Nethermind.Evm.Tracing; using Nethermind.State; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; +using static Nethermind.Evm.VirtualMachine; namespace Nethermind.Evm.CodeAnalysis.IL; /// @@ -84,7 +85,8 @@ public bool TryExecute(EvmState vmState, ulong chainId, re var executionResult = new ILChunkExecutionResult(); if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { - tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Name); + if (typeof(TTracingInstructions) == typeof(IsTracing)) + tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Name, vmState.Env); var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); @@ -104,7 +106,8 @@ public bool TryExecute(EvmState vmState, ulong chainId, re } else if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { - tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.Name); + if (typeof(TTracingInstructions) == typeof(IsTracing)) + tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.Name, vmState.Env); chunk.Invoke(vmState, blockHashProvider, worldState, codeinfoRepository, spec, ref programCounter, ref gasAvailable, ref stack, ref executionResult); } else diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index dd1e44ef466..cf076f84031 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -107,7 +107,7 @@ public void Dispose() { } public void ReportChunkExecutionEnd(long gas, int pc, Type segmentID) => throw new OperationCanceledException(ErrorMessage); - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) => throw new OperationCanceledException(ErrorMessage); + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) => throw new OperationCanceledException(ErrorMessage); - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) => throw new OperationCanceledException(ErrorMessage); + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) => throw new OperationCanceledException(ErrorMessage); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs index 563e3e2b482..8d6c010039b 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -276,13 +276,13 @@ public void Dispose() _currentTxTracer.Dispose(); } - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { - _currentTxTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); + _currentTxTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { - _currentTxTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + _currentTxTracer.ReportCompiledSegmentExecution(gas, pc, segmentId, in env); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs index eea6f909b08..1ac66d592bf 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -469,21 +469,21 @@ public void Dispose() innerTracer.Dispose(); } - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { token.ThrowIfCancellationRequested(); if (innerTracer.IsTracingFees) { - InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); + InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); } } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { token.ThrowIfCancellationRequested(); if (innerTracer.IsTracingFees) { - InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId, in env); } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs index c0ba65b06bc..c8124644642 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -520,26 +520,26 @@ public void Dispose() _txTracers[index].Dispose(); } } - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; if (innerTracer.IsTracingPredefinedPatterns) { - innerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); + innerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); } } } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { for (int index = 0; index < _txTracers.Count; index++) { ITxTracer innerTracer = _txTracers[index]; if (innerTracer.IsTracingCompiledSegments) { - innerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + innerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId, in env); } } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index bb8f305bdc2..954b1d8d854 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -293,10 +293,10 @@ public void ReportStorageRead(in StorageCell storageCell) public void Dispose() => _autoResetEvent.Dispose(); - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) - => InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID); + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + => InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) - => InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId); + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) + => InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId, in env); } #endif diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs index c6b64574ecc..6719457f892 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxMemoryTracer.cs @@ -62,4 +62,63 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe CurrentTraceEntry.Storage = new Dictionary(previousTraceEntry.Storage); } } + + + public override void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + var previousTraceEntry = CurrentTraceEntry; + var previousDepth = CurrentTraceEntry?.Depth ?? 0; + + base.ReportPredefinedPatternExecution(gas, pc, segmentID, env); + + if (CurrentTraceEntry.Depth > previousDepth) + { + CurrentTraceEntry.Storage = new Dictionary(); + + Trace.StoragesByDepth.Push(previousTraceEntry is null ? new() : previousTraceEntry.Storage); + } + else if (CurrentTraceEntry.Depth < previousDepth) + { + if (previousTraceEntry is null) + throw new InvalidOperationException("Missing the previous trace on leaving the call."); + + CurrentTraceEntry.Storage = new Dictionary(Trace.StoragesByDepth.Pop()); + } + else + { + if (previousTraceEntry is null) + throw new InvalidOperationException("Missing the previous trace on continuation."); + + CurrentTraceEntry.Storage = new Dictionary(previousTraceEntry.Storage); + } + } + + public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + var previousTraceEntry = CurrentTraceEntry; + var previousDepth = CurrentTraceEntry?.Depth ?? 0; + + base.ReportCompiledSegmentExecution(gas, pc, segmentID, env); + + if (CurrentTraceEntry.Depth > previousDepth) + { + CurrentTraceEntry.Storage = new Dictionary(); + + Trace.StoragesByDepth.Push(previousTraceEntry is null ? new() : previousTraceEntry.Storage); + } + else if (CurrentTraceEntry.Depth < previousDepth) + { + if (previousTraceEntry is null) + throw new InvalidOperationException("Missing the previous trace on leaving the call."); + + CurrentTraceEntry.Storage = new Dictionary(Trace.StoragesByDepth.Pop()); + } + else + { + if (previousTraceEntry is null) + throw new InvalidOperationException("Missing the previous trace on continuation."); + + CurrentTraceEntry.Storage = new Dictionary(previousTraceEntry.Storage); + } + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs index 67e9ffe8aae..1d2a1b397ac 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs @@ -82,6 +82,38 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe _gasCostAlreadySetForCurrentOp = false; } + public override void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + if (IsTracingCompiledSegments) + return; + + if (CurrentTraceEntry is not null) + AddTraceEntry(CurrentTraceEntry); + + CurrentTraceEntry = new(); + CurrentTraceEntry.Gas = gas; + CurrentTraceEntry.ProgramCounter = pc; + CurrentTraceEntry.SegmentID = segmentID; + CurrentTraceEntry.IsPrecompiled = false; + CurrentTraceEntry.Depth = env.GetGethTraceDepth(); + } + + public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + if(IsTracingCompiledSegments) + return; + + if (CurrentTraceEntry is not null) + AddTraceEntry(CurrentTraceEntry); + + CurrentTraceEntry = new(); + CurrentTraceEntry.Gas = gas; + CurrentTraceEntry.ProgramCounter = pc; + CurrentTraceEntry.SegmentID = segmentID; + CurrentTraceEntry.IsPrecompiled = true; + CurrentTraceEntry.Depth = env.GetGethTraceDepth(); + } + public override void ReportOperationError(EvmExceptionType error) => CurrentTraceEntry.Error = GetErrorDescription(error); public override void ReportOperationRemainingGas(long gas) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethTxTraceEntry.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethTxTraceEntry.cs index 16e9565a56c..a5a21ac740c 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethTxTraceEntry.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethTxTraceEntry.cs @@ -20,6 +20,13 @@ public GethTxTraceEntry() [JsonPropertyName("op")] public string? Opcode { get; set; } + [JsonPropertyName("segment")] + public string? SegmentID { get; set; } + + [JsonPropertyName("isPrecompiled")] + public bool? IsPrecompiled { get; set; } + + [JsonConverter(typeof(LongRawJsonConverter))] public long Gas { get; set; } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs index f8c0a07f6f7..58d0e0c876a 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs @@ -31,7 +31,7 @@ public interface IILVMTracer /// bool IsTracingCompiledSegments { get; } - void ReportPredefinedPatternExecution(long gas, int pc, string segmentID); - void ReportCompiledSegmentExecution(long gas, int pc, string segmentId); + void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env); + void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env); } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 9d877299aaf..d88b3928f39 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -81,7 +81,7 @@ public virtual void ReportRefund(long refund) { } public virtual void ReportExtraGasPressure(long extraGasPressure) { } public virtual void ReportAccess(IReadOnlySet
accessedAddresses, IReadOnlySet accessedStorageCells) { } public virtual void ReportFees(UInt256 fees, UInt256 burntFees) { } - public virtual void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) { } - public virtual void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) { } + public virtual void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { } + public virtual void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { } public virtual void Dispose() { } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3123671c24a..746ab6d157a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -712,11 +712,8 @@ private CallResult ExecuteCode code = env.CodeInfo.MachineCode.Span; EvmExceptionType exceptionType = EvmExceptionType.None; - IlInfo? ilInfo = (typeof(TTracingInstructions) == typeof(NotTracing) && - typeof(TTracingRefunds) == typeof(NotTracing) && - typeof(TTracingStorage) == typeof(NotTracing)) - ? env.CodeInfo.IlInfo - : null; + IlInfo? ilInfo = env.CodeInfo.IlInfo; + bool isRevert = false; #if DEBUG From 2a2c0fcd1fe8f50603c30ddd13cd45ca798b7da6 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 10 Sep 2024 17:01:18 +0100 Subject: [PATCH 079/146] fix build issues --- .../StateTestTxTracer.cs | 22 +++++++++++++++---- .../StateTxTraceEntry.cs | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index e63bcc7d33b..cb8c79357db 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -278,14 +278,28 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) throw new NotImplementedException(); } - public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId) + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) { - throw new NotSupportedException(); + _gasAlreadySetForCurrentOp = false; + _traceEntry = new StateTestTxTraceEntry(); + _traceEntry.Pc = pc; + _traceEntry.Operation = null; + _traceEntry.OperationName = segmentId; + _traceEntry.Gas = gas; + _traceEntry.Depth = env.GetGethTraceDepth(); + _trace.Entries.Add(_traceEntry); } public void Dispose() { } - public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID) + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { - throw new NotImplementedException(); + _gasAlreadySetForCurrentOp = false; + _traceEntry = new StateTestTxTraceEntry(); + _traceEntry.Pc = pc; + _traceEntry.Operation = null; + _traceEntry.OperationName = segmentID; + _traceEntry.Gas = gas; + _traceEntry.Depth = env.GetGethTraceDepth(); + _trace.Entries.Add(_traceEntry); } } diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs b/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs index 4b384c06e5a..5b221f9eb91 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTxTraceEntry.cs @@ -17,7 +17,7 @@ public StateTestTxTraceEntry() public int Pc { get; set; } [JsonPropertyName("op")] - public byte Operation { get; set; } + public byte? Operation { get; set; } [JsonPropertyName("gas")] public long Gas { get; set; } From 371057aa1681c38171c95ce496920fdbd8ed6aab Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 23 Sep 2024 13:51:01 +0100 Subject: [PATCH 080/146] * Fixed issue * Added more tests --- .../CodeAnalysis/IlEvmTests.cs | 391 ++++++++++-------- .../VirtualMachineTestsBase.cs | 5 +- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 21 +- .../CodeAnalysis/IL/IlAnalyzer.cs | 74 ++-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 33 +- .../CodeAnalysis/IL/PredefinedPatterns.cs | 166 ++++++++ src/Nethermind/Nethermind.Evm/Instruction.cs | 1 - .../Nethermind.Evm/VirtualMachine.cs | 17 +- 8 files changed, 467 insertions(+), 241 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 3ab209d3aa4..a61eedec2bf 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -28,6 +28,9 @@ using System.Threading.Tasks; using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; using static Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript.Log; +using Nethermind.Evm.CodeAnalysis.IL.Patterns; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Blockchain; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -87,196 +90,91 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW } } - internal class MethodSelector : InstructionChunk + internal class TestBlockChain: VirtualMachineTestsBase { - public string Name => nameof(MethodSelector); - public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; - public byte CallCount { get; set; } = 0; - - public long GasCost(EvmState vmState, IReleaseSpec spec) + protected IVMConfig config; + public TestBlockChain(IVMConfig config) { - long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.Base + GasCostOf.VeryLow; - return gasCost; + this.config = config; + Setup(); } - public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, - ref int programCounter, - ref long gasAvailable, - ref EvmStack stack, - ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + public TestBlockChain() { - CallCount++; - - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) - result.ExceptionType = EvmExceptionType.OutOfGas; - - byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; - byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; - VirtualMachine.UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, 0, 32); - vmState.Memory.SaveByte(location, value); - stack.PushUInt256(vmState.Env.Value); - stack.PushUInt256(vmState.Env.Value); - - programCounter += 2 + 2 + 1 + 1 + 1; - } - } - - internal class IsContractCheck : InstructionChunk - { - public string Name => nameof(IsContractCheck); - public byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; - public byte CallCount { get; set; } = 0; - - public long GasCost(EvmState vmState, IReleaseSpec spec) - { - long gasCost = spec.GetExtCodeCost() + GasCostOf.VeryLow + GasCostOf.Base; - return gasCost; + config = new VMConfig(); + Setup(); } - - public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, - ref int programCounter, - ref long gasAvailable, - ref EvmStack stack, - ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + public override void Setup() { - CallCount++; - - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) - result.ExceptionType = EvmExceptionType.OutOfGas; - - Address address = stack.PopAddress(); - int contractCodeSize = worldState.GetCode(address).Length; - stack.PushUInt32(contractCodeSize); - if (contractCodeSize == 0) - { - stack.PushOne(); - } - else - { - stack.PushZero(); - } + base.Setup(); + ILogManager logManager = GetLogManager(); - programCounter += 3; - } + _blockhashProvider = new TestBlockhashProvider(SpecProvider); + Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, config); + _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); - } - internal class EmulatedStaticJump : InstructionChunk - { - public string Name => nameof(EmulatedStaticJump); - public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; - public byte CallCount { get; set; } = 0; + var code = Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done; + TestState.CreateAccount(Address.FromNumber(23), 1000000); + TestState.InsertCode(Address.FromNumber(23), code, SpecProvider.GenesisSpec); - public long GasCost(EvmState vmState, IReleaseSpec spec) - { - long gasCost = GasCostOf.VeryLow + GasCostOf.Mid; - return gasCost; + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); } - public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, - ref int programCounter, - ref long gasAvailable, - ref EvmStack stack, - ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + public void Execute(byte[] bytecode, T tracer) + where T : ITxTracer { - CallCount++; - - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) - result.ExceptionType = EvmExceptionType.OutOfGas; - - int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; - if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) - { - programCounter = jumpdestionation; - } - else - { - result.ExceptionType = EvmExceptionType.InvalidJumpDestination; - } + Execute(tracer, bytecode, null, 1_000_000); } - } - internal class EmulatedStaticCJump : InstructionChunk - { - public string Name => nameof(EmulatedStaticCJump); - public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; - public byte CallCount { get; set; } = 0; - - public long GasCost(EvmState vmState, IReleaseSpec spec) + public Address InsertCode(byte[] bytecode) { - long gasCost = GasCostOf.VeryLow + GasCostOf.High; - return gasCost; + var hashcode = Keccak.Compute(bytecode); + var address = new Address(hashcode); + var spec = Prague.Instance; + TestState.CreateAccount(address, 1000000); + TestState.InsertCode(address, bytecode, spec); + return address; } - public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, - ref int programCounter, - ref long gasAvailable, - ref EvmStack stack, - ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + public Hash256 StateRoot { - CallCount++; - - if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) - result.ExceptionType = EvmExceptionType.OutOfGas; - - stack.PopUInt256(out UInt256 condition); - int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; - if (!condition.IsZero) - { - if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) - { - programCounter = jumpdestionation; - } - else - { - result.ExceptionType = EvmExceptionType.InvalidJumpDestination; - } - } - else + get { - programCounter += 4; + TestState.Commit(Spec); + TestState.RecalculateStateRoot(); + return TestState.StateRoot; } } - } - + } [TestFixture] - internal class IlEvmTests : VirtualMachineTestsBase + internal class IlEvmTests : TestBlockChain { private const string AnalyzerField = "_analyzer"; - private readonly IVMConfig _vmConfig = new VMConfig() - { - IsJitEnabled = true, - IsPatternMatchingEnabled = true, - - PatternMatchingThreshold = 4, - JittingThreshold = 256, - }; [SetUp] public override void Setup() { - base.Setup(); - ILogManager logManager = GetLogManager(); - - _blockhashProvider = new TestBlockhashProvider(SpecProvider); - Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, _vmConfig); - _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); + base.config = new VMConfig() + { + IsJitEnabled = true, + IsPatternMatchingEnabled = true, - var code = Prepare.EvmCode - .PushData(23) - .PushData(7) - .ADD() - .STOP().Done; - TestState.CreateAccount(Address.FromNumber(23), 1000000); - TestState.InsertCode(Address.FromNumber(23), code, SpecProvider.GenesisSpec); + PatternMatchingThreshold = 4, + JittingThreshold = 256, + }; - IlAnalyzer.AddPattern(); - IlAnalyzer.AddPattern(); - IlAnalyzer.AddPattern(); - IlAnalyzer.AddPattern(); - IlAnalyzer.AddPattern(); - IlAnalyzer.AddPattern(); + base.Setup(); } [Test] @@ -294,7 +192,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching, NullTxTracer.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -323,7 +221,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling, NullTxTracer.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); codeInfo.IlInfo.Segments.Count.Should().Be(2); } @@ -357,9 +255,15 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() .STOP() .Done; - + /* + var hashcode = Keccak.Compute(bytecode); + var address = new Address(hashcode); + var spec = Prague.Instance; + TestState.CreateAccount(address, 1000000); + TestState.InsertCode(address, bytecode, spec); + */ var accumulatedTraces = new List(); - for (int i = 0; i < IlAnalyzer.CompoundOpThreshold * 2; i++) + for (int i = 0; i < config.PatternMatchingThreshold * 2; i++) { var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); @@ -370,9 +274,125 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() Assert.Greater(accumulatedTraces.Count, 0); } + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] + public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode) testcase) + { + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + var address = standardChain.InsertCode(testcase.bytecode); + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + JittingThreshold = 9, + IsJitEnabled = true + }); + enhancedChain.InsertCode(testcase.bytecode); + int repeatCount = 8; + + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + var bytecode = + Prepare.EvmCode + .JUMPDEST() + .Call(address, 10) + .POP() + .GAS() + .PushData(1000) + .LT() + .JUMPI(0) + .STOP() + .Done; + + for (var i = 0; i <= repeatCount; i++) + { + standardChain.Execute(bytecode, tracer1); + } + + for (var i = 0; i <= repeatCount; i++) + { + enhancedChain.Execute(bytecode, tracer2); + } + + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); + + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; + + var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + + if(testcase.opcode is not null) + { + Assert.That(HasIlvmTraces, Is.True); + } + Assert.That(actual, Is.EqualTo(expected)); + } + + + [Test, TestCaseSource(nameof(GePatBytecodesSamples))] + public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) + { + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + var address = standardChain.InsertCode(testcase.bytecode); + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 9, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = false + }); + enhancedChain.InsertCode(testcase.bytecode); + + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + var bytecode = + Prepare.EvmCode + .JUMPDEST() + .Call(address, 10) + .POP() + .GAS() + .PushData(1000) + .LT() + .JUMPI(0) + .STOP() + .Done; + + for (var i = 0; i < 8; i++) + { + standardChain.Execute(bytecode, tracer1); + } + + for (var i = 0; i < 8; i++) + { + enhancedChain.Execute(bytecode, tracer2); + } + + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); + + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; + + var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + + if (testcase.opcode is not null) + { + Assert.That(HasIlvmTraces, Is.True); + } + Assert.That(actual, Is.EqualTo(expected)); + } + [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() { + var address = InsertCode(Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done); + byte[] bytecode = Prepare.EvmCode .JUMPDEST() @@ -383,7 +403,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() .PushSingle(23) .PushSingle(7) .ADD() - .Call(Address.FromNumber(23), 100) + .Call(address, 100) .POP() .PushSingle(42) .PushSingle(5) @@ -395,7 +415,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) + for (int i = 0; i <= config.JittingThreshold * 2; i++) { var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); @@ -414,10 +434,10 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[0..46]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[48..59]", - "ILEVM_PRECOMPILED_(0x195fe3...9dbe75)[0..46]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", "AbortDestinationPattern" }; @@ -437,7 +457,7 @@ public void JIT_invalid_opcode_results_in_failure() .Done; var accumulatedTraces = new List(); - var numberOfRuns = IlAnalyzer.IlCompilerThreshold * 1024; + var numberOfRuns = config.JittingThreshold * 1024; for (int i = 0; i < numberOfRuns; i++) { var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); @@ -476,7 +496,7 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= IlAnalyzer.IlCompilerThreshold * 32; i++) + for (int i = 0; i <= config.JittingThreshold * 32; i++) { var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); ExecuteBlock(tracer, bytecode); @@ -519,8 +539,36 @@ public void Pure_Opcode_Emition_Coveraga() Assert.That(notYetImplemented.Count == 0, $"{notYetImplemented.Count} opcodes missing: [{missingOpcodes}]"); } + public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() + { + yield return (null, Prepare.EvmCode + .Done); + yield return (typeof(EmulatedStaticCJump), Prepare.EvmCode + .PUSHx([1]) + .PUSHx([0, 7]) + .JUMPI() + .JUMPDEST() + .Done); + yield return (typeof(EmulatedStaticJump), Prepare.EvmCode + .PUSHx([0, 5]) + .JUMP() + .JUMPDEST() + .Done); + yield return (typeof(IsContractCheck), Prepare.EvmCode + .PushData(0) + .PushData(23) + .MSTORE() + .CALLVALUE() + .DUPx(1) + .Done); + yield return (typeof(MethodSelector), Prepare.EvmCode + .EXTCODESIZE(Address.SystemUser) + .DUPx(1) + .ISZERO() + .Done); + } - public static IEnumerable<(Instruction?, byte[])> GetBytecodes() + public static IEnumerable<(Instruction?, byte[])> GeJitBytecodesSamples() { yield return (null, Prepare.EvmCode .Done); @@ -694,10 +742,12 @@ public void Pure_Opcode_Emition_Coveraga() .Done); yield return (Instruction.RETURN, Prepare.EvmCode + .StoreDataInMemory(0, [2, 3, 5, 7]) .RETURN(0, 32) .Done); yield return (Instruction.REVERT, Prepare.EvmCode + .StoreDataInMemory(0, [2, 3, 5, 7]) .REVERT(0, 32) .Done); @@ -871,7 +921,7 @@ public void Pure_Opcode_Emition_Coveraga() .Done); } - [Test, TestCaseSource(nameof(GetBytecodes))] + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) testcase) { var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); @@ -901,6 +951,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance, ctx.Data); + Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); } diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 02994876845..e82e97be09e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -20,6 +20,7 @@ using Nethermind.State; using Nethermind.Trie.Pruning; using NUnit.Framework; +using Nethermind.Evm.Config; namespace Nethermind.Evm.Test; @@ -136,9 +137,9 @@ protected T ExecuteBlock(T tracer, byte[] code, ForkActivation? forkActivatio return tracer; } - protected T Execute(T tracer, byte[] code, ForkActivation? forkActivation = null) where T : ITxTracer + protected T Execute(T tracer, byte[] code, ForkActivation? forkActivation = null, long gasLimit = 100000) where T : ITxTracer { - (Block block, Transaction transaction) = PrepareTx(forkActivation ?? Activation, 100000, code); + (Block block, Transaction transaction) = PrepareTx(forkActivation ?? Activation, gasLimit, code); _processor.Execute(transaction, block.Header, tracer); return tracer; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 8fac330990d..510372a91fe 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -8,6 +8,7 @@ using Nethermind.Evm.Precompiles; using Nethermind.Evm.Tracing; using Nethermind.Core.Crypto; +using Nethermind.Evm.Config; namespace Nethermind.Evm.CodeAnalysis { @@ -21,17 +22,25 @@ public class CodeInfo : IThreadPoolWorkItem // IL-EVM private int _callCount; - public async void NoticeExecution(ITxTracer tracer) + public async void NoticeExecution(IVMConfig vmConfig) { // IL-EVM info already created - if (_callCount > Math.Max(IlAnalyzer.IlCompilerThreshold, IlAnalyzer.CompoundOpThreshold)) + if (_callCount > Math.Max(vmConfig.JittingThreshold, vmConfig.PatternMatchingThreshold)) return; + Interlocked.Increment(ref _callCount); // use Interlocked just in case of concurrent execution to run it only once - IlInfo.ILMode mode = Interlocked.Increment(ref _callCount) == IlAnalyzer.CompoundOpThreshold - ? IlInfo.ILMode.PatternMatching - : _callCount == IlAnalyzer.IlCompilerThreshold ? IlInfo.ILMode.SubsegmentsCompiling : IlInfo.ILMode.NoIlvm; - await IlAnalyzer.StartAnalysis(this, mode, tracer); + IlInfo.ILMode mode = + vmConfig.IsPatternMatchingEnabled && _callCount == vmConfig.PatternMatchingThreshold + ? IlInfo.ILMode.PatternMatching + : vmConfig.IsJitEnabled && _callCount == vmConfig.JittingThreshold + ? IlInfo.ILMode.SubsegmentsCompiling + : IlInfo.ILMode.NoIlvm; + + if(mode == IlInfo.ILMode.NoIlvm) + return; + + await IlAnalyzer.StartAnalysis(this, mode).ConfigureAwait(false); } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 6fd3db37217..937a98fdab2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -50,9 +50,9 @@ public static T GetPatternHandler() where T : InstructionChunk /// Starts the analyzing in a background task and outputs the value in the . ///
thou /// The destination output. - public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tracer) + public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) { - return Task.Run(() => Analysis(codeInfo, mode, tracer)); + return Task.Run(() => Analysis(codeInfo, mode)); } public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) @@ -80,43 +80,57 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ITxTracer tracer) + private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - static FrozenDictionary SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, ITxTracer tracer) + static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, IlInfo ilinfo) { - string GenerateName(List segment) => $"ILEVM_PRECOMPILED_({codeInfo.CodeHash.ToShortString()})[{segment[0].ProgramCounter}..{segment[^1].ProgramCounter + segment[^1].Metadata.AdditionalBytes}]"; + if(codeData.Item1.Length == 0) + { + return; + } + + string GenerateName(Range segmentRange) => $"ILEVM_PRECOMPILED_({codeInfo.CodeHash.ToShortString()})[{segmentRange.Start}..{segmentRange.End}]"; - Dictionary opcodeInfos = []; + int[] statefulOpcodeindex = new int[1 + (codeData.Item1.Length / 5)]; - List segment = []; - foreach (var opcode in codeData.Item1) + int j = 0; + for (int i = 0; i < codeData.Item1.Length; i++) { - if (opcode.Operation.IsStateful()) + if (codeData.Item1[i].Operation.IsStateful()) { - if (segment.Count > 0) - { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment(GenerateName(segment), segment.ToArray(), codeData.Item2)); - segment.Clear(); - } - } - else - { - segment.Add(opcode); + statefulOpcodeindex[j++] = i; } } - if (segment.Count > 0) + + for (int i = -1; i <= j; i++) { - opcodeInfos.Add(segment[0].ProgramCounter, ILCompiler.CompileSegment(GenerateName(segment), segment.ToArray(), codeData.Item2)); + int start = i == -1 ? 0 : statefulOpcodeindex[i] + 1; + int end = i == j - 1 || i + 1 == statefulOpcodeindex.Length ? codeData.Item1.Length : statefulOpcodeindex[i + 1]; + if (start > end) + { + continue; + } + + var segment = codeData.Item1[start..end]; + if (segment.Length == 0) + { + continue; + } + var firstOp = segment[0]; + var lastOp = segment[^1]; + var segmentName = GenerateName(firstOp.ProgramCounter..(lastOp.ProgramCounter + lastOp.Metadata.AdditionalBytes)); + + ilinfo.Segments.GetOrAdd((ushort)segment[0].ProgramCounter,CompileSegment(segmentName, segment, codeData.Item2)); } - return opcodeInfos.ToFrozenDictionary(); + + ilinfo.Mode |= IlInfo.ILMode.SubsegmentsCompiling; } - static FrozenDictionary CheckPatterns(ReadOnlyMemory machineCode, ITxTracer tracer) + static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) { var (strippedBytecode, data) = StripByteCode(machineCode.Span); - var patternFound = new Dictionary(); foreach (var (_, chunkHandler) in Patterns) { for (int i = 0; i < strippedBytecode.Length - chunkHandler.Pattern.Length + 1; i++) @@ -129,28 +143,22 @@ static FrozenDictionary CheckPatterns(ReadOnlyMemory - /// How many execution a should perform before trying to get its opcodes optimized. - ///
- public static int CompoundOpThreshold = 32; - public static int IlCompilerThreshold = 128; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index c70fbb267b5..c8cfdb9f4ab 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -7,6 +7,7 @@ using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; using Nethermind.State; +using NonBlocking; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; using static Nethermind.Evm.VirtualMachine; @@ -40,50 +41,38 @@ public enum ILMode public static IlInfo Empty => new(); /// - /// Represents what mode of IL-EVM is used. 0 is the default. [0 = Pattern matching, 1 = subsegments compiling] + /// Represents what mode of IL-EVM is used. 0 is the default. [0 = No ILVM optimizations, 1 = Pattern matching, 2 = subsegments compiling] /// - public static readonly ILMode Mode = ILMode.PatternMatching; + public ILMode Mode = ILMode.NoIlvm; public bool IsEmpty => Chunks.Count == 0 && Segments.Count == 0; /// /// No overrides. /// private IlInfo() { - Chunks = FrozenDictionary.Empty; - Segments = FrozenDictionary.Empty; + Chunks = new ConcurrentDictionary(); + Segments = new ConcurrentDictionary(); } - public IlInfo WithChunks(FrozenDictionary chunks) - { - Chunks = chunks; - return this; - } - - public IlInfo WithSegments(FrozenDictionary segments) - { - Segments = segments; - return this; - } - - public IlInfo(FrozenDictionary mappedOpcodes, FrozenDictionary segments) + public IlInfo(ConcurrentDictionary mappedOpcodes, ConcurrentDictionary segments) { Chunks = mappedOpcodes; Segments = segments; } // assumes small number of ILed - public FrozenDictionary Chunks { get; set; } - public FrozenDictionary Segments { get; set; } + public ConcurrentDictionary Chunks { get; } = new(); + public ConcurrentDictionary Segments { get; } = new(); public bool TryExecute(EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ITxTracer tracer, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out ILChunkExecutionResult? result) where TTracingInstructions : struct, VirtualMachine.IIsTracing { result = null; - if (programCounter > ushort.MaxValue) + if (programCounter > ushort.MaxValue || Mode == ILMode.NoIlvm) return false; var executionResult = new ILChunkExecutionResult(); - if (Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) + if (Mode.HasFlag(ILMode.SubsegmentsCompiling) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { if (typeof(TTracingInstructions) == typeof(IsTracing)) tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Name, vmState.Env); @@ -104,7 +93,7 @@ public bool TryExecute(EvmState vmState, ulong chainId, re vmState.DataStackHead = ilvmState.StackHead; stack.Head = ilvmState.StackHead; } - else if (Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) + else if (Mode.HasFlag(ILMode.PatternMatching) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { if (typeof(TTracingInstructions) == typeof(IsTracing)) tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.Name, vmState.Env); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs new file mode 100644 index 00000000000..e07f23ce4d4 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Specs; +using Nethermind.Core; +using Nethermind.Int256; +using Nethermind.State; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; + +namespace Nethermind.Evm.CodeAnalysis.IL.Patterns; + +internal class MethodSelector : InstructionChunk +{ + public string Name => nameof(MethodSelector); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.MSTORE, (byte)Instruction.CALLVALUE, (byte)Instruction.DUP1]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.Base + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; + byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; + VirtualMachine.UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, 0, 32); + vmState.Memory.SaveByte(location, value); + stack.PushUInt256(vmState.Env.Value); + stack.PushUInt256(vmState.Env.Value); + + programCounter += 2 + 2 + 1 + 1 + 1; + } +} + +internal class IsContractCheck : InstructionChunk +{ + public string Name => nameof(IsContractCheck); + public byte[] Pattern => [(byte)Instruction.EXTCODESIZE, (byte)Instruction.DUP1, (byte)Instruction.ISZERO]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = spec.GetExtCodeCost() + GasCostOf.VeryLow + GasCostOf.Base; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + Address address = stack.PopAddress(); + int contractCodeSize = worldState.GetCode(address).Length; + stack.PushUInt32(contractCodeSize); + if (contractCodeSize == 0) + { + stack.PushOne(); + } + else + { + stack.PushZero(); + } + + programCounter += 3; + } + +} +internal class EmulatedStaticJump : InstructionChunk +{ + public string Name => nameof(EmulatedStaticJump); + public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMP]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.Mid; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; + if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpdestionation; + } + else + { + result.ExceptionType = EvmExceptionType.InvalidJumpDestination; + } + } + +} +internal class EmulatedStaticCJump : InstructionChunk +{ + public string Name => nameof(EmulatedStaticCJump); + public byte[] Pattern => [(byte)Instruction.PUSH2, (byte)Instruction.JUMPI]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.High; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.PopUInt256(out UInt256 condition); + int jumpdestionation = (vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << 8) | vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]; + if (!condition.IsZero) + { + if (jumpdestionation < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpdestionation] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpdestionation; + } + else + { + result.ExceptionType = EvmExceptionType.InvalidJumpDestination; + } + } + else + { + programCounter += 4; + } + } +} diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index ada25990693..c1a2dc1cbc6 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -242,7 +242,6 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.JUMPI] = new(GasCostOf.High, 0, 2, 0), - [Instruction.SUB] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.DUP1] = new(GasCostOf.VeryLow, 0, 1, 2), [Instruction.DUP2] = new(GasCostOf.VeryLow, 0, 2, 3), diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 746ab6d157a..44055cfba33 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -73,9 +73,6 @@ public VirtualMachine( _evm = logger.IsTrace ? new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger) : new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, _config, logger); - - IlAnalyzer.CompoundOpThreshold = _config.PatternMatchingThreshold; - IlAnalyzer.IlCompilerThreshold = _config.JittingThreshold; } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -640,6 +637,11 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM { _state.IncrementNonce(env.ExecutingAccount); } + + if (_vmConfig.IsVmOptimizationEnabled) + { + vmState.Env.CodeInfo.NoticeExecution(_vmConfig); + } } if (env.CodeInfo.MachineCode.Length == 0) @@ -651,10 +653,6 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM goto Empty; } - if (_vmConfig.IsVmOptimizationEnabled) - { - vmState.Env.CodeInfo.NoticeExecution(_txTracer); - } vmState.InitStacks(); EvmStack stack = new(vmState.DataStack.AsSpan(), vmState.DataStackHead, _txTracer); @@ -778,6 +776,11 @@ private CallResult ExecuteCode= codeLength) + { + goto EmptyReturnNoTrace; + } } Instruction instruction = (Instruction)code[programCounter]; From 4c62e61a8734a02ae1123a3fb54f3e429ea75741 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 23 Sep 2024 14:27:29 +0100 Subject: [PATCH 081/146] fix ws --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 8 ++++---- src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 4 ++-- .../Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs | 2 +- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index a61eedec2bf..f4073b3dff2 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -90,7 +90,7 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW } } - internal class TestBlockChain: VirtualMachineTestsBase + internal class TestBlockChain : VirtualMachineTestsBase { protected IVMConfig config; public TestBlockChain(IVMConfig config) @@ -322,7 +322,7 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); - if(testcase.opcode is not null) + if (testcase.opcode is not null) { Assert.That(HasIlvmTraces, Is.True); } @@ -467,7 +467,7 @@ public void JIT_invalid_opcode_results_in_failure() } var failedTraces = accumulatedTraces.Where(tr => tr.Failed && tr.Entries.Any(subtr => subtr.Error == EvmExceptionType.BadInstruction.ToString())).ToList(); - Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); + Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); } [Test] @@ -565,7 +565,7 @@ public void Pure_Opcode_Emition_Coveraga() .EXTCODESIZE(Address.SystemUser) .DUPx(1) .ISZERO() - .Done); + .Done); } public static IEnumerable<(Instruction?, byte[])> GeJitBytecodesSamples() diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 510372a91fe..3ab93d3e4d6 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -37,7 +37,7 @@ public async void NoticeExecution(IVMConfig vmConfig) ? IlInfo.ILMode.SubsegmentsCompiling : IlInfo.ILMode.NoIlvm; - if(mode == IlInfo.ILMode.NoIlvm) + if (mode == IlInfo.ILMode.NoIlvm) return; await IlAnalyzer.StartAnalysis(this, mode).ConfigureAwait(false); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 937a98fdab2..02486f96c80 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -86,7 +86,7 @@ private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, IlInfo ilinfo) { - if(codeData.Item1.Length == 0) + if (codeData.Item1.Length == 0) { return; } @@ -122,7 +122,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il var lastOp = segment[^1]; var segmentName = GenerateName(firstOp.ProgramCounter..(lastOp.ProgramCounter + lastOp.Metadata.AdditionalBytes)); - ilinfo.Segments.GetOrAdd((ushort)segment[0].ProgramCounter,CompileSegment(segmentName, segment, codeData.Item2)); + ilinfo.Segments.GetOrAdd((ushort)segment[0].ProgramCounter, CompileSegment(segmentName, segment, codeData.Item2)); } ilinfo.Mode |= IlInfo.ILMode.SubsegmentsCompiling; diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs index 1d2a1b397ac..f3acfec6172 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/GethLikeTxTracer.cs @@ -100,7 +100,7 @@ public override void ReportPredefinedPatternExecution(long gas, int pc, string s public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) { - if(IsTracingCompiledSegments) + if (IsTracingCompiledSegments) return; if (CurrentTraceEntry is not null) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index b677d21c3ed..8f0cbae5d1a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -777,7 +777,7 @@ private CallResult ExecuteCode= codeLength) + if (programCounter >= codeLength) { goto EmptyReturnNoTrace; } From 661e74ab2feda795bbd0336011ca0abd09d2ab61 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 24 Sep 2024 00:29:47 +0100 Subject: [PATCH 082/146] modify tests parameters --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index f4073b3dff2..8f245e3e644 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -277,17 +277,18 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode) testcase) { + int repeatCount = 32; + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); var address = standardChain.InsertCode(testcase.bytecode); TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = int.MaxValue, IsPatternMatchingEnabled = false, - JittingThreshold = 9, + JittingThreshold = repeatCount + 1, IsJitEnabled = true }); enhancedChain.InsertCode(testcase.bytecode); - int repeatCount = 8; var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); @@ -333,11 +334,13 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by [Test, TestCaseSource(nameof(GePatBytecodesSamples))] public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) { + int repeatCount = 32; + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); var address = standardChain.InsertCode(testcase.bytecode); TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = 9, + PatternMatchingThreshold = repeatCount + 1, IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, IsJitEnabled = false @@ -359,12 +362,12 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) .STOP() .Done; - for (var i = 0; i < 8; i++) + for (var i = 0; i <= repeatCount; i++) { standardChain.Execute(bytecode, tracer1); } - for (var i = 0; i < 8; i++) + for (var i = 0; i <= repeatCount; i++) { enhancedChain.Execute(bytecode, tracer2); } From 6e744c2eb552c4c5343080918473f9418c8e0803 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 25 Sep 2024 17:21:29 +0100 Subject: [PATCH 083/146] fix bugs update tests --- .../CodeAnalysis/IlEvmTests.cs | 62 ++++++++--- .../CodeAnalysis/IL/ILCompiler.cs | 100 +++++++++++++++--- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 40 ++++++- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 26 +++++ src/Nethermind/Nethermind.Evm/Instruction.cs | 7 +- .../Nethermind.Evm/VirtualMachine.cs | 4 +- 6 files changed, 196 insertions(+), 43 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 8f245e3e644..fc87b49654f 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -274,6 +274,16 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() Assert.Greater(accumulatedTraces.Count, 0); } + [Test] + public void All_Opcodes_Have_Metadata() + { + Instruction[] instructions = System.Enum.GetValues(); + foreach (var opcode in instructions) + { + Assert.That(OpcodeMetadata.Operations.ContainsKey(opcode), Is.True); + } + } + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode) testcase) { @@ -293,24 +303,23 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - var bytecode = - Prepare.EvmCode - .JUMPDEST() - .Call(address, 10) - .POP() - .GAS() - .PushData(1000) - .LT() - .JUMPI(0) - .STOP() - .Done; - - for (var i = 0; i <= repeatCount; i++) + var bytecode = Prepare.EvmCode + .JUMPDEST() + .Call(address, 100) + .POP() + .PushData(1000) + .GAS() + .GT() + .JUMPI(0) + .STOP() + .Done; + + for (var i = 0; i < repeatCount * 2; i++) { standardChain.Execute(bytecode, tracer1); } - for (var i = 0; i <= repeatCount; i++) + for (var i = 0; i < repeatCount * 2; i++) { enhancedChain.Execute(bytecode, tracer2); } @@ -321,6 +330,13 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var actual = standardChain.StateRoot; var expected = enhancedChain.StateRoot; + var ilvm_callsComp = ilvm_traces.Entries.Where(tr => tr.Opcode == "CALL"); + var norm_callsComp = normal_traces.Entries.Where(tr => tr.Opcode == "CALL"); + + var zipped = ilvm_callsComp.Zip(norm_callsComp, (ilvm, norm) => (ilvm, norm)).ToList(); + + var indexOfChange = zipped.FindIndex(pair => pair.ilvm.Gas != pair.norm.Gas); + var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); if (testcase.opcode is not null) @@ -362,12 +378,12 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) .STOP() .Done; - for (var i = 0; i <= repeatCount; i++) + for (var i = 0; i < repeatCount * 2; i++) { standardChain.Execute(bytecode, tracer1); } - for (var i = 0; i <= repeatCount; i++) + for (var i = 0; i < repeatCount * 2; i++) { enhancedChain.Execute(bytecode, tracer2); } @@ -612,6 +628,18 @@ public void Pure_Opcode_Emition_Coveraga() .PushSingle(23) .PushSingle(7) .EXP() + .PushSingle(0) + .PushSingle(7) + .EXP() + .PushSingle(1) + .PushSingle(7) + .EXP() + .PushSingle(1) + .PushSingle(0) + .EXP() + .PushSingle(1) + .PushSingle(1) + .EXP() .Done); yield return (Instruction.MOD, Prepare.EvmCode @@ -647,8 +675,8 @@ public void Pure_Opcode_Emition_Coveraga() .Done); yield return (Instruction.GT, Prepare.EvmCode - .PushSingle(23) .PushSingle(7) + .PushSingle(23) .GT() .Done); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5c635ae2d7c..906bc82fcd3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -3,6 +3,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.IL; using Nethermind.Evm.Tracing; @@ -135,9 +136,9 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.MarkLabel(jumpDestinations[op.ProgramCounter]); method.LoadConstant(op.ProgramCounter); method.StoreLocal(programCounter); - continue; } + // check if opcode is activated in current spec method.LoadArgument(4); method.LoadConstant((byte)op.Operation); @@ -148,25 +149,31 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); method.StoreLocal(programCounter); - // load gasAvailable - method.LoadLocal(gasAvailable); + if(op.Metadata.GasCost != 0) + { + // load gasAvailable + method.LoadLocal(gasAvailable); - // get pc gas cost - method.LoadConstant(op.Metadata.GasCost); + // get pc gas cost + method.LoadConstant(op.Metadata.GasCost); - // subtract the gas cost - method.Subtract(); - // check if gas is available - method.Duplicate(); - method.StoreLocal(gasAvailable); - method.LoadConstant((long)0); + // subtract the gas cost + method.Subtract(); + // check if gas is available + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); - // if gas is not available, branch to out of gas - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + // if gas is not available, branch to out of gas + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + } // else emit switch (op.Operation) { + case Instruction.JUMPDEST: + // we do nothing + break; case Instruction.STOP: method.LoadArgument(0); method.LoadConstant(true); @@ -420,7 +427,62 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EXP: - EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); + Label powerIsZero = method.DefineLabel(); + Label baseIsOneOrZero = method.DefineLabel(); + Label endOfExpImpl = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Duplicate(); + method.Call(Word.LeaddingZeroProp); + method.StoreLocal(uint64A); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + + method.LoadLocalAddress(uint256B); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(powerIsZero); + + // load spec + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExpByteCost))); + method.LoadConstant((long)32); + method.LoadLocal(uint64A); + method.Subtract(); + method.Multiply(); + method.Subtract(); + method.StoreLocal(gasAvailable); + + method.LoadLocalAddress(uint256A); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZeroOrOne)).GetMethod!); + method.BranchIfTrue(baseIsOneOrZero); + + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256R); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + method.Branch(endOfExpImpl); + + method.MarkLabel(powerIsZero); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadConstant(1); + method.StoreField(Word.Byte0Field); + method.Branch(endOfExpImpl); + + method.MarkLabel(baseIsOneOrZero); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(uint256A); + method.Call(Word.SetUInt256); + method.Branch(endOfExpImpl); + + method.MarkLabel(endOfExpImpl); + method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.LT: EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); @@ -631,8 +693,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); method.LoadConstant(GasCostOf.Memory); method.Multiply(); - method.LoadConstant(GasCostOf.VeryLow); - method.Add(); method.Subtract(); method.StoreLocal(gasAvailable); @@ -1252,7 +1312,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(0); method.LoadLocal(uint32A); method.Convert(); - method.Call(typeof(MemoryExtensions).GetMethod(nameof(MemoryExtensions.AsSpan), [typeof(byte[]), typeof(int), typeof(int)])); + method.Call(typeof(System.MemoryExtensions).GetMethod(nameof(System.MemoryExtensions.AsSpan), [typeof(byte[]), typeof(int), typeof(int)])); method.LoadLocalAddress(localSpan); method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); break; @@ -1566,6 +1626,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); break; case Instruction.BALANCE: + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetBalanceCost))); + method.Subtract(); + method.StoreLocal(gasAvailable); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index c8cfdb9f4ab..acd9e43a592 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -74,8 +74,11 @@ public bool TryExecute(EvmState vmState, ulong chainId, re var executionResult = new ILChunkExecutionResult(); if (Mode.HasFlag(ILMode.SubsegmentsCompiling) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { + vmState.DataStackHead = stack.Head; + if (typeof(TTracingInstructions) == typeof(IsTracing)) - tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, ctx.Name, vmState.Env); + StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, ctx); + var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); @@ -90,14 +93,20 @@ public bool TryExecute(EvmState vmState, ulong chainId, re executionResult.ExceptionType = ilvmState.EvmException; executionResult.ReturnData = ilvmState.ReturnBuffer; - vmState.DataStackHead = ilvmState.StackHead; stack.Head = ilvmState.StackHead; + + if (typeof(TTracingInstructions) == typeof(IsTracing)) + tracer.ReportOperationRemainingGas(gasAvailable); } else if (Mode.HasFlag(ILMode.PatternMatching) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { if (typeof(TTracingInstructions) == typeof(IsTracing)) - tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, chunk.Name, vmState.Env); + StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, chunk); + chunk.Invoke(vmState, blockHashProvider, worldState, codeinfoRepository, spec, ref programCounter, ref gasAvailable, ref stack, ref executionResult); + + if (typeof(TTracingInstructions) == typeof(IsTracing)) + tracer.ReportOperationRemainingGas(gasAvailable); } else { @@ -107,4 +116,29 @@ public bool TryExecute(EvmState vmState, ulong chainId, re result = executionResult; return true; } + + private static void StartTracingSegment(in EvmState vmState, in EvmStack stack, ITxTracer tracer, int programCounter, long gasAvailable, T chunk) + where TTracingInstructions : struct, VirtualMachine.IIsTracing + { + if(chunk is SegmentExecutionCtx segment) + { + tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, segment.Name, vmState.Env); + } + else if(chunk is InstructionChunk patternHandler) + { + tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, patternHandler.Name, vmState.Env); + } + + if (tracer.IsTracingMemory) + { + tracer.SetOperationMemory(vmState.Memory.GetTrace()); + tracer.SetOperationMemorySize(vmState.Memory.Size); + } + + if (tracer.IsTracingStack) + { + Memory stackMemory = vmState.DataStack.AsMemory().Slice(0, stack.Head * EvmStack.WordSize); + tracer.SetOperationStack(new TraceStack(stackMemory)); + } + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 0aae551c2c0..a82c3052213 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -11,6 +11,7 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -230,6 +231,30 @@ public ulong ULong0 } } + + public long LeadingZeros + { + get + { + // use _long and BitOperations to count leading zeros + if (_ulong3 != 0) + { + return BitOperations.LeadingZeroCount(_ulong3); + } + if (_ulong2 != 0) + { + return 64 + BitOperations.LeadingZeroCount(_ulong2); + } + if (_ulong1 != 0) + { + return 128 + BitOperations.LeadingZeroCount(_ulong1); + } + return 192 + BitOperations.LeadingZeroCount(_ulong0); + + } + } + + public static readonly MethodInfo LeaddingZeroProp = typeof(Word).GetProperty(nameof(LeadingZeros))!.GetMethod; public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(_uByte0)); public static readonly MethodInfo GetInt0 = typeof(Word).GetProperty(nameof(Int0))!.GetMethod; @@ -268,4 +293,5 @@ public static explicit operator Word(Span span) return result; } } + } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index c1a2dc1cbc6..64db74af871 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -305,7 +305,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.KECCAK256] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.ADDRESS] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.BALANCE] = new(GasCostOf.Balance, 0, 1, 1), + [Instruction.BALANCE] = new(0, 0, 1, 1), // we need call GetBalanceCost in ILCompiler [Instruction.ORIGIN] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLER] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLVALUE] = new(GasCostOf.Base, 0, 0, 1), @@ -341,7 +341,6 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.SLOAD] = new(GasCostOf.SLoad, 0, 1, 1), [Instruction.SSTORE] = new(GasCostOf.SSet, 0, 2, 0), [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), - [Instruction.JUMPI] = new(GasCostOf.Mid, 0, 2, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.GAS] = new(GasCostOf.Base, 0, 0, 1), @@ -360,11 +359,11 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.CREATE] = new(GasCostOf.Create, 0, 3, 1), [Instruction.CALL] = new(GasCostOf.Call, 0, 7, 1), [Instruction.CALLCODE] = new(GasCostOf.Call, 0, 7, 1), - [Instruction.RETURN] = new(GasCostOf.Base, 0, 2, 0), + [Instruction.RETURN] = new(0, 0, 2, 0), // has memory costs [Instruction.DELEGATECALL] = new(GasCostOf.Call, 0, 6, 1), [Instruction.CREATE2] = new(GasCostOf.Create, 0, 4, 1), [Instruction.STATICCALL] = new(GasCostOf.Call, 0, 6, 1), - [Instruction.REVERT] = new(GasCostOf.Base, 0, 2, 0), + [Instruction.REVERT] = new(0, 0, 2, 0), // has memory costs [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct, 0, 1, 0), }.ToFrozenDictionary(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index c7e84ed0db4..3a870dff27e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -739,13 +739,13 @@ private CallResult ExecuteCode)chunkExecutionResult.Value.ReturnData).ToArray(); goto DataReturn; } if (chunkExecutionResult.Value.ShouldRevert) { isRevert = true; - returnData = chunkExecutionResult.Value.ReturnData; + returnData = ((ReadOnlyMemory)chunkExecutionResult.Value.ReturnData).ToArray(); goto DataReturn; } if (chunkExecutionResult.Value.ShouldStop) From 20a0d50dfd607af25f95b7d61fac3e3bb40b3144 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 26 Sep 2024 02:42:11 +0100 Subject: [PATCH 084/146] fixed failing tests (EXP, Pattern) --- .../CodeAnalysis/IlEvmTests.cs | 6 ++--- .../CodeAnalysis/IL/ILCompiler.cs | 5 +++- .../CodeAnalysis/IL/PredefinedPatterns.cs | 11 +++++--- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 26 ++++++++----------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index fc87b49654f..2b869005f02 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -369,7 +369,7 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) var bytecode = Prepare.EvmCode .JUMPDEST() - .Call(address, 10) + .Call(address, 100) .POP() .GAS() .PushData(1000) @@ -573,14 +573,14 @@ public void Pure_Opcode_Emition_Coveraga() .JUMP() .JUMPDEST() .Done); - yield return (typeof(IsContractCheck), Prepare.EvmCode + yield return (typeof(MethodSelector), Prepare.EvmCode .PushData(0) .PushData(23) .MSTORE() .CALLVALUE() .DUPx(1) .Done); - yield return (typeof(MethodSelector), Prepare.EvmCode + yield return (typeof(IsContractCheck), Prepare.EvmCode .EXTCODESIZE(Address.SystemUser) .DUPx(1) .ISZERO() diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 906bc82fcd3..6fd14711c98 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -436,7 +436,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256A); method.StackLoadPrevious(stack, head, 2); method.Duplicate(); - method.Call(Word.LeaddingZeroProp); + method.Call(Word.LeadingZeroProp); method.StoreLocal(uint64A); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); @@ -455,7 +455,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Subtract(); method.Multiply(); method.Subtract(); + method.Duplicate(); method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadLocalAddress(uint256A); method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZeroOrOne)).GetMethod!); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs index e07f23ce4d4..4b74ae09870 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs @@ -11,6 +11,7 @@ using System.Text; using System.Threading.Tasks; using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; +using Nethermind.Evm.Tracing; namespace Nethermind.Evm.CodeAnalysis.IL.Patterns; @@ -39,7 +40,7 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW byte value = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; byte location = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; - VirtualMachine.UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, 0, 32); + VirtualMachine.UpdateMemoryCost(ref vmState.Memory, ref gasAvailable, location, 32); vmState.Memory.SaveByte(location, value); stack.PushUInt256(vmState.Env.Value); stack.PushUInt256(vmState.Env.Value); @@ -56,7 +57,7 @@ internal class IsContractCheck : InstructionChunk public long GasCost(EvmState vmState, IReleaseSpec spec) { - long gasCost = spec.GetExtCodeCost() + GasCostOf.VeryLow + GasCostOf.Base; + long gasCost = spec.GetExtCodeCost() + GasCostOf.VeryLow + GasCostOf.VeryLow; return gasCost; } @@ -72,7 +73,11 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW result.ExceptionType = EvmExceptionType.OutOfGas; Address address = stack.PopAddress(); - int contractCodeSize = worldState.GetCode(address).Length; + + if(!VirtualMachine.ChargeAccountAccessGas(ref gasAvailable, vmState, address, spec, NullTxTracer.Instance)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + int contractCodeSize = codeInfoRepository.GetCachedCodeInfo(worldState, address, spec).MachineCode.Length; stack.PushUInt32(contractCodeSize); if (contractCodeSize == 0) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index a82c3052213..b33442f969b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -232,29 +232,25 @@ public ulong ULong0 } - public long LeadingZeros + public unsafe long LeadingZeros { get { - // use _long and BitOperations to count leading zeros - if (_ulong3 != 0) - { - return BitOperations.LeadingZeroCount(_ulong3); - } - if (_ulong2 != 0) - { - return 64 + BitOperations.LeadingZeroCount(_ulong2); - } - if (_ulong1 != 0) + fixed (byte* ptr = _buffer) { - return 128 + BitOperations.LeadingZeroCount(_ulong1); + byte* end = ptr + 32; + byte* current = ptr; + while (current < end && *current == 0) + { + current++; + } + + return current - ptr; } - return 192 + BitOperations.LeadingZeroCount(_ulong0); - } } - public static readonly MethodInfo LeaddingZeroProp = typeof(Word).GetProperty(nameof(LeadingZeros))!.GetMethod; + public static readonly MethodInfo LeadingZeroProp = typeof(Word).GetProperty(nameof(LeadingZeros))!.GetMethod; public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(_uByte0)); public static readonly MethodInfo GetInt0 = typeof(Word).GetProperty(nameof(Int0))!.GetMethod; From ee86b49a120e6e0e964ade64340c440f7b7a5efe Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 26 Sep 2024 02:43:20 +0100 Subject: [PATCH 085/146] ws fixes --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 2 +- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 4 ++-- .../Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 6fd14711c98..45b5d74d308 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -149,7 +149,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); method.StoreLocal(programCounter); - if(op.Metadata.GasCost != 0) + if (op.Metadata.GasCost != 0) { // load gasAvailable method.LoadLocal(gasAvailable); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index acd9e43a592..f2d651189c6 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -120,11 +120,11 @@ public bool TryExecute(EvmState vmState, ulong chainId, re private static void StartTracingSegment(in EvmState vmState, in EvmStack stack, ITxTracer tracer, int programCounter, long gasAvailable, T chunk) where TTracingInstructions : struct, VirtualMachine.IIsTracing { - if(chunk is SegmentExecutionCtx segment) + if (chunk is SegmentExecutionCtx segment) { tracer.ReportCompiledSegmentExecution(gasAvailable, programCounter, segment.Name, vmState.Env); } - else if(chunk is InstructionChunk patternHandler) + else if (chunk is InstructionChunk patternHandler) { tracer.ReportPredefinedPatternExecution(gasAvailable, programCounter, patternHandler.Name, vmState.Env); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs index 4b74ae09870..7a1be172761 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs @@ -74,7 +74,7 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW Address address = stack.PopAddress(); - if(!VirtualMachine.ChargeAccountAccessGas(ref gasAvailable, vmState, address, spec, NullTxTracer.Instance)) + if (!VirtualMachine.ChargeAccountAccessGas(ref gasAvailable, vmState, address, spec, NullTxTracer.Instance)) result.ExceptionType = EvmExceptionType.OutOfGas; int contractCodeSize = codeInfoRepository.GetCachedCodeInfo(worldState, address, spec).MachineCode.Length; From bf59fd006621a12eb5531be4da10472fb24dd722 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 27 Sep 2024 12:02:23 +0100 Subject: [PATCH 086/146] - Added pattern addition in Initialize blockchain --- .../CodeAnalysis/IlEvmTests.cs | 11 ++++++ .../CodeAnalysis/IL/IlAnalyzer.cs | 34 ++++++++++++------- .../CodeAnalysis/IL/InstructionChunk.cs | 2 +- .../Steps/InitializeBlockchain.cs | 2 ++ 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 2b869005f02..56412e8e48d 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -161,6 +161,7 @@ public Hash256 StateRoot internal class IlEvmTests : TestBlockChain { private const string AnalyzerField = "_analyzer"; + private const string PatternField = "_patterns"; [SetUp] public override void Setup() @@ -952,6 +953,16 @@ public void Pure_Opcode_Emition_Coveraga() .Done); } + [Test] + public void ILAnalyzer_Initialize_Add_All_Patterns() + { + IlAnalyzer.Initialize(); + // get static field _patterns from IlAnalyzer + var patterns = typeof(IlAnalyzer).GetField(PatternField, BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Dictionary; + + Assert.That(patterns.Count, Is.GreaterThan(0)); + } + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) testcase) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 02486f96c80..b5db192b9cf 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -19,38 +19,46 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// /// Provides /// -internal static class IlAnalyzer +public static class IlAnalyzer { - private static Dictionary Patterns = new Dictionary(); - public static void AddPattern(InstructionChunk handler) + private static Dictionary _patterns = new Dictionary(); + internal static void AddPattern(InstructionChunk handler) { - lock (Patterns) + lock (_patterns) { - Patterns[handler.GetType()] = handler; + _patterns[handler.GetType()] = handler; } } - public static void AddPattern() where T : InstructionChunk + internal static void AddPattern() where T : InstructionChunk { var handler = Activator.CreateInstance(); - lock (Patterns) + lock (_patterns) { - Patterns[typeof(T)] = handler; + _patterns[typeof(T)] = handler; } } - public static T GetPatternHandler() where T : InstructionChunk + internal static T GetPatternHandler() where T : InstructionChunk { - lock (Patterns) + lock (_patterns) { - return (T)Patterns[typeof(T)]; + return (T)_patterns[typeof(T)]; } } + public static void Initialize() + { + Type[] InstructionChunks = typeof(InstructionChunk).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(InstructionChunk))).ToArray(); + foreach (var chunkType in InstructionChunks) + { + _patterns[chunkType] = (InstructionChunk)Activator.CreateInstance(chunkType); + } + } /// /// Starts the analyzing in a background task and outputs the value in the . /// thou /// The destination output. - public static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) + internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) { return Task.Run(() => Analysis(codeInfo, mode)); } @@ -131,7 +139,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) { var (strippedBytecode, data) = StripByteCode(machineCode.Span); - foreach (var (_, chunkHandler) in Patterns) + foreach (var (_, chunkHandler) in _patterns) { for (int i = 0; i < strippedBytecode.Length - chunkHandler.Pattern.Length + 1; i++) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs index a6bf35a390b..7501abfe6c7 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// Represents a chunk of s that is optimized and ready to be run in an efficient manner. /// /// -interface InstructionChunk +internal interface InstructionChunk { string Name { get; } byte[] Pattern { get; } diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 26754abb3f7..670fdebe010 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -22,6 +22,7 @@ using Nethermind.Core; using Nethermind.Core.Attributes; using Nethermind.Evm; +using Nethermind.Evm.CodeAnalysis.IL; using Nethermind.Evm.Config; using Nethermind.Evm.TransactionProcessing; using Nethermind.JsonRpc.Modules.Eth.GasPrice; @@ -186,6 +187,7 @@ protected VirtualMachine CreateVirtualMachine(CodeInfoRepository codeInfoReposit BlockhashProvider blockhashProvider = new( _api.BlockTree, _api.SpecProvider, _api.WorldState, _api.LogManager); + IlAnalyzer.Initialize(); VirtualMachine virtualMachine = new( blockhashProvider, _api.SpecProvider, From 2f2f5623da740d33aba6334823e935cd5b0e0ccd Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 27 Sep 2024 12:06:06 +0100 Subject: [PATCH 087/146] only add patterns if IlvmPatternMode is activated --- .../Nethermind.Init/Steps/InitializeBlockchain.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 670fdebe010..f73be03efc6 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -187,7 +187,11 @@ protected VirtualMachine CreateVirtualMachine(CodeInfoRepository codeInfoReposit BlockhashProvider blockhashProvider = new( _api.BlockTree, _api.SpecProvider, _api.WorldState, _api.LogManager); - IlAnalyzer.Initialize(); + if(_api.VMConfig is not null && _api.VMConfig.IsPatternMatchingEnabled) + { + IlAnalyzer.Initialize(); + } + VirtualMachine virtualMachine = new( blockhashProvider, _api.SpecProvider, From 95631ccd27af63dfa40fd2d9f164f84d4176a57a Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 27 Sep 2024 23:54:14 +0100 Subject: [PATCH 088/146] moved stack checks happen before attempting pop/push --- .../CodeAnalysis/IlEvmTests.cs | 1 - .../CodeAnalysis/IL/ILCompiler.cs | 197 ++++++++++-------- .../CodeAnalysis/IL/ILExtensions.cs | 18 +- .../CodeAnalysis/IL/IlAnalyzer.cs | 2 +- 4 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 56412e8e48d..3b51ef99683 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -89,7 +89,6 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW programCounter += 5; } } - internal class TestBlockChain : VirtualMachineTestsBase { protected IVMConfig config; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 45b5d74d308..5a7898a3d85 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -168,6 +168,23 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); } + if(op.Metadata.StackBehaviorPop > 0) + { + method.LoadLocal(head); + method.LoadConstant(op.Metadata.StackBehaviorPop); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.StackUnderflow]); + } + + if(op.Metadata.StackBehaviorPush > 0) + { + int delta = op.Metadata.StackBehaviorPush - op.Metadata.StackBehaviorPop; + method.LoadLocal(head); + method.LoadConstant(delta); + method.Add(); + method.LoadConstant(EvmStack.MaxStackSize); + method.BranchIfGreaterOrEqual(evmExceptionLabels[EvmExceptionType.StackOverflow]); + } + // else emit switch (op.Operation) { @@ -189,13 +206,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.NOT: method.Load(stack, head); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); + method.StackPop(head); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256R); @@ -224,11 +241,11 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(jumpTable); method.MarkLabel(noJump); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); break; case Instruction.PUSH0: method.CleanWord(stack, head); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -271,7 +288,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(op.Arguments.Value); method.LoadElement(); method.Call(Word.SetArray); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.ADD: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); @@ -440,7 +457,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint64A); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadLocalAddress(uint256B); method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); @@ -485,7 +502,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(endOfExpImpl); method.MarkLabel(endOfExpImpl); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.LT: EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); @@ -507,17 +524,17 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetIsZero); method.StoreLocal(byte8A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); // we convert the result to a Uint256 and store it in the stack method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(byte8A); method.StoreField(Word.Byte0Field); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.POP: - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); + method.StackPop(head); break; case Instruction.DUP1: case Instruction.DUP2: @@ -540,7 +557,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, count); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -583,14 +600,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadConstant(code.Length); method.Call(Word.SetInt0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.PC: method.CleanWord(stack, head); method.Load(stack, head); method.LoadLocal(programCounter); method.Call(Word.SetUInt0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.COINBASE: method.CleanWord(stack, head); @@ -600,7 +617,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); method.Call(Word.SetAddress); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.TIMESTAMP: method.CleanWord(stack, head); @@ -610,7 +627,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.NUMBER: method.CleanWord(stack, head); @@ -620,7 +637,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.GASLIMIT: method.CleanWord(stack, head); @@ -630,7 +647,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.CALLER: method.CleanWord(stack, head); @@ -639,7 +656,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.ADDRESS: method.CleanWord(stack, head); @@ -648,7 +665,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.ORIGIN: method.CleanWord(stack, head); @@ -657,7 +674,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); method.Call(Word.SetAddress); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.CALLVALUE: method.CleanWord(stack, head); @@ -666,7 +683,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.GASPRICE: method.CleanWord(stack, head); @@ -675,7 +692,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.CALLDATACOPY: Label endOfOpcode = method.DefineLabel(); @@ -689,7 +706,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); + method.StackPop(head, 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -735,7 +752,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.CleanWord(stack, head); method.Load(stack, head); @@ -750,7 +767,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.LoadField(GetFieldInfo(typeof(ZeroPaddedSpan), nameof(ZeroPaddedSpan.Span))); method.Call(Word.SetSpan); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.CALLDATASIZE: method.CleanWord(stack, head); @@ -759,7 +776,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.MSIZE: method.CleanWord(stack, head); @@ -769,7 +786,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.MSTORE: method.StackLoadPrevious(stack, head, 1); @@ -778,7 +795,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -807,7 +824,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.LoadField(Word.Byte0Field); method.StoreLocal(byte8A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -835,7 +852,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -858,7 +875,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.MCOPY: method.StackLoadPrevious(stack, head, 1); @@ -873,7 +890,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); + method.StackPop(head, 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -914,7 +931,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256B); @@ -940,7 +957,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); method.Call(Word.SetKeccak); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.BYTE: // load a @@ -950,7 +967,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localReadonOnlySpan); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); Label pushZeroLabel = method.DefineLabel(); Label endOfInstructionImpl = method.DefineLabel(); @@ -972,14 +989,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); method.LoadIndirect(); method.StoreField(Word.Byte0Field); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); method.Branch(endOfInstructionImpl); method.MarkLabel(pushZeroLabel); method.CleanWord(stack, head); method.Load(stack, head); method.Call(Word.SetToZero); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); method.MarkLabel(endOfInstructionImpl); break; @@ -995,7 +1012,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); + method.StackPop(head, 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -1042,7 +1059,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocal(gasAvailable); method.Call(Word.SetULong0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.RETURNDATASIZE: method.CleanWord(stack, head); @@ -1051,7 +1068,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.RETURNDATACOPY: endOfOpcode = method.DefineLabel(); @@ -1065,7 +1082,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 3); + method.StackPop(head, 3); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -1115,7 +1132,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -1155,7 +1172,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.BLOBBASEFEE: method.CleanWord(stack, head); @@ -1165,7 +1182,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); method.Call(GetPropertyInfo(typeof(UInt256?), nameof(Nullable.Value), false, out _)); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.PREVRANDAO: Label isPostMergeBranch = method.DefineLabel(); @@ -1183,12 +1200,12 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); method.Branch(endOfOpcode); method.MarkLabel(isPostMergeBranch); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); method.MarkLabel(endOfOpcode); break; @@ -1199,7 +1216,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetInt0); method.StoreLocal(uint32A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); @@ -1233,7 +1250,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.SetToZero); method.MarkLabel(endOfOpcode); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.BLOCKHASH: Label blockHashReturnedNull = method.DefineLabel(); @@ -1245,7 +1262,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(long.MaxValue); method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); method.StoreLocal(int64A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadArgument(1); method.LoadArgument(0); @@ -1279,7 +1296,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.SIGNEXTEND: Label signIsNegative = method.DefineLabel(); @@ -1291,7 +1308,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localSpan); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadConstant((uint)31); method.LoadLocal(uint32A); @@ -1341,7 +1358,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.GetArray); method.StoreLocal(localArray); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1359,7 +1376,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1377,7 +1394,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.SSTORE: @@ -1387,7 +1404,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetSpan); method.StoreLocal(localReadonOnlySpan); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 2); + method.StackPop(head, 2); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); @@ -1427,7 +1444,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadArgument(0); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1455,7 +1472,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Load(stack, head); method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.EXTCODESIZE: @@ -1468,7 +1485,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1494,7 +1511,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.EXTCODECOPY: @@ -1518,7 +1535,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 4); method.Call(Word.GetUInt256); method.StoreLocal(uint256C); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 4); + method.StackPop(head, 4); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256C); @@ -1585,7 +1602,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1615,7 +1632,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); method.Call(Word.SetKeccak); method.MarkLabel(endOfOpcode); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.SELFBALANCE: method.CleanWord(stack, head); @@ -1626,7 +1643,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; case Instruction.BALANCE: method.LoadLocal(gasAvailable); @@ -1638,7 +1655,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetAddress); method.StoreLocal(address); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], 1); + method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); method.LoadArgument(0); @@ -1656,7 +1673,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocal(address); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); method.Call(Word.SetUInt256); - method.StackPush(head, evmExceptionLabels[EvmExceptionType.StackOverflow]); + method.StackPush(head); break; default: throw new NotSupportedException(); @@ -1711,9 +1728,9 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(Word.GetInt0); method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow]); + method.StackPop(head); - method.StackPop(head, evmExceptionLabels[EvmExceptionType.StackUnderflow], consumeJumpCondition); + method.StackPop(head, consumeJumpCondition); method.LoadConstant(0); method.StoreLocal(consumeJumpCondition); @@ -1827,18 +1844,18 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.Call(Word.GetInt0); il.LoadLocalAddress(uint256R); il.Call(shiftOp); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, 1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, 1); il.MarkLabel(endOfOpcode); } @@ -1867,16 +1884,16 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow], 1); + il.StackPush(stack.idx, 1); il.Branch(endOfOpcode); il.MarkLabel(skipPop); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); il.LoadLocalAddress(locals[0]); il.Call(GetAsMethodInfo()); @@ -1887,7 +1904,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); il.Call(Word.SetToZero); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); il.Branch(endOfOpcode); // sign @@ -1898,7 +1915,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(GetAsMethodInfo()); il.LoadObject(); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); il.Branch(endOfOpcode); il.MarkLabel(endOfOpcode); @@ -1914,7 +1931,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // invoke op on the uint256 il.LoadLocalAddress(locals[0]); @@ -1927,7 +1944,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) @@ -1939,7 +1956,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // invoke op on the uint256 il.LoadLocalAddress(locals[0]); @@ -1956,7 +1973,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, Dictionary exceptions, params Local[] locals) @@ -1970,7 +1987,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // invoke op on the uint256 il.LoadLocalAddress(locals[0]); @@ -2003,7 +2020,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -2017,7 +2034,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -2036,7 +2053,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -2050,7 +2067,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(locals[1]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -2072,7 +2089,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Action, Label, Local[]> customHandling, Dictionary exceptions, params Local[] locals) @@ -2089,7 +2106,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.StackLoadPrevious(stack.span, stack.idx, 3); il.Call(Word.GetUInt256); il.StoreLocal(locals[2]); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 3); + il.StackPop(stack.idx, 3); // incase of custom handling, we branch to the label customHandling?.Invoke(il, label, locals); @@ -2109,7 +2126,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.Load(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); - il.StackPush(stack.idx, exceptions[EvmExceptionType.StackOverflow]); + il.StackPush(stack.idx); } @@ -2172,7 +2189,7 @@ private static void EmitLogMethod( il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); il.StoreLocal(uint256B); // length - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], 2); + il.StackPop(stack.idx, 2); // UpdateMemoryCost il.LoadArgument(0); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -2218,7 +2235,7 @@ private static void EmitLogMethod( // Creat an LogEntry Object from Items on the Stack il.NewObject(typeof(LogEntry), typeof(Address), typeof(byte[]), typeof(Hash256[])); il.StoreLocal(logEntry); - il.StackPop(stack.idx, exceptions[EvmExceptionType.StackUnderflow], topicsCount); + il.StackPop(stack.idx, topicsCount); il.LoadArgument(0); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index c39039d1bcb..3fb8e5aa740 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -98,46 +98,34 @@ public static void CleanWord(this Emit il, Local local, Local idx) /// /// Advances the stack one word up. /// - public static void StackPush(this Emit il, Local idx, Sigil.Label stackOverflowLabel, int count = 1) + public static void StackPush(this Emit il, Local idx, int count = 1) { il.LoadLocal(idx); il.LoadConstant(count); il.Add(); il.StoreLocal(idx); - - il.LoadLocal(idx); - il.LoadConstant(1024); - il.BranchIfGreater(stackOverflowLabel); } /// /// Moves the stack words down. /// - public static void StackPop(this Emit il, Local idx, Sigil.Label stackUnderflowLabel, int count = 1) + public static void StackPop(this Emit il, Local idx, int count = 1) { il.LoadLocal(idx); il.LoadConstant(count); il.Subtract(); il.StoreLocal(idx); - - il.LoadLocal(idx); - il.LoadConstant(0); - il.BranchIfLess(stackUnderflowLabel); } /// /// Moves the stack words down. /// - public static void StackPop(this Emit il, Local local, Sigil.Label stackUnderflowLabel, Local count) + public static void StackPop(this Emit il, Local local, Local count) { il.LoadLocal(local); il.LoadLocal(count); il.Subtract(); il.StoreLocal(local); - - il.LoadLocal(local); - il.LoadConstant(0); - il.BranchIfLess(stackUnderflowLabel); } public static void WhileBranch(this Emit il, Local cond, Action, Local> action) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index b5db192b9cf..3112588e135 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -139,7 +139,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) { var (strippedBytecode, data) = StripByteCode(machineCode.Span); - foreach (var (_, chunkHandler) in _patterns) + foreach ((Type _, InstructionChunk chunkHandler) in _patterns) { for (int i = 0; i < strippedBytecode.Length - chunkHandler.Pattern.Length + 1; i++) { From 9f3b645e726ce41dce085a91603e79b271349974 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 28 Sep 2024 00:40:46 +0100 Subject: [PATCH 089/146] remove ammotised gas cost for now --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5a7898a3d85..67b904e3b96 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -124,8 +124,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method - - Dictionary gasCost = BuildCostLookup(code); for (int i = 0; i < code.Length; i++) { OpcodeInfo op = code[i]; From ea3837940ae474b4aebaeca702b8cbf8f12f1659 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 1 Oct 2024 13:07:31 +0100 Subject: [PATCH 090/146] updated tests fixed opcodes implementation fixed types mismatch added gas amortization added pc amortization refactored stack behavior (WIP) --- .../CodeAnalysis/IlEvmTests.cs | 1443 +++++----- .../CodeAnalysis/IL/ILCompiler.cs | 2505 +++++++++-------- .../CodeAnalysis/IL/ILExtensions.cs | 16 + src/Nethermind/Nethermind.Evm/Instruction.cs | 34 +- 4 files changed, 2181 insertions(+), 1817 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 3b51ef99683..2ba1373597a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -31,6 +31,7 @@ using Nethermind.Evm.CodeAnalysis.IL.Patterns; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Blockchain; +using Polly; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -116,6 +117,8 @@ public override void Setup() .PushData(23) .PushData(7) .ADD() + .MSTORE(0, Enumerable.Range(0, 32).Select(i => (byte)i).ToArray()) + .RETURN(0, 32) .STOP().Done; TestState.CreateAccount(Address.FromNumber(23), 1000000); TestState.InsertCode(Address.FromNumber(23), code, SpecProvider.GenesisSpec); @@ -176,782 +179,954 @@ public override void Setup() base.Setup(); } + public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() + { + yield return (null, Prepare.EvmCode + .Done); + yield return (typeof(EmulatedStaticCJump), Prepare.EvmCode + .PUSHx([1]) + .PUSHx([0, 7]) + .JUMPI() + .JUMPDEST() + .Done); + yield return (typeof(EmulatedStaticJump), Prepare.EvmCode + .PUSHx([0, 5]) + .JUMP() + .JUMPDEST() + .Done); + yield return (typeof(MethodSelector), Prepare.EvmCode + .PushData(0) + .PushData(23) + .MSTORE() + .CALLVALUE() + .DUPx(1) + .Done); + yield return (typeof(IsContractCheck), Prepare.EvmCode + .EXTCODESIZE(Address.SystemUser) + .DUPx(1) + .ISZERO() + .Done); + } - [Test] - public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() + public static IEnumerable<(Instruction?, byte[], EvmExceptionType)> GeJitBytecodesSamples() { - byte[] bytecode = - Prepare.EvmCode + yield return (null, Prepare.EvmCode + .Done, EvmExceptionType.None); + yield return (Instruction.PUSH32, Prepare.EvmCode + .PushSingle(1) + .Done, EvmExceptionType.None); + yield return (Instruction.ISZERO, Prepare.EvmCode + .ISZERO(7) + .ISZERO(0) + .ISZERO(7) + .Done, EvmExceptionType.None); + yield return (Instruction.SUB, Prepare.EvmCode .PushSingle(23) .PushSingle(7) - .ADD() - .PushSingle(42) - .PushSingle(5) - .ADD() - .Done; + .SUB() + .Done, EvmExceptionType.None); - CodeInfo codeInfo = new CodeInfo(bytecode); + yield return (Instruction.ADD, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .ADD() + .Done, EvmExceptionType.None); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); + yield return (Instruction.ADDMOD, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .PushSingle(5) + .ADDMOD() + .Done, EvmExceptionType.None); - codeInfo.IlInfo.Chunks.Count.Should().Be(2); - } + yield return (Instruction.MUL, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .MUL() + .Done, EvmExceptionType.None); + yield return (Instruction.EXP, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .EXP() + .PushSingle(0) + .PushSingle(7) + .EXP() + .PushSingle(1) + .PushSingle(7) + .EXP() + .PushSingle(1) + .PushSingle(0) + .EXP() + .PushSingle(1) + .PushSingle(1) + .EXP() + .Done, EvmExceptionType.None); - [Test] - public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() - { - byte[] bytecode = - Prepare.EvmCode + yield return (Instruction.MOD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) - .ADD() - .PushSingle(42) - .PushSingle(5) - .ADD() - .Call(Address.FromNumber(23), 10000) + .MOD() + .Done, EvmExceptionType.None); + + yield return (Instruction.DIV, Prepare.EvmCode .PushSingle(23) .PushSingle(7) - .ADD() - .PushSingle(42) - .PushSingle(5) - .ADD() - .STOP() - .Done; + .DIV() + .Done, EvmExceptionType.None); - CodeInfo codeInfo = new CodeInfo(bytecode); + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(0, ((UInt256)23).PaddedBytes(32)) + .MLOAD(0) + .Done, EvmExceptionType.None); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(0, ((UInt256)23).PaddedBytes(32)) + .MLOAD(0) + .Done, EvmExceptionType.None); - codeInfo.IlInfo.Segments.Count.Should().Be(2); - } + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(0, ((UInt256)23).PaddedBytes(32)) + .MCOPY(32, 0, 32) + .Done, EvmExceptionType.None); - [Test] - public void Execution_Swap_Happens_When_Pattern_Occurs() - { - var pattern1 = IlAnalyzer.GetPatternHandler(); - var pattern2 = IlAnalyzer.GetPatternHandler(); - var pattern3 = IlAnalyzer.GetPatternHandler(); + yield return (Instruction.EQ, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .EQ() + .Done, EvmExceptionType.None); - byte[] bytecode = - Prepare.EvmCode - .JUMPDEST() - .PushSingle(1000) - .GAS() - .LT() - .PUSHx([0, 26]) - .JUMPI() + yield return (Instruction.GT, Prepare.EvmCode + .PushSingle(7) + .PushSingle(23) + .GT() + .Done, EvmExceptionType.None); + + yield return (Instruction.LT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) - .ADD() - .POP() - .PushSingle(42) - .PushSingle(5) - .ADD() - .POP() - .PUSHx([0, 0]) - .JUMP() - .JUMPDEST() - .STOP() - .Done; + .LT() + .Done, EvmExceptionType.None); - /* - var hashcode = Keccak.Compute(bytecode); - var address = new Address(hashcode); - var spec = Prague.Instance; - TestState.CreateAccount(address, 1000000); - TestState.InsertCode(address, bytecode, spec); - */ - var accumulatedTraces = new List(); - for (int i = 0; i < config.PatternMatchingThreshold * 2; i++) - { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && !tr.IsPrecompiled.Value).ToList(); - accumulatedTraces.AddRange(traces); - } + yield return (Instruction.NOT, Prepare.EvmCode + .PushSingle(1) + .NOT() + .Done, EvmExceptionType.None); - Assert.Greater(accumulatedTraces.Count, 0); - } + yield return (Instruction.BLOBHASH, Prepare.EvmCode + .PushSingle(0) + .BLOBHASH() + .Done, EvmExceptionType.None); - [Test] - public void All_Opcodes_Have_Metadata() - { - Instruction[] instructions = System.Enum.GetValues(); - foreach (var opcode in instructions) - { - Assert.That(OpcodeMetadata.Operations.ContainsKey(opcode), Is.True); - } - } + yield return (Instruction.BLOCKHASH, Prepare.EvmCode + .BLOCKHASH(0) + .Done, EvmExceptionType.None); - [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] - public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode) testcase) - { - int repeatCount = 32; + yield return (Instruction.CALLDATACOPY, Prepare.EvmCode + .CALLDATACOPY(0, 0, 32) + .Done, EvmExceptionType.None); - TestBlockChain standardChain = new TestBlockChain(new VMConfig()); - var address = standardChain.InsertCode(testcase.bytecode); - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig - { - PatternMatchingThreshold = int.MaxValue, - IsPatternMatchingEnabled = false, - JittingThreshold = repeatCount + 1, - IsJitEnabled = true - }); - enhancedChain.InsertCode(testcase.bytecode); + yield return (Instruction.CALLDATALOAD, Prepare.EvmCode + .CALLDATALOAD(0) + .Done, EvmExceptionType.None); - var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + yield return (Instruction.MSIZE, Prepare.EvmCode + .MSIZE() + .Done, EvmExceptionType.None); - var bytecode = Prepare.EvmCode - .JUMPDEST() - .Call(address, 100) - .POP() - .PushData(1000) - .GAS() - .GT() - .JUMPI(0) - .STOP() - .Done; + yield return (Instruction.GASPRICE, Prepare.EvmCode + .GASPRICE() + .Done, EvmExceptionType.None); - for (var i = 0; i < repeatCount * 2; i++) - { - standardChain.Execute(bytecode, tracer1); - } + yield return (Instruction.CODESIZE, Prepare.EvmCode + .CODESIZE() + .Done, EvmExceptionType.None); - for (var i = 0; i < repeatCount * 2; i++) - { - enhancedChain.Execute(bytecode, tracer2); - } + yield return (Instruction.PC, Prepare.EvmCode + .PC() + .Done, EvmExceptionType.None); - var normal_traces = tracer1.BuildResult(); - var ilvm_traces = tracer2.BuildResult(); + yield return (Instruction.COINBASE, Prepare.EvmCode + .COINBASE() + .Done, EvmExceptionType.None); - var actual = standardChain.StateRoot; - var expected = enhancedChain.StateRoot; + yield return (Instruction.TIMESTAMP, Prepare.EvmCode + .TIMESTAMP() + .Done, EvmExceptionType.None); - var ilvm_callsComp = ilvm_traces.Entries.Where(tr => tr.Opcode == "CALL"); - var norm_callsComp = normal_traces.Entries.Where(tr => tr.Opcode == "CALL"); + yield return (Instruction.NUMBER, Prepare.EvmCode + .NUMBER() + .Done, EvmExceptionType.None); - var zipped = ilvm_callsComp.Zip(norm_callsComp, (ilvm, norm) => (ilvm, norm)).ToList(); + yield return (Instruction.GASLIMIT, Prepare.EvmCode + .GASLIMIT() + .Done, EvmExceptionType.None); - var indexOfChange = zipped.FindIndex(pair => pair.ilvm.Gas != pair.norm.Gas); + yield return (Instruction.CALLER, Prepare.EvmCode + .CALLER() + .Done, EvmExceptionType.None); - var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + yield return (Instruction.ADDRESS, Prepare.EvmCode + .ADDRESS() + .Done, EvmExceptionType.None); - if (testcase.opcode is not null) - { - Assert.That(HasIlvmTraces, Is.True); - } - Assert.That(actual, Is.EqualTo(expected)); - } + yield return (Instruction.ORIGIN, Prepare.EvmCode + .ORIGIN() + .Done, EvmExceptionType.None); + yield return (Instruction.CALLVALUE, Prepare.EvmCode + .CALLVALUE() + .Done, EvmExceptionType.None); - [Test, TestCaseSource(nameof(GePatBytecodesSamples))] - public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) - { - int repeatCount = 32; - - TestBlockChain standardChain = new TestBlockChain(new VMConfig()); - var address = standardChain.InsertCode(testcase.bytecode); - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig - { - PatternMatchingThreshold = repeatCount + 1, - IsPatternMatchingEnabled = true, - JittingThreshold = int.MaxValue, - IsJitEnabled = false - }); - enhancedChain.InsertCode(testcase.bytecode); - - var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + yield return (Instruction.CHAINID, Prepare.EvmCode + .CHAINID() + .Done, EvmExceptionType.None); - var bytecode = - Prepare.EvmCode - .JUMPDEST() - .Call(address, 100) + yield return (Instruction.GAS, Prepare.EvmCode + .PushData(23) + .PushData(46) + .ADD() .POP() .GAS() - .PushData(1000) - .LT() - .JUMPI(0) - .STOP() - .Done; - - for (var i = 0; i < repeatCount * 2; i++) - { - standardChain.Execute(bytecode, tracer1); - } - - for (var i = 0; i < repeatCount * 2; i++) - { - enhancedChain.Execute(bytecode, tracer2); - } + .PushData(23) + .PushData(46) + .ADD() + .POP() + .Done, EvmExceptionType.None); - var normal_traces = tracer1.BuildResult(); - var ilvm_traces = tracer2.BuildResult(); + yield return (Instruction.RETURNDATASIZE, Prepare.EvmCode + .RETURNDATASIZE() + .Done, EvmExceptionType.None); - var actual = standardChain.StateRoot; - var expected = enhancedChain.StateRoot; + yield return (Instruction.BASEFEE, Prepare.EvmCode + .BASEFEE() + .Done, EvmExceptionType.None); - var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + yield return (Instruction.RETURN, Prepare.EvmCode + .StoreDataInMemory(0, [2, 3, 5, 7]) + .RETURN(0, 32) + .Done, EvmExceptionType.None); - if (testcase.opcode is not null) - { - Assert.That(HasIlvmTraces, Is.True); - } - Assert.That(actual, Is.EqualTo(expected)); - } + yield return (Instruction.REVERT, Prepare.EvmCode + .StoreDataInMemory(0, [2, 3, 5, 7]) + .REVERT(0, 32) + .Done, EvmExceptionType.None); - [Test] - public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() - { - var address = InsertCode(Prepare.EvmCode - .PushData(23) - .PushData(7) - .ADD() - .STOP().Done); + yield return (Instruction.CALLDATASIZE, Prepare.EvmCode + .CALLDATASIZE() + .Done, EvmExceptionType.None); - byte[] bytecode = - Prepare.EvmCode + yield return (Instruction.JUMPI | Instruction.JUMPDEST, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .JUMPI(9) + .PushSingle(3) .JUMPDEST() - .PushSingle(1000) - .GAS() - .LT() - .JUMPI(58) + .PushSingle(0) + .MUL() + .Done, EvmExceptionType.None); + + yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode .PushSingle(23) - .PushSingle(7) - .ADD() - .Call(address, 100) - .POP() - .PushSingle(42) - .PushSingle(5) - .ADD() - .POP() - .JUMP(0) + .JUMP(10) .JUMPDEST() + .PushSingle(3) + .MUL() .STOP() - .Done; - - var accumulatedTraces = new List(); - for (int i = 0; i <= config.JittingThreshold * 2; i++) - { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.SegmentID is not null).ToList(); - accumulatedTraces.AddRange(traces); + .JUMPDEST() + .JUMP(5) + .Done, EvmExceptionType.None); - } + yield return (Instruction.SHL, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SHL() + .Done, EvmExceptionType.None); - // in the last stint gas is almost below 1000 - // it executes segment 0 (0..46) - // then calls address 23 (segment 0..5 since it is precompiled as well) - // then it executes segment 48..59 which ends in jump back to pc = 0 - // then it executes segment 0..46 again but this time gas is below 1000 - // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) - // so the last segment executed is AbortDestinationPattern + yield return (Instruction.SHR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SHR() + .Done, EvmExceptionType.None); - string[] desiredTracePattern = new[] - { - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "AbortDestinationPattern" - }; + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SAR() + .Done, EvmExceptionType.None); - string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); - Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); - } + yield return (Instruction.AND, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .AND() + .Done, EvmExceptionType.None); + yield return (Instruction.OR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .OR() + .Done, EvmExceptionType.None); - [Test] - public void JIT_invalid_opcode_results_in_failure() - { - byte[] bytecode = - Prepare.EvmCode - .PUSHx() // PUSH0 - .POP() - .STOP() - .Done; + yield return (Instruction.XOR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .XOR() + .Done, EvmExceptionType.None); - var accumulatedTraces = new List(); - var numberOfRuns = config.JittingThreshold * 1024; - for (int i = 0; i < numberOfRuns; i++) - { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode, (1024, null)); - var traces = tracer.BuildResult().ToList(); - accumulatedTraces.AddRange(traces); - } + yield return (Instruction.SLT, Prepare.EvmCode + .PushData(23) + .PushSingle(4) + .SLT() + .Done, EvmExceptionType.None); - var failedTraces = accumulatedTraces.Where(tr => tr.Failed && tr.Entries.Any(subtr => subtr.Error == EvmExceptionType.BadInstruction.ToString())).ToList(); - Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); - } + yield return (Instruction.SGT, Prepare.EvmCode + .PushData(23) + .PushData(1) + .SGT() + .Done, EvmExceptionType.None); - [Test] - public void Execution_Swap_Happens_When_Segments_are_compiled() - { - byte[] bytecode = - Prepare.EvmCode - .JUMPDEST() - .PushSingle(1000) - .GAS() - .LT() - .PUSHx([0, 26]) - .JUMPI() - .PushSingle(23) - .PushSingle(7) - .ADD() - .POP() - .PushSingle(42) - .PushSingle(5) - .ADD() - .POP() - .PUSHx([0, 0]) - .JUMP() - .JUMPDEST() - .STOP() - .Done; + yield return (Instruction.BYTE, Prepare.EvmCode + .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) + .Done, EvmExceptionType.None); - var accumulatedTraces = new List(); - for (int i = 0; i <= config.JittingThreshold * 32; i++) - { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); - accumulatedTraces.AddRange(traces); - } + yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode + .JUMP(31) + .Done, EvmExceptionType.None); + yield return (Instruction.LOG0, Prepare.EvmCode + .Log(0, 0) + .Done, EvmExceptionType.None); - Assert.Greater(accumulatedTraces.Count, 0); - } + yield return (Instruction.LOG1, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .Log(1, 0, [TestItem.KeccakA]) + .Done, EvmExceptionType.None); - [Test] - public void Pure_Opcode_Emition_Coveraga() - { - Instruction[] instructions = - System.Enum.GetValues() - .Where(opcode => !opcode.IsStateful()) - .ToArray(); + yield return (Instruction.LOG2, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(64) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(96) + .Op(Instruction.MSTORE) + .Log(4, 0, [TestItem.KeccakA, TestItem.KeccakB]) + .Done, EvmExceptionType.None); + yield return (Instruction.LOG3, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .Log(2, 0, [TestItem.KeccakA, TestItem.KeccakA, TestItem.KeccakB]) + .Done, EvmExceptionType.None); - List<(Instruction, Exception)> notYetImplemented = []; - foreach (var instruction in instructions) + yield return (Instruction.LOG4, Prepare.EvmCode + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(32) + .Op(Instruction.MSTORE) + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(64) + .Op(Instruction.MSTORE) + .Log(3, 0, [TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakA, TestItem.KeccakB]) + .Done, EvmExceptionType.None); + + yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode + .PushData(23) + .PushData(7) + .TSTORE() + .PushData(7) + .TLOAD() + .Done, EvmExceptionType.None); + + yield return (Instruction.SSTORE | Instruction.SLOAD, Prepare.EvmCode + .PushData(23) + .PushData(7) + .SSTORE() + .PushData(7) + .SLOAD() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODESIZE, Prepare.EvmCode + .EXTCODESIZE(Address.FromNumber(1)) + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODEHASH, Prepare.EvmCode + .EXTCODEHASH(Address.FromNumber(1)) + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODECOPY, Prepare.EvmCode + .PushData(0) + .PushData(0) + .PushData(0) + .EXTCODECOPY(Address.FromNumber(1)) + .Done, EvmExceptionType.None); + + yield return (Instruction.BALANCE, Prepare.EvmCode + .BALANCE(Address.FromNumber(1)) + .Done, EvmExceptionType.None); + + yield return (Instruction.SELFBALANCE, Prepare.EvmCode + .SELFBALANCE() + .Done, EvmExceptionType.None); + + yield return (Instruction.INVALID, Prepare.EvmCode + .INVALID() + .Done, EvmExceptionType.BadInstruction); + + yield return (Instruction.STOP, Prepare.EvmCode + .STOP() + .Done, EvmExceptionType.None); + + yield return (Instruction.POP, Prepare.EvmCode + .PUSHx() + .POP() + .Done, EvmExceptionType.None); + + for (byte opcode = (byte)Instruction.DUP1; opcode <= (byte)Instruction.DUP16; opcode++) { - string name = $"ILEVM_TEST_{instruction}"; - OpcodeInfo opcode = new OpcodeInfo(0, instruction, null); - try - { - ILCompiler.CompileSegment(name, [opcode], []); - } - catch (NotSupportedException nse) + int n = opcode - (byte)Instruction.DUP1 + 1; + var test = Prepare.EvmCode; + for (int i = 0; i < n; i++) { - notYetImplemented.Add((instruction, nse)); + test.PushData(i); } - catch (Exception) + test.Op((Instruction)opcode); + + yield return ((Instruction)opcode, test.Done, EvmExceptionType.None); + } + + for (byte opcode = (byte)Instruction.PUSH0; opcode <= (byte)Instruction.PUSH32; opcode++) + { + int n = opcode - (byte)Instruction.PUSH0; + byte[] args = n == 0 ? null : Enumerable.Range(0, n).Select(i => (byte)i).ToArray(); + + yield return ((Instruction)opcode, Prepare.EvmCode.PUSHx(args).Done, EvmExceptionType.None); + } + + for (byte opcode = (byte)Instruction.SWAP1; opcode <= (byte)Instruction.SWAP16; opcode++) + { + int n = opcode - (byte)Instruction.SWAP1 + 2; + var test = Prepare.EvmCode; + for (int i = 0; i < n; i++) { + test.PushData(i); } + test.Op((Instruction)opcode); + + yield return ((Instruction)opcode, test.Done, EvmExceptionType.None); } - string missingOpcodes = String.Join("; ", notYetImplemented.Select(op => op.Item1.ToString())); - Assert.That(notYetImplemented.Count == 0, $"{notYetImplemented.Count} opcodes missing: [{missingOpcodes}]"); + yield return (Instruction.SDIV, Prepare.EvmCode + .PushData(23) + .PushData(7) + .SDIV() + .Done, EvmExceptionType.None); + + yield return (Instruction.SMOD, Prepare.EvmCode + .PushData(23) + .PushData(7) + .SMOD() + .Done, EvmExceptionType.None); + + yield return (Instruction.CODECOPY, Prepare.EvmCode + .PushData(0) + .PushData(32) + .PushData(7) + .CODECOPY() + .Done, EvmExceptionType.None); + + yield return (Instruction.MULMOD, Prepare.EvmCode + .PushData(23) + .PushData(3) + .PushData(7) + .MULMOD() + .Done, EvmExceptionType.None); + + yield return (Instruction.KECCAK256, Prepare.EvmCode + .MSTORE(0, Enumerable.Range(0, 16).Select(i => (byte)i).ToArray()) + .PushData(0) + .PushData(16) + .KECCAK256() + .Done, EvmExceptionType.None); + + yield return (Instruction.PREVRANDAO, Prepare.EvmCode + .PREVRANDAO() + .Done, EvmExceptionType.None); + + yield return (Instruction.RETURNDATACOPY, Prepare.EvmCode + .PushData(0) + .PushData(32) + .PushData(0) + .RETURNDATACOPY() + .Done, EvmExceptionType.None); + + yield return (Instruction.BLOBBASEFEE, Prepare.EvmCode + .Op(Instruction.BLOBBASEFEE) + .Done, EvmExceptionType.None); + + yield return (Instruction.SIGNEXTEND, Prepare.EvmCode + .PushData(1024) + .PushData(16) + .SIGNEXTEND() + .Done, EvmExceptionType.None); + + yield return (Instruction.INVALID, Prepare.EvmCode + .JUMPDEST() + .MUL(23, 3) + .POP() + .JUMP(0) + .Done, EvmExceptionType.OutOfGas); + + yield return (Instruction.INVALID, Prepare.EvmCode + .JUMPDEST() + .PUSHx() + .DUPx(1) + .DUPx(1) + .DUPx(1) + .DUPx(1) + .DUPx(1) + .DUPx(1) + .DUPx(1) + .JUMP(0) + .Done, EvmExceptionType.StackOverflow); + + yield return (Instruction.INVALID, Prepare.EvmCode + .JUMPDEST() + .MUL(23) + .JUMP(0) + .Done, EvmExceptionType.StackUnderflow); } - public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() + [Test] + public void All_Stateless_Opcodes_Are_Covered_in_JIT_Tests() { - yield return (null, Prepare.EvmCode - .Done); - yield return (typeof(EmulatedStaticCJump), Prepare.EvmCode - .PUSHx([1]) - .PUSHx([0, 7]) - .JUMPI() - .JUMPDEST() - .Done); - yield return (typeof(EmulatedStaticJump), Prepare.EvmCode - .PUSHx([0, 5]) - .JUMP() - .JUMPDEST() - .Done); - yield return (typeof(MethodSelector), Prepare.EvmCode - .PushData(0) - .PushData(23) - .MSTORE() - .CALLVALUE() - .DUPx(1) - .Done); - yield return (typeof(IsContractCheck), Prepare.EvmCode - .EXTCODESIZE(Address.SystemUser) - .DUPx(1) - .ISZERO() - .Done); + List instructions = System.Enum.GetValues().ToList(); + instructions.Remove(Instruction.MSTORE); + instructions.Remove(Instruction.MLOAD); + instructions.Remove(Instruction.SSTORE); + instructions.Remove(Instruction.SLOAD); + instructions.Remove(Instruction.TSTORE); + instructions.Remove(Instruction.TLOAD); + instructions.Remove(Instruction.JUMP); + instructions.Remove(Instruction.JUMPI); + instructions.Remove(Instruction.JUMPDEST); + + instructions.Add(Instruction.MSTORE | Instruction.MLOAD); + instructions.Add(Instruction.TSTORE | Instruction.TLOAD); + instructions.Add(Instruction.SSTORE | Instruction.SLOAD); + instructions.Add(Instruction.JUMP | Instruction.JUMPDEST); + instructions.Add(Instruction.JUMPI | Instruction.JUMPDEST); + + var tests = GeJitBytecodesSamples().Select(test => test.Item1); + + + + List notCovered = new List(); + foreach (var opcode in instructions) + { + if (!opcode.IsStateful() && !tests.Contains(opcode)) + { + notCovered.Add(opcode); + } + } + + Assert.That(notCovered.Count, Is.EqualTo(0), $"[{String.Join(", ", notCovered)}]"); } - public static IEnumerable<(Instruction?, byte[])> GeJitBytecodesSamples() + [Test] + public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() { - yield return (null, Prepare.EvmCode - .Done); - yield return (Instruction.PUSH32, Prepare.EvmCode - .PushSingle(1) - .Done); - yield return (Instruction.ISZERO, Prepare.EvmCode - .ISZERO(7) - .ISZERO(0) - .ISZERO(7) - .Done); - yield return (Instruction.SUB, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .SUB() - .Done); - - yield return (Instruction.ADD, Prepare.EvmCode + byte[] bytecode = + Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() - .Done); - - yield return (Instruction.ADDMOD, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) + .PushSingle(42) .PushSingle(5) - .ADDMOD() - .Done); + .ADD() + .Done; - yield return (Instruction.MUL, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .MUL() - .Done); + CodeInfo codeInfo = new CodeInfo(bytecode); - yield return (Instruction.EXP, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .EXP() - .PushSingle(0) - .PushSingle(7) - .EXP() - .PushSingle(1) - .PushSingle(7) - .EXP() - .PushSingle(1) - .PushSingle(0) - .EXP() - .PushSingle(1) - .PushSingle(1) - .EXP() - .Done); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); - yield return (Instruction.MOD, Prepare.EvmCode + codeInfo.IlInfo.Chunks.Count.Should().Be(2); + } + + + [Test] + public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() + { + byte[] bytecode = + Prepare.EvmCode .PushSingle(23) .PushSingle(7) - .MOD() - .Done); - - yield return (Instruction.DIV, Prepare.EvmCode + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .Call(Address.FromNumber(23), 10000) .PushSingle(23) .PushSingle(7) - .DIV() - .Done); - - yield return (Instruction.MSTORE, Prepare.EvmCode - .MSTORE(0, ((UInt256)23).PaddedBytes(32)) - .Done); + .ADD() + .PushSingle(42) + .PushSingle(5) + .ADD() + .STOP() + .Done; - yield return (Instruction.MLOAD, Prepare.EvmCode - .MSTORE(0, ((UInt256)23).PaddedBytes(32)) - .MLOAD(0) - .Done); + CodeInfo codeInfo = new CodeInfo(bytecode); - yield return (Instruction.MCOPY, Prepare.EvmCode - .MSTORE(0, ((UInt256)23).PaddedBytes(32)) - .MCOPY(32, 0, 32) - .Done); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); - yield return (Instruction.EQ, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .EQ() - .Done); + codeInfo.IlInfo.Segments.Count.Should().Be(2); + } - yield return (Instruction.GT, Prepare.EvmCode - .PushSingle(7) - .PushSingle(23) - .GT() - .Done); + [Test] + public void Execution_Swap_Happens_When_Pattern_Occurs() + { + var pattern1 = IlAnalyzer.GetPatternHandler(); + var pattern2 = IlAnalyzer.GetPatternHandler(); + var pattern3 = IlAnalyzer.GetPatternHandler(); - yield return (Instruction.LT, Prepare.EvmCode + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .PUSHx([0, 26]) + .JUMPI() .PushSingle(23) .PushSingle(7) - .LT() - .Done); - - yield return (Instruction.NOT, Prepare.EvmCode - .PushSingle(1) - .NOT() - .Done); - - yield return (Instruction.BLOBHASH, Prepare.EvmCode - .PushSingle(0) - .BLOBHASH() - .Done); + .ADD() + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .PUSHx([0, 0]) + .JUMP() + .JUMPDEST() + .STOP() + .Done; - yield return (Instruction.BLOCKHASH, Prepare.EvmCode - .BLOCKHASH(0) - .Done); + /* + var hashcode = Keccak.Compute(bytecode); + var address = new Address(hashcode); + var spec = Prague.Instance; + TestState.CreateAccount(address, 1000000); + TestState.InsertCode(address, bytecode, spec); + */ + var accumulatedTraces = new List(); + for (int i = 0; i < config.PatternMatchingThreshold * 2; i++) + { + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && !tr.IsPrecompiled.Value).ToList(); + accumulatedTraces.AddRange(traces); + } - yield return (Instruction.CALLDATACOPY, Prepare.EvmCode - .CALLDATACOPY(0, 0, 32) - .Done); + Assert.Greater(accumulatedTraces.Count, 0); + } - yield return (Instruction.CALLDATALOAD, Prepare.EvmCode - .CALLDATALOAD(0) - .Done); + [Test] + public void All_Opcodes_Have_Metadata() + { + Instruction[] instructions = System.Enum.GetValues(); + foreach (var opcode in instructions) + { + Assert.That(OpcodeMetadata.Operations.ContainsKey(opcode), Is.True); + } + } - yield return (Instruction.MSIZE, Prepare.EvmCode - .MSIZE() - .Done); + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] + public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode, EvmExceptionType _) testcase) + { + int repeatCount = 32; - yield return (Instruction.GASPRICE, Prepare.EvmCode - .GASPRICE() - .Done); + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + var address = standardChain.InsertCode(testcase.bytecode); + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + JittingThreshold = repeatCount + 1, + IsJitEnabled = true + }); + enhancedChain.InsertCode(testcase.bytecode); - yield return (Instruction.CODESIZE, Prepare.EvmCode - .CODESIZE() - .Done); + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - yield return (Instruction.PC, Prepare.EvmCode - .PC() - .Done); + var bytecode = Prepare.EvmCode + .JUMPDEST() + .Call(address, 100) + .POP() + .PushData(1000) + .GAS() + .GT() + .JUMPI(0) + .STOP() + .Done; - yield return (Instruction.COINBASE, Prepare.EvmCode - .COINBASE() - .Done); + for (var i = 0; i < repeatCount * 2; i++) + { + standardChain.Execute(bytecode, tracer1); + } - yield return (Instruction.TIMESTAMP, Prepare.EvmCode - .TIMESTAMP() - .Done); + for (var i = 0; i < repeatCount * 2; i++) + { + enhancedChain.Execute(bytecode, tracer2); + } - yield return (Instruction.NUMBER, Prepare.EvmCode - .NUMBER() - .Done); + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); - yield return (Instruction.GASLIMIT, Prepare.EvmCode - .GASLIMIT() - .Done); + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; - yield return (Instruction.CALLER, Prepare.EvmCode - .CALLER() - .Done); + var ilvm_callsComp = ilvm_traces.Entries.Where(tr => tr.Opcode == "CALL"); + var norm_callsComp = normal_traces.Entries.Where(tr => tr.Opcode == "CALL"); - yield return (Instruction.ADDRESS, Prepare.EvmCode - .ADDRESS() - .Done); + var zipped = ilvm_callsComp.Zip(norm_callsComp, (ilvm, norm) => (ilvm, norm)).ToList(); - yield return (Instruction.ORIGIN, Prepare.EvmCode - .ORIGIN() - .Done); + var indexOfChange = zipped.FindIndex(pair => pair.ilvm.Gas != pair.norm.Gas); - yield return (Instruction.CALLVALUE, Prepare.EvmCode - .CALLVALUE() - .Done); + var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); - yield return (Instruction.CHAINID, Prepare.EvmCode - .CHAINID() - .Done); + if (testcase.opcode is not null) + { + Assert.That(HasIlvmTraces, Is.True); + } + Assert.That(actual, Is.EqualTo(expected)); + } - yield return (Instruction.GAS, Prepare.EvmCode - .GAS() - .Done); - yield return (Instruction.RETURNDATASIZE, Prepare.EvmCode - .RETURNDATASIZE() - .Done); + [Test, TestCaseSource(nameof(GePatBytecodesSamples))] + public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) + { + int repeatCount = 32; - yield return (Instruction.BASEFEE, Prepare.EvmCode - .BASEFEE() - .Done); + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + var address = standardChain.InsertCode(testcase.bytecode); + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = false + }); + enhancedChain.InsertCode(testcase.bytecode); - yield return (Instruction.RETURN, Prepare.EvmCode - .StoreDataInMemory(0, [2, 3, 5, 7]) - .RETURN(0, 32) - .Done); + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - yield return (Instruction.REVERT, Prepare.EvmCode - .StoreDataInMemory(0, [2, 3, 5, 7]) - .REVERT(0, 32) - .Done); + var bytecode = + Prepare.EvmCode + .JUMPDEST() + .Call(address, 100) + .POP() + .GAS() + .PushData(1000) + .LT() + .JUMPI(0) + .STOP() + .Done; - yield return (Instruction.CALLDATASIZE, Prepare.EvmCode - .CALLDATASIZE() - .Done); + for (var i = 0; i < repeatCount * 2; i++) + { + standardChain.Execute(bytecode, tracer1); + } - yield return (Instruction.JUMPI, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .JUMPI(9) - .PushSingle(3) - .JUMPDEST() - .PushSingle(0) - .MUL() - .Done); + for (var i = 0; i < repeatCount * 2; i++) + { + enhancedChain.Execute(bytecode, tracer2); + } - yield return (Instruction.JUMP, Prepare.EvmCode - .PushSingle(23) - .JUMP(10) - .JUMPDEST() - .PushSingle(3) - .MUL() - .STOP() - .JUMPDEST() - .JUMP(5) - .Done); + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); - yield return (Instruction.SHL, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SHL() - .Done); + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; - yield return (Instruction.SHR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SHR() - .Done); + var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); - yield return (Instruction.SAR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SAR() - .Done); + if (testcase.opcode is not null) + { + Assert.That(HasIlvmTraces, Is.True); + } + Assert.That(actual, Is.EqualTo(expected)); + } - yield return (Instruction.AND, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .AND() - .Done); + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() + { + var address = InsertCode(Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done); - yield return (Instruction.OR, Prepare.EvmCode + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .JUMPI(58) .PushSingle(23) - .PushSingle(1) - .OR() - .Done); + .PushSingle(7) + .ADD() + .Call(address, 100) + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .JUMP(0) + .JUMPDEST() + .STOP() + .Done; - yield return (Instruction.XOR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .XOR() - .Done); + var accumulatedTraces = new List(); + for (int i = 0; i <= config.JittingThreshold * 2; i++) + { + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.SegmentID is not null).ToList(); + accumulatedTraces.AddRange(traces); - yield return (Instruction.SLT, Prepare.EvmCode - .PushData(23) - .PushSingle(4) - .SLT() - .Done); + } - yield return (Instruction.SGT, Prepare.EvmCode - .PushData(23) - .PushData(1) - .SGT() - .Done); + // in the last stint gas is almost below 1000 + // it executes segment 0 (0..46) + // then calls address 23 (segment 0..5 since it is precompiled as well) + // then it executes segment 48..59 which ends in jump back to pc = 0 + // then it executes segment 0..46 again but this time gas is below 1000 + // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) + // so the last segment executed is AbortDestinationPattern - yield return (Instruction.BYTE, Prepare.EvmCode - .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) - .Done); + string[] desiredTracePattern = new[] + { + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", + "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", + "AbortDestinationPattern" + }; - yield return (Instruction.JUMP, Prepare.EvmCode - .JUMP(31) - .Done); + string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); + } - yield return (Instruction.LOG0, Prepare.EvmCode - .Log(0, 0) - .Done); - yield return (Instruction.LOG1, Prepare.EvmCode - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(0) - .Op(Instruction.MSTORE) - .Log(1, 0, [TestItem.KeccakA]) - .Done); + [Test] + public void JIT_invalid_opcode_results_in_failure() + { + byte[] bytecode = + Prepare.EvmCode + .PUSHx() // PUSH0 + .POP() + .STOP() + .Done; - yield return (Instruction.LOG2, Prepare.EvmCode - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(0) - .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(64) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(96) - .Op(Instruction.MSTORE) - .Log(4, 0, [TestItem.KeccakA, TestItem.KeccakB]) - .Done); + var accumulatedTraces = new List(); + var numberOfRuns = config.JittingThreshold * 1024; + for (int i = 0; i < numberOfRuns; i++) + { + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); + ExecuteBlock(tracer, bytecode, (1024, null)); + var traces = tracer.BuildResult().ToList(); + accumulatedTraces.AddRange(traces); + } - yield return (Instruction.LOG3, Prepare.EvmCode - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(0) - .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .Log(2, 0, [TestItem.KeccakA, TestItem.KeccakA, TestItem.KeccakB]) - .Done); + var failedTraces = accumulatedTraces.Where(tr => tr.Failed && tr.Entries.Any(subtr => subtr.Error == EvmExceptionType.BadInstruction.ToString())).ToList(); + Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); + } - yield return (Instruction.LOG4, Prepare.EvmCode - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(0) - .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(64) - .Op(Instruction.MSTORE) - .Log(3, 0, [TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakA, TestItem.KeccakB]) - .Done); + [Test] + public void Execution_Swap_Happens_When_Segments_are_compiled() + { + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .PUSHx([0, 26]) + .JUMPI() + .PushSingle(23) + .PushSingle(7) + .ADD() + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .PUSHx([0, 0]) + .JUMP() + .JUMPDEST() + .STOP() + .Done; - yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode - .PushData(23) - .PushData(7) - .TSTORE() - .PushData(7) - .TLOAD() - .Done); + var accumulatedTraces = new List(); + for (int i = 0; i <= config.JittingThreshold * 32; i++) + { + var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); + ExecuteBlock(tracer, bytecode); + var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); + accumulatedTraces.AddRange(traces); + } - yield return (Instruction.SSTORE | Instruction.SLOAD, Prepare.EvmCode - .PushData(23) - .PushData(7) - .SSTORE() - .PushData(7) - .SLOAD() - .Done); - yield return (Instruction.EXTCODESIZE, Prepare.EvmCode - .EXTCODESIZE(Address.FromNumber(1)) - .Done); + Assert.Greater(accumulatedTraces.Count, 0); + } - yield return (Instruction.EXTCODEHASH, Prepare.EvmCode - .EXTCODEHASH(Address.FromNumber(1)) - .Done); + [Test] + public void Pure_Opcode_Emition_Coveraga() + { + Instruction[] instructions = + System.Enum.GetValues() + .Where(opcode => !opcode.IsStateful()) + .ToArray(); - yield return (Instruction.EXTCODECOPY, Prepare.EvmCode - .PushData(0) - .PushData(0) - .PushData(0) - .EXTCODECOPY(Address.FromNumber(1)) - .Done); - yield return (Instruction.BALANCE, Prepare.EvmCode - .BALANCE(Address.FromNumber(1)) - .Done); + List<(Instruction, Exception)> notYetImplemented = []; + foreach (var instruction in instructions) + { + string name = $"ILEVM_TEST_{instruction}"; + OpcodeInfo opcode = new OpcodeInfo(0, instruction, null); + try + { + ILCompiler.CompileSegment(name, [opcode], []); + } + catch (NotSupportedException nse) + { + notYetImplemented.Add((instruction, nse)); + } + catch (Exception) + { + } + } - yield return (Instruction.SELFBALANCE, Prepare.EvmCode - .SELFBALANCE() - .Done); + string missingOpcodes = String.Join("; ", notYetImplemented.Select(op => op.Item1.ToString())); + Assert.That(notYetImplemented.Count == 0, $"{notYetImplemented.Count} opcodes missing: [{missingOpcodes}]"); } + [Test] public void ILAnalyzer_Initialize_Add_All_Patterns() { @@ -963,14 +1138,16 @@ public void ILAnalyzer_Initialize_Add_All_Patterns() } [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] - public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) testcase) + public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, EvmExceptionType exceptionType) testcase) { var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()]); var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); var stack = new byte[1024 * 32]; var inputBuffer = envExCtx.InputData; - var returnBuffer = ReadOnlyMemory.Empty; + var returnBuffer = + new ReadOnlyMemory(Enumerable.Range(0, 32) + .Select(i => (byte)i).ToArray()); TestState.CreateAccount(Address.FromNumber(1), 1000000); TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); @@ -993,7 +1170,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode) var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, codeInfoRepository, Prague.Instance, ctx.Data); - Assert.IsTrue(iLEvmState.EvmException == EvmExceptionType.None); + Assert.IsTrue(iLEvmState.EvmException == testcase.exceptionType); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 67b904e3b96..4d67c27e04a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -3,7 +3,6 @@ using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.IL; using Nethermind.Evm.Tracing; @@ -76,8 +75,11 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local uint64A = method.DeclareLocal(typeof(ulong)); using Local uint32A = method.DeclareLocal(typeof(uint)); + using Local uint32B = method.DeclareLocal(typeof(uint)); using Local int64A = method.DeclareLocal(typeof(long)); + using Local int64B = method.DeclareLocal(typeof(long)); using Local byte8A = method.DeclareLocal(typeof(byte)); + using Local byte8B = method.DeclareLocal(typeof(byte)); using Local buffer = method.DeclareLocal(typeof(byte*)); using Local storageCell = method.DeclareLocal(typeof(StorageCell)); @@ -122,6 +124,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co Dictionary jumpDestinations = new(); + var costs = BuildCostLookup(code); // Idea(Ayman) : implement every opcode as a method, and then inline the IL of the method in the main method for (int i = 0; i < code.Length; i++) @@ -143,30 +146,25 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.BadInstruction]); - // set pc - method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); - method.StoreLocal(programCounter); - - if (op.Metadata.GasCost != 0) + if (costs.ContainsKey(op.ProgramCounter) && costs[op.ProgramCounter] > 0) { - // load gasAvailable method.LoadLocal(gasAvailable); + method.LoadConstant(costs[op.ProgramCounter]); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - // get pc gas cost - method.LoadConstant(op.Metadata.GasCost); - - // subtract the gas cost + method.LoadLocal(gasAvailable); + method.LoadConstant(costs[op.ProgramCounter]); method.Subtract(); - // check if gas is available - method.Duplicate(); method.StoreLocal(gasAvailable); - method.LoadConstant((long)0); + } - // if gas is not available, branch to out of gas - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + if(i == code.Length - 1) + { + method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); + method.StoreLocal(programCounter); } - if(op.Metadata.StackBehaviorPop > 0) + if (op.Metadata.StackBehaviorPop > 0) { method.LoadLocal(head); method.LoadConstant(op.Metadata.StackBehaviorPop); @@ -190,60 +188,73 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // we do nothing break; case Instruction.STOP: - method.LoadArgument(0); - method.LoadConstant(true); - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); - method.Branch(ret); + { + method.LoadArgument(0); + method.LoadConstant(true); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); + method.Branch(ret); + } break; case Instruction.INVALID: - method.Branch(evmExceptionLabels[EvmExceptionType.BadInstruction]); + { + method.Branch(evmExceptionLabels[EvmExceptionType.BadInstruction]); + } break; case Instruction.CHAINID: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); - method.Call(Word.SetULong0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); + method.Call(Word.SetULong0); + method.StackPush(head); + } break; case Instruction.NOT: - method.Load(stack, head); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head); - - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256R); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Not))); - method.Load(stack, head); - method.LoadLocal(uint256R); - method.Call(Word.SetUInt256); + { + method.Load(stack, head); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head); + + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256R); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Not))); + method.Load(stack, head); + method.LoadLocal(uint256R); + method.Call(Word.SetUInt256); + } break; case Instruction.JUMP: - // we jump into the jump table - method.Branch(jumpTable); + { + // we jump into the jump table + method.Branch(jumpTable); + } break; case Instruction.JUMPI: - // consume the jump condition - Label noJump = method.DefineLabel(); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetIsZero); - // if the jump condition is false, we do not jump - method.BranchIfTrue(noJump); - - // load the jump address - method.LoadConstant(1); - method.StoreLocal(consumeJumpCondition); - - // we jump into the jump table - method.Branch(jumpTable); - - method.MarkLabel(noJump); - method.StackPop(head, 2); + {// consume the jump condition + Label noJump = method.DefineLabel(); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetIsZero); + // if the jump condition is false, we do not jump + method.BranchIfTrue(noJump); + + // load the jump address + method.LoadConstant(1); + method.StoreLocal(consumeJumpCondition); + + // we jump into the jump table + method.Branch(jumpTable); + + method.MarkLabel(noJump); + method.StackPop(head, 2); + } break; case Instruction.PUSH0: - method.CleanWord(stack, head); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.StackPush(head); + } break; case Instruction.PUSH1: case Instruction.PUSH2: @@ -277,16 +288,17 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.PUSH30: case Instruction.PUSH31: case Instruction.PUSH32: - // we load the stack - method.CleanWord(stack, head); - method.Load(stack, head); - - // we load the span of bytes - method.LoadArgument(5); - method.LoadConstant(op.Arguments.Value); - method.LoadElement(); - method.Call(Word.SetArray); - method.StackPush(head); + {// we load the stack + method.CleanWord(stack, head); + method.Load(stack, head); + + // we load the span of bytes + method.LoadArgument(5); + method.LoadConstant(op.Arguments.Value); + method.LoadElement(); + method.Call(Word.SetArray); + method.StackPush(head); + } break; case Instruction.ADD: EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Add), BindingFlags.Public | BindingFlags.Static)!, null, evmExceptionLabels, uint256A, uint256B); @@ -316,7 +328,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SMOD: - EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Mod), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.Mod), BindingFlags.Public | BindingFlags.Static, [typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType()])!, (il, postInstructionLabel, locals) => { Label label = il.DefineLabel(); @@ -352,18 +364,17 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.SDIV: - EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static)!, + EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static, [typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType()])!, (il, postInstructionLabel, locals) => { Label label1 = il.DefineLabel(); Label label2 = il.DefineLabel(); il.LoadLocalAddress(locals[1]); - il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); il.BranchIfFalse(label1); - il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.LoadField(typeof(UInt256).GetField(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); @@ -371,16 +382,17 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co il.LoadLocalAddress(locals[1]); il.Call(GetAsMethodInfo()); - il.LoadField(typeof(Int256.Int256).GetField(nameof(Int256.Int256.MinusOne), BindingFlags.Static | BindingFlags.Public)); + il.LoadFieldAddress(typeof(Int256.Int256).GetField(nameof(Int256.Int256.MinusOne), BindingFlags.Static | BindingFlags.Public)); il.Call(typeof(Int256.Int256).GetMethod("op_Equality")); il.LoadLocalAddress(locals[0]); - il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static).GetMethod); - il.Call(typeof(Int256.Int256).GetMethod("op_Equality")); + il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static | BindingFlags.NonPublic).GetMethod); + il.Call(typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() })); il.And(); il.BranchIfFalse(label2); - il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static).GetMethod); + il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static | BindingFlags.NonPublic).GetMethod); + il.LoadObject(typeof(UInt256)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); @@ -442,65 +454,67 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EXP: - Label powerIsZero = method.DefineLabel(); - Label baseIsOneOrZero = method.DefineLabel(); - Label endOfExpImpl = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Duplicate(); - method.Call(Word.LeadingZeroProp); - method.StoreLocal(uint64A); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackPop(head, 2); - - method.LoadLocalAddress(uint256B); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); - method.BranchIfTrue(powerIsZero); - - // load spec - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExpByteCost))); - method.LoadConstant((long)32); - method.LoadLocal(uint64A); - method.Subtract(); - method.Multiply(); - method.Subtract(); - method.Duplicate(); - method.StoreLocal(gasAvailable); - method.LoadConstant((long)0); - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadLocalAddress(uint256A); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZeroOrOne)).GetMethod!); - method.BranchIfTrue(baseIsOneOrZero); - - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.LoadLocalAddress(uint256R); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); - method.Branch(endOfExpImpl); - - method.MarkLabel(powerIsZero); - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadConstant(1); - method.StoreField(Word.Byte0Field); - method.Branch(endOfExpImpl); - - method.MarkLabel(baseIsOneOrZero); - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(uint256A); - method.Call(Word.SetUInt256); - method.Branch(endOfExpImpl); - - method.MarkLabel(endOfExpImpl); - method.StackPush(head); + { + Label powerIsZero = method.DefineLabel(); + Label baseIsOneOrZero = method.DefineLabel(); + Label endOfExpImpl = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Duplicate(); + method.Call(Word.LeadingZeroProp); + method.StoreLocal(uint64A); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadLocalAddress(uint256B); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(powerIsZero); + + // load spec + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExpByteCost))); + method.LoadConstant((long)32); + method.LoadLocal(uint64A); + method.Subtract(); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(uint256A); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZeroOrOne)).GetMethod!); + method.BranchIfTrue(baseIsOneOrZero); + + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256R); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + method.Branch(endOfExpImpl); + + method.MarkLabel(powerIsZero); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadConstant(1); + method.StoreField(Word.Byte0Field); + method.Branch(endOfExpImpl); + + method.MarkLabel(baseIsOneOrZero); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(uint256A); + method.Call(Word.SetUInt256); + method.Branch(endOfExpImpl); + + method.MarkLabel(endOfExpImpl); + method.StackPush(head); + } break; case Instruction.LT: EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); @@ -518,21 +532,24 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); break; case Instruction.ISZERO: - // we load the stack - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetIsZero); - method.StoreLocal(byte8A); - method.StackPop(head, 1); - - // we convert the result to a Uint256 and store it in the stack - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(byte8A); - method.StoreField(Word.Byte0Field); - method.StackPush(head); + {// we load the stack + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetIsZero); + method.StoreLocal(byte8A); + method.StackPop(head, 1); + + // we convert the result to a Uint256 and store it in the stack + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(byte8A); + method.StoreField(Word.Byte0Field); + method.StackPush(head); + } break; case Instruction.POP: - method.StackPop(head); + { + method.StackPop(head); + } break; case Instruction.DUP1: case Instruction.DUP2: @@ -550,12 +567,14 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.DUP14: case Instruction.DUP15: case Instruction.DUP16: - int count = (int)op.Operation - (int)Instruction.DUP1 + 1; - method.Load(stack, head); - method.StackLoadPrevious(stack, head, count); - method.LoadObject(typeof(Word)); - method.StoreObject(typeof(Word)); - method.StackPush(head); + { + int count = (int)op.Operation - (int)Instruction.DUP1 + 1; + method.Load(stack, head); + method.StackLoadPrevious(stack, head, count); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + method.StackPush(head); + } break; case Instruction.SWAP1: case Instruction.SWAP2: @@ -573,491 +592,551 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.SWAP14: case Instruction.SWAP15: case Instruction.SWAP16: - count = (int)op.Operation - (int)Instruction.SWAP1 + 1; - - method.LoadLocalAddress(uint256R); - method.StackLoadPrevious(stack, head, 1); - method.LoadObject(typeof(Word)); - method.StoreObject(typeof(Word)); - - method.StackLoadPrevious(stack, head, 1); - method.StackLoadPrevious(stack, head, count); - method.LoadObject(typeof(Word)); - method.StoreObject(typeof(Word)); - - method.StackLoadPrevious(stack, head, count); - method.LoadLocalAddress(uint256R); - method.LoadObject(typeof(Word)); - method.StoreObject(typeof(Word)); + { + var count = (int)op.Operation - (int)Instruction.SWAP1 + 1; + + method.LoadLocalAddress(uint256R); + method.StackLoadPrevious(stack, head, 1); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + + method.StackLoadPrevious(stack, head, 1); + method.StackLoadPrevious(stack, head, count); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + + method.StackLoadPrevious(stack, head, count); + method.LoadLocalAddress(uint256R); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + } break; - - // Note(Ayman): following opcode need double checking - // is pushing to stack happening correctly case Instruction.CODESIZE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadConstant(code.Length); - method.Call(Word.SetInt0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadConstant(code.Length); + method.Call(Word.SetInt0); + method.StackPush(head); + } break; case Instruction.PC: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(programCounter); - method.Call(Word.SetUInt0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadConstant((uint)op.ProgramCounter); + method.Call(Word.SetUInt0); + method.StackPush(head); + } break; case Instruction.COINBASE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); - method.Call(Word.SetAddress); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); + method.Call(Word.SetAddress); + method.StackPush(head); + } break; case Instruction.TIMESTAMP: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); - method.Call(Word.SetULong0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } break; case Instruction.NUMBER: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); - method.Call(Word.SetULong0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } break; case Instruction.GASLIMIT: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); - method.Call(Word.SetULong0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } break; case Instruction.CALLER: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); - method.Call(Word.SetAddress); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); + method.Call(Word.SetAddress); + method.StackPush(head); + } break; case Instruction.ADDRESS: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); - method.Call(Word.SetAddress); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.Call(Word.SetAddress); + method.StackPush(head); + } break; case Instruction.ORIGIN: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); - method.Call(Word.SetAddress); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); + method.Call(Word.SetAddress); + method.StackPush(head); + } break; case Instruction.CALLVALUE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.GASPRICE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.CALLDATACOPY: - Label endOfOpcode = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackLoadPrevious(stack, head, 3); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); - method.StackPop(head, 3); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant(GasCostOf.Memory); - method.Multiply(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.LoadLocalAddress(uint256C); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); - method.BranchIfTrue(endOfOpcode); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); - method.LoadObject(typeof(ReadOnlyMemory)); - method.LoadLocalAddress(uint256B); - method.LoadLocal(uint256C); - method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); - method.Convert(); - method.LoadConstant((int)PadDirection.Right); - method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); - method.StoreLocal(localZeroPaddedSpan); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localZeroPaddedSpan); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); - - method.MarkLabel(endOfOpcode); + { + Label endOfOpcode = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); + method.LoadObject(typeof(ReadOnlyMemory)); + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); + + method.MarkLabel(endOfOpcode); + } break; case Instruction.CALLDATALOAD: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head, 1); - - method.CleanWord(stack, head); - method.Load(stack, head); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); - method.LoadObject(typeof(ReadOnlyMemory)); - - method.LoadLocalAddress(uint256A); - method.LoadConstant(Word.Size); - method.LoadConstant((int)PadDirection.Right); - method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); - method.LoadField(GetFieldInfo(typeof(ZeroPaddedSpan), nameof(ZeroPaddedSpan.Span))); - method.Call(Word.SetSpan); - method.StackPush(head); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, 1); + + method.CleanWord(stack, head); + method.Load(stack, head); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); + method.LoadObject(typeof(ReadOnlyMemory)); + + method.LoadLocalAddress(uint256A); + method.LoadConstant(Word.Size); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.LoadField(GetFieldInfo(typeof(ZeroPaddedSpan), nameof(ZeroPaddedSpan.Span))); + method.Call(Word.SetSpan); + method.StackPush(head); + } break; case Instruction.CALLDATASIZE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); - method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.Call(Word.SetInt0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + method.Call(Word.SetInt0); + method.StackPush(head); + } break; case Instruction.MSIZE: - method.CleanWord(stack, head); - method.Load(stack, head); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); - method.Call(Word.SetULong0); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } break; case Instruction.MSTORE: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackPop(head, 2); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.StoreLocal(uint256C); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(byte[]) })); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(Word.Size); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadConstant(Word.Size); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); + } break; case Instruction.MSTORE8: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.LoadField(Word.Byte0Field); - method.StoreLocal(byte8A); - method.StackPop(head, 2); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadConstant(1); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.StoreLocal(uint256C); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); - method.LoadConstant(0); - method.LoadElement(); - - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveByte))); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.LoadField(Word.Byte0Field); + method.StoreLocal(byte8A); + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(1); + method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.LoadConstant(Word.Size); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); + method.LoadConstant(0); + method.LoadElement(); + + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveByte))); + } break; case Instruction.MLOAD: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head, 1); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadFieldAddress(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BigInt32))); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); - method.StoreLocal(localReadonOnlySpan); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadFieldAddress(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BigInt32))); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(BitConverter.IsLittleEndian); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.MCOPY: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - - method.StackLoadPrevious(stack, head, 3); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); - - method.StackPop(head, 3); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant((long)1); - method.Add(); - method.LoadConstant(GasCostOf.VeryLow); - method.Multiply(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Max))); - method.StoreLocal(uint256R); - method.LoadLocalAddress(uint256R); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256B); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant((long)1); + method.Add(); + method.LoadConstant(GasCostOf.VeryLow); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Max))); + method.StoreLocal(uint256R); + method.LoadLocalAddress(uint256R); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); + } break; case Instruction.KECCAK256: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackPop(head, 2); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256B); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant(GasCostOf.Sha3Word); - method.Multiply(); - method.LoadConstant(GasCostOf.Sha3); - method.Add(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); - method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); - method.Call(Word.SetKeccak); - method.StackPush(head); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Sha3Word); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); + method.Call(Word.SetKeccak); + method.StackPush(head); + } break; case Instruction.BYTE: - // load a - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetSpan); - method.StoreLocal(localReadonOnlySpan); - method.StackPop(head, 2); - - Label pushZeroLabel = method.DefineLabel(); - Label endOfInstructionImpl = method.DefineLabel(); - method.LoadLocalAddress(uint256A); - method.LoadConstant(Word.Size - 1); - method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); - method.LoadLocalAddress(uint256A); - method.LoadConstant(0); - method.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); - method.Or(); - method.BranchIfTrue(pushZeroLabel); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadLocal(uint256A); - method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); - method.Convert(); - method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); - method.LoadIndirect(); - method.StoreField(Word.Byte0Field); - method.StackPush(head); - method.Branch(endOfInstructionImpl); - - method.MarkLabel(pushZeroLabel); - method.CleanWord(stack, head); - method.Load(stack, head); - method.Call(Word.SetToZero); - method.StackPush(head); - - method.MarkLabel(endOfInstructionImpl); + {// load a + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetSpan); + method.StoreLocal(localReadonOnlySpan); + method.StackPop(head, 2); + + Label pushZeroLabel = method.DefineLabel(); + Label endOfInstructionImpl = method.DefineLabel(); + method.LoadLocalAddress(uint256A); + method.LoadConstant(Word.Size - 1); + method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.LoadLocalAddress(uint256A); + method.LoadConstant(0); + method.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.Or(); + method.BranchIfTrue(pushZeroLabel); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadLocal(uint256A); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); + method.LoadIndirect(); + method.StoreField(Word.Byte0Field); + method.StackPush(head); + method.Branch(endOfInstructionImpl); + + method.MarkLabel(pushZeroLabel); + method.CleanWord(stack, head); + method.Load(stack, head); + method.Call(Word.SetToZero); + method.StackPush(head); + + method.MarkLabel(endOfInstructionImpl); + } break; case Instruction.CODECOPY: - endOfOpcode = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackLoadPrevious(stack, head, 3); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); - method.StackPop(head, 3); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant(GasCostOf.Memory); - method.Multiply(); - method.LoadConstant(GasCostOf.VeryLow); - method.Add(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.LoadLocalAddress(uint256C); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); - method.BranchIfTrue(endOfOpcode); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); - method.LoadLocalAddress(uint256B); - method.LoadLocal(uint256C); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); - method.StoreLocal(localReadonOnlySpan); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localReadonOnlySpan); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); - - method.MarkLabel(endOfOpcode); + { + var endOfOpcode = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); + method.StoreLocal(localReadOnlyMemory); + + method.LoadLocal(localReadOnlyMemory); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256C); + method.Call(MethodInfo("op_Explicit", typeof(Int32), new[] { typeof(UInt256).MakeByRefType() })); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); + + method.MarkLabel(endOfOpcode); + } break; case Instruction.GAS: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(gasAvailable); - method.Call(Word.SetULong0); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(gasAvailable); + method.Call(Word.SetULong0); - method.StackPush(head); + method.StackPush(head); + } break; case Instruction.RETURNDATASIZE: method.CleanWord(stack, head); @@ -1069,609 +1148,701 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPush(head); break; case Instruction.RETURNDATACOPY: - endOfOpcode = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackLoadPrevious(stack, head, 3); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); - method.StackPop(head, 3); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant(GasCostOf.Memory); - method.Multiply(); - method.LoadConstant(GasCostOf.VeryLow); - method.Add(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - // Note : check if c + b > returnData.Size - - method.LoadLocalAddress(uint256C); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); - method.BranchIfTrue(endOfOpcode); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - method.Call(GetPropertyInfo(typeof(ReadOnlyMemory), nameof(ReadOnlyMemory.Span), false, out _)); - method.LoadLocalAddress(uint256B); - method.LoadLocal(uint256C); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); - method.Call(typeof(ReadOnlySpan).GetMethod(nameof(ReadOnlySpan.Slice), [typeof(UInt256).MakeByRefType(), typeof(int)])); - method.StoreLocal(localReadonOnlySpan); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localReadonOnlySpan); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(Span)])); - - method.MarkLabel(endOfOpcode); + { + var endOfOpcode = method.DefineLabel(); + using Local tempResult = method.DeclareLocal(typeof(UInt256)); + + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 3); + + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256C); + method.LoadLocalAddress(tempResult); + method.Call(typeof(UInt256).GetMethod(nameof(UInt256.AddOverflow))); + method.LoadLocalAddress(tempResult); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.Call(typeof(ReadOnlyMemory).GetProperty(nameof(ReadOnlyMemory.Length)).GetMethod!); + method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + method.Or(); + method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.AccessViolation]); + + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + // Note : check if c + b > returnData.Size + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.LoadObject(typeof(ReadOnlyMemory)); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(uint256C); + method.Call(MethodInfo("op_Explicit", typeof(Int32), new[] { typeof(UInt256).MakeByRefType() })); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); + + method.MarkLabel(endOfOpcode); + } break; case Instruction.RETURN or Instruction.REVERT: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackPop(head, 2); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); - method.StoreObject>(); - - method.LoadArgument(0); - method.LoadConstant(true); - switch (op.Operation) { - case Instruction.REVERT: - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldRevert))); - break; - case Instruction.RETURN: - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldReturn))); - break; + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); + method.StoreObject>(); + + method.LoadArgument(0); + method.LoadConstant(true); + switch (op.Operation) + { + case Instruction.REVERT: + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldRevert))); + break; + case Instruction.RETURN: + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldReturn))); + break; + } } - break; case Instruction.BASEFEE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.BLOBBASEFEE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); - method.Call(GetPropertyInfo(typeof(UInt256?), nameof(Nullable.Value), false, out _)); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + using Local uint256Nullable = method.DeclareLocal(typeof(UInt256?)); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); + method.StoreLocal(uint256Nullable); + method.LoadLocalAddress(uint256Nullable); + method.Call(GetPropertyInfo(typeof(UInt256?), nameof(Nullable.Value), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.PREVRANDAO: - Label isPostMergeBranch = method.DefineLabel(); - endOfOpcode = method.DefineLabel(); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.Duplicate(); - method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.IsPostMerge), false, out _)); - method.BranchIfTrue(isPostMergeBranch); - method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); - method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(Span) })); - method.StoreLocal(localReadonOnlySpan); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.StackPush(head); - method.Branch(endOfOpcode); - - method.MarkLabel(isPostMergeBranch); - method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); - method.StackPush(head); - - method.MarkLabel(endOfOpcode); + { + Label isPostMergeBranch = method.DefineLabel(); + Label endOfOpcode = method.DefineLabel(); + method.CleanWord(stack, head); + method.Load(stack, head); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.Duplicate(); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.IsPostMerge), false, out _)); + method.BranchIfTrue(isPostMergeBranch); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); + method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(BitConverter.IsLittleEndian); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.Branch(endOfOpcode); + + method.MarkLabel(isPostMergeBranch); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); + + method.MarkLabel(endOfOpcode); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.BLOBHASH: - Label blobVersionedHashNotFound = method.DefineLabel(); - endOfOpcode = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetInt0); - method.StoreLocal(uint32A); - method.StackPop(head, 1); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); - method.LoadNull(); - method.BranchIfEqual(blobVersionedHashNotFound); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); - method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); - method.LoadLocal(uint32A); - method.BranchIfLessOrEqual(blobVersionedHashNotFound); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); - method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); - method.LoadLocal(uint32A); - method.LoadElement(); - method.StoreLocal(localArray); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(localArray); - method.Call(Word.SetArray); - method.Branch(endOfOpcode); - - method.MarkLabel(blobVersionedHashNotFound); - method.CleanWord(stack, head); - method.Load(stack, head); - method.Call(Word.SetToZero); - - method.MarkLabel(endOfOpcode); - method.StackPush(head); + { + Label blobVersionedHashNotFound = method.DefineLabel(); + Label endOfOpcode = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetInt0); + method.StoreLocal(uint32A); + method.StackPop(head, 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.LoadNull(); + method.BranchIfEqual(blobVersionedHashNotFound); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); + method.LoadLocal(uint32A); + method.BranchIfLessOrEqual(blobVersionedHashNotFound); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); + method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); + method.LoadLocal(uint32A); + method.LoadElement(); + method.StoreLocal(localArray); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(localArray); + method.Call(Word.SetArray); + method.Branch(endOfOpcode); + + method.MarkLabel(blobVersionedHashNotFound); + method.CleanWord(stack, head); + method.Load(stack, head); + method.Call(Word.SetToZero); + + method.MarkLabel(endOfOpcode); + method.StackPush(head); + } break; case Instruction.BLOCKHASH: - Label blockHashReturnedNull = method.DefineLabel(); - Label pushToStackRegion = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt0); - method.Convert(); - method.LoadConstant(long.MaxValue); - method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); - method.StoreLocal(int64A); - method.StackPop(head, 1); - - method.LoadArgument(1); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); - method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); - method.LoadLocalAddress(int64A); - method.CallVirtual(typeof(IBlockhashProvider).GetMethod(nameof(IBlockhashProvider.GetBlockhash), [typeof(BlockHeader), typeof(long).MakeByRefType()])); - method.Duplicate(); - method.StoreLocal(hash256); - method.LoadNull(); - method.BranchIfEqual(blockHashReturnedNull); - - // not equal - method.LoadLocal(hash256); - method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); - method.StoreLocal(localReadonOnlySpan); - method.Branch(pushToStackRegion); - // equal to null - - method.MarkLabel(blockHashReturnedNull); - - method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); - method.StoreLocal(localReadonOnlySpan); - - method.MarkLabel(pushToStackRegion); - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + Label blockHashReturnedNull = method.DefineLabel(); + Label pushToStackRegion = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt0); + method.Convert(); + method.LoadConstant(long.MaxValue); + method.Call(typeof(Math).GetMethod(nameof(Math.Min), [typeof(long), typeof(long)])); + method.StoreLocal(int64A); + method.StackPop(head, 1); + + method.LoadArgument(1); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); + method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); + method.LoadLocalAddress(int64A); + method.CallVirtual(typeof(IBlockhashProvider).GetMethod(nameof(IBlockhashProvider.GetBlockhash), [typeof(BlockHeader), typeof(long).MakeByRefType()])); + method.Duplicate(); + method.StoreLocal(hash256); + method.LoadNull(); + method.BranchIfEqual(blockHashReturnedNull); + + // not equal + method.LoadLocal(hash256); + method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); + method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.StoreLocal(localReadonOnlySpan); + method.Branch(pushToStackRegion); + // equal to null + + method.MarkLabel(blockHashReturnedNull); + + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); + method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.StoreLocal(localReadonOnlySpan); + + method.MarkLabel(pushToStackRegion); + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadConstant(BitConverter.IsLittleEndian); + method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.SIGNEXTEND: - Label signIsNegative = method.DefineLabel(); - Label endOfOpcodeHandling = method.DefineLabel(); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt0); - method.StoreLocal(uint32A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetSpan); - method.StoreLocal(localSpan); - method.StackPop(head, 2); - - method.LoadConstant((uint)31); - method.LoadLocal(uint32A); - method.Subtract(); - method.StoreLocal(uint32A); - - method.LoadLocal(localArray); - method.LoadLocal(uint32A); - method.LoadElement(); - method.Convert(); - method.LoadConstant((sbyte)0); - method.BranchIfLess(signIsNegative); - - method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); - method.Branch(endOfOpcodeHandling); - - method.MarkLabel(signIsNegative); - method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesMax32))); - - method.MarkLabel(endOfOpcodeHandling); - method.LoadConstant(0); - method.LoadLocal(uint32A); - method.Convert(); - method.Call(typeof(System.MemoryExtensions).GetMethod(nameof(System.MemoryExtensions.AsSpan), [typeof(byte[]), typeof(int), typeof(int)])); - method.LoadLocalAddress(localSpan); - method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); + { + Label signIsNegative = method.DefineLabel(); + Label endOfOpcodeHandling = method.DefineLabel(); + Label argumentGt32 = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt0); + method.StoreLocal(uint32A); + method.StackPop(head, 1); + + method.LoadLocal(uint32A); + method.LoadConstant(32); + method.BranchIfGreaterOrEqual(argumentGt32); + + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetArray); + method.StoreLocal(localArray); + + method.LoadConstant((uint)31); + method.LoadLocal(uint32A); + method.Subtract(); + method.StoreLocal(uint32A); + + method.LoadLocal(localArray); + method.LoadLocal(uint32A); + method.LoadElement(); + method.Convert(); + method.LoadConstant((sbyte)0); + method.BranchIfLess(signIsNegative); + + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); + method.Branch(endOfOpcodeHandling); + + method.MarkLabel(signIsNegative); + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesMax32))); + + method.MarkLabel(endOfOpcodeHandling); + method.LoadConstant(0); + method.LoadLocal(uint32A); + method.EmitAsSpan(); + method.StoreLocal(localSpan); + + using Local tempLocalSpan = method.DeclareLocal(typeof(Span)); + method.LoadLocal(localArray); + method.Duplicate(); + method.Call(GetPropertyInfo(typeof(byte[]), nameof(Array.Length), false, out _)); + method.StoreLocal(uint32B); + method.LoadConstant(0); + method.LoadLocal(uint32B); + method.EmitAsSpan(); + method.StoreLocal(tempLocalSpan); + + method.LoadLocalAddress(localSpan); + method.LoadLocal(tempLocalSpan); + method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); + method.MarkLabel(argumentGt32); + } break; case Instruction.LOG0: case Instruction.LOG1: case Instruction.LOG2: case Instruction.LOG3: case Instruction.LOG4: - sbyte topicsCount = (sbyte)(op.Operation - Instruction.LOG0); - EmitLogMethod(method, (stack, head), topicsCount, evmExceptionLabels, uint256A, uint256B, int64A, gasAvailable, hash256, localReadOnlyMemory); + { + sbyte topicsCount = (sbyte)(op.Operation - Instruction.LOG0); + EmitLogMethod(method, (stack, head), topicsCount, evmExceptionLabels, uint256A, uint256B, int64A, gasAvailable, hash256, localReadOnlyMemory); + } break; case Instruction.TSTORE: - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); - method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetArray); - method.StoreLocal(localArray); - - method.StackPop(head, 2); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); - method.LoadLocalAddress(uint256A); - method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); - method.StoreLocal(storageCell); - - method.LoadArgument(2); - method.LoadLocalAddress(storageCell); - method.LoadLocal(localArray); - method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); + { + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); + method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetArray); + method.StoreLocal(localArray); + + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.LoadLocal(localArray); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.SetTransientState), [typeof(StorageCell).MakeByRefType(), typeof(byte[])])); + } break; case Instruction.TLOAD: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head, 1); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); - method.LoadLocalAddress(uint256A); - method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); - method.StoreLocal(storageCell); - - method.LoadArgument(2); - method.LoadLocalAddress(storageCell); - method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); - method.StoreLocal(localReadonOnlySpan); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(localReadonOnlySpan); - method.Call(Word.SetSpan); - method.StackPush(head); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(localReadonOnlySpan); + method.Call(Word.SetSpan); + method.StackPush(head); + } break; - case Instruction.SSTORE: - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetSpan); - method.StoreLocal(localReadonOnlySpan); - method.StackPop(head, 2); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadArgument(2); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - - MethodInfo sstoreMethod = - typeof(VirtualMachine) - .GetMethod(nameof(VirtualMachine.InstructionSStore), BindingFlags.Static | BindingFlags.NonPublic) - .MakeGenericMethod(typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing)); - method.Call(sstoreMethod); - - endOfOpcode = method.DefineLabel(); - method.Duplicate(); - method.StoreLocal(uint32A); - method.LoadConstant((int)EvmExceptionType.None); - method.BranchIfEqual(endOfOpcode); - - method.LoadArgument(0); - method.LoadLocal(uint32A); - method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); - method.Branch(exit); - - method.MarkLabel(endOfOpcode); + { + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetSpan); + method.StoreLocal(localReadonOnlySpan); + method.StackPop(head, 2); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadArgument(2); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + + MethodInfo sstoreMethod = + typeof(VirtualMachine) + .GetMethod(nameof(VirtualMachine.InstructionSStore), BindingFlags.Static | BindingFlags.NonPublic) + .MakeGenericMethod(typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing), typeof(VirtualMachine.NotTracing)); + method.Call(sstoreMethod); + + Label endOfOpcode = method.DefineLabel(); + method.Duplicate(); + method.StoreLocal(uint32A); + method.LoadConstant((int)EvmExceptionType.None); + method.BranchIfEqual(endOfOpcode); + + method.LoadArgument(0); + method.LoadLocal(uint32A); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); + method.Branch(exit); + + method.MarkLabel(endOfOpcode); + } break; case Instruction.SLOAD: - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetSLoadCost))); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head, 1); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); - method.LoadLocalAddress(uint256A); - method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); - method.StoreLocal(storageCell); - - method.LoadLocalAddress(gasAvailable); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadLocalAddress(storageCell); - method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeStorageAccessGas), BindingFlags.Static | BindingFlags.NonPublic)); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(2); - method.LoadLocalAddress(storageCell); - method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.Get), [typeof(StorageCell).MakeByRefType()])); - method.StoreLocal(localReadonOnlySpan); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(localReadonOnlySpan); - method.Call(Word.SetSpan); - method.StackPush(head); + { + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetSLoadCost))); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackPop(head, 1); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.LoadLocalAddress(uint256A); + method.NewObject(typeof(StorageCell), [typeof(Address), typeof(UInt256).MakeByRefType()]); + method.StoreLocal(storageCell); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(storageCell); + method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeStorageAccessGas), BindingFlags.Static | BindingFlags.NonPublic)); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(2); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.Get), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(localReadonOnlySpan); + method.Call(Word.SetSpan); + method.StackPush(head); + } break; - case Instruction.EXTCODESIZE: - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetAddress); - method.StoreLocal(address); - method.StackPop(head, 1); - - method.LoadLocalAddress(gasAvailable); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadLocal(address); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - method.LoadConstant(true); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.CleanWord(stack, head); - method.Load(stack, head); - - method.LoadArgument(3); - method.LoadArgument(2); - method.LoadLocal(address); - method.LoadArgument(4); - method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); - method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); - method.StoreLocal(localReadOnlyMemory); - method.LoadLocalAddress(localReadOnlyMemory); - method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - - method.Call(Word.SetInt0); - method.StackPush(head); + { + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.CleanWord(stack, head); + method.Load(stack, head); + + method.LoadArgument(3); + method.LoadArgument(2); + method.LoadLocal(address); + method.LoadArgument(4); + method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); + method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); + method.StoreLocal(localReadOnlyMemory); + method.LoadLocalAddress(localReadOnlyMemory); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + + method.Call(Word.SetInt0); + method.StackPush(head); + } break; - case Instruction.EXTCODECOPY: - endOfOpcode = method.DefineLabel(); - - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetAddress); - method.StoreLocal(address); - method.StackLoadPrevious(stack, head, 2); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackLoadPrevious(stack, head, 3); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256B); - method.StackLoadPrevious(stack, head, 4); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); - method.StackPop(head, 4); - - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); - method.LoadConstant(GasCostOf.Memory); - method.Multiply(); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.LoadLocalAddress(gasAvailable); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadLocal(address); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - method.LoadConstant(true); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadLocalAddress(uint256C); - method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); - method.BranchIfTrue(endOfOpcode); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(gasAvailable); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256C); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(3); - method.LoadArgument(2); - method.LoadLocal(address); - method.LoadArgument(4); - method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); - method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); - - method.LoadLocalAddress(uint256B); - method.LoadLocal(uint256C); - method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); - method.Convert(); - method.LoadConstant((int)PadDirection.Right); - method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); - method.StoreLocal(localZeroPaddedSpan); - - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(localZeroPaddedSpan); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); - - method.MarkLabel(endOfOpcode); + { + Label endOfOpcode = method.DefineLabel(); + + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackLoadPrevious(stack, head, 2); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256A); + method.StackLoadPrevious(stack, head, 3); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256B); + method.StackLoadPrevious(stack, head, 4); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.StackPop(head, 4); + + method.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256C); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling))); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(3); + method.LoadArgument(2); + method.LoadLocal(address); + method.LoadArgument(4); + method.CallVirtual(typeof(ICodeInfoRepository).GetMethod(nameof(ICodeInfoRepository.GetCachedCodeInfo), [typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); + method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); + + method.LoadLocalAddress(uint256B); + method.LoadLocal(uint256C); + method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + method.Convert(); + method.LoadConstant((int)PadDirection.Right); + method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); + method.StoreLocal(localZeroPaddedSpan); + + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Save), [typeof(UInt256).MakeByRefType(), typeof(ZeroPaddedSpan).MakeByRefType()])); + + method.MarkLabel(endOfOpcode); + } break; case Instruction.EXTCODEHASH: - endOfOpcode = method.DefineLabel(); - - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeHashCost))); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetAddress); - method.StoreLocal(address); - method.StackPop(head, 1); - - method.LoadLocalAddress(gasAvailable); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadLocal(address); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - method.LoadConstant(true); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadArgument(2); - method.LoadLocal(address); - method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.AccountExists))); - method.LoadConstant(false); - method.CompareEqual(); - method.LoadArgument(2); - method.LoadLocal(address); - method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.IsDeadAccount))); - method.Or(); - method.BranchIfTrue(endOfOpcode); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(2); - method.LoadLocal(address); - method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); - method.Call(Word.SetKeccak); - method.MarkLabel(endOfOpcode); - method.StackPush(head); + { + Label endOfOpcode = method.DefineLabel(); + + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeHashCost))); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.AccountExists))); + method.LoadConstant(false); + method.CompareEqual(); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.IsDeadAccount))); + method.Or(); + method.BranchIfTrue(endOfOpcode); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); + method.Call(Word.SetKeccak); + method.MarkLabel(endOfOpcode); + method.StackPush(head); + } break; case Instruction.SELFBALANCE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(2); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); - method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; case Instruction.BALANCE: - method.LoadLocal(gasAvailable); - method.LoadArgument(4); - method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetBalanceCost))); - method.Subtract(); - method.StoreLocal(gasAvailable); - - method.StackLoadPrevious(stack, head, 1); - method.Call(Word.GetAddress); - method.StoreLocal(address); - method.StackPop(head, 1); - - method.LoadLocalAddress(gasAvailable); - method.LoadArgument(0); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - method.LoadLocal(address); - method.LoadArgument(4); - method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); - method.LoadConstant(true); - method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); - method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(2); - method.LoadLocal(address); - method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); - method.Call(Word.SetUInt256); - method.StackPush(head); + { + method.LoadLocal(gasAvailable); + method.LoadArgument(4); + method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetBalanceCost))); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetAddress); + method.StoreLocal(address); + method.StackPop(head, 1); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadArgument(4); + method.Call(GetPropertyInfo(nameof(NullTxTracer.Instance), false, out _)); + method.LoadConstant(true); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadArgument(2); + method.LoadLocal(address); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); + method.Call(Word.SetUInt256); + method.StackPush(head); + } break; default: throw new NotSupportedException(); @@ -1734,6 +1905,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co //check if jump crosses segment boundaies Label jumpIsLocal = method.DefineLabel(); + Label jumpIsNotLocal = method.DefineLabel(); int maxJump = code[^1].ProgramCounter + code[^1].Metadata.AdditionalBytes; int minJump = code[0].ProgramCounter; @@ -1741,17 +1913,16 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // if (jumpDest <= maxJump) method.LoadLocal(jmpDestination); method.LoadConstant(maxJump); - method.CompareLessThan(); + method.BranchIfGreater(jumpIsNotLocal); // if (jumpDest >= minJump) method.LoadLocal(jmpDestination); method.LoadConstant(minJump); - method.CompareGreaterThan(); - - method.And(); + method.BranchIfLess(jumpIsNotLocal); - method.BranchIfTrue(jumpIsLocal); + method.Branch(jumpIsLocal); + method.MarkLabel(jumpIsNotLocal); method.LoadArgument(0); method.Duplicate(); method.LoadConstant(true); @@ -1765,7 +1936,9 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.MarkLabel(jumpIsLocal); + // if (jumpDest > uint.MaxValue) + method.Print(jmpDestination); method.LoadConstant(uint.MaxValue); method.LoadLocal(jmpDestination); // goto invalid address @@ -2214,7 +2387,6 @@ private static void EmitLogMethod( il.Subtract(); il.Duplicate(); il.StoreLocal(gasAvailable); // gasAvailable -= gasCost - il.LoadConstant((ulong)0); il.BranchIfLess(exceptions[EvmExceptionType.OutOfGas]); @@ -2257,7 +2429,7 @@ private static void EmitGasAvailabilityCheck( private static Dictionary BuildCostLookup(ReadOnlySpan code) { Dictionary costs = new(); - int costStart = 0; + int costStart = code[0].ProgramCounter; long coststack = 0; for (int pc = 0; pc < code.Length; pc++) @@ -2267,19 +2439,18 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co { case Instruction.JUMPDEST: costs[costStart] = coststack; // remember the stack chain of opcodes - costStart = pc; + costStart = op.ProgramCounter; coststack = op.Metadata.GasCost; break; case Instruction.JUMPI: case Instruction.JUMP: coststack += op.Metadata.GasCost; costs[costStart] = coststack; // remember the stack chain of opcodes - costStart = pc + 1; // start with the next again + costStart = op.ProgramCounter + 1; // start with the next again coststack = 0; break; default: coststack += op.Metadata.GasCost; - costs[pc] = coststack; break; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 3fb8e5aa740..86178731188 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -4,6 +4,7 @@ using Nethermind.Core; using Nethermind.Db; using Nethermind.Evm.CodeAnalysis.IL; +using Org.BouncyCastle.Tls; using Sigil; using System; using System.Diagnostics; @@ -106,6 +107,11 @@ public static void StackPush(this Emit il, Local idx, int count = 1) il.StoreLocal(idx); } + public static MethodInfo MethodInfo(string name,Type returnType, Type[] argTypes, BindingFlags flags = BindingFlags.Public) + { + return typeof(T).GetMethods().First(m => m.Name == name && m.ReturnType == returnType && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(argTypes)); + } + /// /// Moves the stack words down. /// @@ -117,6 +123,16 @@ public static void StackPop(this Emit il, Local idx, int count = 1) il.StoreLocal(idx); } + public static void EmitAsSpan(this Emit il) + { + MethodInfo method = typeof(System.MemoryExtensions).GetMethods() + .Where(m => m.Name == nameof(System.MemoryExtensions.AsSpan) + && m.GetParameters().Length == 3 + && m.IsGenericMethod) + .First(); + il.Call(method.MakeGenericMethod(typeof(byte))); + } + /// /// Moves the stack words down. /// diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 64db74af871..1556773f7d4 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -260,22 +260,22 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.DUP15] = new(GasCostOf.VeryLow, 0, 15, 16), [Instruction.DUP16] = new(GasCostOf.VeryLow, 0, 16, 17), - [Instruction.SWAP1] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP2] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP3] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP4] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP5] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP6] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP7] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP8] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP9] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP10] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP11] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP12] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP13] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP14] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP15] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP16] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP1] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP2] = new(GasCostOf.VeryLow, 0, 3, 3), + [Instruction.SWAP3] = new(GasCostOf.VeryLow, 0, 4, 4), + [Instruction.SWAP4] = new(GasCostOf.VeryLow, 0, 5, 5), + [Instruction.SWAP5] = new(GasCostOf.VeryLow, 0, 6, 6), + [Instruction.SWAP6] = new(GasCostOf.VeryLow, 0, 7, 7), + [Instruction.SWAP7] = new(GasCostOf.VeryLow, 0, 8, 8), + [Instruction.SWAP8] = new(GasCostOf.VeryLow, 0, 9, 9), + [Instruction.SWAP9] = new(GasCostOf.VeryLow, 0, 10, 10), + [Instruction.SWAP10] = new(GasCostOf.VeryLow, 0, 11, 11), + [Instruction.SWAP11] = new(GasCostOf.VeryLow, 0, 12, 12), + [Instruction.SWAP12] = new(GasCostOf.VeryLow, 0, 13, 13), + [Instruction.SWAP13] = new(GasCostOf.VeryLow, 0, 14, 14), + [Instruction.SWAP14] = new(GasCostOf.VeryLow, 0, 15, 15), + [Instruction.SWAP15] = new(GasCostOf.VeryLow, 0, 16, 16), + [Instruction.SWAP16] = new(GasCostOf.VeryLow, 0, 17, 17), [Instruction.ADD] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.MUL] = new(GasCostOf.Low, 0, 2, 1), @@ -303,7 +303,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.SAR] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.BYTE] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.KECCAK256] = new(GasCostOf.VeryLow, 0, 2, 1), + [Instruction.KECCAK256] = new(GasCostOf.Sha3, 0, 2, 1), [Instruction.ADDRESS] = new(GasCostOf.Base, 0, 0, 1), [Instruction.BALANCE] = new(0, 0, 1, 1), // we need call GetBalanceCost in ILCompiler [Instruction.ORIGIN] = new(GasCostOf.Base, 0, 0, 1), From dc2752f17a37673180548649faf88b39e91f2cd1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 1 Oct 2024 13:41:30 +0100 Subject: [PATCH 091/146] format whitespace --- .../CodeAnalysis/IlEvmTests.cs | 2 +- .../CodeAnalysis/IL/ILCompiler.cs | 4 ++-- .../CodeAnalysis/IL/ILExtensions.cs | 2 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 18 +++++++++--------- .../Steps/InitializeBlockchain.cs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 6b169f01c15..83308034eb0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -731,7 +731,7 @@ public void All_Stateless_Opcodes_Are_Covered_in_JIT_Tests() var tests = GeJitBytecodesSamples().Select(test => test.Item1); - + List notCovered = new List(); foreach (var opcode in instructions) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 4d67c27e04a..78075004591 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -158,7 +158,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(gasAvailable); } - if(i == code.Length - 1) + if (i == code.Length - 1) { method.LoadConstant(op.ProgramCounter + op.Metadata.AdditionalBytes); method.StoreLocal(programCounter); @@ -171,7 +171,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfLess(evmExceptionLabels[EvmExceptionType.StackUnderflow]); } - if(op.Metadata.StackBehaviorPush > 0) + if (op.Metadata.StackBehaviorPush > 0) { int delta = op.Metadata.StackBehaviorPush - op.Metadata.StackBehaviorPop; method.LoadLocal(head); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 86178731188..9b897b95c6c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -107,7 +107,7 @@ public static void StackPush(this Emit il, Local idx, int count = 1) il.StoreLocal(idx); } - public static MethodInfo MethodInfo(string name,Type returnType, Type[] argTypes, BindingFlags flags = BindingFlags.Public) + public static MethodInfo MethodInfo(string name, Type returnType, Type[] argTypes, BindingFlags flags = BindingFlags.Public) { return typeof(T).GetMethods().First(m => m.Name == name && m.ReturnType == returnType && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(argTypes)); } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 1556773f7d4..74af89212e9 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -260,15 +260,15 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.DUP15] = new(GasCostOf.VeryLow, 0, 15, 16), [Instruction.DUP16] = new(GasCostOf.VeryLow, 0, 16, 17), - [Instruction.SWAP1] = new(GasCostOf.VeryLow, 0, 2, 2), - [Instruction.SWAP2] = new(GasCostOf.VeryLow, 0, 3, 3), - [Instruction.SWAP3] = new(GasCostOf.VeryLow, 0, 4, 4), - [Instruction.SWAP4] = new(GasCostOf.VeryLow, 0, 5, 5), - [Instruction.SWAP5] = new(GasCostOf.VeryLow, 0, 6, 6), - [Instruction.SWAP6] = new(GasCostOf.VeryLow, 0, 7, 7), - [Instruction.SWAP7] = new(GasCostOf.VeryLow, 0, 8, 8), - [Instruction.SWAP8] = new(GasCostOf.VeryLow, 0, 9, 9), - [Instruction.SWAP9] = new(GasCostOf.VeryLow, 0, 10, 10), + [Instruction.SWAP1] = new(GasCostOf.VeryLow, 0, 2, 2), + [Instruction.SWAP2] = new(GasCostOf.VeryLow, 0, 3, 3), + [Instruction.SWAP3] = new(GasCostOf.VeryLow, 0, 4, 4), + [Instruction.SWAP4] = new(GasCostOf.VeryLow, 0, 5, 5), + [Instruction.SWAP5] = new(GasCostOf.VeryLow, 0, 6, 6), + [Instruction.SWAP6] = new(GasCostOf.VeryLow, 0, 7, 7), + [Instruction.SWAP7] = new(GasCostOf.VeryLow, 0, 8, 8), + [Instruction.SWAP8] = new(GasCostOf.VeryLow, 0, 9, 9), + [Instruction.SWAP9] = new(GasCostOf.VeryLow, 0, 10, 10), [Instruction.SWAP10] = new(GasCostOf.VeryLow, 0, 11, 11), [Instruction.SWAP11] = new(GasCostOf.VeryLow, 0, 12, 12), [Instruction.SWAP12] = new(GasCostOf.VeryLow, 0, 13, 13), diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 64ebf986d75..cbfc2536448 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -186,7 +186,7 @@ protected VirtualMachine CreateVirtualMachine(CodeInfoRepository codeInfoReposit BlockhashProvider blockhashProvider = new( _api.BlockTree, _api.SpecProvider, _api.WorldState, _api.LogManager); - if(_api.VMConfig is not null && _api.VMConfig.IsPatternMatchingEnabled) + if (_api.VMConfig is not null && _api.VMConfig.IsPatternMatchingEnabled) { IlAnalyzer.Initialize(); } From da2343e3c0b66c28a3bc353adaf767f71935494b Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 1 Oct 2024 14:22:35 +0100 Subject: [PATCH 092/146] fix GAS opcode --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 78075004591..73c174c472e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2442,6 +2442,7 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co costStart = op.ProgramCounter; coststack = op.Metadata.GasCost; break; + case Instruction.GAS: case Instruction.JUMPI: case Instruction.JUMP: coststack += op.Metadata.GasCost; From d65166b5a899fc3c0a0407c50d6de199eb431274 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 1 Oct 2024 17:35:23 +0100 Subject: [PATCH 093/146] Added Logging --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 4 ++-- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 5 +++-- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 12 +++++++----- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 6 +++++- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 83308034eb0..d8a8cf9ad23 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -760,7 +760,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching, NullLogger.Instance); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -789,7 +789,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling, NullLogger.Instance); codeInfo.IlInfo.Segments.Count.Should().Be(2); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 3ab93d3e4d6..be4860b756e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -9,6 +9,7 @@ using Nethermind.Evm.Tracing; using Nethermind.Core.Crypto; using Nethermind.Evm.Config; +using Nethermind.Logging; namespace Nethermind.Evm.CodeAnalysis { @@ -22,7 +23,7 @@ public class CodeInfo : IThreadPoolWorkItem // IL-EVM private int _callCount; - public async void NoticeExecution(IVMConfig vmConfig) + public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) { // IL-EVM info already created if (_callCount > Math.Max(vmConfig.JittingThreshold, vmConfig.PatternMatchingThreshold)) @@ -40,7 +41,7 @@ public async void NoticeExecution(IVMConfig vmConfig) if (mode == IlInfo.ILMode.NoIlvm) return; - await IlAnalyzer.StartAnalysis(this, mode).ConfigureAwait(false); + await IlAnalyzer.StartAnalysis(this, mode, logger).ConfigureAwait(false); } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 3112588e135..9237a2d73d8 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -5,14 +5,13 @@ using Nethermind.Core.Extensions; using Nethermind.Evm.Tracing; using Nethermind.Int256; +using Nethermind.Logging; using System; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; -using static System.Net.Mime.MediaTypeNames; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -58,9 +57,10 @@ public static void Initialize() /// Starts the analyzing in a background task and outputs the value in the . /// thou /// The destination output. - internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode) + internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogger logger) { - return Task.Run(() => Analysis(codeInfo, mode)); + if(logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); + return Task.Run(() => Analysis(codeInfo, mode, logger)); } public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) @@ -88,7 +88,7 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode) + private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogger logger) { ReadOnlyMemory machineCode = codeInfo.MachineCode; @@ -162,9 +162,11 @@ static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) switch (mode) { case IlInfo.ILMode.PatternMatching: + if(logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.CodeHash}"); CheckPatterns(machineCode, codeInfo.IlInfo); break; case IlInfo.ILMode.SubsegmentsCompiling: + if(logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo); break; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index f2d651189c6..b80228cb876 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -6,6 +6,7 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; +using Nethermind.Logging; using Nethermind.State; using NonBlocking; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; @@ -64,7 +65,7 @@ public IlInfo(ConcurrentDictionary mappedOpcodes, Conc public ConcurrentDictionary Chunks { get; } = new(); public ConcurrentDictionary Segments { get; } = new(); - public bool TryExecute(EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ITxTracer tracer, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out ILChunkExecutionResult? result) + public bool TryExecute(ILogger logger, EvmState vmState, ulong chainId, ref ReadOnlyMemory outputBuffer, IWorldState worldState, IBlockhashProvider blockHashProvider, ICodeInfoRepository codeinfoRepository, IReleaseSpec spec, ITxTracer tracer, ref int programCounter, ref long gasAvailable, ref EvmStack stack, out ILChunkExecutionResult? result) where TTracingInstructions : struct, VirtualMachine.IIsTracing { result = null; @@ -74,10 +75,12 @@ public bool TryExecute(EvmState vmState, ulong chainId, re var executionResult = new ILChunkExecutionResult(); if (Mode.HasFlag(ILMode.SubsegmentsCompiling) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { + vmState.DataStackHead = stack.Head; if (typeof(TTracingInstructions) == typeof(IsTracing)) StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, ctx); + if(logger.IsInfo) logger.Info($"Executing segment {ctx.Name} at {programCounter}"); var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); @@ -102,6 +105,7 @@ public bool TryExecute(EvmState vmState, ulong chainId, re { if (typeof(TTracingInstructions) == typeof(IsTracing)) StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, chunk); + if (logger.IsInfo) logger.Info($"Executing segment {chunk.Name} at {programCounter}"); chunk.Invoke(vmState, blockHashProvider, worldState, codeinfoRepository, spec, ref programCounter, ref gasAvailable, ref stack, ref executionResult); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 3a870dff27e..10702cac053 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -640,7 +640,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM if (_vmConfig.IsVmOptimizationEnabled) { - vmState.Env.CodeInfo.NoticeExecution(_vmConfig); + vmState.Env.CodeInfo.NoticeExecution(_vmConfig, _logger); } } @@ -734,7 +734,7 @@ private CallResult ExecuteCode Date: Wed, 2 Oct 2024 13:56:44 +0100 Subject: [PATCH 094/146] Added some fields in Metrics API --- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 2 ++ src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 7 ++++--- src/Nethermind/Nethermind.Evm/Metrics.cs | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 9237a2d73d8..e30d4dd29bd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -59,6 +59,8 @@ public static void Initialize() /// The destination output. internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogger logger) { + Metrics.IlvmContractsAnalyzed++; + if(logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); return Task.Run(() => Analysis(codeInfo, mode, logger)); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index b80228cb876..fbdeac65699 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -75,13 +75,12 @@ public bool TryExecute(ILogger logger, EvmState vmState, u var executionResult = new ILChunkExecutionResult(); if (Mode.HasFlag(ILMode.SubsegmentsCompiling) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { - - vmState.DataStackHead = stack.Head; - + Metrics.IlvmPrecompiledSegmentsExecutions++; if (typeof(TTracingInstructions) == typeof(IsTracing)) StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, ctx); if(logger.IsInfo) logger.Info($"Executing segment {ctx.Name} at {programCounter}"); + vmState.DataStackHead = stack.Head; var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); @@ -103,6 +102,8 @@ public bool TryExecute(ILogger logger, EvmState vmState, u } else if (Mode.HasFlag(ILMode.PatternMatching) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { + Metrics.IlvmPredefinedPatternsExecutions++; + if (typeof(TTracingInstructions) == typeof(IsTracing)) StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, chunk); if (logger.IsInfo) logger.Info($"Executing segment {chunk.Name} at {programCounter}"); diff --git a/src/Nethermind/Nethermind.Evm/Metrics.cs b/src/Nethermind/Nethermind.Evm/Metrics.cs index c4efd23b75a..d4a5d19c640 100644 --- a/src/Nethermind/Nethermind.Evm/Metrics.cs +++ b/src/Nethermind/Nethermind.Evm/Metrics.cs @@ -85,6 +85,15 @@ public class Metrics [Description("Number of Point Evaluation precompile calls.")] public static long PointEvaluationPrecompile { get; set; } + [Description("Number of contracts analyzed by ILVM")] // "ILVM" is an abbreviation for "Intermediate Language Virtual Machine + public static long IlvmContractsAnalyzed { get; set; } + + [Description("Number of ILVM predefined pattern executions.")] + public static long IlvmPredefinedPatternsExecutions { get; set; } + + [Description("Number of ILVM precompiled segment executions.")] + public static long IlvmPrecompiledSegmentsExecutions { get; set; } + [CounterMetric] [Description("Number of calls made to addresses without code.")] public static long EmptyCalls => _emptyCalls.GetTotalValue(); From d6a593b9e307dffdaceb97da35560b0cbb2c5a56 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 2 Oct 2024 13:59:29 +0100 Subject: [PATCH 095/146] fx whitespace formatting --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 6 +++--- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index e30d4dd29bd..6dacd44a4de 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -61,7 +61,7 @@ internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogge { Metrics.IlvmContractsAnalyzed++; - if(logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); return Task.Run(() => Analysis(codeInfo, mode, logger)); } @@ -164,11 +164,11 @@ static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) switch (mode) { case IlInfo.ILMode.PatternMatching: - if(logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.CodeHash}"); CheckPatterns(machineCode, codeInfo.IlInfo); break; case IlInfo.ILMode.SubsegmentsCompiling: - if(logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo); break; } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index fbdeac65699..37257dbdcfd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -78,7 +78,7 @@ public bool TryExecute(ILogger logger, EvmState vmState, u Metrics.IlvmPrecompiledSegmentsExecutions++; if (typeof(TTracingInstructions) == typeof(IsTracing)) StartTracingSegment(in vmState, in stack, tracer, programCounter, gasAvailable, ctx); - if(logger.IsInfo) logger.Info($"Executing segment {ctx.Name} at {programCounter}"); + if (logger.IsInfo) logger.Info($"Executing segment {ctx.Name} at {programCounter}"); vmState.DataStackHead = stack.Head; var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); From d92fc06e608f92a2ef8da657426ed574e44eec8e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 2 Oct 2024 23:09:51 +0100 Subject: [PATCH 096/146] fix test pattern implimentation --- src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index d8a8cf9ad23..b87edf01689 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -68,7 +68,7 @@ internal class SomeAfterTwoPush : InstructionChunk public long GasCost(EvmState vmState, IReleaseSpec spec) { - long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow + GasCostOf.Base; + long gasCost = GasCostOf.VeryLow * 3; return gasCost; } From 4246e5d62a90fad5f1804f88298c47fd907cb5ed Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 8 Oct 2024 17:47:54 +0100 Subject: [PATCH 097/146] various fixes --- .../Extensions/IntExtensions.cs | 5 +++++ .../CodeAnalysis/IlEvmTests.cs | 4 ++-- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 13 ++++++------ .../CodeAnalysis/IL/ILCompiler.cs | 21 +++++++++---------- .../CodeAnalysis/IL/ILExtensions.cs | 5 +++++ .../CodeAnalysis/IL/IlAnalyzer.cs | 20 +++++++++++------- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 17 ++++++++------- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- 8 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs index 71505417f89..0648038ae70 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/IntExtensions.cs @@ -9,6 +9,11 @@ namespace Nethermind.Core.Extensions; public static class IntExtensions { + public static bool HasFlag(this int @this, int flag) + { + return (@this & flag) == flag; + } + public static string ToHexString(this int @this) { return $"0x{@this:x}"; diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index b87edf01689..2d040b16103 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -760,7 +760,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.PatternMatching, NullLogger.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, NullLogger.Instance); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -789,7 +789,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.SubsegmentsCompiling, NullLogger.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, NullLogger.Instance); codeInfo.IlInfo.Segments.Count.Should().Be(2); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index be4860b756e..4b6bf0d238b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -10,6 +10,7 @@ using Nethermind.Core.Crypto; using Nethermind.Evm.Config; using Nethermind.Logging; +using ILMode = int; namespace Nethermind.Evm.CodeAnalysis { @@ -31,15 +32,15 @@ public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) Interlocked.Increment(ref _callCount); // use Interlocked just in case of concurrent execution to run it only once - IlInfo.ILMode mode = + ILMode mode = vmConfig.IsPatternMatchingEnabled && _callCount == vmConfig.PatternMatchingThreshold - ? IlInfo.ILMode.PatternMatching + ? IlInfo.ILMode.PAT_MODE : vmConfig.IsJitEnabled && _callCount == vmConfig.JittingThreshold - ? IlInfo.ILMode.SubsegmentsCompiling - : IlInfo.ILMode.NoIlvm; + ? IlInfo.ILMode.JIT_MODE + : IlInfo.ILMode.NO_ILVM; - if (mode == IlInfo.ILMode.NoIlvm) - return; + if (mode == IlInfo.ILMode.NO_ILVM) + return; await IlAnalyzer.StartAnalysis(this, mode, logger).ConfigureAwait(false); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 73c174c472e..384d8964235 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -12,6 +12,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using static Nethermind.Evm.IL.EmitExtensions; @@ -33,7 +34,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? // Note(Ayman) : verify all endianness arguments and bytes - Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit method = Emit.NewDynamicMethod(segmentName, doVerify: false, strictBranchVerification: false); if (code.Length == 0) { @@ -139,7 +140,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(programCounter); } - // check if opcode is activated in current spec method.LoadArgument(4); method.LoadConstant((byte)op.Operation); @@ -192,12 +192,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadArgument(0); method.LoadConstant(true); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); - method.Branch(ret); - } - break; - case Instruction.INVALID: - { - method.Branch(evmExceptionLabels[EvmExceptionType.BadInstruction]); + method.FakeBranch(ret); } break; case Instruction.CHAINID: @@ -228,7 +223,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.JUMP: { // we jump into the jump table - method.Branch(jumpTable); + method.FakeBranch(jumpTable); } break; case Instruction.JUMPI: @@ -1845,7 +1840,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co } break; default: - throw new NotSupportedException(); + { + method.FakeBranch(evmExceptionLabels[EvmExceptionType.BadInstruction]); + } + break; } } @@ -2434,7 +2432,9 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co for (int pc = 0; pc < code.Length; pc++) { + OpcodeInfo op = code[pc]; + Debug.WriteLine(op); switch (op.Operation) { case Instruction.JUMPDEST: @@ -2460,7 +2460,6 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co { costs[costStart] = coststack; } - return costs; } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 9b897b95c6c..7df6a9a633a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -233,4 +233,9 @@ public static void LoadArray(this Emit il, ReadOnlySpan value) il.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); } + public static void FakeBranch(this Emit il, Sigil.Label label) + { + il.LoadConstant(true); + il.BranchIfTrue(label); + } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 6dacd44a4de..af4c6233951 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -10,8 +10,11 @@ using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; +using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; +using ILMode = int; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -57,7 +60,7 @@ public static void Initialize() /// Starts the analyzing in a background task and outputs the value in the . /// thou /// The destination output. - internal static Task StartAnalysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogger logger) + internal static Task StartAnalysis(CodeInfo codeInfo, ILMode mode, ILogger logger) { Metrics.IlvmContractsAnalyzed++; @@ -78,7 +81,7 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) { ushort immediatesCount = opcode - Instruction.PUSH0; - data.Add(machineCode.SliceWithZeroPadding((UInt256)i + 1, immediatesCount, PadDirection.Left).ToArray()); + data.Add(machineCode.SliceWithZeroPadding((UInt256)i + 1, immediatesCount).ToArray()); argsIndex = data.Count - 1; i += immediatesCount; } @@ -90,7 +93,7 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, IlInfo.ILMode mode, ILogger logger) + private static void Analysis(CodeInfo codeInfo, ILMode mode, ILogger logger) { ReadOnlyMemory machineCode = codeInfo.MachineCode; @@ -114,6 +117,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il } } + long segmentAvgSize = 0; for (int i = -1; i <= j; i++) { int start = i == -1 ? 0 : statefulOpcodeindex[i] + 1; @@ -132,10 +136,11 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il var lastOp = segment[^1]; var segmentName = GenerateName(firstOp.ProgramCounter..(lastOp.ProgramCounter + lastOp.Metadata.AdditionalBytes)); + segmentAvgSize += segment.Length; ilinfo.Segments.GetOrAdd((ushort)segment[0].ProgramCounter, CompileSegment(segmentName, segment, codeData.Item2)); } - ilinfo.Mode |= IlInfo.ILMode.SubsegmentsCompiling; + Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); } static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) @@ -158,16 +163,17 @@ static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) } } } - ilinfo.Mode |= IlInfo.ILMode.PatternMatching; + + Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.PAT_MODE); } switch (mode) { - case IlInfo.ILMode.PatternMatching: + case IlInfo.ILMode.PAT_MODE: if (logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.CodeHash}"); CheckPatterns(machineCode, codeInfo.IlInfo); break; - case IlInfo.ILMode.SubsegmentsCompiling: + case IlInfo.ILMode.JIT_MODE: if (logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo); break; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 37257dbdcfd..9ac0db28fdd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using Microsoft.IdentityModel.Tokens; using Nethermind.Core; +using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; using Nethermind.Logging; @@ -29,11 +30,11 @@ internal struct ILChunkExecutionResult public EvmExceptionType ExceptionType; } - public enum ILMode + public static class ILMode { - NoIlvm = 0, - PatternMatching = 1, - SubsegmentsCompiling = 2 + public const int NO_ILVM = 0; + public const int PAT_MODE = 1; + public const int JIT_MODE = 2; } /// @@ -44,7 +45,7 @@ public enum ILMode /// /// Represents what mode of IL-EVM is used. 0 is the default. [0 = No ILVM optimizations, 1 = Pattern matching, 2 = subsegments compiling] /// - public ILMode Mode = ILMode.NoIlvm; + public int Mode = ILMode.NO_ILVM; public bool IsEmpty => Chunks.Count == 0 && Segments.Count == 0; /// /// No overrides. @@ -69,11 +70,11 @@ public bool TryExecute(ILogger logger, EvmState vmState, u where TTracingInstructions : struct, VirtualMachine.IIsTracing { result = null; - if (programCounter > ushort.MaxValue || Mode == ILMode.NoIlvm) + if (programCounter > ushort.MaxValue || Mode == ILMode.NO_ILVM) return false; var executionResult = new ILChunkExecutionResult(); - if (Mode.HasFlag(ILMode.SubsegmentsCompiling) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) + if (Mode.HasFlag(ILMode.JIT_MODE) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { Metrics.IlvmPrecompiledSegmentsExecutions++; if (typeof(TTracingInstructions) == typeof(IsTracing)) @@ -100,7 +101,7 @@ public bool TryExecute(ILogger logger, EvmState vmState, u if (typeof(TTracingInstructions) == typeof(IsTracing)) tracer.ReportOperationRemainingGas(gasAvailable); } - else if (Mode.HasFlag(ILMode.PatternMatching) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) + else if (Mode.HasFlag(ILMode.PAT_MODE) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { Metrics.IlvmPredefinedPatternsExecutions++; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 74af89212e9..58b8f4bdfdc 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -370,7 +370,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav } public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) { - public OpcodeMetadata Metadata => OpcodeMetadata.Operations[instruction]; + public OpcodeMetadata Metadata => OpcodeMetadata.Operations.GetValueOrDefault(instruction, OpcodeMetadata.Operations[Instruction.INVALID]); public Instruction Operation => instruction; public ushort ProgramCounter => pc; public int? Arguments { get; set; } = argumentIndex; From ee4867420879ac02cffacc21d0bc8df384a18870 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 12 Oct 2024 03:35:22 +0100 Subject: [PATCH 098/146] * added cross segment jumping --- .../CodeAnalysis/IlEvmTests.cs | 7 ++-- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../CodeAnalysis/IL/ILCompiler.cs | 41 ++++++++++++------- .../CodeAnalysis/IL/IlAnalyzer.cs | 34 ++++++++++++--- .../Nethermind.Evm/Config/IVMConfig.cs | 5 +++ .../Nethermind.Evm/Config/VMConfig.cs | 1 + 6 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 5e6550a0965..c4bdbd19168 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -172,6 +172,7 @@ public override void Setup() { IsJitEnabled = true, IsPatternMatchingEnabled = true, + AggressiveJitMode = true, PatternMatchingThreshold = 4, JittingThreshold = 256, @@ -760,7 +761,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, NullLogger.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, NullLogger.Instance, config); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } @@ -789,7 +790,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, NullLogger.Instance); + await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, NullLogger.Instance, config); codeInfo.IlInfo.Segments.Count.Should().Be(2); } @@ -1025,7 +1026,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "AbortDestinationPattern" + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", }; string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 4b6bf0d238b..a4286561ddc 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -42,7 +42,7 @@ public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) if (mode == IlInfo.ILMode.NO_ILVM) return; - await IlAnalyzer.StartAnalysis(this, mode, logger).ConfigureAwait(false); + await IlAnalyzer.StartAnalysis(this, mode, logger, vmConfig).ConfigureAwait(false); } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 92b2a2b447c..a0a15023f56 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -100,6 +100,19 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local stack = method.DeclareLocal(typeof(Span)); using Local head = method.DeclareLocal(typeof(int)); + + Dictionary evmExceptionLabels = new(); + + foreach (var exception in Enum.GetValues()) + { + evmExceptionLabels.Add(exception, method.DefineLabel()); + } + + Label exit = method.DefineLabel(); // the label just before return + Label jumpTable = method.DefineLabel(); // jump table + Label isContinuation = method.DefineLabel(); // jump table + Label ret = method.DefineLabel(); + // allocate stack method.LoadArgument(VMSTATE_INDEX); method.Duplicate(); @@ -120,17 +133,11 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.StoreLocal(programCounter); - Dictionary evmExceptionLabels = new(); - - foreach (var exception in Enum.GetValues()) - { - evmExceptionLabels.Add(exception, method.DefineLabel()); - } - - Label exit = method.DefineLabel(); // the label just before return - Label jumpTable = method.DefineLabel(); // jump table - Label ret = method.DefineLabel(); - + // if last ilvmstate was a jump + method.LoadLocal(programCounter); + method.LoadConstant(code[0].ProgramCounter); + method.CompareEqual(); + method.BranchIfFalse(isContinuation); Dictionary jumpDestinations = new(); @@ -1912,6 +1919,15 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // go to return method.Branch(exit); + Label jumpIsLocal = method.DefineLabel(); + Label jumpIsNotLocal = method.DefineLabel(); + + // isContinuation + method.MarkLabel(isContinuation); + method.LoadLocal(programCounter); + method.StoreLocal(jmpDestination); + method.Branch(jumpIsLocal); + // jump table method.MarkLabel(jumpTable); method.StackLoadPrevious(stack, head, 1); @@ -1925,9 +1941,6 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(consumeJumpCondition); //check if jump crosses segment boundaies - Label jumpIsLocal = method.DefineLabel(); - Label jumpIsNotLocal = method.DefineLabel(); - int maxJump = code[^1].ProgramCounter + code[^1].Metadata.AdditionalBytes; int minJump = code[0].ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index af4c6233951..dcb1a8d303c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -3,6 +3,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Evm.Config; using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; @@ -60,12 +61,12 @@ public static void Initialize() /// Starts the analyzing in a background task and outputs the value in the . /// thou /// The destination output. - internal static Task StartAnalysis(CodeInfo codeInfo, ILMode mode, ILogger logger) + internal static Task StartAnalysis(CodeInfo codeInfo, ILMode mode, ILogger logger, IVMConfig vmConfig) { Metrics.IlvmContractsAnalyzed++; if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); - return Task.Run(() => Analysis(codeInfo, mode, logger)); + return Task.Run(() => Analysis(codeInfo, mode, vmConfig, logger)); } public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) @@ -93,11 +94,11 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, ILMode mode, ILogger logger) + private static void Analysis(CodeInfo codeInfo, ILMode mode, IVMConfig vmConfig, ILogger logger) { ReadOnlyMemory machineCode = codeInfo.MachineCode; - static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, IlInfo ilinfo) + static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, IlInfo ilinfo, IVMConfig vmConfig) { if (codeData.Item1.Length == 0) { @@ -137,7 +138,28 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il var segmentName = GenerateName(firstOp.ProgramCounter..(lastOp.ProgramCounter + lastOp.Metadata.AdditionalBytes)); segmentAvgSize += segment.Length; - ilinfo.Segments.GetOrAdd((ushort)segment[0].ProgramCounter, CompileSegment(segmentName, segment, codeData.Item2)); + + var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2); + if(vmConfig.AggressiveJitMode) + { + List pcKeys = [segment[0].ProgramCounter]; + for (int k = 0; k < segment.Length; k++) + { + if (segment[k].Operation == Instruction.JUMPDEST) + { + pcKeys.Add(segment[k].ProgramCounter); + } + } + foreach (ushort pc in pcKeys) + { + ilinfo.Segments.GetOrAdd(pc, segmentExecutionCtx); + } + } else + { + ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); + } + + Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); } Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); @@ -175,7 +197,7 @@ static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) break; case IlInfo.ILMode.JIT_MODE: if (logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); - SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo); + SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo, vmConfig); break; } } diff --git a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs index b72cb08d0d2..306b2b70583 100644 --- a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -31,5 +31,10 @@ public interface IVMConfig : IConfig DefaultValue = "32")] public int PatternMatchingThreshold { get; set; } + [ConfigItem( + Description = "Activates or Deactivates aggressive JIT optimizations", + DefaultValue = "false")] + public bool AggressiveJitMode { get; set; } + public bool IsVmOptimizationEnabled => IsPatternMatchingEnabled || IsJitEnabled; } diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs index 373aad22b22..688ab9b0abf 100644 --- a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -15,4 +15,5 @@ public class VMConfig : IVMConfig public bool IsJitEnabled { get; set; } = false; public int PatternMatchingThreshold { get; set; } = 32; public int JittingThreshold { get; set; } = 128; + public bool AggressiveJitMode { get; set; } = false; } From 8bf63376f1c6667a9b5738e866389e0d142d27d2 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sat, 12 Oct 2024 15:57:47 +0100 Subject: [PATCH 099/146] ws fixes --- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 2 +- .../CodeAnalysis/IL/ILCompiler.cs | 142 +++++++++--------- .../CodeAnalysis/IL/IlAnalyzer.cs | 5 +- 3 files changed, 75 insertions(+), 74 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index a4286561ddc..0ef6daed25c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -40,7 +40,7 @@ public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) : IlInfo.ILMode.NO_ILVM; if (mode == IlInfo.ILMode.NO_ILVM) - return; + return; await IlAnalyzer.StartAnalysis(this, mode, logger, vmConfig).ConfigureAwait(false); } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index a0a15023f56..b188bc67cbe 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -123,13 +123,13 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(head); // set gas to local - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); method.Convert(); method.StoreLocal(gasAvailable); // set pc to local - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.StoreLocal(programCounter); @@ -205,7 +205,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co break; case Instruction.STOP: { - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadConstant(true); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); method.FakeBranch(ret); @@ -215,7 +215,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.Call(Word.SetULong0); method.StackPush(head); @@ -644,7 +644,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); @@ -656,7 +656,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); @@ -668,7 +668,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); @@ -680,7 +680,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); @@ -692,7 +692,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); @@ -703,7 +703,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); @@ -714,7 +714,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); method.Call(Word.SetAddress); @@ -725,7 +725,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); @@ -736,7 +736,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); method.Call(Word.SetUInt256); @@ -774,7 +774,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -783,7 +783,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256B); @@ -794,7 +794,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -813,7 +813,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); @@ -830,7 +830,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); @@ -842,7 +842,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.Call(Word.SetULong0); @@ -859,7 +859,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256B); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -870,7 +870,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -890,7 +890,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(byte8A); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -901,7 +901,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -920,7 +920,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -928,7 +928,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); @@ -974,7 +974,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant((long)0); method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -986,10 +986,10 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256B); method.LoadLocalAddress(uint256C); @@ -1019,7 +1019,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant((long)0); method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1030,7 +1030,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -1114,7 +1114,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1122,7 +1122,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); method.StoreLocal(localReadOnlyMemory); @@ -1134,7 +1134,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1156,7 +1156,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co case Instruction.RETURNDATASIZE: method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); @@ -1184,7 +1184,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocalAddress(tempResult); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.AddOverflow))); method.LoadLocalAddress(tempResult); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(typeof(ReadOnlyMemory).GetProperty(nameof(ReadOnlyMemory.Length)).GetMethod!); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); @@ -1210,7 +1210,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1218,7 +1218,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256B); @@ -1228,7 +1228,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1247,7 +1247,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256B); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1255,16 +1255,16 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.StoreObject>(); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadConstant(true); switch (op.Operation) { @@ -1281,7 +1281,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); @@ -1294,7 +1294,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co using Local uint256Nullable = method.DeclareLocal(typeof(UInt256?)); method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); method.StoreLocal(uint256Nullable); @@ -1311,7 +1311,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Duplicate(); @@ -1344,20 +1344,20 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint32A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.LoadNull(); method.BranchIfEqual(blobVersionedHashNotFound); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); method.LoadLocal(uint32A); method.BranchIfLessOrEqual(blobVersionedHashNotFound); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.LoadLocal(uint32A); @@ -1392,8 +1392,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(int64A); method.StackPop(head, 1); - method.LoadArgument(BLOCKHASH_PROVIDER_INDEX);; - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(BLOCKHASH_PROVIDER_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.LoadLocalAddress(int64A); @@ -1498,7 +1498,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co break; case Instruction.TSTORE: { - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); @@ -1513,7 +1513,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1533,7 +1533,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1562,7 +1562,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(localReadonOnlySpan); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocalAddress(gasAvailable); @@ -1583,7 +1583,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadConstant((int)EvmExceptionType.None); method.BranchIfEqual(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadLocal(uint32A); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); method.Branch(exit); @@ -1607,7 +1607,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1615,7 +1615,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StoreLocal(storageCell); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(storageCell); method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); @@ -1653,7 +1653,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1721,7 +1721,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1736,7 +1736,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1759,7 +1759,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1787,7 +1787,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1824,7 +1824,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(WORLD_STATE_INDEX); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); @@ -1849,7 +1849,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(false); @@ -1883,18 +1883,18 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // check if returnState is null method.MarkLabel(ret); // we get stack size - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadLocal(head); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); // set stack - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadLocal(stack); method.Call(GetCastMethodInfo()); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); // set gas available - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadLocal(gasAvailable); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); @@ -1902,7 +1902,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.LoadLocal(isEphemeralJump); method.BranchIfTrue(skipProgramCounterSetting); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadLocal(programCounter); method.LoadConstant(1); method.Add(); @@ -1912,7 +1912,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // set exception - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadConstant((int)EvmExceptionType.None); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); @@ -1957,7 +1957,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.Branch(jumpIsLocal); method.MarkLabel(jumpIsNotLocal); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.Duplicate(); method.LoadConstant(true); method.StoreLocal(isEphemeralJump); @@ -2016,7 +2016,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co foreach (var kvp in evmExceptionLabels) { method.MarkLabel(kvp.Value); - method.LoadArgument(VMSTATE_INDEX);; + method.LoadArgument(VMSTATE_INDEX); ; method.LoadConstant((int)kvp.Key); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); method.Branch(exit); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index dcb1a8d303c..2d7b1e892f3 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -140,7 +140,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il segmentAvgSize += segment.Length; var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2); - if(vmConfig.AggressiveJitMode) + if (vmConfig.AggressiveJitMode) { List pcKeys = [segment[0].ProgramCounter]; for (int k = 0; k < segment.Length; k++) @@ -154,7 +154,8 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il { ilinfo.Segments.GetOrAdd(pc, segmentExecutionCtx); } - } else + } + else { ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); } From 78501d479412cae8820c0dadbea5e7381f6f32de Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 13 Oct 2024 04:44:48 +0100 Subject: [PATCH 100/146] updated tests --- .../CodeAnalysis/IlEvmTests.cs | 162 +++++++++++++++--- 1 file changed, 136 insertions(+), 26 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index c4bdbd19168..6c9ca345fe3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -32,6 +32,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Test.Blockchain; using Polly; +using Nethermind.Core.Collections; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -131,10 +132,10 @@ public override void Setup() IlAnalyzer.AddPattern(); } - public void Execute(byte[] bytecode, T tracer) + public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null) where T : ITxTracer { - Execute(tracer, bytecode, null, 1_000_000); + Execute(tracer, bytecode, fork, 1_000_000); } public Address InsertCode(byte[] bytecode) @@ -798,6 +799,17 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { + int repeatCount = 32; + + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = false, + AggressiveJitMode = false + }); + var pattern1 = IlAnalyzer.GetPatternHandler(); var pattern2 = IlAnalyzer.GetPatternHandler(); var pattern3 = IlAnalyzer.GetPatternHandler(); @@ -834,9 +846,9 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() var accumulatedTraces = new List(); for (int i = 0; i < config.PatternMatchingThreshold * 2; i++) { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && !tr.IsPrecompiled.Value).ToList(); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.IsPrecompiled is not null && !tr.IsPrecompiled.Value).ToList(); accumulatedTraces.AddRange(traces); } @@ -899,13 +911,6 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var actual = standardChain.StateRoot; var expected = enhancedChain.StateRoot; - var ilvm_callsComp = ilvm_traces.Entries.Where(tr => tr.Opcode == "CALL"); - var norm_callsComp = normal_traces.Entries.Where(tr => tr.Opcode == "CALL"); - - var zipped = ilvm_callsComp.Zip(norm_callsComp, (ilvm, norm) => (ilvm, norm)).ToList(); - - var indexOfChange = zipped.FindIndex(pair => pair.ilvm.Gas != pair.norm.Gas); - var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); if (testcase.opcode is not null) @@ -973,8 +978,19 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) } [Test] - public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() { + int repeatCount = 32; + + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = true, + AggressiveJitMode = true + }); + var address = InsertCode(Prepare.EvmCode .PushData(23) .PushData(7) @@ -1005,9 +1021,9 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() var accumulatedTraces = new List(); for (int i = 0; i <= config.JittingThreshold * 2; i++) { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.SegmentID is not null).ToList(); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); accumulatedTraces.AddRange(traces); } @@ -1034,9 +1050,93 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment() } + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() + { + int repeatCount = 32; + + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = true, + AggressiveJitMode = false + }); + + var address = InsertCode(Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done); + + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .JUMPI(58) + .PushSingle(23) + .PushSingle(7) + .ADD() + .Call(address, 100) + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .JUMP(0) + .JUMPDEST() + .STOP() + .Done; + + var accumulatedTraces = new List(); + for (int i = 0; i <= config.JittingThreshold * 2; i++) + { + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); + accumulatedTraces.AddRange(traces); + + } + + // in the last stint gas is almost below 1000 + // it executes segment 0 (0..46) + // then calls address 23 (segment 0..5 since it is precompiled as well) + // then it executes segment 48..59 which ends in jump back to pc = 0 + // then it executes segment 0..46 again but this time gas is below 1000 + // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) + // so the last segment executed is AbortDestinationPattern + + string[] desiredTracePattern = new[] + { + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", + "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", + "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", + "AbortDestinationPattern", + }; + + string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); + } + + [Test] public void JIT_invalid_opcode_results_in_failure() { + int repeatCount = 32; + + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = true, + AggressiveJitMode = false + }); + byte[] bytecode = Prepare.EvmCode .PUSHx() // PUSH0 @@ -1044,23 +1144,33 @@ public void JIT_invalid_opcode_results_in_failure() .STOP() .Done; - var accumulatedTraces = new List(); + var accumulatedTraces = new List(); var numberOfRuns = config.JittingThreshold * 1024; for (int i = 0; i < numberOfRuns; i++) { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode, (1024, null)); - var traces = tracer.BuildResult().ToList(); - accumulatedTraces.AddRange(traces); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult(); + accumulatedTraces.AddRange(traces.Failed); } - var failedTraces = accumulatedTraces.Where(tr => tr.Failed && tr.Entries.Any(subtr => subtr.Error == EvmExceptionType.BadInstruction.ToString())).ToList(); - Assert.That(failedTraces.Count, Is.EqualTo(numberOfRuns)); + Assert.That(accumulatedTraces.All(status => status is false), Is.True); } [Test] public void Execution_Swap_Happens_When_Segments_are_compiled() { + int repeatCount = 32; + + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = repeatCount + 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = true, + AggressiveJitMode = false + }); + byte[] bytecode = Prepare.EvmCode .JUMPDEST() @@ -1086,9 +1196,9 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() var accumulatedTraces = new List(); for (int i = 0; i <= config.JittingThreshold * 32; i++) { - var tracer = new GethLikeBlockMemoryTracer(GethTraceOptions.Default); - ExecuteBlock(tracer, bytecode); - var traces = tracer.BuildResult().SelectMany(txTrace => txTrace.Entries).Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); accumulatedTraces.AddRange(traces); } From dbdeb62abfecdc4563c66e6830b41901f4ff9e7f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 15 Oct 2024 02:47:55 +0100 Subject: [PATCH 101/146] - fixed tests : clear code cache and run test sequencially --- .../CodeAnalysis/IlEvmTests.cs | 73 ++++++++++--------- .../VirtualMachineTestsBase.cs | 8 +- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 17 +++-- .../CodeAnalysis/IL/ILCompiler.cs | 15 ++-- .../CodeAnalysis/IL/IlAnalyzer.cs | 23 ++---- .../Nethermind.Evm/CodeInfoRepository.cs | 66 +++++++++-------- .../TransactionProcessor.cs | 2 +- .../Nethermind.Evm/VirtualMachine.cs | 7 +- .../StateOverridesExtensions.cs | 2 +- 9 files changed, 116 insertions(+), 97 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 6c9ca345fe3..e0a3c9c1ce5 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -108,6 +108,7 @@ public TestBlockChain() public override void Setup() { base.Setup(); + ILogManager logManager = GetLogManager(); _blockhashProvider = new TestBlockhashProvider(SpecProvider); @@ -132,10 +133,10 @@ public override void Setup() IlAnalyzer.AddPattern(); } - public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null) + public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null, long gasAvailable = 1_000_000) where T : ITxTracer { - Execute(tracer, bytecode, fork, 1_000_000); + Execute(tracer, bytecode, fork, gasAvailable); } public Address InsertCode(byte[] bytecode) @@ -161,6 +162,7 @@ public Hash256 StateRoot } [TestFixture] + [NonParallelizable] internal class IlEvmTests : TestBlockChain { private const string AnalyzerField = "_analyzer"; @@ -169,6 +171,8 @@ internal class IlEvmTests : TestBlockChain [SetUp] public override void Setup() { + base.Setup(); + base.config = new VMConfig() { IsJitEnabled = true, @@ -179,7 +183,7 @@ public override void Setup() JittingThreshold = 256, }; - base.Setup(); + CodeInfoRepository.ClearCache(); } public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() { @@ -760,7 +764,7 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() .ADD() .Done; - CodeInfo codeInfo = new CodeInfo(bytecode); + CodeInfo codeInfo = new CodeInfo(bytecode, TestItem.AddressA); await IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, NullLogger.Instance, config); @@ -789,7 +793,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() .STOP() .Done; - CodeInfo codeInfo = new CodeInfo(bytecode); + CodeInfo codeInfo = new CodeInfo(bytecode, TestItem.AddressA); await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, NullLogger.Instance, config); @@ -799,7 +803,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - int repeatCount = 32; + int repeatCount = 128; TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { @@ -844,7 +848,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() TestState.InsertCode(address, bytecode, spec); */ var accumulatedTraces = new List(); - for (int i = 0; i < config.PatternMatchingThreshold * 2; i++) + for (int i = 0; i < repeatCount * 2; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -985,13 +989,13 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = repeatCount + 1, - IsPatternMatchingEnabled = true, - JittingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + JittingThreshold = repeatCount + 1, IsJitEnabled = true, AggressiveJitMode = true }); - var address = InsertCode(Prepare.EvmCode + var address = enhancedChain.InsertCode(Prepare.EvmCode .PushData(23) .PushData(7) .ADD() @@ -1003,10 +1007,11 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .PushSingle(1000) .GAS() .LT() - .JUMPI(58) + .JUMPI(59) .PushSingle(23) .PushSingle(7) .ADD() + .POP() .Call(address, 100) .POP() .PushSingle(42) @@ -1019,7 +1024,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= config.JittingThreshold * 2; i++) + for (int i = 0; i <= repeatCount * 32; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1038,11 +1043,11 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", + "ILEVM_PRECOMPILED_(0x4df55fd3a4afecd4a0b4550f05b7cca2aa1db9a1)[0..5]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", }; string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); @@ -1057,14 +1062,14 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, + PatternMatchingThreshold = repeatCount * 2 + 1, IsPatternMatchingEnabled = true, - JittingThreshold = int.MaxValue, + JittingThreshold = repeatCount + 1, IsJitEnabled = true, AggressiveJitMode = false }); - var address = InsertCode(Prepare.EvmCode + var address = enhancedChain.InsertCode(Prepare.EvmCode .PushData(23) .PushData(7) .ADD() @@ -1076,10 +1081,11 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .PushSingle(1000) .GAS() .LT() - .JUMPI(58) + .JUMPI(59) .PushSingle(23) .PushSingle(7) .ADD() + .POP() .Call(address, 100) .POP() .PushSingle(42) @@ -1092,7 +1098,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= config.JittingThreshold * 2; i++) + for (int i = 0; i <= repeatCount * 2; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1111,10 +1117,10 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", - "ILEVM_PRECOMPILED_(0x3dff15...1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[48..59]", - "ILEVM_PRECOMPILED_(0x401dfc...0f4912)[0..46]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", + "ILEVM_PRECOMPILED_(0x4df55fd3a4afecd4a0b4550f05b7cca2aa1db9a1)[0..5]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", + "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", "AbortDestinationPattern", }; @@ -1154,7 +1160,7 @@ public void JIT_invalid_opcode_results_in_failure() accumulatedTraces.AddRange(traces.Failed); } - Assert.That(accumulatedTraces.All(status => status is false), Is.True); + Assert.That(accumulatedTraces.All(status => status), Is.True); } [Test] @@ -1164,9 +1170,9 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, - IsPatternMatchingEnabled = true, - JittingThreshold = int.MaxValue, + PatternMatchingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + JittingThreshold = repeatCount + 1, IsJitEnabled = true, AggressiveJitMode = false }); @@ -1194,7 +1200,7 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= config.JittingThreshold * 32; i++) + for (int i = 0; i <= repeatCount * 32; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1250,9 +1256,10 @@ public void ILAnalyzer_Initialize_Add_All_Patterns() [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, EvmExceptionType exceptionType) testcase) { + var codeInfo = new CodeInfo(testcase.bytecode, TestItem.AddressA); var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()], CodeInfoRepository); - var envExCtx = new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); + var envExCtx = new ExecutionEnvironment(codeInfo, Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); var stack = new byte[1024 * 32]; var inputBuffer = envExCtx.InputData; var returnBuffer = @@ -1264,7 +1271,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, var state = new EvmState( 1_000_000, - new ExecutionEnvironment(new CodeInfo(testcase.bytecode), Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), + new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, isTopLevel: false, Snapshot.Empty, diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 9f1b257b914..4052e4ada1f 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -26,10 +26,10 @@ namespace Nethermind.Evm.Test; public class VirtualMachineTestsBase { - protected const string SampleHexData1 = "a01234"; - protected const string SampleHexData2 = "b15678"; - protected const string HexZero = "00"; - protected const long DefaultBlockGasLimit = 8000000; + public const string SampleHexData1 = "a01234"; + public const string SampleHexData2 = "b15678"; + public const string HexZero = "00"; + public const long DefaultBlockGasLimit = 8000000; protected IEthereumEcdsa _ethereumEcdsa; protected IBlockhashProvider _blockhashProvider; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 0ef6daed25c..ef1bbfba1c4 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -11,12 +11,13 @@ using Nethermind.Evm.Config; using Nethermind.Logging; using ILMode = int; +using Nethermind.Core; namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo : IThreadPoolWorkItem { - public Hash256 CodeHash { get; init; } + public Address? Address { get; init; } public ReadOnlyMemory MachineCode { get; } public IPrecompile? Precompile { get; set; } @@ -46,18 +47,18 @@ public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); - public static CodeInfo Empty { get; } = new CodeInfo(Array.Empty(), Keccak.OfAnEmptyString); + public static CodeInfo Empty { get; } = new CodeInfo(Array.Empty(), null); - public CodeInfo(byte[] code, Hash256 codeHash = null) + public CodeInfo(byte[] code, Address source = null) { - CodeHash = codeHash ?? Keccak.Compute(code.AsSpan()); + Address = source; MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } - public CodeInfo(ReadOnlyMemory code, Hash256 codeHash = null) + public CodeInfo(ReadOnlyMemory code, Address source = null) { - CodeHash = codeHash ?? Keccak.Compute(code.Span); + Address = source; MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } @@ -70,12 +71,12 @@ public CodeInfo(ReadOnlyMemory code, Hash256 codeHash = null) /// internal IlInfo? IlInfo { get; set; } = IlInfo.Empty; - public CodeInfo(IPrecompile precompile) + public CodeInfo(IPrecompile precompile, Address source) { + Address = source; Precompile = precompile; MachineCode = Array.Empty(); _analyzer = _emptyAnalyzer; - CodeHash = Keccak.OfAnEmptyString; } public bool ValidateJump(int destination) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index b188bc67cbe..f00cd2d28da 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -35,6 +35,7 @@ public class SegmentExecutionCtx public string Name => PrecompiledSegment.Method.Name; public ExecuteSegment PrecompiledSegment; public byte[][] Data; + public ushort[] JumpDestinations; } public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[] code, byte[][] data) { @@ -44,24 +45,26 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + ushort[] jumpdests = Array.Empty(); if (code.Length == 0) { method.Return(); } else { - EmitSegmentBody(method, code); + jumpdests = EmitSegmentBody(method, code); } ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); return new SegmentExecutionCtx { PrecompiledSegment = dynEmitedDelegate, - Data = data + Data = data, + JumpDestinations = jumpdests }; } - private static void EmitSegmentBody(Emit method, OpcodeInfo[] code) + private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[] code) { using Local jmpDestination = method.DeclareLocal(typeof(int)); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); @@ -139,7 +142,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co method.CompareEqual(); method.BranchIfFalse(isContinuation); - Dictionary jumpDestinations = new(); + Dictionary jumpDestinations = new(); var costs = BuildCostLookup(code); @@ -2001,7 +2004,7 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co { method.MarkLabel(jumps[i]); // for each destination matching the bit mask emit check for the equality - foreach (int dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) + foreach (ushort dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) { method.LoadLocal(jmpDestination); method.LoadConstant(dest); @@ -2025,6 +2028,8 @@ private static void EmitSegmentBody(Emit method, OpcodeInfo[] co // return method.MarkLabel(exit); method.Return(); + + return jumpDestinations.Keys.ToArray(); } private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, Dictionary exceptions, params Local[] locals) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 2d7b1e892f3..5e81791d4e9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -65,7 +65,7 @@ internal static Task StartAnalysis(CodeInfo codeInfo, ILMode mode, ILogger logge { Metrics.IlvmContractsAnalyzed++; - if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.Address}"); return Task.Run(() => Analysis(codeInfo, mode, vmConfig, logger)); } @@ -105,7 +105,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il return; } - string GenerateName(Range segmentRange) => $"ILEVM_PRECOMPILED_({codeInfo.CodeHash.ToShortString()})[{segmentRange.Start}..{segmentRange.End}]"; + string GenerateName(Range segmentRange) => $"ILEVM_PRECOMPILED_({codeInfo.Address.ToString()})[{segmentRange.Start}..{segmentRange.End}]"; int[] statefulOpcodeindex = new int[1 + (codeData.Item1.Length / 5)]; @@ -119,7 +119,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il } long segmentAvgSize = 0; - for (int i = -1; i <= j; i++) + for (int i = -1; i < j; i++) { int start = i == -1 ? 0 : statefulOpcodeindex[i] + 1; int end = i == j - 1 || i + 1 == statefulOpcodeindex.Length ? codeData.Item1.Length : statefulOpcodeindex[i + 1]; @@ -142,17 +142,10 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2); if (vmConfig.AggressiveJitMode) { - List pcKeys = [segment[0].ProgramCounter]; - for (int k = 0; k < segment.Length; k++) - { - if (segment[k].Operation == Instruction.JUMPDEST) - { - pcKeys.Add(segment[k].ProgramCounter); - } - } - foreach (ushort pc in pcKeys) + ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); + for (int k = 0; k < segmentExecutionCtx.JumpDestinations.Length; k++) { - ilinfo.Segments.GetOrAdd(pc, segmentExecutionCtx); + ilinfo.Segments.GetOrAdd(segmentExecutionCtx.JumpDestinations[k], segmentExecutionCtx); } } else @@ -193,11 +186,11 @@ static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) switch (mode) { case IlInfo.ILMode.PAT_MODE: - if (logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Analyzing patterns of code {codeInfo.Address}"); CheckPatterns(machineCode, codeInfo.IlInfo); break; case IlInfo.ILMode.JIT_MODE: - if (logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.CodeHash}"); + if (logger.IsInfo) logger.Info($"Precompiling of segments of code {codeInfo.Address}"); SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo, vmConfig); break; } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 79e8124e29a..a345f7b584b 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -30,31 +30,26 @@ private static FrozenDictionary InitializePrecompiledCon { return new Dictionary { - [EcRecoverPrecompile.Address] = new(EcRecoverPrecompile.Instance), - [Sha256Precompile.Address] = new(Sha256Precompile.Instance), - [Ripemd160Precompile.Address] = new(Ripemd160Precompile.Instance), - [IdentityPrecompile.Address] = new(IdentityPrecompile.Instance), - - [Bn254AddPrecompile.Address] = new(Bn254AddPrecompile.Instance), - [Bn254MulPrecompile.Address] = new(Bn254MulPrecompile.Instance), - [Bn254PairingPrecompile.Address] = new(Bn254PairingPrecompile.Instance), - [ModExpPrecompile.Address] = new(ModExpPrecompile.Instance), - - [Blake2FPrecompile.Address] = new(Blake2FPrecompile.Instance), - - [G1AddPrecompile.Address] = new(G1AddPrecompile.Instance), - [G1MulPrecompile.Address] = new(G1MulPrecompile.Instance), - [G1MSMPrecompile.Address] = new(G1MSMPrecompile.Instance), - [G2AddPrecompile.Address] = new(G2AddPrecompile.Instance), - [G2MulPrecompile.Address] = new(G2MulPrecompile.Instance), - [G2MSMPrecompile.Address] = new(G2MSMPrecompile.Instance), - [PairingCheckPrecompile.Address] = new(PairingCheckPrecompile.Instance), - [MapFpToG1Precompile.Address] = new(MapFpToG1Precompile.Instance), - [MapFp2ToG2Precompile.Address] = new(MapFp2ToG2Precompile.Instance), - - [PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance), - - [Secp256r1Precompile.Address] = new(Secp256r1Precompile.Instance), + [EcRecoverPrecompile.Address] = new(EcRecoverPrecompile.Instance, EcRecoverPrecompile.Address), + [Sha256Precompile.Address] = new(Sha256Precompile.Instance, Sha256Precompile.Address), + [Ripemd160Precompile.Address] = new(Ripemd160Precompile.Instance, Ripemd160Precompile.Address), + [IdentityPrecompile.Address] = new(IdentityPrecompile.Instance, IdentityPrecompile.Address), + [Bn254AddPrecompile.Address] = new(Bn254AddPrecompile.Instance, Bn254AddPrecompile.Address), + [Bn254MulPrecompile.Address] = new(Bn254MulPrecompile.Instance, Bn254MulPrecompile.Address), + [Bn254PairingPrecompile.Address] = new(Bn254PairingPrecompile.Instance, Bn254PairingPrecompile.Address), + [ModExpPrecompile.Address] = new(ModExpPrecompile.Instance, ModExpPrecompile.Address), + [Blake2FPrecompile.Address] = new(Blake2FPrecompile.Instance, Blake2FPrecompile.Address), + [G1AddPrecompile.Address] = new(G1AddPrecompile.Instance, G1AddPrecompile.Address), + [G1MulPrecompile.Address] = new(G1MulPrecompile.Instance, G1MulPrecompile.Address), + [G1MSMPrecompile.Address] = new(G1MSMPrecompile.Instance, G1MSMPrecompile.Address), + [G2AddPrecompile.Address] = new(G2AddPrecompile.Instance, G2AddPrecompile.Address), + [G2MulPrecompile.Address] = new(G2MulPrecompile.Instance, G2MulPrecompile.Address), + [G2MSMPrecompile.Address] = new(G2MSMPrecompile.Instance, G2MSMPrecompile.Address), + [PairingCheckPrecompile.Address] = new(PairingCheckPrecompile.Instance, PairingCheckPrecompile.Address), + [MapFpToG1Precompile.Address] = new(MapFpToG1Precompile.Instance, MapFpToG1Precompile.Address), + [MapFp2ToG2Precompile.Address] = new(MapFp2ToG2Precompile.Instance, MapFp2ToG2Precompile.Address), + [PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance, PointEvaluationPrecompile.Address), + [Secp256r1Precompile.Address] = new(Secp256r1Precompile.Instance, Secp256r1Precompile.Address), }.ToFrozenDictionary(); } @@ -102,7 +97,7 @@ private static CodeInfo InternalGetCachedCode(IReadOnlyStateProvider worldState, MissingCode(codeSource, codeHash); } - cachedCodeInfo = new CodeInfo(code, codeHash); + cachedCodeInfo = new CodeInfo(code, codeSource); cachedCodeInfo.AnalyseInBackgroundIfRequired(); _codeCache.Set(codeHash, cachedCodeInfo); } @@ -123,7 +118,7 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - CodeInfo codeInfo = new(code); + CodeInfo codeInfo = new(code, codeOwner); codeInfo.AnalyseInBackgroundIfRequired(); ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); @@ -138,7 +133,7 @@ public void SetDelegation(IWorldState state, Address codeSource, Address authori codeSource.Bytes.CopyTo(authorizedBuffer, Eip7702Constants.DelegationHeader.Length); ValueHash256 codeHash = ValueKeccak.Compute(authorizedBuffer); state.InsertCode(authority, codeHash, authorizedBuffer.AsMemory(), spec); - _codeCache.Set(codeHash, new CodeInfo(authorizedBuffer)); + _codeCache.Set(codeHash, new CodeInfo(authorizedBuffer, codeSource)); } /// @@ -181,11 +176,16 @@ private static bool TryGetDelegatedAddress(ReadOnlySpan code, [NotNullWhen private CodeInfo CreateCachedPrecompile( in KeyValuePair originalPrecompile, ConcurrentDictionary, bool)> cache) => - new(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache)); + new(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache), originalPrecompile.Key.Value); public bool TryGetDelegation(IReadOnlyStateProvider worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress) => TryGetDelegatedAddress(InternalGetCachedCode(worldState, address).MachineCode.Span, out delegatedAddress); + public static void ClearCache() + { + _codeCache.Clear(); + } + private class CachedPrecompile( Address address, IPrecompile precompile, @@ -247,6 +247,14 @@ public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out CodeInfo? c codeInfo = Get(codeHash); return codeInfo is not null; } + + public void Clear() + { + foreach (ClockCache cache in _caches) + { + cache.Clear(); + } + } } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 3ebf373ab25..1343f1e0d88 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -518,7 +518,7 @@ private ExecutionEnvironment BuildExecutionEnvironment( TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository); Address? delegationAddress = null; CodeInfo codeInfo = tx.IsContractCreation - ? new(tx.Data ?? Memory.Empty) + ? new(tx.Data ?? Memory.Empty, tx.SenderAddress) : codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec, out delegationAddress); if (delegationAddress is not null) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d9c54e3de7b..cb09326fc7b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -786,6 +786,8 @@ private CallResult ExecuteCode(EvmState vmState, ref // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid // for another tx as returned to pool. - CodeInfo codeInfo = new(initCode); + CodeInfo codeInfo = new(initCode, env.ExecutingAccount); codeInfo.AnalyseInBackgroundIfRequired(); ExecutionEnvironment callEnv = new diff --git a/src/Nethermind/Nethermind.Facade/StateOverridesExtensions.cs b/src/Nethermind/Nethermind.Facade/StateOverridesExtensions.cs index 080e6c2a81b..4d54768858c 100644 --- a/src/Nethermind/Nethermind.Facade/StateOverridesExtensions.cs +++ b/src/Nethermind/Nethermind.Facade/StateOverridesExtensions.cs @@ -82,7 +82,7 @@ private static void UpdateCode( stateProvider, currentSpec, address, - new CodeInfo(accountOverride.Code), + new CodeInfo(accountOverride.Code, address), accountOverride.MovePrecompileToAddress); } } From 42e78a910689f33781b7c0c35a69d21e82a9637c Mon Sep 17 00:00:00 2001 From: Siddharth Vaderaa Date: Tue, 15 Oct 2024 03:54:40 +0200 Subject: [PATCH 102/146] Top opcode patterns (#7597) Co-authored-by: Ayman Bouchareb --- .../CodeAnalysis/IlEvmTests.cs | 130 +++++- .../CodeAnalysis/IL/PredefinedPatterns.cs | 436 ++++++++++++++++++ 2 files changed, 560 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index e0a3c9c1ce5..4ff7bbd3861 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -33,6 +33,7 @@ using Nethermind.Core.Test.Blockchain; using Polly; using Nethermind.Core.Collections; +using Nethermind.Specs.Test; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -131,6 +132,20 @@ public override void Setup() IlAnalyzer.AddPattern(); IlAnalyzer.AddPattern(); IlAnalyzer.AddPattern(); + + IlAnalyzer.AddPattern(); + //IlAnalyzer.AddPattern(); + //IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); } public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null, long gasAvailable = 1_000_000) @@ -143,6 +158,7 @@ public Address InsertCode(byte[] bytecode) { var hashcode = Keccak.Compute(bytecode); var address = new Address(hashcode); + var spec = Prague.Instance; TestState.CreateAccount(address, 1000000); TestState.InsertCode(address, bytecode, spec); @@ -185,10 +201,113 @@ public override void Setup() CodeInfoRepository.ClearCache(); } + public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() { - yield return (null, Prepare.EvmCode + + yield return (typeof(D01P04EQ), Prepare.EvmCode + .PUSHx([1, 2, 3, 4]) + .DUPx(1) + .PUSHx([1, 2, 3, 4]) + .EQ() + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(D01P04GT), Prepare.EvmCode + .PUSHx([1, 2, 3, 4]) + .DUPx(1) + .PUSHx([1, 2, 3, 5]) + .GT() + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(D02MST), Prepare.EvmCode + .PUSHx([1]) + .PUSHx([3]) + .PUSHx([3]) + .POP() //to avooid PUSHxDUPx pattern detection + .DUPx(2) + .MSTORE() + .POP() + .MLOAD(1) + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(P01D03), Prepare.EvmCode + .PUSHx([5]) + .PUSHx([1]) + .PUSHx([3]) + .DUPx(3) + .PushData(0x1) + .Op(Instruction.SSTORE) + .PushData(0x2) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(P01D02), Prepare.EvmCode + .PUSHx([1]) + .PUSHx([3]) + .DUPx(2) + .PushData(0x1) + .Op(Instruction.SSTORE) + .PushData(0x2) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(S02S01), Prepare.EvmCode + .PUSHx([5]) + .PUSHx([1]) + .PUSHx([3]) + .SWAPx(2) + .SWAPx(1) + .PushData(0x1) + .Op(Instruction.SSTORE) + .PushData(0x2) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(S02P), Prepare.EvmCode + .PUSHx([5]) + .PUSHx([1]) + .PUSHx([3]) + .SWAPx(2) + .POP() + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(S01P), Prepare.EvmCode + .PUSHx([2]) + .PUSHx([3]) + .SWAPx(1) + .POP() + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(PJ), Prepare.EvmCode + .PUSHx([23]) + .PUSHx([13]) + .PUSHx([9]) + .POP() + .JUMP() + .JUMPDEST() + .PushSingle(3) + .MUL() + .STOP() + .JUMPDEST() + .JUMP(8) .Done); + yield return (typeof(P01P01SHL), Prepare.EvmCode + .PUSHx([2]) + .PUSHx([3]) + .Op(Instruction.SHL) + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); + yield return (typeof(PP), Prepare.EvmCode + .PushData(((UInt256)3).PaddedBytes(32)) + .PushData(((UInt256)4).PaddedBytes(32)) + .PushData(((UInt256)5).PaddedBytes(32)) + .POP() + .POP() + .PushData(0x1) + .Op(Instruction.SSTORE) + .Done); yield return (typeof(EmulatedStaticCJump), Prepare.EvmCode .PUSHx([1]) .PUSHx([0, 7]) @@ -924,11 +1043,10 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by Assert.That(actual, Is.EqualTo(expected)); } - [Test, TestCaseSource(nameof(GePatBytecodesSamples))] public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) { - int repeatCount = 32; + int repeatCount = 10; TestBlockChain standardChain = new TestBlockChain(new VMConfig()); var address = standardChain.InsertCode(testcase.bytecode); @@ -947,7 +1065,7 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) var bytecode = Prepare.EvmCode .JUMPDEST() - .Call(address, 100) + .Call(address, 100000) .POP() .GAS() .PushData(1000) @@ -958,12 +1076,12 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) for (var i = 0; i < repeatCount * 2; i++) { - standardChain.Execute(bytecode, tracer1); + standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); } for (var i = 0; i < repeatCount * 2; i++) { - enhancedChain.Execute(bytecode, tracer2); + enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); } var normal_traces = tracer1.BuildResult(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs index 567e45dfc0d..a45c9e31d61 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs @@ -169,3 +169,439 @@ public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IW } } } +internal class PP : InstructionChunk +{ + public string Name => nameof(PP); + public byte[] Pattern => [(byte)Instruction.POP, (byte)Instruction.POP]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.Base + GasCostOf.Base; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.Head -= 2; + //stack.PopLimbo(); + //stack.PopLimbo(); + + programCounter += 2; + } +} +internal class P01P01SHL : InstructionChunk +{ + public string Name => nameof(P01P01SHL); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.PUSH1, (byte)Instruction.SHL]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow * 3; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.PushUInt256((UInt256)vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1] << (int)((UInt256)vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]).u0); + + programCounter += 5; + + } +} +internal class PJ : InstructionChunk +{ + public string Name => nameof(PJ); + public byte[] Pattern => [(byte)Instruction.POP, (byte)Instruction.JUMP]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.Base + GasCostOf.Mid; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + //stack.Head--; + stack.PopLimbo(); + stack.PopUInt256(out UInt256 jumpDestination); + + var jumpDestinationInt = (int)jumpDestination; + + if (jumpDestinationInt < vmState.Env.CodeInfo.MachineCode.Length && vmState.Env.CodeInfo.MachineCode.Span[jumpDestinationInt] == (byte)Instruction.JUMPDEST) + { + programCounter = jumpDestinationInt; + } + else + { + result.ExceptionType = EvmExceptionType.InvalidJumpDestination; + } + } +} +internal class S02P : InstructionChunk +{ + public string Name => nameof(S02P); + public byte[] Pattern => [(byte)Instruction.SWAP2, (byte)Instruction.POP]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.Base; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.Swap(3); + stack.Head--; + + programCounter += 2; + } +} +internal class S01P : InstructionChunk +{ + public string Name => nameof(S01P); + public byte[] Pattern => [(byte)Instruction.SWAP1, (byte)Instruction.POP]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.Base; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.Swap(2); + stack.Head--; + + programCounter += 2; + } +} +internal class P01SHL : InstructionChunk +{ + public string Name => nameof(P01SHL); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.SHL]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.PopUInt256(out UInt256 value); + + stack.PushUInt256(value << (int)vmState.Env.CodeInfo.MachineCode.Span[programCounter + 2]); + programCounter += 3; + + } +} +internal class P01D02 : InstructionChunk +{ + public string Name => nameof(P01D02); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.DUP2]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.PushUInt256(vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]); + stack.Dup(2); + + programCounter += 3; + + } +} +internal class P01D03 : InstructionChunk +{ + public string Name => nameof(P01D03); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.DUP3]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.PushUInt256(vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]); + stack.Dup(3); + + programCounter += 3; + + } +} +internal class S02S01 : InstructionChunk +{ + public string Name => nameof(S02S01); + public byte[] Pattern => [(byte)Instruction.SWAP2, (byte)Instruction.SWAP1]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = GasCostOf.VeryLow + GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + stack.Swap(3); + stack.Swap(2); + + programCounter += 2; + + } +} +internal class D01P04EQ : InstructionChunk +{ + public string Name => nameof(D01P04EQ); + public byte[] Pattern => [(byte)Instruction.DUP1, (byte)Instruction.PUSH4, (byte)Instruction.EQ]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = 3 * GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + + ReadOnlySpan fourByteSpan = vmState.Env.CodeInfo.MachineCode.Span.Slice(programCounter + 2, 4); + + Span word = stack.PeekWord256(); + + Span paddedSpan = stackalloc byte[32]; + + fourByteSpan.CopyTo(paddedSpan.Slice(28, 4)); + if (paddedSpan.SequenceEqual(word)) + { + stack.PushOne(); + } + else + { + stack.PushZero(); + } + + programCounter += 7; + + } +} +internal class D01P04GT : InstructionChunk +{ + public string Name => nameof(D01P04GT); + public byte[] Pattern => [(byte)Instruction.DUP1, (byte)Instruction.PUSH4, (byte)Instruction.GT]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = 3 * GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + ReadOnlySpan fourByteSpan = vmState.Env.CodeInfo.MachineCode.Span.Slice(programCounter + 2, 4); + + UInt256 rhs = new UInt256(stack.PeekWord256(), true); + UInt256 lhs = new UInt256(fourByteSpan, true); + + if (lhs > rhs) + { + stack.PushOne(); + } + else + { + stack.PushZero(); + } + + programCounter += 7; + + } +} +internal class D02MST : InstructionChunk +{ + public string Name => nameof(D02MST); + public byte[] Pattern => [(byte)Instruction.DUP2, (byte)Instruction.MSTORE]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = 2 * GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + + stack.Dup(2); + stack.PopUInt256(out UInt256 location); + var bytes = stack.PopWord256(); + if (!VirtualMachine.UpdateMemoryCost(vmState, ref gasAvailable, in location, (UInt256)32)) + result.ExceptionType = EvmExceptionType.OutOfGas; + vmState.Memory.SaveWord(in location, bytes); + stack.PushUInt256(location); + + programCounter += 2; + + } +} +internal class P01ADDS01D02MST : InstructionChunk +{ + public string Name => nameof(P01ADDS01D02MST); + public byte[] Pattern => [(byte)Instruction.PUSH1, (byte)Instruction.ADD, (byte)Instruction.SWAP1, (byte)Instruction.DUP2, (byte)Instruction.MSTORE]; + public byte CallCount { get; set; } = 0; + + public long GasCost(EvmState vmState, IReleaseSpec spec) + { + long gasCost = 3 * GasCostOf.VeryLow; + return gasCost; + } + + public void Invoke(EvmState vmState, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, + ref int programCounter, + ref long gasAvailable, + ref EvmStack stack, + ref ILChunkExecutionResult result) where T : struct, VirtualMachine.IIsTracing + { + CallCount++; + + if (!VirtualMachine.UpdateGas(GasCost(vmState, spec), ref gasAvailable)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + + if (!stack.PopUInt256(out UInt256 location)) + result.ExceptionType = EvmExceptionType.StackUnderflow; + + location = location + (new UInt256(vmState.Env.CodeInfo.MachineCode.Span.Slice(programCounter + 3, 1))); + + if (!VirtualMachine.UpdateMemoryCost(vmState, ref gasAvailable, location, 32)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + vmState.Memory.SaveWord(location, stack.PopWord256()); + stack.PushUInt256(location); + + programCounter += 6; + + } +} From 5098c889ab9f8c91b643ee29a9b0a9f3be20580e Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 15 Oct 2024 04:13:08 +0100 Subject: [PATCH 103/146] Added single opcode tracing mode to ILVM --- .../CodeAnalysis/IlEvmTests.cs | 57 +++++++++++- .../CodeAnalysis/IL/ILCompiler.cs | 93 ++++++++++++++++--- .../CodeAnalysis/IL/IlAnalyzer.cs | 2 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- .../Nethermind.Evm/Config/IVMConfig.cs | 5 + .../Nethermind.Evm/Config/VMConfig.cs | 1 + .../Nethermind.Evm/VirtualMachine.cs | 40 ++++---- 7 files changed, 163 insertions(+), 37 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 4ff7bbd3861..04cf7c74278 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -194,6 +194,7 @@ public override void Setup() IsJitEnabled = true, IsPatternMatchingEnabled = true, AggressiveJitMode = true, + BakeInTracingInJitMode = true, PatternMatchingThreshold = 4, JittingThreshold = 256, @@ -1345,7 +1346,7 @@ public void Pure_Opcode_Emition_Coveraga() OpcodeInfo opcode = new OpcodeInfo(0, instruction, null); try { - ILCompiler.CompileSegment(name, [opcode], []); + ILCompiler.CompileSegment(name, [opcode], [], config); } catch (NotSupportedException nse) { @@ -1399,12 +1400,62 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, state.InitStacks(); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); - var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2); - ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, ctx.Data); + var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2, config); + ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, tracer, ctx.Data); Assert.That(iLEvmState.EvmException == testcase.exceptionType); } + + + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] + public void Test_ILVM_Trace_Mode((Instruction? opcode, byte[] bytecode, EvmExceptionType exceptionType) testcase) + { + var codeInfo = new CodeInfo(testcase.bytecode, TestItem.AddressA); + var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); + var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()], CodeInfoRepository); + var envExCtx = new ExecutionEnvironment(codeInfo, Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); + var stack = new byte[1024 * 32]; + var inputBuffer = envExCtx.InputData; + var returnBuffer = + new ReadOnlyMemory(Enumerable.Range(0, 32) + .Select(i => (byte)i).ToArray()); + + TestState.CreateAccount(Address.FromNumber(1), 1000000); + TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); + + var state = new EvmState( + 1_000_000, + new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), + ExecutionType.CALL, + isTopLevel: false, + Snapshot.Empty, + isContinuation: false); + + IVirtualMachine evm = typeof(VirtualMachine).GetField("_evm", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Machine) as IVirtualMachine; + + state.InitStacks(); + + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); + var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); + var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2, config); + ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, tracer, ctx.Data); + + var tracedOpcodes = tracer.BuildResult().Entries; + + if(testcase.opcode is not null) + { + Assert.That(tracedOpcodes.Count, Is.GreaterThan(0)); + } + else + { + Assert.That(tracedOpcodes.Count, Is.EqualTo(0)); + } + } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index f00cd2d28da..cabc2350aa7 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -4,6 +4,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; +using Nethermind.Evm.Config; using Nethermind.Evm.IL; using Nethermind.Evm.Tracing; using Nethermind.Int256; @@ -21,14 +22,15 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal class ILCompiler { - public delegate void ExecuteSegment(ref ILEvmState vmstate, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, byte[][] immediatesData); + public delegate void ExecuteSegment(ref ILEvmState vmstate, IBlockhashProvider blockhashProvider, IWorldState worldState, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec, ITxTracer trace, byte[][] immediatesData); private const int VMSTATE_INDEX = 0; private const int BLOCKHASH_PROVIDER_INDEX = 1; private const int WORLD_STATE_INDEX = 2; private const int CODE_INFO_REPOSITORY_INDEX = 3; private const int SPEC_INDEX = 4; - private const int IMMEDIATES_DATA_INDEX = 5; + private const int TXTRACER_INDEX = 5; + private const int IMMEDIATES_DATA_INDEX = 6; public class SegmentExecutionCtx { @@ -37,7 +39,7 @@ public class SegmentExecutionCtx public byte[][] Data; public ushort[] JumpDestinations; } - public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[] code, byte[][] data) + public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[] code, byte[][] data, IVMConfig config) { // code is optimistic assumes stack underflow and stack overflow to not occure (WE NEED EOF FOR THIS) // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? @@ -52,7 +54,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ } else { - jumpdests = EmitSegmentBody(method, code); + jumpdests = EmitSegmentBody(method, code, config.BakeInTracingInJitMode); } ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); @@ -64,7 +66,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ }; } - private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[] code) + private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[] code, bool bakeInTracerCalls) { using Local jmpDestination = method.DeclareLocal(typeof(int)); using Local consumeJumpCondition = method.DeclareLocal(typeof(int)); @@ -159,22 +161,39 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(programCounter); } + + if (bakeInTracerCalls) + { + EmitCallToStartInstructionTrace(method, gasAvailable, head, op); + } + // check if opcode is activated in current spec method.LoadArgument(SPEC_INDEX); method.LoadConstant((byte)op.Operation); method.Call(typeof(InstructionExtensions).GetMethod(nameof(InstructionExtensions.IsEnabled))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.BadInstruction]); - if (costs.ContainsKey(op.ProgramCounter) && costs[op.ProgramCounter] > 0) + if (!bakeInTracerCalls) { + if (costs.ContainsKey(op.ProgramCounter) && costs[op.ProgramCounter] > 0) + { + method.LoadLocal(gasAvailable); + method.LoadConstant(costs[op.ProgramCounter]); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadLocal(gasAvailable); + method.LoadConstant(costs[op.ProgramCounter]); + method.Subtract(); + method.StoreLocal(gasAvailable); + } + } else { method.LoadLocal(gasAvailable); - method.LoadConstant(costs[op.ProgramCounter]); - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - - method.LoadLocal(gasAvailable); - method.LoadConstant(costs[op.ProgramCounter]); + method.LoadConstant(op.Metadata.GasCost); method.Subtract(); + method.Duplicate(); method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); } if (i == code.Length - 1) @@ -242,6 +261,10 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.JUMP: { // we jump into the jump table + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } method.FakeBranch(jumpTable); } break; @@ -258,6 +281,11 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(consumeJumpCondition); // we jump into the jump table + + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } method.Branch(jumpTable); method.MarkLabel(noJump); @@ -1878,6 +1906,11 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ } break; } + + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } } Label skipProgramCounterSetting = method.DefineLabel(); @@ -2019,7 +2052,12 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ foreach (var kvp in evmExceptionLabels) { method.MarkLabel(kvp.Value); - method.LoadArgument(VMSTATE_INDEX); ; + if(bakeInTracerCalls) + { + EmitCallToErrorTrace(method, gasAvailable, kvp); + } + + method.LoadArgument(VMSTATE_INDEX); method.LoadConstant((int)kvp.Key); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); method.Branch(exit); @@ -2032,6 +2070,37 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ return jumpDestinations.Keys.ToArray(); } + private static void EmitCallToErrorTrace(Emit method, Local gasAvailable, KeyValuePair kvp) + { + method.LoadArgument(TXTRACER_INDEX); + method.LoadLocal(gasAvailable); + method.LoadConstant((int)kvp.Key); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.EndInstructionTraceError), BindingFlags.Static | BindingFlags.NonPublic)); + } + + private static void EmitCallToEndInstructionTrace(Emit method, Local gasAvailable) + { + method.LoadArgument(TXTRACER_INDEX); + method.LoadLocal(gasAvailable); + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.EndInstructionTrace), BindingFlags.Static | BindingFlags.NonPublic)); + } + + + private static void EmitCallToStartInstructionTrace(Emit method, Local gasAvailable, Local head, OpcodeInfo op) + { + method.LoadArgument(TXTRACER_INDEX); + method.LoadConstant((int)op.Operation); + method.LoadArgument(0); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(gasAvailable); + method.LoadConstant(op.ProgramCounter); + method.LoadLocal(head); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.StartInstructionTrace), BindingFlags.Static | BindingFlags.NonPublic)); + } + private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, Dictionary exceptions, params Local[] locals) { MethodInfo shiftOp = typeof(UInt256).GetMethod(isLeft ? nameof(UInt256.LeftShift) : nameof(UInt256.RightShift)); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 5e81791d4e9..47c51f242b1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -139,7 +139,7 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il segmentAvgSize += segment.Length; - var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2); + var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2, vmConfig); if (vmConfig.AggressiveJitMode) { ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 9ac0db28fdd..ef17d3a1db2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -84,7 +84,7 @@ public bool TryExecute(ILogger logger, EvmState vmState, u vmState.DataStackHead = stack.Head; var ilvmState = new ILEvmState(chainId, vmState, EvmExceptionType.None, (ushort)programCounter, gasAvailable, ref outputBuffer); - ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, ctx.Data); + ctx.PrecompiledSegment.Invoke(ref ilvmState, blockHashProvider, worldState, codeinfoRepository, spec, tracer, ctx.Data); gasAvailable = ilvmState.GasAvailable; programCounter = ilvmState.ProgramCounter; diff --git a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs index 306b2b70583..9da5ed6e886 100644 --- a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -36,5 +36,10 @@ public interface IVMConfig : IConfig DefaultValue = "false")] public bool AggressiveJitMode { get; set; } + [ConfigItem( + Description = "Activates or Deactivates traces in JIT optimizations", + DefaultValue = "false")] + public bool BakeInTracingInJitMode { get; set; } + public bool IsVmOptimizationEnabled => IsPatternMatchingEnabled || IsJitEnabled; } diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs index 688ab9b0abf..4e4342597e3 100644 --- a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -16,4 +16,5 @@ public class VMConfig : IVMConfig public int PatternMatchingThreshold { get; set; } = 32; public int JittingThreshold { get; set; } = 128; public bool AggressiveJitMode { get; set; } = false; + public bool BakeInTracingInJitMode { get; set; } = false; } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index cb09326fc7b..f53d3ce979e 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -29,6 +29,7 @@ namespace Nethermind.Evm; using Int256; using Nethermind.Evm.Config; +using System.Diagnostics; public class VirtualMachine : IVirtualMachine @@ -818,7 +819,7 @@ private CallResult ExecuteCode bytes; @@ -2092,7 +2093,7 @@ private CallResult ExecuteCode(EvmState vmState, ref return (EvmExceptionType.None, null); } - if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(gasAvailable, vmState.Memory.Size); + if (typeof(TTracing) == typeof(IsTracing)) EndInstructionTrace(_txTracer, gasAvailable, vmState.Memory.Size); // todo: === below is a new call - refactor / move long callGas = spec.Use63Over64Rule ? gasAvailable - gasAvailable / 64L : gasAvailable; @@ -2779,7 +2780,7 @@ static internal EvmExceptionType InstructionSStore(long gasAvailable, EvmExceptionType exceptionType) where TTracingInstructions : struct, IIsTracing { - if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTraceError(gasAvailable, exceptionType); + if (typeof(TTracingInstructions) == typeof(IsTracing)) EndInstructionTraceError(_txTracer, gasAvailable, exceptionType); return exceptionType switch { @@ -2832,34 +2833,33 @@ private static bool Jump(in UInt256 jumpDest, ref int programCounter, in Executi } [MethodImpl(MethodImplOptions.NoInlining)] - private void StartInstructionTrace(Instruction instruction, EvmState vmState, long gasAvailable, int programCounter, in EvmStack stackValue) - where TIsTracing : struct, IIsTracing + internal static void StartInstructionTrace(ITxTracer tracer, Instruction instruction, EvmState vmState, long gasAvailable, int programCounter, int stackHead) { - _txTracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env); - if (_txTracer.IsTracingMemory) + tracer.StartOperation(programCounter, instruction, gasAvailable, vmState.Env); + if (tracer.IsTracingMemory) { - _txTracer.SetOperationMemory(vmState.Memory.GetTrace()); - _txTracer.SetOperationMemorySize(vmState.Memory.Size); + tracer.SetOperationMemory(vmState.Memory.GetTrace()); + tracer.SetOperationMemorySize(vmState.Memory.Size); } - if (_txTracer.IsTracingStack) + if (tracer.IsTracingStack) { - Memory stackMemory = vmState.DataStack.AsMemory().Slice(0, stackValue.Head * EvmStack.WordSize); - _txTracer.SetOperationStack(new TraceStack(stackMemory)); + Memory stackMemory = vmState.DataStack.AsMemory().Slice(0, stackHead * EvmStack.WordSize); + tracer.SetOperationStack(new TraceStack(stackMemory)); } } [MethodImpl(MethodImplOptions.NoInlining)] - private void EndInstructionTrace(long gasAvailable, ulong memorySize) + internal static void EndInstructionTrace(ITxTracer tracer, long gasAvailable, ulong memorySize) { - _txTracer.ReportOperationRemainingGas(gasAvailable); + tracer.ReportOperationRemainingGas(gasAvailable); } [MethodImpl(MethodImplOptions.NoInlining)] - private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExceptionType) + internal static void EndInstructionTraceError(ITxTracer tracer, long gasAvailable, EvmExceptionType evmExceptionType) { - _txTracer.ReportOperationRemainingGas(gasAvailable); - _txTracer.ReportOperationError(evmExceptionType); + tracer.ReportOperationRemainingGas(gasAvailable); + tracer.ReportOperationError(evmExceptionType); } private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) => From 8db8d2b110ae926654750a402622a23e9cc12c05 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 16 Oct 2024 02:02:48 +0100 Subject: [PATCH 104/146] ILVM tracing mode, skip tracing in runtime if tracer is not tracing instructions --- .../CodeAnalysis/IL/ILCompiler.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index cabc2350aa7..05c9958a6c0 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2072,25 +2072,44 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ private static void EmitCallToErrorTrace(Emit method, Local gasAvailable, KeyValuePair kvp) { + Label skipTracing = method.DefineLabel(); + method.LoadArgument(TXTRACER_INDEX); + method.CallVirtual(typeof(ITxTracer).GetProperty(nameof(ITxTracer.IsTracingInstructions)).GetGetMethod()); + method.BranchIfFalse(skipTracing); + method.LoadArgument(TXTRACER_INDEX); method.LoadLocal(gasAvailable); method.LoadConstant((int)kvp.Key); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.EndInstructionTraceError), BindingFlags.Static | BindingFlags.NonPublic)); + + method.MarkLabel(skipTracing); } private static void EmitCallToEndInstructionTrace(Emit method, Local gasAvailable) { + Label skipTracing = method.DefineLabel(); + method.LoadArgument(TXTRACER_INDEX); + method.CallVirtual(typeof(ITxTracer).GetProperty(nameof(ITxTracer.IsTracingInstructions)).GetGetMethod()); + method.BranchIfFalse(skipTracing); + method.LoadArgument(TXTRACER_INDEX); method.LoadLocal(gasAvailable); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.EndInstructionTrace), BindingFlags.Static | BindingFlags.NonPublic)); + + method.MarkLabel(skipTracing); } private static void EmitCallToStartInstructionTrace(Emit method, Local gasAvailable, Local head, OpcodeInfo op) { + Label skipTracing = method.DefineLabel(); + method.LoadArgument(TXTRACER_INDEX); + method.CallVirtual(typeof(ITxTracer).GetProperty(nameof(ITxTracer.IsTracingInstructions)).GetGetMethod()); + method.BranchIfFalse(skipTracing); + method.LoadArgument(TXTRACER_INDEX); method.LoadConstant((int)op.Operation); method.LoadArgument(0); @@ -2099,6 +2118,8 @@ private static void EmitCallToStartInstructionTrace(Emit method, method.LoadConstant(op.ProgramCounter); method.LoadLocal(head); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.StartInstructionTrace), BindingFlags.Static | BindingFlags.NonPublic)); + + method.MarkLabel(skipTracing); } private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, bool isLeft, Dictionary exceptions, params Local[] locals) From aa7aae956cac2fe69d789e3faa495c7dd0d6fcdc Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 16 Oct 2024 02:09:10 +0100 Subject: [PATCH 105/146] added more tests --- .../CodeAnalysis/IlEvmTests.cs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 04cf7c74278..22d75e60069 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1448,7 +1448,7 @@ public void Test_ILVM_Trace_Mode((Instruction? opcode, byte[] bytecode, EvmExcep var tracedOpcodes = tracer.BuildResult().Entries; - if(testcase.opcode is not null) + if (testcase.opcode is not null) { Assert.That(tracedOpcodes.Count, Is.GreaterThan(0)); } @@ -1457,5 +1457,45 @@ public void Test_ILVM_Trace_Mode((Instruction? opcode, byte[] bytecode, EvmExcep Assert.That(tracedOpcodes.Count, Is.EqualTo(0)); } } + + [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] + public void Test_ILVM_Trace_Mode_Has_0_Traces_When_TraceInstructions_Is_Off((Instruction? opcode, byte[] bytecode, EvmExceptionType exceptionType) testcase) + { + var codeInfo = new CodeInfo(testcase.bytecode, TestItem.AddressA); + var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); + var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()], CodeInfoRepository); + var envExCtx = new ExecutionEnvironment(codeInfo, Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); + var stack = new byte[1024 * 32]; + var inputBuffer = envExCtx.InputData; + var returnBuffer = + new ReadOnlyMemory(Enumerable.Range(0, 32) + .Select(i => (byte)i).ToArray()); + + TestState.CreateAccount(Address.FromNumber(1), 1000000); + TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); + + var state = new EvmState( + 1_000_000, + new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), + ExecutionType.CALL, + isTopLevel: false, + Snapshot.Empty, + isContinuation: false); + + IVirtualMachine evm = typeof(VirtualMachine).GetField("_evm", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Machine) as IVirtualMachine; + + state.InitStacks(); + + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); + var metadata = IlAnalyzer.StripByteCode(testcase.bytecode); + var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2, config); + ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, NullTxTracer.Instance, ctx.Data); + + var tracedOpcodes = tracer.BuildResult().Entries; + + Assert.That(tracedOpcodes.Count, Is.EqualTo(0)); + } } } From d72dbd14806f37d16431bfa250a39f32fb98184f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 17 Oct 2024 21:29:20 +0100 Subject: [PATCH 106/146] Added Analysis Queue --- .../CodeAnalysis/IlEvmTests.cs | 28 +++++++++--- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 6 +-- .../CodeAnalysis/IL/ILCompiler.cs | 3 +- .../CodeAnalysis/IL/IlAnalyzer.cs | 43 +++++++++++++------ .../Nethermind.Evm/Config/IVMConfig.cs | 5 +++ .../Nethermind.Evm/Config/VMConfig.cs | 1 + 6 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 22d75e60069..fdcbc41fbe2 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -552,6 +552,17 @@ public override void Setup() .MUL() .Done, EvmExceptionType.None); + + yield return (Instruction.JUMPI | Instruction.JUMPDEST, Prepare.EvmCode + .PushSingle(23) + .PushSingle(0) + .JUMPI(9) + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .Done, EvmExceptionType.None); + yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode .PushSingle(23) .JUMP(10) @@ -872,7 +883,7 @@ public void All_Stateless_Opcodes_Are_Covered_in_JIT_Tests() } [Test] - public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() + public void Pattern_Analyzer_Find_All_Instance_Of_Pattern() { byte[] bytecode = Prepare.EvmCode @@ -886,14 +897,14 @@ public async Task Pattern_Analyzer_Find_All_Instance_Of_Pattern() CodeInfo codeInfo = new CodeInfo(bytecode, TestItem.AddressA); - await IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, NullLogger.Instance, config); + IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, config, NullLogger.Instance); codeInfo.IlInfo.Chunks.Count.Should().Be(2); } [Test] - public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() + public void JIT_Analyzer_Compiles_stateless_bytecode_chunk() { byte[] bytecode = Prepare.EvmCode @@ -915,7 +926,7 @@ public async Task JIT_Analyzer_Compiles_stateless_bytecode_chunk() CodeInfo codeInfo = new CodeInfo(bytecode, TestItem.AddressA); - await IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, NullLogger.Instance, config); + IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, config, NullLogger.Instance); codeInfo.IlInfo.Segments.Count.Should().Be(2); } @@ -931,7 +942,8 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, IsJitEnabled = false, - AggressiveJitMode = false + AggressiveJitMode = false, + AnalysisQueueMaxSize = 1, }); var pattern1 = IlAnalyzer.GetPatternHandler(); @@ -1001,6 +1013,7 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by PatternMatchingThreshold = int.MaxValue, IsPatternMatchingEnabled = false, JittingThreshold = repeatCount + 1, + AnalysisQueueMaxSize = 1, IsJitEnabled = true }); enhancedChain.InsertCode(testcase.bytecode); @@ -1056,6 +1069,7 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) PatternMatchingThreshold = repeatCount + 1, IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, + AnalysisQueueMaxSize = 1, IsJitEnabled = false }); enhancedChain.InsertCode(testcase.bytecode); @@ -1110,6 +1124,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() PatternMatchingThreshold = repeatCount + 1, IsPatternMatchingEnabled = false, JittingThreshold = repeatCount + 1, + AnalysisQueueMaxSize = 1, IsJitEnabled = true, AggressiveJitMode = true }); @@ -1182,6 +1197,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = repeatCount * 2 + 1, + AnalysisQueueMaxSize = 1, IsPatternMatchingEnabled = true, JittingThreshold = repeatCount + 1, IsJitEnabled = true, @@ -1259,6 +1275,7 @@ public void JIT_invalid_opcode_results_in_failure() IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, IsJitEnabled = true, + AnalysisQueueMaxSize = 1, AggressiveJitMode = false }); @@ -1291,6 +1308,7 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() { PatternMatchingThreshold = int.MaxValue, IsPatternMatchingEnabled = false, + AnalysisQueueMaxSize = 1, JittingThreshold = repeatCount + 1, IsJitEnabled = true, AggressiveJitMode = false diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index ef1bbfba1c4..358f45c8f2d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -12,7 +12,6 @@ using Nethermind.Logging; using ILMode = int; using Nethermind.Core; - namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo : IThreadPoolWorkItem @@ -25,7 +24,7 @@ public class CodeInfo : IThreadPoolWorkItem // IL-EVM private int _callCount; - public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) + public void NoticeExecution(IVMConfig vmConfig, ILogger logger) { // IL-EVM info already created if (_callCount > Math.Max(vmConfig.JittingThreshold, vmConfig.PatternMatchingThreshold)) @@ -43,7 +42,8 @@ public async void NoticeExecution(IVMConfig vmConfig, ILogger logger) if (mode == IlInfo.ILMode.NO_ILVM) return; - await IlAnalyzer.StartAnalysis(this, mode, logger, vmConfig).ConfigureAwait(false); + IlAnalyzer.Enqueue(this, mode, vmConfig, logger); + } private readonly JumpDestinationAnalyzer _analyzer; private static readonly JumpDestinationAnalyzer _emptyAnalyzer = new(Array.Empty()); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 05c9958a6c0..dd2ed57a0c9 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -45,7 +45,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? // Note(Ayman) : verify all endianness arguments and bytes - Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + Emit method = Emit.NewDynamicMethod(segmentName, doVerify: false, strictBranchVerification: false); ushort[] jumpdests = Array.Empty(); if (code.Length == 0) @@ -2565,7 +2565,6 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co { OpcodeInfo op = code[pc]; - Debug.WriteLine(op); switch (op.Operation) { case Instruction.JUMPDEST: diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 47c51f242b1..a0b61616b27 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -8,6 +8,7 @@ using Nethermind.Int256; using Nethermind.Logging; using System; +using System.Collections.Concurrent; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -15,6 +16,7 @@ using System.Threading.Tasks; using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; +using static Org.BouncyCastle.Crypto.Engines.SM2Engine; using ILMode = int; namespace Nethermind.Evm.CodeAnalysis.IL; @@ -24,6 +26,32 @@ namespace Nethermind.Evm.CodeAnalysis.IL; /// public static class IlAnalyzer { + public class AnalysisWork(CodeInfo codeInfo, ILMode mode) + { + public CodeInfo CodeInfo = codeInfo; + public ILMode Mode = mode; + } + private static readonly ConcurrentQueue _queue = new(); + + public static void Enqueue(CodeInfo codeInfo, ILMode mode, IVMConfig config, ILogger logger) + { + _queue.Enqueue(new AnalysisWork(codeInfo, mode)); + if(config.AnalysisQueueMaxSize == _queue.Count) + { + Task.Run(() => AnalyzeQueue(config, logger)); + } + } + + private static void AnalyzeQueue(IVMConfig config, ILogger logger) + { + int itemsLeft = config.AnalysisQueueMaxSize; + while (itemsLeft-- > 0 && _queue.TryDequeue(out AnalysisWork worklet)) + { + if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {worklet.CodeInfo.Address}"); + IlAnalyzer.StartAnalysis(worklet.CodeInfo, worklet.Mode, config, logger); + } + } + private static Dictionary _patterns = new Dictionary(); internal static void AddPattern(InstructionChunk handler) { @@ -57,18 +85,6 @@ public static void Initialize() } } - /// - /// Starts the analyzing in a background task and outputs the value in the . - /// thou - /// The destination output. - internal static Task StartAnalysis(CodeInfo codeInfo, ILMode mode, ILogger logger, IVMConfig vmConfig) - { - Metrics.IlvmContractsAnalyzed++; - - if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {codeInfo.Address}"); - return Task.Run(() => Analysis(codeInfo, mode, vmConfig, logger)); - } - public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineCode) { OpcodeInfo[] opcodes = new OpcodeInfo[machineCode.Length]; @@ -94,8 +110,9 @@ public static (OpcodeInfo[], byte[][]) StripByteCode(ReadOnlySpan machineC /// /// For now, return null always to default to EVM. /// - private static void Analysis(CodeInfo codeInfo, ILMode mode, IVMConfig vmConfig, ILogger logger) + internal static void StartAnalysis(CodeInfo codeInfo, ILMode mode, IVMConfig vmConfig, ILogger logger) { + Metrics.IlvmContractsAnalyzed++; ReadOnlyMemory machineCode = codeInfo.MachineCode; static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, IlInfo ilinfo, IVMConfig vmConfig) diff --git a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs index 9da5ed6e886..ac1f97d9ab2 100644 --- a/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -41,5 +41,10 @@ public interface IVMConfig : IConfig DefaultValue = "false")] public bool BakeInTracingInJitMode { get; set; } + [ConfigItem( + Description = "Sets Analysis Queue Max Size", + DefaultValue = "8")] + public int AnalysisQueueMaxSize { get; set; } + public bool IsVmOptimizationEnabled => IsPatternMatchingEnabled || IsJitEnabled; } diff --git a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs index 4e4342597e3..8c9bc74b132 100644 --- a/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -17,4 +17,5 @@ public class VMConfig : IVMConfig public int JittingThreshold { get; set; } = 128; public bool AggressiveJitMode { get; set; } = false; public bool BakeInTracingInJitMode { get; set; } = false; + public int AnalysisQueueMaxSize { get; set; } = 8; } From 9bbf63923451348e6e025176665d0b2e5ddb246f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 17 Oct 2024 21:33:20 +0100 Subject: [PATCH 107/146] removed empty lines --- .../CodeAnalysis/IL/ILCompiler.cs | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index dd2ed57a0c9..5210099146e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -128,13 +128,13 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(head); // set gas to local - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); method.Convert(); method.StoreLocal(gasAvailable); // set pc to local - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); method.StoreLocal(programCounter); @@ -227,7 +227,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.STOP: { - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadConstant(true); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); method.FakeBranch(ret); @@ -237,7 +237,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.Call(Word.SetULong0); method.StackPush(head); @@ -675,7 +675,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); @@ -687,7 +687,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); @@ -699,7 +699,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); @@ -711,7 +711,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); @@ -723,7 +723,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); method.Call(Word.SetAddress); @@ -734,7 +734,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.Call(Word.SetAddress); @@ -745,7 +745,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); method.Call(Word.SetAddress); @@ -756,7 +756,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); method.Call(Word.SetUInt256); @@ -767,7 +767,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); method.Call(Word.SetUInt256); @@ -805,7 +805,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -814,7 +814,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256B); @@ -825,7 +825,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -844,7 +844,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); @@ -861,7 +861,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); @@ -873,7 +873,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.Call(GetPropertyInfo(nameof(EvmPooledMemory.Size), false, out _)); method.Call(Word.SetULong0); @@ -890,7 +890,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256B); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -901,7 +901,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -921,7 +921,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(byte8A); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -932,7 +932,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -951,7 +951,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -959,7 +959,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); @@ -1005,7 +1005,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadConstant((long)0); method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1017,10 +1017,10 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256B); method.LoadLocalAddress(uint256C); @@ -1050,7 +1050,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadConstant((long)0); method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1061,7 +1061,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); @@ -1145,7 +1145,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1153,7 +1153,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.MachineCode))); method.StoreLocal(localReadOnlyMemory); @@ -1165,7 +1165,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1187,7 +1187,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.RETURNDATASIZE: method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); method.Call(Word.SetInt0); @@ -1215,7 +1215,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(tempResult); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.AddOverflow))); method.LoadLocalAddress(tempResult); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.Call(typeof(ReadOnlyMemory).GetProperty(nameof(ReadOnlyMemory.Length)).GetMethod!); method.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); @@ -1241,7 +1241,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1249,7 +1249,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); method.LoadObject(typeof(ReadOnlyMemory)); method.LoadLocalAddress(uint256B); @@ -1259,7 +1259,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1278,7 +1278,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256B); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1286,16 +1286,16 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Load), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.StoreObject>(); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadConstant(true); switch (op.Operation) { @@ -1312,7 +1312,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); @@ -1325,7 +1325,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ using Local uint256Nullable = method.DeclareLocal(typeof(UInt256?)); method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); method.StoreLocal(uint256Nullable); @@ -1342,7 +1342,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Duplicate(); @@ -1375,20 +1375,20 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint32A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.LoadNull(); method.BranchIfEqual(blobVersionedHashNotFound); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.Call(GetPropertyInfo(typeof(byte[][]), nameof(Array.Length), false, out _)); method.LoadLocal(uint32A); method.BranchIfLessOrEqual(blobVersionedHashNotFound); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.BlobVersionedHashes), false, out _)); method.LoadLocal(uint32A); @@ -1423,8 +1423,8 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(int64A); method.StackPop(head, 1); - method.LoadArgument(BLOCKHASH_PROVIDER_INDEX); ; - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(BLOCKHASH_PROVIDER_INDEX); + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.LoadLocalAddress(int64A); @@ -1529,7 +1529,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.TSTORE: { - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); @@ -1544,7 +1544,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1564,7 +1564,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1593,7 +1593,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(localReadonOnlySpan); method.StackPop(head, 2); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocalAddress(gasAvailable); @@ -1614,7 +1614,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadConstant((int)EvmExceptionType.None); method.BranchIfEqual(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadLocal(uint32A); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); method.Branch(exit); @@ -1638,7 +1638,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256A); method.StackPop(head, 1); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.LoadLocalAddress(uint256A); @@ -1646,7 +1646,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(storageCell); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(storageCell); method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); @@ -1684,7 +1684,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1752,7 +1752,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1767,7 +1767,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); method.BranchIfTrue(endOfOpcode); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); @@ -1790,7 +1790,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(ByteArrayExtensions).GetMethod(nameof(ByteArrayExtensions.SliceWithZeroPadding), [typeof(ReadOnlyMemory), typeof(UInt256).MakeByRefType(), typeof(int), typeof(PadDirection)])); method.StoreLocal(localZeroPaddedSpan); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(localZeroPaddedSpan); @@ -1818,7 +1818,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(true); @@ -1855,7 +1855,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CleanWord(stack, head); method.Load(stack, head); method.LoadArgument(WORLD_STATE_INDEX); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); @@ -1880,7 +1880,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackPop(head, 1); method.LoadLocalAddress(gasAvailable); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); method.LoadLocal(address); method.LoadConstant(false); @@ -1919,18 +1919,18 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ // check if returnState is null method.MarkLabel(ret); // we get stack size - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadLocal(head); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); // set stack - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadLocal(stack); method.Call(GetCastMethodInfo()); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); // set gas available - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadLocal(gasAvailable); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); @@ -1938,7 +1938,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocal(isEphemeralJump); method.BranchIfTrue(skipProgramCounterSetting); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadLocal(programCounter); method.LoadConstant(1); method.Add(); @@ -1948,7 +1948,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ // set exception - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.LoadConstant((int)EvmExceptionType.None); method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); @@ -1993,7 +1993,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Branch(jumpIsLocal); method.MarkLabel(jumpIsNotLocal); - method.LoadArgument(VMSTATE_INDEX); ; + method.LoadArgument(VMSTATE_INDEX); method.Duplicate(); method.LoadConstant(true); method.StoreLocal(isEphemeralJump); From 13d793e5cb4808dda6879293c917e71c7af9610c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 17 Oct 2024 21:38:56 +0100 Subject: [PATCH 108/146] minor refactor --- .../CodeAnalysis/IL/ILEvmState.cs | 1 + .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 39 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs index 35a3c6e5198..9aaa5a8efd1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -47,6 +47,7 @@ internal ref struct ILEvmState public ref readonly ReadOnlyMemory InputBuffer; public ref ReadOnlyMemory ReturnBuffer; + public ILEvmState(ulong chainId, EvmState evmState, EvmExceptionType evmException, ushort programCounter, long gasAvailable, ref ReadOnlyMemory returnBuffer) { ChainId = chainId; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index ef17d3a1db2..506b09a448c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Numerics; using System.Runtime.CompilerServices; using Microsoft.IdentityModel.Tokens; using Nethermind.Core; @@ -28,6 +29,20 @@ internal struct ILChunkExecutionResult public bool ShouldReturn; public object ReturnData; public EvmExceptionType ExceptionType; + + + public static explicit operator ILChunkExecutionResult(ILEvmState state) + { + return new ILChunkExecutionResult + { + ShouldJump = state.ShouldJump, + ShouldStop = state.ShouldStop, + ShouldRevert = state.ShouldRevert, + ShouldReturn = state.ShouldReturn, + ReturnData = state.ReturnBuffer, + ExceptionType = state.EvmException + }; + } } public static class ILMode @@ -73,7 +88,6 @@ public bool TryExecute(ILogger logger, EvmState vmState, u if (programCounter > ushort.MaxValue || Mode == ILMode.NO_ILVM) return false; - var executionResult = new ILChunkExecutionResult(); if (Mode.HasFlag(ILMode.JIT_MODE) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) { Metrics.IlvmPrecompiledSegmentsExecutions++; @@ -88,21 +102,17 @@ public bool TryExecute(ILogger logger, EvmState vmState, u gasAvailable = ilvmState.GasAvailable; programCounter = ilvmState.ProgramCounter; - - executionResult.ShouldReturn = ilvmState.ShouldReturn; - executionResult.ShouldRevert = ilvmState.ShouldRevert; - executionResult.ShouldStop = ilvmState.ShouldStop; - executionResult.ShouldJump = ilvmState.ShouldJump; - executionResult.ExceptionType = ilvmState.EvmException; - executionResult.ReturnData = ilvmState.ReturnBuffer; - + result = (ILChunkExecutionResult)ilvmState; stack.Head = ilvmState.StackHead; if (typeof(TTracingInstructions) == typeof(IsTracing)) tracer.ReportOperationRemainingGas(gasAvailable); + + return true; } else if (Mode.HasFlag(ILMode.PAT_MODE) && Chunks.TryGetValue((ushort)programCounter, out InstructionChunk chunk)) { + var executionResult = new ILChunkExecutionResult(); Metrics.IlvmPredefinedPatternsExecutions++; if (typeof(TTracingInstructions) == typeof(IsTracing)) @@ -113,14 +123,11 @@ public bool TryExecute(ILogger logger, EvmState vmState, u if (typeof(TTracingInstructions) == typeof(IsTracing)) tracer.ReportOperationRemainingGas(gasAvailable); - } - else - { - return false; - } - result = executionResult; - return true; + result = executionResult; + return true; + } + return false; } private static void StartTracingSegment(in EvmState vmState, in EvmStack stack, ITxTracer tracer, int programCounter, long gasAvailable, T chunk) From d3bc4fc296697e23e5295637b83513611e1d4bdb Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 22 Oct 2024 01:50:17 +0100 Subject: [PATCH 109/146] minor cleanup --- .../Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index a0b61616b27..8c2107c8d1d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -156,6 +156,11 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il segmentAvgSize += segment.Length; + if(segment.Length <= 1) + { + continue; + } + var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2, vmConfig); if (vmConfig.AggressiveJitMode) { @@ -169,8 +174,6 @@ static void SegmentCode(CodeInfo codeInfo, (OpcodeInfo[], byte[][]) codeData, Il { ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); } - - Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); } Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); From 47394013a2bc3e22546e14f20a2ead84b1be47ce Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 22 Oct 2024 02:06:24 +0100 Subject: [PATCH 110/146] minor refactor --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5210099146e..61aa5ad5794 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -47,16 +47,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ Emit method = Emit.NewDynamicMethod(segmentName, doVerify: false, strictBranchVerification: false); - ushort[] jumpdests = Array.Empty(); - if (code.Length == 0) - { - method.Return(); - } - else - { - jumpdests = EmitSegmentBody(method, code, config.BakeInTracingInJitMode); - } - + ushort[] jumpdests = EmitSegmentBody(method, code, config.BakeInTracingInJitMode); ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); return new SegmentExecutionCtx { From 2841d2fa399f816feb4fe64f476bdf400c6af71d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 22 Oct 2024 23:53:54 +0100 Subject: [PATCH 111/146] fixes to PUSH0 and SHR and SHL --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 13 ++++++++++--- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 61aa5ad5794..3162cad7e89 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2127,14 +2127,21 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.LoadLocalAddress(locals[0]); il.LoadConstant(Word.Size * sizeof(byte)); - il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); - il.BranchIfTrue(skipPop); + il.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + il.BranchIfFalse(skipPop); - il.LoadLocalAddress(locals[0]); il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.LoadLocalAddress(locals[1]); + + il.LoadLocal(locals[0]); il.Call(Word.GetInt0); + il.LoadLocalAddress(uint256R); + il.Call(shiftOp); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 58b8f4bdfdc..90474c60d56 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -205,7 +205,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.STOP] = new(0, 0, 0, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.PUSH0] = new(GasCostOf.VeryLow, 0, 0, 1), + [Instruction.PUSH0] = new(GasCostOf.Base, 0, 0, 1), [Instruction.PUSH1] = new(GasCostOf.VeryLow, 1, 0, 1), [Instruction.PUSH2] = new(GasCostOf.VeryLow, 2, 0, 1), [Instruction.PUSH3] = new(GasCostOf.VeryLow, 3, 0, 1), From e77ac731e32a3829d3f5f83ac43f45285675ef46 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Wed, 23 Oct 2024 00:55:57 +0100 Subject: [PATCH 112/146] fix SHR / SHL --- .../CodeAnalysis/IlEvmTests.cs | 46 +++++++++++++++++++ .../CodeAnalysis/IL/ILCompiler.cs | 27 +++++++++-- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 28 +++++++++-- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index fdcbc41fbe2..70e8e73ec06 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1515,5 +1515,51 @@ public void Test_ILVM_Trace_Mode_Has_0_Traces_When_TraceInstructions_Is_Off((Ins Assert.That(tracedOpcodes.Count, Is.EqualTo(0)); } + + [Test] + public void Extra() + { + var bytecode = Prepare.EvmCode + .PushData("0x2213bc0b00000000000000000000000070bf6634ee8cb27d04478f184b9b8bb1") + .PushData("0xe0") + .SHR() + .Done; + + var codeInfo = new CodeInfo(bytecode, TestItem.AddressA); + var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); + var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()], CodeInfoRepository); + var envExCtx = new ExecutionEnvironment(codeInfo, Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); + var stack = new byte[1024 * 32]; + var inputBuffer = envExCtx.InputData; + var returnBuffer = + new ReadOnlyMemory(Enumerable.Range(0, 32) + .Select(i => (byte)i).ToArray()); + + TestState.CreateAccount(Address.FromNumber(1), 1000000); + TestState.InsertCode(Address.FromNumber(1), bytecode, Prague.Instance); + + var state = new EvmState( + 1_000_000, + new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), + ExecutionType.CALL, + isTopLevel: false, + Snapshot.Empty, + isContinuation: false); + + IVirtualMachine evm = typeof(VirtualMachine).GetField("_evm", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Machine) as IVirtualMachine; + + state.InitStacks(); + + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); + var metadata = IlAnalyzer.StripByteCode(bytecode); + var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2, config); + ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, tracer, ctx.Data); + + var tracedOpcodes = tracer.BuildResult().Entries; + + Assert.That(tracedOpcodes.Count, Is.GreaterThan(0)); + } } } diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 3162cad7e89..16c8fb108fa 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -45,7 +45,7 @@ public static SegmentExecutionCtx CompileSegment(string segmentName, OpcodeInfo[ // Note(Ayman) : What stops us from adopting stack analysis from EOF in ILVM? // Note(Ayman) : verify all endianness arguments and bytes - Emit method = Emit.NewDynamicMethod(segmentName, doVerify: false, strictBranchVerification: false); + Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); ushort[] jumpdests = EmitSegmentBody(method, code, config.BakeInTracingInJitMode); ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); @@ -2121,12 +2121,18 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local // Note: Use Vector256 directoly if UInt256 does not use it internally // we the two uint256 from the stack + Local shiftBit = il.DeclareLocal(); + il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); + il.Duplicate(); + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + il.Convert(); + il.StoreLocal(shiftBit); il.StoreLocal(locals[0]); il.LoadLocalAddress(locals[0]); - il.LoadConstant(Word.Size * sizeof(byte)); + il.LoadConstant(Word.FullSize); il.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); il.BranchIfFalse(skipPop); @@ -2135,13 +2141,23 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.StoreLocal(locals[1]); il.LoadLocalAddress(locals[1]); - il.LoadLocal(locals[0]); - il.Call(Word.GetInt0); + il.LoadLocal(shiftBit); il.LoadLocalAddress(uint256R); il.Call(shiftOp); + + string message = "I am here"; + + Local str = il.DeclareLocal(); + il.LoadConstant(message); + il.StoreLocal(str); + il.Print(locals[1]); + il.Print(shiftBit); + il.Print(uint256R); + il.Print(str); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.Load(stack.span, stack.idx); @@ -2151,6 +2167,7 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.Branch(endOfOpcode); il.MarkLabel(skipPop); + il.StackPop(stack.idx, 2); il.CleanWord(stack.span, stack.idx); il.StackPush(stack.idx, 1); @@ -2171,7 +2188,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.StoreLocal(locals[0]); il.LoadLocalAddress(locals[0]); - il.LoadConstant(Word.Size * sizeof(byte)); + il.LoadConstant(Word.FullSize); il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); il.BranchIfTrue(skipPop); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index b33442f969b..0631d05b20a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -23,6 +23,7 @@ namespace Nethermind.Evm.CodeAnalysis.IL; internal struct Word { public const int Size = 32; + public const int FullSize = 256; [FieldOffset(0)] public unsafe fixed byte _buffer[Size]; @@ -178,7 +179,14 @@ public uint UInt0 { get { - return _uInt0; + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_uInt0); + } + else + { + return _uInt0; + } } set { @@ -197,7 +205,14 @@ public int Int0 { get { - return _sInt0; + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_sInt0); + } + else + { + return _sInt0; + } } set { @@ -216,7 +231,14 @@ public ulong ULong0 { get { - return _ulong0; + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_ulong0); + } + else + { + return _ulong0; + } } set { From a6cbb878d5a526c2e4178fe372cdcb292f272e62 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 24 Oct 2024 11:44:05 +0100 Subject: [PATCH 113/146] fix jjmp opcodes --- .../CodeAnalysis/IlEvmTests.cs | 4 +--- .../CodeAnalysis/IL/ILCompiler.cs | 17 ++++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 70e8e73ec06..d797b7af5a3 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -336,8 +336,6 @@ public override void Setup() public static IEnumerable<(Instruction?, byte[], EvmExceptionType)> GeJitBytecodesSamples() { - yield return (null, Prepare.EvmCode - .Done, EvmExceptionType.None); yield return (Instruction.PUSH32, Prepare.EvmCode .PushSingle(1) .Done, EvmExceptionType.None); @@ -1126,7 +1124,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() JittingThreshold = repeatCount + 1, AnalysisQueueMaxSize = 1, IsJitEnabled = true, - AggressiveJitMode = true + AggressiveJitMode = true, }); var address = enhancedChain.InsertCode(Prepare.EvmCode diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 16c8fb108fa..2ab75071f29 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -13,7 +13,6 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; using static Nethermind.Evm.IL.EmitExtensions; @@ -400,18 +399,18 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.Divide), BindingFlags.Public | BindingFlags.Static, [typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType()])!, (il, postInstructionLabel, locals) => { - Label label1 = il.DefineLabel(); - Label label2 = il.DefineLabel(); + Label bIsNotZero = il.DefineLabel(); + Label bIsNotMinusOneLabel = il.DefineLabel(); il.LoadLocalAddress(locals[1]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); - il.BranchIfFalse(label1); + il.BranchIfFalse(bIsNotZero); il.LoadField(typeof(UInt256).GetField(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); - il.MarkLabel(label1); + il.MarkLabel(bIsNotZero); il.LoadLocalAddress(locals[1]); il.Call(GetAsMethodInfo()); @@ -422,14 +421,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static | BindingFlags.NonPublic).GetMethod); il.Call(typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() })); il.And(); - il.BranchIfFalse(label2); + il.BranchIfFalse(bIsNotMinusOneLabel); il.Call(typeof(VirtualMachine).GetProperty(nameof(VirtualMachine.P255), BindingFlags.Static | BindingFlags.NonPublic).GetMethod); il.LoadObject(typeof(UInt256)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); - il.MarkLabel(label2); + il.MarkLabel(bIsNotMinusOneLabel); }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.ADDMOD: @@ -1458,7 +1457,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt0); method.StoreLocal(uint32A); - method.StackPop(head, 1); + method.Print(uint32A); method.LoadLocal(uint32A); method.LoadConstant(32); @@ -1506,6 +1505,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocal(tempLocalSpan); method.Call(typeof(Span).GetMethod(nameof(Span.CopyTo), [typeof(Span)])); method.MarkLabel(argumentGt32); + method.StackPop(head, 1); } break; case Instruction.LOG0: @@ -1959,7 +1959,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.MarkLabel(jumpTable); method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetInt0); - method.Call(typeof(BinaryPrimitives).GetMethod(nameof(BinaryPrimitives.ReverseEndianness), BindingFlags.Public | BindingFlags.Static, new[] { typeof(uint) }), null); method.StoreLocal(jmpDestination); method.StackPop(head); From 23caab817432716e4e62ed63dfd8f505c15770da Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 04:37:06 +0100 Subject: [PATCH 114/146] refactor Bitwise OPs to use Vector256 operations --- .../CodeAnalysis/IL/ILCompiler.cs | 91 ++++++++++++------- .../CodeAnalysis/IL/ILExtensions.cs | 31 +++++-- 2 files changed, 82 insertions(+), 40 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2ab75071f29..0a6a6d53380 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.Intrinsics; using static Nethermind.Evm.IL.EmitExtensions; using Label = Sigil.Label; @@ -352,7 +353,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ il.BranchIfFalse(label); il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.Call(ConvertionExplicit()); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); @@ -363,18 +364,17 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitBinaryInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.Mod), BindingFlags.Public | BindingFlags.Static, [typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType(), typeof(Int256.Int256).MakeByRefType()])!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel(); + Label bIsNotZeroOrOneLabel = il.DefineLabel(); il.LoadLocalAddress(locals[1]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); - il.BranchIfFalse(label); + il.BranchIfFalse(bIsNotZeroOrOneLabel); - il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); - il.MarkLabel(label); + il.MarkLabel(bIsNotZeroOrOneLabel); }, evmExceptionLabels, uint256A, uint256B); break; case Instruction.DIV: @@ -387,8 +387,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); il.BranchIfFalse(label); - il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); @@ -435,36 +434,34 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.AddMod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel(); + Label cIsNotZeroLabel = il.DefineLabel(); il.LoadLocalAddress(locals[2]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); - il.BranchIfFalse(label); + il.BranchIfFalse(cIsNotZeroLabel); - il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); - il.MarkLabel(label); + il.MarkLabel(cIsNotZeroLabel); }, evmExceptionLabels, uint256A, uint256B, uint256C); break; case Instruction.MULMOD: EmitTrinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.MultiplyMod), BindingFlags.Public | BindingFlags.Static)!, (il, postInstructionLabel, locals) => { - Label label = il.DefineLabel(); + Label cIsNotZeroLabel = il.DefineLabel(); il.LoadLocalAddress(locals[2]); il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); - il.BranchIfFalse(label); + il.BranchIfFalse(cIsNotZeroLabel); - il.LoadConstant(0); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); il.StoreLocal(uint256R); il.Branch(postInstructionLabel); - il.MarkLabel(label); + il.MarkLabel(cIsNotZeroLabel); }, evmExceptionLabels, uint256A, uint256B, uint256C); break; case Instruction.SHL: @@ -477,13 +474,13 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitShiftInt256Method(method, uint256R, (stack, head), evmExceptionLabels, uint256A, uint256B); break; case Instruction.AND: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.And), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.BitwiseAnd), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); break; case Instruction.OR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Or), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.BitwiseOr), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); break; case Instruction.XOR: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); break; case Instruction.EXP: { @@ -885,7 +882,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.Call(ConvertionExplicit()); method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); @@ -897,7 +894,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(uint256B); method.LoadConstant(Word.Size); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.Call(ConvertionImplicit(typeof(Span), typeof(byte[]))); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveWord))); } break; @@ -916,7 +913,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(gasAvailable); method.LoadLocalAddress(uint256A); method.LoadConstant(1); - method.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + method.Call(ConvertionExplicit()); method.StoreLocal(uint256C); method.LoadLocalAddress(uint256C); method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); @@ -953,7 +950,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); method.StoreLocal(localReadonOnlySpan); method.CleanWord(stack, head); @@ -1056,7 +1053,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); method.Call(Word.SetKeccak); method.StackPush(head); @@ -1340,7 +1337,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.BranchIfTrue(isPostMergeBranch); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); method.StoreLocal(localReadonOnlySpan); method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); @@ -1427,7 +1424,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ // not equal method.LoadLocal(hash256); method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); - method.Call(typeof(Span).GetMethod("op_Implicit", new[] { typeof(Span) })); + method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); method.StoreLocal(localReadonOnlySpan); method.Branch(pushToStackRegion); // equal to null @@ -1435,7 +1432,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.MarkLabel(blockHashReturnedNull); method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); - method.Call(typeof(ReadOnlySpan).GetMethod("op_Implicit", new[] { typeof(byte[]) })); + method.Call(ConvertionImplicit(typeof(ReadOnlySpan), typeof(byte[]))); method.StoreLocal(localReadonOnlySpan); method.MarkLabel(pushToStackRegion); @@ -2236,6 +2233,34 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local } private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) + { + // Note: Use Vector256 directoly if UInt256 does not use it internally + // we the two uint256 from the stack + var refWordToRefByteMethod = GetAsMethodInfo(); + var readVector256Method = GetReadUnalignedMethodInfo>(); + var writeVector256Method = GetWriteUnalignedMethodInfo>(); + var operationUnegenerified = operation.MakeGenericMethod(typeof(byte)); + + using Local vectorResult = il.DeclareLocal>(); + + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(refWordToRefByteMethod); + il.Call(readVector256Method); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(refWordToRefByteMethod); + il.Call(readVector256Method); + + il.Call(operationUnegenerified); + il.StoreLocal(vectorResult); + + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(refWordToRefByteMethod); + il.LoadLocal(vectorResult); + il.Call(writeVector256Method); + il.StackPop(stack.idx); + } + + private static void EmitBitwiseUInt256Method2(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) { // Note: Use Vector256 directoly if UInt256 does not use it internally // we the two uint256 from the stack @@ -2251,7 +2276,7 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(locals[1]); il.LoadLocalAddress(uint256R); - il.Call(operation, null); + il.Call(operation); // push the result to the stack il.CleanWord(stack.span, stack.idx); @@ -2275,11 +2300,11 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, // invoke op on the uint256 il.LoadLocalAddress(locals[0]); il.LoadLocalAddress(locals[1]); - il.Call(operation, null); + il.Call(operation); // convert to conv_i il.Convert(); - il.Call(typeof(UInt256).GetMethod("op_Explicit", new[] { typeof(int) })); + il.Call(ConvertionExplicit()); il.StoreLocal(uint256R); // push the result to the stack @@ -2309,7 +2334,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.LoadLocalAddress(locals[1]); il.Call(GetAsMethodInfo()); il.LoadObject(); - il.Call(operation, null); + il.Call(operation); il.LoadConstant(0); if (isGreaterThan) { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index 7df6a9a633a..c93031a298d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -4,6 +4,7 @@ using Nethermind.Core; using Nethermind.Db; using Nethermind.Evm.CodeAnalysis.IL; +using Nethermind.Int256; using Org.BouncyCastle.Tls; using Sigil; using System; @@ -20,6 +21,18 @@ namespace Nethermind.Evm.IL; /// static class EmitExtensions { + public static MethodInfo GetReadUnalignedMethodInfo() + { + MethodInfo method = typeof(Unsafe).GetMethods().First((m) => m.Name == nameof(Unsafe.ReadUnaligned) && m.GetParameters()[0].ParameterType.IsByRef); + return method.MakeGenericMethod(typeof(TResult)); + } + + public static MethodInfo GetWriteUnalignedMethodInfo() + { + MethodInfo method = typeof(Unsafe).GetMethods().First((m) => m.Name == nameof(Unsafe.WriteUnaligned) && m.GetParameters()[0].ParameterType.IsByRef); + return method.MakeGenericMethod(typeof(TResult)); + } + public static MethodInfo GetAsMethodInfo() { MethodInfo method = typeof(Unsafe).GetMethods().First((m) => m.Name == nameof(Unsafe.As) && m.ReturnType.IsByRef); @@ -45,16 +58,20 @@ public unsafe static Span ReinterpretCast(Span(string name) => GetFieldInfo(typeof(T), name); - public static FieldInfo GetFieldInfo(Type TypeInstance, string name) + public static MethodInfo ConvertionImplicit() => ConvertionImplicit(typeof(TFrom), typeof(TTo)); + public static MethodInfo ConvertionImplicit(Type tfrom, Type tto) => tfrom.GetMethod("op_Implicit", new[] { tto }); + public static MethodInfo ConvertionExplicit() => ConvertionExplicit(typeof(TFrom), typeof(TTo)); + public static MethodInfo ConvertionExplicit(Type tfrom, Type tto) => tfrom.GetMethod("op_Explicit", new[] { tto }); + public static FieldInfo GetFieldInfo(string name, BindingFlags? flags = null) => GetFieldInfo(typeof(T), name, flags); + public static FieldInfo GetFieldInfo(Type TypeInstance, string name, BindingFlags? flags = null) { - return TypeInstance.GetField(name); + return flags is null ? TypeInstance.GetField(name) : TypeInstance.GetField(name, flags.Value); } - public static MethodInfo GetPropertyInfo(string name, bool getSetter, out PropertyInfo propInfo) - => GetPropertyInfo(typeof(T), name, getSetter, out propInfo); - public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool getSetter, out PropertyInfo propInfo) + public static MethodInfo GetPropertyInfo(string name, bool getSetter, out PropertyInfo propInfo, BindingFlags? flags = null) + => GetPropertyInfo(typeof(T), name, getSetter, out propInfo, flags); + public static MethodInfo GetPropertyInfo(Type typeInstance, string name, bool getSetter, out PropertyInfo propInfo, BindingFlags? flags = null) { - propInfo = typeInstance.GetProperty(name); + propInfo = flags is null ? typeInstance.GetProperty(name) : typeInstance.GetProperty(name, flags.Value); return getSetter ? propInfo.GetSetMethod() : propInfo.GetGetMethod(); } From 814fe992936ce5b8700cef24ed600f485aa96fc1 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 04:46:59 +0100 Subject: [PATCH 115/146] use Vector256 in NOT opcode --- .../CodeAnalysis/IL/ILCompiler.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 0a6a6d53380..5f9418a838f 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -236,17 +236,20 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.NOT: { - method.Load(stack, head); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256A); - method.StackPop(head); + var refWordToRefByteMethod = GetAsMethodInfo(); + var readVector256Method = GetReadUnalignedMethodInfo>(); + var writeVector256Method = GetWriteUnalignedMethodInfo>(); + var notVector256Method = typeof(Vector256) + .GetMethod(nameof(Vector256.OnesComplement), BindingFlags.Public | BindingFlags.Static)! + .MakeGenericMethod(typeof(byte)); - method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256R); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Not))); - method.Load(stack, head); - method.LoadLocal(uint256R); - method.Call(Word.SetUInt256); + method.StackLoadPrevious(stack, head, 1); + + method.Duplicate(); + method.Call(refWordToRefByteMethod); + method.Call(readVector256Method); + method.Call(notVector256Method); + method.Call(writeVector256Method); } break; case Instruction.JUMP: From a02a46275d8a52810e66abb1012f35e4549159f7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 05:18:45 +0100 Subject: [PATCH 116/146] make KECCAK256 use KeccakCache --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5f9418a838f..081244edf9a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -1020,13 +1020,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.KECCAK256: { + var refWordToRefValueHashMethod = GetAsMethodInfo(); + method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); method.StackLoadPrevious(stack, head, 2); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackPop(head, 2); method.LoadLocal(gasAvailable); method.LoadLocalAddress(uint256B); @@ -1049,17 +1050,16 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.CleanWord(stack, head); - method.Load(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); method.LoadLocalAddress(uint256B); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()])); method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); - method.Call(typeof(ValueKeccak).GetMethod(nameof(ValueKeccak.Compute), [typeof(ReadOnlySpan)])); - method.Call(Word.SetKeccak); - method.StackPush(head); + method.StackLoadPrevious(stack, head, 2); + method.Call(refWordToRefValueHashMethod); + method.Call(typeof(KeccakCache).GetMethod(nameof(KeccakCache.ComputeTo), [typeof(ReadOnlySpan), typeof(ValueHash256).MakeByRefType()])); + method.StackPop(head); } break; case Instruction.BYTE: From fbc1b394248d035e82f9a6da744b0dad66ad30f6 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 05:25:12 +0100 Subject: [PATCH 117/146] add parity tracing mode --- .../Tracing/ParityStyle/ParityLikeTxTracer.cs | 26 +++++++++++++++++++ .../ParityStyle/ParityVmOperationTrace.cs | 2 ++ .../ParityVmOperationTraceConverter.cs | 2 ++ 3 files changed, 30 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs index 0d4e90daad0..f474b280169 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityLikeTxTracer.cs @@ -266,6 +266,32 @@ public override void StartOperation(int pc, Instruction opcode, long gas, in Exe _currentVmTrace.Ops.Add(operationTrace); } + public override void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + ParityVmOperationTrace operationTrace = new(); + _gasAlreadySetForCurrentOp = false; + operationTrace.Pc = pc; + operationTrace.Cost = gas; + operationTrace.IsPrecompiledSegment = false; + operationTrace.SegmentId = segmentID; + _currentOperation = operationTrace; + _currentPushList.Clear(); + _currentVmTrace.Ops.Add(operationTrace); + } + + public override void ReportCompiledSegmentExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + ParityVmOperationTrace operationTrace = new(); + _gasAlreadySetForCurrentOp = false; + operationTrace.Pc = pc; + operationTrace.Cost = gas; + operationTrace.IsPrecompiledSegment = true; + operationTrace.SegmentId = segmentID; + _currentOperation = operationTrace; + _currentPushList.Clear(); + _currentVmTrace.Ops.Add(operationTrace); + } + public override void ReportOperationError(EvmExceptionType error) { if (error != EvmExceptionType.InvalidJumpDestination && diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTrace.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTrace.cs index ba44a80c022..1c0982e1fda 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTrace.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTrace.cs @@ -24,6 +24,8 @@ public class ParityVmOperationTrace public byte[][] Push { get; set; } public ParityStorageChangeTrace Store { get; set; } public long Used { get; set; } + public bool IsPrecompiledSegment { get; set; } + public string? SegmentId { get; set; } public int Pc { get; set; } public ParityVmTrace Sub { get; set; } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTraceConverter.cs b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTraceConverter.cs index 6d25e255741..e38c9350530 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTraceConverter.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ParityStyle/ParityVmOperationTraceConverter.cs @@ -74,6 +74,8 @@ public override void Write( writer.WriteEndObject(); writer.WriteNumber("pc"u8, value.Pc); + writer.WriteBoolean("isPrecompiledSegment"u8, value.IsPrecompiledSegment); + writer.WriteString("segmentId"u8, value.SegmentId); writer.WritePropertyName("sub"u8); JsonSerializer.Serialize(writer, value.Sub, options); From 5cfc132d5bec01f77a5d2dbe3025193448e388ab Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 05:44:47 +0100 Subject: [PATCH 118/146] fix NOT opcode --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 081244edf9a..91e5904946a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -244,9 +244,8 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ .MakeGenericMethod(typeof(byte)); method.StackLoadPrevious(stack, head, 1); - - method.Duplicate(); method.Call(refWordToRefByteMethod); + method.Duplicate(); method.Call(readVector256Method); method.Call(notVector256Method); method.Call(writeVector256Method); From 2b3a80ceaa8eac69b927881cdc93ffa30a5392d9 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 18:08:30 +0100 Subject: [PATCH 119/146] remove unused function --- .../CodeAnalysis/IL/ILCompiler.cs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 91e5904946a..9f91c26d981 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2262,32 +2262,6 @@ private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Loc il.StackPop(stack.idx); } - private static void EmitBitwiseUInt256Method2(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) - { - // Note: Use Vector256 directoly if UInt256 does not use it internally - // we the two uint256 from the stack - il.StackLoadPrevious(stack.span, stack.idx, 1); - il.Call(Word.GetUInt256); - il.StoreLocal(locals[0]); - il.StackLoadPrevious(stack.span, stack.idx, 2); - il.Call(Word.GetUInt256); - il.StoreLocal(locals[1]); - il.StackPop(stack.idx, 2); - - // invoke op on the uint256 - il.LoadLocalAddress(locals[0]); - il.LoadLocalAddress(locals[1]); - il.LoadLocalAddress(uint256R); - il.Call(operation); - - // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); - il.LoadLocal(uint256R); // stack: word*, uint256 - il.Call(Word.SetUInt256); - il.StackPush(stack.idx); - } - private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) { // we the two uint256 from the stack From be53a039648e401092670c69e4daa9d1f2176fa6 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 25 Oct 2024 18:34:50 +0100 Subject: [PATCH 120/146] simplify ISZERO and EQ --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 9f91c26d981..09525a99f2c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -560,21 +560,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EQ: - EmitComparaisonUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() }), evmExceptionLabels, uint256A, uint256B); + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.Equals), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); break; case Instruction.ISZERO: {// we load the stack method.StackLoadPrevious(stack, head, 1); + method.Duplicate(); method.Call(Word.GetIsZero); - method.StoreLocal(byte8A); - method.StackPop(head, 1); - - // we convert the result to a Uint256 and store it in the stack - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadLocal(byte8A); method.StoreField(Word.Byte0Field); - method.StackPush(head); } break; case Instruction.POP: From 95ca473907a923ae69b117418260e9a1e6325809 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 27 Oct 2024 18:04:00 +0100 Subject: [PATCH 121/146] fix EQ and certain operations on Word struct --- .../CodeAnalysis/IL/ILCompiler.cs | 23 ++++++++++++++++++- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 6 +++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 09525a99f2c..5ab15bbd156 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -560,7 +560,28 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ EmitComparaisonInt256Method(method, uint256R, (stack, head), typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.CompareTo), new[] { typeof(Int256.Int256) }), true, evmExceptionLabels, uint256A, uint256B); break; case Instruction.EQ: - EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.Equals), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); + { + var refWordToRefByteMethod = GetAsMethodInfo(); + var readVector256Method = GetReadUnalignedMethodInfo>(); + var writeVector256Method = GetWriteUnalignedMethodInfo>(); + var operationUnegenerified = typeof(Vector256).GetMethod(nameof(Vector256.EqualsAll), BindingFlags.Public | BindingFlags.Static)!.MakeGenericMethod(typeof(byte)); + + method.StackLoadPrevious(stack, head, 1); + method.Call(refWordToRefByteMethod); + method.Call(readVector256Method); + method.StackLoadPrevious(stack, head, 2); + method.Call(refWordToRefByteMethod); + method.Call(readVector256Method); + + method.Call(operationUnegenerified); + method.StoreLocal(lbool); + + method.CleanWord(stack, head); + method.Load(stack, head); + method.LoadLocal(lbool); + method.Call(Word.SetUInt0); + method.StackPush(head); + } break; case Instruction.ISZERO: {// we load the stack diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 0631d05b20a..2f6941ef7bc 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -3,6 +3,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Int256; using Nethermind.Trie; using Newtonsoft.Json.Linq; @@ -13,6 +14,7 @@ using System.Linq; using System.Numerics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -108,9 +110,9 @@ public unsafe ValueHash256 Keccak set { ReadOnlySpan buffer = value.Bytes; - for (int i = 0; i < 20; i++) + fixed (byte* src = buffer, dest = _buffer) { - _buffer[i] = buffer[i]; + Buffer.MemoryCopy(src, dest, 32, 32); } } } From d683e09c75d839db922f71bc856a958855712531 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 27 Oct 2024 18:24:06 +0100 Subject: [PATCH 122/146] fix type mismatch in EQ, and other refactors --- .../CodeAnalysis/IL/ILCompiler.cs | 159 ++++++------------ .../CodeAnalysis/IL/ILExtensions.cs | 13 ++ 2 files changed, 65 insertions(+), 107 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5ab15bbd156..f04f96a6b17 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -226,8 +226,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CHAINID: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); method.Call(Word.SetULong0); @@ -324,8 +323,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.PUSH31: case Instruction.PUSH32: {// we load the stack - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); // we load the span of bytes method.LoadArgument(IMMEDIATES_DATA_INDEX); @@ -530,15 +528,13 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Branch(endOfExpImpl); method.MarkLabel(powerIsZero); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadConstant(1); method.StoreField(Word.Byte0Field); method.Branch(endOfExpImpl); method.MarkLabel(baseIsOneOrZero); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(uint256A); method.Call(Word.SetUInt256); method.Branch(endOfExpImpl); @@ -572,13 +568,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackLoadPrevious(stack, head, 2); method.Call(refWordToRefByteMethod); method.Call(readVector256Method); + method.StackPop(head, 2); method.Call(operationUnegenerified); method.StoreLocal(lbool); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(lbool); + method.Convert(); method.Call(Word.SetUInt0); method.StackPush(head); } @@ -658,8 +655,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CODESIZE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadConstant(code.Length); method.Call(Word.SetInt0); method.StackPush(head); @@ -667,8 +663,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.PC: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadConstant((uint)op.ProgramCounter); method.Call(Word.SetUInt0); method.StackPush(head); @@ -676,8 +671,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.COINBASE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -688,8 +682,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.TIMESTAMP: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -700,8 +693,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.NUMBER: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -712,8 +704,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.GASLIMIT: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -724,8 +715,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CALLER: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Caller))); @@ -735,8 +725,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.ADDRESS: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.ExecutingAccount))); @@ -746,8 +735,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.ORIGIN: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.Origin), false, out _)); @@ -757,8 +745,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CALLVALUE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); method.LoadField(GetFieldInfo(typeof(ExecutionEnvironment), nameof(ExecutionEnvironment.Value))); @@ -768,8 +755,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.GASPRICE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.TxCtx))); method.Call(GetPropertyInfo(typeof(TxExecutionContext), nameof(TxExecutionContext.GasPrice), false, out _)); @@ -844,8 +830,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(uint256A); method.StackPop(head, 1); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); @@ -862,8 +847,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CALLDATASIZE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.InputBuffer))); method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); @@ -873,8 +857,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.MSIZE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); @@ -969,8 +952,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); method.StoreLocal(localReadonOnlySpan); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); @@ -1096,8 +1078,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Or(); method.BranchIfTrue(pushZeroLabel); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocalAddress(localReadonOnlySpan); method.LoadLocal(uint256A); method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); @@ -1110,8 +1091,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.MarkLabel(pushZeroLabel); method.CleanWord(stack, head); - method.Load(stack, head); - method.Call(Word.SetToZero); method.StackPush(head); method.MarkLabel(endOfInstructionImpl); @@ -1179,8 +1158,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.GAS: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(gasAvailable); method.Call(Word.SetULong0); @@ -1188,13 +1166,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ } break; case Instruction.RETURNDATASIZE: - method.CleanWord(stack, head); - method.Load(stack, head); - method.LoadArgument(VMSTATE_INDEX); - method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); - method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); - method.Call(Word.SetInt0); - method.StackPush(head); + { + method.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + method.Call(GetPropertyInfo>(nameof(ReadOnlyMemory.Length), false, out _)); + method.Call(Word.SetInt0); + method.StackPush(head); + } break; case Instruction.RETURNDATACOPY: { @@ -1313,8 +1292,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.BASEFEE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); @@ -1326,8 +1304,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.BLOBBASEFEE: { using Local uint256Nullable = method.DeclareLocal(typeof(UInt256?)); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.BlobBaseFee), false, out _)); @@ -1342,8 +1319,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { Label isPostMergeBranch = method.DefineLabel(); Label endOfOpcode = method.DefineLabel(); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.BlkCtx))); @@ -1398,16 +1374,13 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadElement(); method.StoreLocal(localArray); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(localArray); method.Call(Word.SetArray); method.Branch(endOfOpcode); method.MarkLabel(blobVersionedHashNotFound); method.CleanWord(stack, head); - method.Load(stack, head); - method.Call(Word.SetToZero); method.MarkLabel(endOfOpcode); method.StackPush(head); @@ -1452,8 +1425,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(localReadonOnlySpan); method.MarkLabel(pushToStackRegion); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocalAddress(localReadonOnlySpan); method.LoadConstant(BitConverter.IsLittleEndian); method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); @@ -1580,8 +1552,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); method.StoreLocal(localReadonOnlySpan); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); method.StackPush(head); @@ -1664,8 +1635,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.Get), [typeof(StorageCell).MakeByRefType()])); method.StoreLocal(localReadonOnlySpan); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadLocal(localReadonOnlySpan); method.Call(Word.SetSpan); method.StackPush(head); @@ -1699,8 +1669,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(CODE_INFO_REPOSITORY_INDEX); method.LoadArgument(WORLD_STATE_INDEX); @@ -1844,8 +1813,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Or(); method.BranchIfTrue(endOfOpcode); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocal(address); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetCodeHash))); @@ -1856,8 +1824,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.SELFBALANCE: { - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(WORLD_STATE_INDEX); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); @@ -1895,8 +1862,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.ChargeAccountAccessGas))); method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.CleanWord(stack, head); - method.Load(stack, head); + method.CleanAndLoadWord(stack, head); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocal(address); method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); @@ -2159,20 +2125,8 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local il.Call(shiftOp); - - string message = "I am here"; - - Local str = il.DeclareLocal(); - il.LoadConstant(message); - il.StoreLocal(str); - il.Print(locals[1]); - il.Print(shiftBit); - il.Print(uint256R); - il.Print(str); - il.StackPop(stack.idx, 2); - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); il.StackPush(stack.idx, 1); @@ -2212,8 +2166,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); il.StackPop(stack.idx, 2); - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); il.StackPush(stack.idx, 1); @@ -2228,16 +2181,13 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadConstant(0); il.BranchIfLess(signIsNeg); - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); - il.Call(Word.SetToZero); + il.CleanAndLoadWord(stack.span, stack.idx); il.StackPush(stack.idx); il.Branch(endOfOpcode); // sign il.MarkLabel(signIsNeg); - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadFieldAddress(GetFieldInfo(typeof(Int256.Int256), nameof(Int256.Int256.MinusOne))); il.Call(GetAsMethodInfo()); il.LoadObject(); @@ -2298,8 +2248,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, il.StoreLocal(uint256R); // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(stack.idx); @@ -2345,8 +2294,7 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.StoreLocal(uint256R); il.MarkLabel(endOpcodeHandling); // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(stack.idx); @@ -2378,8 +2326,7 @@ private static void EmitBinaryUInt256Method(Emit il, Local uint256R, (Loca il.MarkLabel(label); // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(stack.idx); @@ -2414,8 +2361,7 @@ private static void EmitBinaryInt256Method(Emit il, Local uint256R, (Local il.MarkLabel(label); // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(stack.idx); @@ -2451,8 +2397,7 @@ private static void EmitTrinaryUInt256Method(Emit il, Local uint256R, (Loc il.MarkLabel(label); // push the result to the stack - il.CleanWord(stack.span, stack.idx); - il.Load(stack.span, stack.idx); + il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); // stack: word*, uint256 il.Call(Word.SetUInt256); il.StackPush(stack.idx); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index c93031a298d..bf909a3a84d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -113,6 +113,19 @@ public static void CleanWord(this Emit il, Local local, Local idx) il.InitializeObject(typeof(Word)); } + + public static void CleanAndLoadWord(this Emit il, Local local, Local idx) + { + il.CleanWord(local, idx); + il.Load(local, idx); + } + + public static void ZeroWord(this Emit il, Local local, Local idx) + { + il.Load(local, idx); + il.Call(typeof(Word).GetMethod(nameof(Word.SetToZero))); + } + /// /// Advances the stack one word up. /// From a8984618c7adb69fa1d86d4dd31d5191134fd87f Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 27 Oct 2024 18:47:55 +0100 Subject: [PATCH 123/146] optimise CleanAndLoad method --- .../Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs index bf909a3a84d..c719423644c 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -116,8 +116,12 @@ public static void CleanWord(this Emit il, Local local, Local idx) public static void CleanAndLoadWord(this Emit il, Local local, Local idx) { - il.CleanWord(local, idx); - il.Load(local, idx); + il.LoadLocalAddress(local); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); + il.Duplicate(); + + il.InitializeObject(typeof(Word)); } public static void ZeroWord(this Emit il, Local local, Local idx) From f04d6463ca6686a6731a5d268c286b55d3a6092d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 29 Oct 2024 23:30:50 +0100 Subject: [PATCH 124/146] fix JITTED code for SAR --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index f04f96a6b17..224da03893a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2181,7 +2181,7 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.LoadConstant(0); il.BranchIfLess(signIsNeg); - il.CleanAndLoadWord(stack.span, stack.idx); + il.CleanWord(stack.span, stack.idx); il.StackPush(stack.idx); il.Branch(endOfOpcode); From bee027023aad84c32734a0f141b20a090efaab8d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 01:25:12 +0100 Subject: [PATCH 125/146] opcode fixes, and tests upgrades fix : ExtCodecopy --- .../CodeAnalysis/IlEvmTests.cs | 315 ++++++++++++++---- .../CodeAnalysis/IL/ILCompiler.cs | 53 +-- .../Nethermind.Evm/CodeAnalysis/IL/Word.cs | 34 +- src/Nethermind/Nethermind.Evm/Instruction.cs | 3 +- 4 files changed, 316 insertions(+), 89 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index d797b7af5a3..1eec8914bc1 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -160,7 +160,7 @@ public Address InsertCode(byte[] bytecode) var address = new Address(hashcode); var spec = Prague.Instance; - TestState.CreateAccount(address, 1000000); + TestState.CreateAccount(address, 1_000_000_000); TestState.InsertCode(address, bytecode, spec); return address; } @@ -183,6 +183,7 @@ internal class IlEvmTests : TestBlockChain { private const string AnalyzerField = "_analyzer"; private const string PatternField = "_patterns"; + private const int RepeatCount = 256; [SetUp] public override void Setup() @@ -311,49 +312,65 @@ public override void Setup() .Done); yield return (typeof(EmulatedStaticCJump), Prepare.EvmCode .PUSHx([1]) - .PUSHx([0, 7]) + .PUSHx([0, 8]) .JUMPI() + .INVALID() .JUMPDEST() .Done); yield return (typeof(EmulatedStaticJump), Prepare.EvmCode - .PUSHx([0, 5]) + .PUSHx([0, 6]) .JUMP() + .INVALID() .JUMPDEST() .Done); yield return (typeof(MethodSelector), Prepare.EvmCode - .PushData(0) .PushData(23) + .PushData(32) .MSTORE() .CALLVALUE() .DUPx(1) + .PushData(32) + .MLOAD() + .SSTORE() .Done); yield return (typeof(IsContractCheck), Prepare.EvmCode - .EXTCODESIZE(Address.SystemUser) + .ADDRESS() + .EXTCODESIZE() .DUPx(1) .ISZERO() + .SSTORE() .Done); } public static IEnumerable<(Instruction?, byte[], EvmExceptionType)> GeJitBytecodesSamples() { yield return (Instruction.PUSH32, Prepare.EvmCode + .PushSingle(23) .PushSingle(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.ISZERO, Prepare.EvmCode .ISZERO(7) + .PushData(7) + .SSTORE() .ISZERO(0) - .ISZERO(7) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SUB, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .SUB() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.ADD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .ADD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.ADDMOD, Prepare.EvmCode @@ -361,149 +378,223 @@ public override void Setup() .PushSingle(7) .PushSingle(5) .ADDMOD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MUL, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MUL() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.EXP, Prepare.EvmCode - .PushSingle(23) - .PushSingle(7) - .EXP() .PushSingle(0) .PushSingle(7) .EXP() + .PushData(2) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXP, Prepare.EvmCode .PushSingle(1) .PushSingle(7) .EXP() + .PushData(3) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXP, Prepare.EvmCode .PushSingle(1) .PushSingle(0) .EXP() + .PushData(4) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXP, Prepare.EvmCode .PushSingle(1) .PushSingle(1) .EXP() + .PushData(5) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MOD, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .MOD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.DIV, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .DIV() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MSTORE8, Prepare.EvmCode .MSTORE8(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MCOPY, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MCOPY(32, 0, 32) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.EQ, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .EQ() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.GT, Prepare.EvmCode .PushSingle(7) .PushSingle(23) .GT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.LT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) .LT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.NOT, Prepare.EvmCode .PushSingle(1) .NOT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BLOBHASH, Prepare.EvmCode .PushSingle(0) .BLOBHASH() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BLOCKHASH, Prepare.EvmCode .BLOCKHASH(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CALLDATACOPY, Prepare.EvmCode .CALLDATACOPY(0, 0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CALLDATALOAD, Prepare.EvmCode .CALLDATALOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MSIZE, Prepare.EvmCode .MSIZE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.GASPRICE, Prepare.EvmCode .GASPRICE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CODESIZE, Prepare.EvmCode .CODESIZE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.PC, Prepare.EvmCode .PC() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.COINBASE, Prepare.EvmCode .COINBASE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.TIMESTAMP, Prepare.EvmCode .TIMESTAMP() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.NUMBER, Prepare.EvmCode .NUMBER() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.GASLIMIT, Prepare.EvmCode .GASLIMIT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CALLER, Prepare.EvmCode .CALLER() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.ADDRESS, Prepare.EvmCode .ADDRESS() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.ORIGIN, Prepare.EvmCode .ORIGIN() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CALLVALUE, Prepare.EvmCode .CALLVALUE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CHAINID, Prepare.EvmCode .CHAINID() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.GAS, Prepare.EvmCode @@ -512,32 +603,48 @@ public override void Setup() .ADD() .POP() .GAS() + .PushData(1) + .SSTORE() .PushData(23) .PushData(46) .ADD() .POP() + .GAS() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.RETURNDATASIZE, Prepare.EvmCode .RETURNDATASIZE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BASEFEE, Prepare.EvmCode .BASEFEE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.RETURN, Prepare.EvmCode .StoreDataInMemory(0, [2, 3, 5, 7]) .RETURN(0, 32) - .Done, EvmExceptionType.None); + .MLOAD(0) + .PushData(1) + .SSTORE().Done, EvmExceptionType.None); yield return (Instruction.REVERT, Prepare.EvmCode .StoreDataInMemory(0, [2, 3, 5, 7]) .REVERT(0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CALLDATASIZE, Prepare.EvmCode .CALLDATASIZE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.JUMPI | Instruction.JUMPDEST, Prepare.EvmCode @@ -548,6 +655,9 @@ public override void Setup() .JUMPDEST() .PushSingle(0) .MUL() + .GAS() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); @@ -559,14 +669,20 @@ public override void Setup() .JUMPDEST() .PushSingle(0) .MUL() + .GAS() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode .PushSingle(23) - .JUMP(10) + .JUMP(14) .JUMPDEST() .PushSingle(3) .MUL() + .GAS() + .PUSHx([1]) + .SSTORE() .STOP() .JUMPDEST() .JUMP(5) @@ -576,56 +692,87 @@ public override void Setup() .PushSingle(23) .PushSingle(1) .SHL() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SHR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) .SHR() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SAR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) .SAR() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.AND, Prepare.EvmCode .PushSingle(23) .PushSingle(1) .AND() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.OR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) .OR() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.XOR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) .XOR() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SLT, Prepare.EvmCode .PushData(23) .PushSingle(4) .SLT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SGT, Prepare.EvmCode .PushData(23) .PushData(1) .SGT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.BYTE, Prepare.EvmCode + .BYTE(0, ((UInt256)(23)).PaddedBytes(32)) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BYTE, Prepare.EvmCode .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.BYTE, Prepare.EvmCode + .BYTE(16, ((UInt256)(23)).PaddedBytes(32)) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode .JUMP(31) + // this assumes that the code segment is jumping to another segment beyond it's boundaries .Done, EvmExceptionType.None); yield return (Instruction.LOG0, Prepare.EvmCode @@ -683,6 +830,8 @@ public override void Setup() .TSTORE() .PushData(7) .TLOAD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SSTORE | Instruction.SLOAD, Prepare.EvmCode @@ -691,37 +840,53 @@ public override void Setup() .SSTORE() .PushData(7) .SLOAD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.EXTCODESIZE, Prepare.EvmCode - .EXTCODESIZE(Address.FromNumber(1)) + .EXTCODESIZE(Address.FromNumber(23)) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.EXTCODEHASH, Prepare.EvmCode - .EXTCODEHASH(Address.FromNumber(1)) + .EXTCODEHASH(Address.FromNumber(23)) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.EXTCODECOPY, Prepare.EvmCode - .PushData(0) - .PushData(0) - .PushData(0) - .EXTCODECOPY(Address.FromNumber(1)) + .EXTCODECOPY(Address.FromNumber(23), 0, 0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BALANCE, Prepare.EvmCode - .BALANCE(Address.FromNumber(1)) + .BALANCE(Address.FromNumber(23)) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SELFBALANCE, Prepare.EvmCode .SELFBALANCE() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.INVALID, Prepare.EvmCode .INVALID() + .PushData(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.BadInstruction); yield return (Instruction.STOP, Prepare.EvmCode .STOP() + .PushData(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.POP, Prepare.EvmCode @@ -729,6 +894,11 @@ public override void Setup() .POP() .Done, EvmExceptionType.None); + yield return (Instruction.POP | Instruction.INVALID, Prepare.EvmCode + .POP() + .Done, EvmExceptionType.StackUnderflow); + + for (byte opcode = (byte)Instruction.DUP1; opcode <= (byte)Instruction.DUP16; opcode++) { int n = opcode - (byte)Instruction.DUP1 + 1; @@ -737,7 +907,9 @@ public override void Setup() { test.PushData(i); } - test.Op((Instruction)opcode); + test.Op((Instruction)opcode) + .PushData(1) + .SSTORE(); yield return ((Instruction)opcode, test.Done, EvmExceptionType.None); } @@ -747,7 +919,10 @@ public override void Setup() int n = opcode - (byte)Instruction.PUSH0; byte[] args = n == 0 ? null : Enumerable.Range(0, n).Select(i => (byte)i).ToArray(); - yield return ((Instruction)opcode, Prepare.EvmCode.PUSHx(args).Done, EvmExceptionType.None); + yield return ((Instruction)opcode, Prepare.EvmCode.PUSHx(args) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); } for (byte opcode = (byte)Instruction.SWAP1; opcode <= (byte)Instruction.SWAP16; opcode++) @@ -758,7 +933,9 @@ public override void Setup() { test.PushData(i); } - test.Op((Instruction)opcode); + test.Op((Instruction)opcode) + .PushData(1) + .SSTORE(); yield return ((Instruction)opcode, test.Done, EvmExceptionType.None); } @@ -767,12 +944,16 @@ public override void Setup() .PushData(23) .PushData(7) .SDIV() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SMOD, Prepare.EvmCode .PushData(23) .PushData(7) .SMOD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.CODECOPY, Prepare.EvmCode @@ -780,6 +961,9 @@ public override void Setup() .PushData(32) .PushData(7) .CODECOPY() + .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.MULMOD, Prepare.EvmCode @@ -787,6 +971,8 @@ public override void Setup() .PushData(3) .PushData(7) .MULMOD() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.KECCAK256, Prepare.EvmCode @@ -794,10 +980,14 @@ public override void Setup() .PushData(0) .PushData(16) .KECCAK256() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.PREVRANDAO, Prepare.EvmCode .PREVRANDAO() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.RETURNDATACOPY, Prepare.EvmCode @@ -805,16 +995,23 @@ public override void Setup() .PushData(32) .PushData(0) .RETURNDATACOPY() + .MLOAD(0) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BLOBBASEFEE, Prepare.EvmCode .Op(Instruction.BLOBBASEFEE) + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.SIGNEXTEND, Prepare.EvmCode .PushData(1024) .PushData(16) .SIGNEXTEND() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.INVALID, Prepare.EvmCode @@ -932,11 +1129,9 @@ public void JIT_Analyzer_Compiles_stateless_bytecode_chunk() [Test] public void Execution_Swap_Happens_When_Pattern_Occurs() { - int repeatCount = 128; - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, + PatternMatchingThreshold = 1, IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, IsJitEnabled = false, @@ -978,7 +1173,7 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() TestState.InsertCode(address, bytecode, spec); */ var accumulatedTraces = new List(); - for (int i = 0; i < repeatCount * 2; i++) + for (int i = 0; i < RepeatCount ; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1002,26 +1197,30 @@ public void All_Opcodes_Have_Metadata() [Test, TestCaseSource(nameof(GeJitBytecodesSamples))] public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] bytecode, EvmExceptionType _) testcase) { - int repeatCount = 32; - TestBlockChain standardChain = new TestBlockChain(new VMConfig()); var address = standardChain.InsertCode(testcase.bytecode); TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = int.MaxValue, IsPatternMatchingEnabled = false, - JittingThreshold = repeatCount + 1, + BakeInTracingInJitMode = true, + JittingThreshold = 1, AnalysisQueueMaxSize = 1, IsJitEnabled = true }); enhancedChain.InsertCode(testcase.bytecode); - var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + GethTraceOptions tracerOptions = new GethTraceOptions + { + EnableMemory = true, + }; + + var tracer1 = new GethLikeTxMemoryTracer(tracerOptions); + var tracer2 = new GethLikeTxMemoryTracer(tracerOptions); var bytecode = Prepare.EvmCode .JUMPDEST() - .Call(address, 100) + .Call(address, 25000) .POP() .PushData(1000) .GAS() @@ -1030,12 +1229,12 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by .STOP() .Done; - for (var i = 0; i < repeatCount * 2; i++) + for (var i = 0; i < RepeatCount ; i++) { standardChain.Execute(bytecode, tracer1); } - for (var i = 0; i < repeatCount * 2; i++) + for (var i = 0; i < RepeatCount ; i++) { enhancedChain.Execute(bytecode, tracer2); } @@ -1058,13 +1257,11 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by [Test, TestCaseSource(nameof(GePatBytecodesSamples))] public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) testcase) { - int repeatCount = 10; - TestBlockChain standardChain = new TestBlockChain(new VMConfig()); var address = standardChain.InsertCode(testcase.bytecode); TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, + PatternMatchingThreshold = 1, IsPatternMatchingEnabled = true, JittingThreshold = int.MaxValue, AnalysisQueueMaxSize = 1, @@ -1087,12 +1284,12 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) .STOP() .Done; - for (var i = 0; i < repeatCount * 2; i++) + for (var i = 0; i < RepeatCount; i++) { standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); } - for (var i = 0; i < repeatCount * 2; i++) + for (var i = 0; i < RepeatCount ; i++) { enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); } @@ -1115,13 +1312,11 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() { - int repeatCount = 32; - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, + PatternMatchingThreshold = 1, IsPatternMatchingEnabled = false, - JittingThreshold = repeatCount + 1, + JittingThreshold = 1, AnalysisQueueMaxSize = 1, IsJitEnabled = true, AggressiveJitMode = true, @@ -1156,7 +1351,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= repeatCount * 32; i++) + for (int i = 0; i <= RepeatCount; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1190,14 +1385,12 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() { - int repeatCount = 32; - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount * 2 + 1, + PatternMatchingThreshold = 2, AnalysisQueueMaxSize = 1, IsPatternMatchingEnabled = true, - JittingThreshold = repeatCount + 1, + JittingThreshold = 1, IsJitEnabled = true, AggressiveJitMode = false }); @@ -1231,7 +1424,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= repeatCount * 2; i++) + for (int i = 0; i <= RepeatCount; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1265,13 +1458,11 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() [Test] public void JIT_invalid_opcode_results_in_failure() { - int repeatCount = 32; - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { - PatternMatchingThreshold = repeatCount + 1, + PatternMatchingThreshold = 1, IsPatternMatchingEnabled = true, - JittingThreshold = int.MaxValue, + JittingThreshold = 1, IsJitEnabled = true, AnalysisQueueMaxSize = 1, AggressiveJitMode = false @@ -1284,30 +1475,30 @@ public void JIT_invalid_opcode_results_in_failure() .STOP() .Done; - var accumulatedTraces = new List(); - var numberOfRuns = config.JittingThreshold * 1024; - for (int i = 0; i < numberOfRuns; i++) + var accumulatedTraces = new List(); + for (int i = 0; i < RepeatCount; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); var traces = tracer.BuildResult(); - accumulatedTraces.AddRange(traces.Failed); + accumulatedTraces.AddRange(traces); } - Assert.That(accumulatedTraces.All(status => status), Is.True); + var HasIlvmTraces = accumulatedTraces.SelectMany(tr => tr.Entries).Where(tr => tr.SegmentID is not null).Any(); + var allFailed = accumulatedTraces.All(tr => tr.Failed); + Assert.That(HasIlvmTraces, Is.True); + Assert.That(allFailed, Is.True); } [Test] public void Execution_Swap_Happens_When_Segments_are_compiled() { - int repeatCount = 32; - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = int.MaxValue, IsPatternMatchingEnabled = false, AnalysisQueueMaxSize = 1, - JittingThreshold = repeatCount + 1, + JittingThreshold = 1, IsJitEnabled = true, AggressiveJitMode = false }); @@ -1335,7 +1526,7 @@ public void Execution_Swap_Happens_When_Segments_are_compiled() .Done; var accumulatedTraces = new List(); - for (int i = 0; i <= repeatCount * 32; i++) + for (int i = 0; i <= RepeatCount; i++) { var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(bytecode, tracer); @@ -1405,7 +1596,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); var state = new EvmState( - 1_000_000, + 100_000_000, new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, isTopLevel: false, diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 224da03893a..126096009b8 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -530,7 +530,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.MarkLabel(powerIsZero); method.CleanAndLoadWord(stack, head); method.LoadConstant(1); - method.StoreField(Word.Byte0Field); + method.Call(Word.SetUInt0); method.Branch(endOfExpImpl); method.MarkLabel(baseIsOneOrZero); @@ -585,7 +585,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackLoadPrevious(stack, head, 1); method.Duplicate(); method.Call(Word.GetIsZero); - method.StoreField(Word.Byte0Field); + method.Call(Word.SetByte0); } break; case Instruction.POP: @@ -903,7 +903,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(Word.GetUInt256); method.StoreLocal(uint256A); method.StackLoadPrevious(stack, head, 2); - method.LoadField(Word.Byte0Field); + method.Call(Word.GetByte0); method.StoreLocal(byte8A); method.StackPop(head, 2); @@ -1060,6 +1060,9 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.BYTE: {// load a method.StackLoadPrevious(stack, head, 1); + method.Duplicate(); + method.Call(Word.GetUInt0); + method.StoreLocal(uint32A); method.Call(Word.GetUInt256); method.StoreLocal(uint256A); method.StackLoadPrevious(stack, head, 2); @@ -1067,6 +1070,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreLocal(localReadonOnlySpan); method.StackPop(head, 2); + Label pushZeroLabel = method.DefineLabel(); Label endOfInstructionImpl = method.DefineLabel(); method.LoadLocalAddress(uint256A); @@ -1078,14 +1082,19 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Or(); method.BranchIfTrue(pushZeroLabel); + using Local tempValue = method.DeclareLocal(); + method.CleanAndLoadWord(stack, head); method.LoadLocalAddress(localReadonOnlySpan); - method.LoadLocal(uint256A); - method.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); - method.Convert(); + method.LoadLocal(uint32A); method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); method.LoadIndirect(); - method.StoreField(Word.Byte0Field); + method.Duplicate(); + method.StoreLocal(tempValue); + + method.Print(tempValue); + + method.Call(Word.SetByte0); method.StackPush(head); method.Branch(endOfInstructionImpl); @@ -1288,6 +1297,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldReturn))); break; } + method.FakeBranch(ret); } break; case Instruction.BASEFEE: @@ -1689,9 +1699,19 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ { Label endOfOpcode = method.DefineLabel(); + method.StackLoadPrevious(stack, head, 4); + method.Call(Word.GetUInt256); + method.StoreLocal(uint256C); + method.LoadLocal(gasAvailable); method.LoadArgument(SPEC_INDEX); method.Call(typeof(ReleaseSpecExtensions).GetMethod(nameof(ReleaseSpecExtensions.GetExtCodeCost))); + method.LoadLocalAddress(uint256C); + method.LoadLocalAddress(lbool); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling), [typeof(UInt256).MakeByRefType(), typeof(bool).MakeByRefType()])); + method.LoadConstant(GasCostOf.Memory); + method.Multiply(); + method.Add(); method.Subtract(); method.Duplicate(); method.StoreLocal(gasAvailable); @@ -1707,23 +1727,8 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackLoadPrevious(stack, head, 3); method.Call(Word.GetUInt256); method.StoreLocal(uint256B); - method.StackLoadPrevious(stack, head, 4); - method.Call(Word.GetUInt256); - method.StoreLocal(uint256C); method.StackPop(head, 4); - method.LoadLocal(gasAvailable); - method.LoadLocalAddress(uint256C); - method.LoadLocalAddress(lbool); - method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling), [typeof(UInt256).MakeByRefType(), typeof(bool).MakeByRefType()])); - method.LoadConstant(GasCostOf.Memory); - method.Multiply(); - method.Subtract(); - method.Duplicate(); - method.StoreLocal(gasAvailable); - method.LoadConstant((long)0); - method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); - method.LoadLocalAddress(gasAvailable); method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); @@ -1751,6 +1756,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadArgument(CODE_INFO_REPOSITORY_INDEX); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocal(address); + method.Print(address); method.LoadArgument(SPEC_INDEX); method.Call(typeof(CodeInfoRepositoryExtensions).GetMethod(nameof(CodeInfoRepositoryExtensions.GetCachedCodeInfo), [typeof(ICodeInfoRepository), typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); @@ -2546,6 +2552,9 @@ private static Dictionary BuildCostLookup(ReadOnlySpan co costStart = op.ProgramCounter; coststack = op.Metadata.GasCost; break; + case Instruction.RETURN: + case Instruction.REVERT: + case Instruction.STOP: case Instruction.GAS: case Instruction.JUMPI: case Instruction.JUMP: diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 2f6941ef7bc..55e822fa3e5 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -124,7 +124,7 @@ public unsafe Address Address byte[] buffer = new byte[20]; for (int i = 0; i < 20; i++) { - buffer[i] = _buffer[i]; + buffer[i] = _buffer[12 + i]; } return new Address(buffer); @@ -132,7 +132,7 @@ public unsafe Address Address set { byte[] buffer = value.Bytes; - for (int i = 0; i < 20; i++) + for (int i = 12; i < 32; i++) { _buffer[i] = buffer[i]; } @@ -203,6 +203,32 @@ public uint UInt0 } } + public byte Byte0 + { + get + { + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_uByte0); + } + else + { + return _uByte0; + } + } + set + { + if (BitConverter.IsLittleEndian) + { + _uByte0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _uByte0 = value; + } + } + } + public int Int0 { get @@ -275,7 +301,9 @@ public unsafe long LeadingZeros } public static readonly MethodInfo LeadingZeroProp = typeof(Word).GetProperty(nameof(LeadingZeros))!.GetMethod; - public static readonly FieldInfo Byte0Field = typeof(Word).GetField(nameof(_uByte0)); + + public static readonly MethodInfo GetByte0 = typeof(Word).GetProperty(nameof(Byte0))!.GetMethod; + public static readonly MethodInfo SetByte0 = typeof(Word).GetProperty(nameof(Byte0))!.SetMethod; public static readonly MethodInfo GetInt0 = typeof(Word).GetProperty(nameof(Int0))!.GetMethod; public static readonly MethodInfo SetInt0 = typeof(Word).GetProperty(nameof(Int0))!.SetMethod; diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 90474c60d56..d9c0ccf5907 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -320,7 +320,6 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.RETURNDATASIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.RETURNDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), [Instruction.EXTCODEHASH] = new(0, 0, 1, 1), - [Instruction.EXTCODECOPY] = new(GasCostOf.ExtCode, 0, 4, 0), [Instruction.BLOCKHASH] = new(GasCostOf.BlockHash, 0, 1, 1), [Instruction.COINBASE] = new(GasCostOf.Base, 0, 0, 1), @@ -339,7 +338,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.MSTORE] = new(GasCostOf.VeryLow, 0, 2, 0), [Instruction.MSTORE8] = new(GasCostOf.VeryLow, 0, 2, 0), [Instruction.SLOAD] = new(GasCostOf.SLoad, 0, 1, 1), - [Instruction.SSTORE] = new(GasCostOf.SSet, 0, 2, 0), + [Instruction.SSTORE] = new(0, 0, 2, 0), [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), From 5899f7836fc7d718f2eef3a653f6983f3f15f9a0 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 02:01:22 +0100 Subject: [PATCH 126/146] fix MSTORE8 --- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 126096009b8..65356fbf357 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -525,6 +525,11 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(uint256B); method.LoadLocalAddress(uint256R); method.Call(typeof(UInt256).GetMethod(nameof(UInt256.Exp), BindingFlags.Public | BindingFlags.Static)!); + + method.CleanAndLoadWord(stack, head); + method.LoadLocal(uint256R); + method.Call(Word.SetUInt256); + method.Branch(endOfExpImpl); method.MarkLabel(powerIsZero); @@ -921,11 +926,7 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadArgument(VMSTATE_INDEX); method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); method.LoadLocalAddress(uint256A); - method.LoadLocalAddress(uint256B); - method.LoadConstant(Word.Size); - method.Call(typeof(UInt256).GetMethod(nameof(UInt256.PaddedBytes))); - method.LoadConstant(0); - method.LoadElement(); + method.LoadLocal(byte8A); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.SaveByte))); } From ecb3170151721b3ab27e82c91574bd04d89a72f3 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 02:17:16 +0100 Subject: [PATCH 127/146] fix SWAPN --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 65356fbf357..2d9538864e4 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -648,11 +648,11 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StoreObject(typeof(Word)); method.StackLoadPrevious(stack, head, 1); - method.StackLoadPrevious(stack, head, count); + method.StackLoadPrevious(stack, head, count + 1); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); - method.StackLoadPrevious(stack, head, count); + method.StackLoadPrevious(stack, head, count + 1); method.LoadLocalAddress(uint256R); method.LoadObject(typeof(Word)); method.StoreObject(typeof(Word)); From 69cc361d96384178c63b73d47bd1b808e5b31cbf Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 03:15:43 +0100 Subject: [PATCH 128/146] fix LOG opcodes --- .../CodeAnalysis/IL/ILCompiler.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2d9538864e4..601bf316235 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -1511,6 +1511,12 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.LOG4: { sbyte topicsCount = (sbyte)(op.Operation - Instruction.LOG0); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.Call(GetPropertyInfo(typeof(EvmState), nameof(EvmState.IsStatic), false, out _)); + method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.StaticCallViolation]); + EmitLogMethod(method, (stack, head), topicsCount, evmExceptionLabels, uint256A, uint256B, int64A, gasAvailable, hash256, localReadOnlyMemory); } break; @@ -2416,14 +2422,14 @@ private static void EmitLogMethod( (Local span, Local idx) stack, sbyte topicsCount, Dictionary exceptions, - Local uint256A, Local uint256B, Local int64A, Local gasAvailable, Local hash256, Local localReadOnlyMemory + Local uint256Position, Local uint256Length, Local int64A, Local gasAvailable, Local hash256, Local localReadOnlyMemory ) { using Local logEntry = il.DeclareLocal(); Action loadExecutingAccount = () => { // Executing account - il.LoadArgument(0); + il.LoadArgument(VMSTATE_INDEX); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); il.LoadField( GetFieldInfo( @@ -2436,10 +2442,10 @@ private static void EmitLogMethod( Action loadMemoryIntoByteArray = () => { // memory load - il.LoadArgument(0); + il.LoadArgument(VMSTATE_INDEX); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - il.LoadLocalAddress(uint256A); // position - il.LoadLocalAddress(uint256B); // length + il.LoadLocalAddress(uint256Position); // position + il.LoadLocalAddress(uint256Length); // length il.Call( typeof(EvmPooledMemory).GetMethod( nameof(EvmPooledMemory.Load), @@ -2466,17 +2472,17 @@ private static void EmitLogMethod( il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); - il.StoreLocal(uint256A); // position + il.StoreLocal(uint256Position); // position il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetUInt256); - il.StoreLocal(uint256B); // length + il.StoreLocal(uint256Length); // length il.StackPop(stack.idx, 2); // UpdateMemoryCost - il.LoadArgument(0); + il.LoadArgument(VMSTATE_INDEX); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); il.LoadLocalAddress(gasAvailable); - il.LoadLocalAddress(uint256A); // position - il.LoadLocalAddress(uint256B); // length + il.LoadLocalAddress(uint256Position); // position + il.LoadLocalAddress(uint256Length); // length il.Call( typeof(VirtualMachine).GetMethod( nameof(VirtualMachine.UpdateMemoryCost) @@ -2486,10 +2492,10 @@ private static void EmitLogMethod( // update gasAvailable il.LoadLocal(gasAvailable); - il.LoadConstant(topicsCount * GasCostOf.LogTopic + GasCostOf.Log); + il.LoadConstant(topicsCount * GasCostOf.LogTopic); il.Convert(); - il.LoadLocal(uint256B); // length - il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.u0))); + il.LoadLocalAddress(uint256Length); // length + il.Call(typeof(UInt256Extensions).GetMethod(nameof(UInt256Extensions.ToLong), BindingFlags.Static | BindingFlags.Public, [typeof(UInt256).MakeByRefType()])); il.Convert(); il.LoadConstant(GasCostOf.LogData); il.Multiply(); From 355036936856509325f9d5944d58dba54a75e3b3 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 03:32:45 +0100 Subject: [PATCH 129/146] fix Address property in Word --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs index 55e822fa3e5..f8f4d8d7111 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -134,7 +134,7 @@ public unsafe Address Address byte[] buffer = value.Bytes; for (int i = 12; i < 32; i++) { - _buffer[i] = buffer[i]; + _buffer[i] = buffer[i - 12]; } } } From b2371489c5960db5b5c9f7ff3bf1f9aab48a7c1c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 10:12:47 +0100 Subject: [PATCH 130/146] fix merge issue for LOG opcode --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 89b702a0e00..021a25786c1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2525,7 +2525,7 @@ private static void EmitLogMethod( il.LoadArgument(0); il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); - il.CallVirtual(GetPropertyInfo(typeof(EvmState), nameof(EvmState.AccessTracker), getSetter: false, out _)); + il.LoadFieldAddress(typeof(EvmState).GetField("_accessTracker", BindingFlags.Instance | BindingFlags.NonPublic)); il.CallVirtual(GetPropertyInfo(typeof(StackAccessTracker), nameof(StackAccessTracker.Logs), getSetter: false, out _)); il.LoadLocal(logEntry); il.CallVirtual( From 3930961543e3a437fcc4b94ca03f12379fc5ce4d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 16:25:43 +0100 Subject: [PATCH 131/146] fixes for CODESIZE and BYTE and PREVRANDAO opcodes, rework for tests, minor cleanup --- .../CodeAnalysis/IlEvmTests.cs | 621 +++++++++--------- .../CodeAnalysis/IL/ILCompiler.cs | 32 +- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- 3 files changed, 326 insertions(+), 329 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 041f08eca75..c1a5c77a804 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -165,6 +165,13 @@ public Address InsertCode(byte[] bytecode) return address; } + public void ForceRunAnalysis(Address address) + { + var codeinfo = CodeInfoRepository.GetCachedCodeInfo(TestState, address, Prague.Instance); + codeinfo.NoticeExecution(config, NullLogger.Instance); + while (codeinfo.IlInfo.IsEmpty) ; // wait for analysis to finish + } + public Hash256 StateRoot { get @@ -501,277 +508,278 @@ public override void Setup() .Done, EvmExceptionType.None); yield return (Instruction.BLOCKHASH, Prepare.EvmCode - .BLOCKHASH(0) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .BLOCKHASH(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CALLDATACOPY, Prepare.EvmCode - .CALLDATACOPY(0, 0, 32) - .MLOAD(0) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CALLDATACOPY(0, 0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CALLDATALOAD, Prepare.EvmCode - .CALLDATALOAD(0) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CALLDATALOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.MSIZE, Prepare.EvmCode - .MSIZE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .MSIZE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.GASPRICE, Prepare.EvmCode - .GASPRICE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .GASPRICE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CODESIZE, Prepare.EvmCode - .CODESIZE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CODESIZE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.PC, Prepare.EvmCode - .PC() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PC() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.COINBASE, Prepare.EvmCode - .COINBASE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .COINBASE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.TIMESTAMP, Prepare.EvmCode - .TIMESTAMP() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .TIMESTAMP() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.NUMBER, Prepare.EvmCode - .NUMBER() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .NUMBER() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.GASLIMIT, Prepare.EvmCode - .GASLIMIT() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .GASLIMIT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CALLER, Prepare.EvmCode - .CALLER() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CALLER() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.ADDRESS, Prepare.EvmCode - .ADDRESS() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .ADDRESS() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.ORIGIN, Prepare.EvmCode - .ORIGIN() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .ORIGIN() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CALLVALUE, Prepare.EvmCode - .CALLVALUE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CALLVALUE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CHAINID, Prepare.EvmCode - .CHAINID() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CHAINID() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.GAS, Prepare.EvmCode - .PushData(23) - .PushData(46) - .ADD() - .POP() - .GAS() - .PushData(1) - .SSTORE() - .PushData(23) - .PushData(46) - .ADD() - .POP() - .GAS() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushData(23) + .PushData(46) + .ADD() + .POP() + .GAS() + .PushData(1) + .SSTORE() + .PushData(23) + .PushData(46) + .ADD() + .POP() + .GAS() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.RETURNDATASIZE, Prepare.EvmCode - .RETURNDATASIZE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .RETURNDATASIZE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.BASEFEE, Prepare.EvmCode - .BASEFEE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .BASEFEE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.RETURN, Prepare.EvmCode - .StoreDataInMemory(0, [2, 3, 5, 7]) - .RETURN(0, 32) - .MLOAD(0) - .PushData(1) - .SSTORE().Done, EvmExceptionType.None); + .StoreDataInMemory(0, [2, 3, 5, 7]) + .RETURN(0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE().Done, EvmExceptionType.None); yield return (Instruction.REVERT, Prepare.EvmCode - .StoreDataInMemory(0, [2, 3, 5, 7]) - .REVERT(0, 32) - .MLOAD(0) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .StoreDataInMemory(0, [2, 3, 5, 7]) + .REVERT(0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.CALLDATASIZE, Prepare.EvmCode - .CALLDATASIZE() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .CALLDATASIZE() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.JUMPI | Instruction.JUMPDEST, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .JUMPI(9) - .PushSingle(3) - .JUMPDEST() - .PushSingle(0) - .MUL() - .GAS() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .JUMPI(9) + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .GAS() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.JUMPI | Instruction.JUMPDEST, Prepare.EvmCode - .PushSingle(23) - .PushSingle(0) - .JUMPI(9) - .PushSingle(3) - .JUMPDEST() - .PushSingle(0) - .MUL() - .GAS() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(0) + .JUMPI(9) + .PushSingle(3) + .JUMPDEST() + .PushSingle(0) + .MUL() + .GAS() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode - .PushSingle(23) - .JUMP(14) - .JUMPDEST() - .PushSingle(3) - .MUL() - .GAS() - .PUSHx([1]) - .SSTORE() - .STOP() - .JUMPDEST() - .JUMP(5) - .Done, EvmExceptionType.None); + .PushSingle(23) + .JUMP(14) + .JUMPDEST() + .PushSingle(3) + .MUL() + .GAS() + .PUSHx([1]) + .SSTORE() + .STOP() + .JUMPDEST() + .JUMP(5) + .Done, EvmExceptionType.None); yield return (Instruction.SHL, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SHL() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .SHL() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.SHR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SHR() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .SHR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.SAR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .SAR() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .SAR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.AND, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .AND() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .AND() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.OR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .OR() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .OR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.XOR, Prepare.EvmCode - .PushSingle(23) - .PushSingle(1) - .XOR() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushSingle(23) + .PushSingle(1) + .XOR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.SLT, Prepare.EvmCode - .PushData(23) - .PushSingle(4) - .SLT() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushData(23) + .PushSingle(4) + .SLT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.SGT, Prepare.EvmCode - .PushData(23) - .PushData(1) - .SGT() - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .PushData(23) + .PushData(1) + .SGT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.BYTE, Prepare.EvmCode - .BYTE(0, ((UInt256)(23)).PaddedBytes(32)) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .BYTE(0, ((UInt256)(23)).PaddedBytes(32)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.BYTE, Prepare.EvmCode - .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .BYTE(16, UInt256.MaxValue.PaddedBytes(32)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.BYTE, Prepare.EvmCode - .BYTE(16, ((UInt256)(23)).PaddedBytes(32)) - .PushData(1) - .SSTORE() - .Done, EvmExceptionType.None); + .BYTE(16, ((UInt256)(23)).PaddedBytes(32)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); yield return (Instruction.JUMP | Instruction.JUMPDEST, Prepare.EvmCode .JUMP(31) + .INVALID() // this assumes that the code segment is jumping to another segment beyond it's boundaries .Done, EvmExceptionType.None); @@ -895,6 +903,9 @@ public override void Setup() .Done, EvmExceptionType.None); yield return (Instruction.POP | Instruction.INVALID, Prepare.EvmCode + .POP() + .POP() + .POP() .POP() .Done, EvmExceptionType.StackUnderflow); @@ -1184,6 +1195,53 @@ public void Execution_Swap_Happens_When_Pattern_Occurs() Assert.That(accumulatedTraces.Count, Is.GreaterThan(0)); } + [Test] + public void Execution_Swap_Happens_When_Segments_are_compiled() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + AnalysisQueueMaxSize = 1, + JittingThreshold = 1, + IsJitEnabled = true, + AggressiveJitMode = false + }); + + byte[] bytecode = + Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .PUSHx([0, 26]) + .JUMPI() + .PushSingle(23) + .PushSingle(7) + .ADD() + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .PUSHx([0, 0]) + .JUMP() + .JUMPDEST() + .STOP() + .Done; + + var accumulatedTraces = new List(); + for (int i = 0; i < RepeatCount; i++) + { + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(bytecode, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); + accumulatedTraces.AddRange(traces); + } + + Assert.That(accumulatedTraces.Count, Is.GreaterThan(0)); + } + [Test] public void All_Opcodes_Have_Metadata() { @@ -1229,15 +1287,11 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by .STOP() .Done; - for (var i = 0; i < RepeatCount ; i++) - { - standardChain.Execute(bytecode, tracer1); - } + standardChain.Execute(bytecode, tracer1); - for (var i = 0; i < RepeatCount ; i++) - { - enhancedChain.Execute(bytecode, tracer2); - } + enhancedChain.ForceRunAnalysis(address); + + enhancedChain.Execute(bytecode, tracer2); var normal_traces = tracer1.BuildResult(); var ilvm_traces = tracer2.BuildResult(); @@ -1245,11 +1299,13 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var actual = standardChain.StateRoot; var expected = enhancedChain.StateRoot; - var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var enhancedHasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var normalHasIlvmTraces = normal_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); if (testcase.opcode is not null) { - Assert.That(HasIlvmTraces, Is.True); + Assert.That(enhancedHasIlvmTraces, Is.True); + Assert.That(normalHasIlvmTraces, Is.False); } Assert.That(actual, Is.EqualTo(expected)); } @@ -1284,15 +1340,11 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) .STOP() .Done; - for (var i = 0; i < RepeatCount; i++) - { - standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); - } + standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); - for (var i = 0; i < RepeatCount ; i++) - { - enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); - } + enhancedChain.ForceRunAnalysis(address); + + enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); var normal_traces = tracer1.BuildResult(); var ilvm_traces = tracer2.BuildResult(); @@ -1300,11 +1352,13 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) var actual = standardChain.StateRoot; var expected = enhancedChain.StateRoot; - var HasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var enhancedHasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var normalHasIlvmTraces = normal_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); if (testcase.opcode is not null) { - Assert.That(HasIlvmTraces, Is.True); + Assert.That(enhancedHasIlvmTraces, Is.True); + Assert.That(normalHasIlvmTraces, Is.False); } Assert.That(actual, Is.EqualTo(expected)); } @@ -1322,13 +1376,13 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() AggressiveJitMode = true, }); - var address = enhancedChain.InsertCode(Prepare.EvmCode + var aux = enhancedChain.InsertCode(Prepare.EvmCode .PushData(23) .PushData(7) .ADD() .STOP().Done); - byte[] bytecode = + Address main = enhancedChain.InsertCode( Prepare.EvmCode .JUMPDEST() .PushSingle(1000) @@ -1339,7 +1393,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .PushSingle(7) .ADD() .POP() - .Call(address, 100) + .Call(aux, 100) .POP() .PushSingle(42) .PushSingle(5) @@ -1348,25 +1402,17 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .JUMP(0) .JUMPDEST() .STOP() - .Done; - - var accumulatedTraces = new List(); - for (int i = 0; i <= RepeatCount; i++) - { - var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - enhancedChain.Execute(bytecode, tracer); - var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); - accumulatedTraces.AddRange(traces); + .Done); - } + byte[] driver = + Prepare.EvmCode + .Call(main, 1_000_000) + .Done; - // in the last stint gas is almost below 1000 - // it executes segment 0 (0..46) - // then calls address 23 (segment 0..5 since it is precompiled as well) - // then it executes segment 48..59 which ends in jump back to pc = 0 - // then it executes segment 0..46 again but this time gas is below 1000 - // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) - // so the last segment executed is AbortDestinationPattern + enhancedChain.ForceRunAnalysis(main); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(driver, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); string[] desiredTracePattern = new[] { @@ -1377,7 +1423,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", }; - string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + string[] actualTracePattern = traces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); } @@ -1395,13 +1441,13 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() AggressiveJitMode = false }); - var address = enhancedChain.InsertCode(Prepare.EvmCode + var aux = enhancedChain.InsertCode(Prepare.EvmCode .PushData(23) .PushData(7) .ADD() .STOP().Done); - byte[] bytecode = + Address main = enhancedChain.InsertCode( Prepare.EvmCode .JUMPDEST() .PushSingle(1000) @@ -1412,7 +1458,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .PushSingle(7) .ADD() .POP() - .Call(address, 100) + .Call(aux, 100) .POP() .PushSingle(42) .PushSingle(5) @@ -1421,17 +1467,17 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .JUMP(0) .JUMPDEST() .STOP() - .Done; + .Done); - var accumulatedTraces = new List(); - for (int i = 0; i <= RepeatCount; i++) - { - var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - enhancedChain.Execute(bytecode, tracer); - var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); - accumulatedTraces.AddRange(traces); + byte[] driver = + Prepare.EvmCode + .Call(main, 1_000_000) + .Done; - } + enhancedChain.ForceRunAnalysis(main); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(driver, tracer); + var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); // in the last stint gas is almost below 1000 // it executes segment 0 (0..46) @@ -1450,7 +1496,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() "AbortDestinationPattern", }; - string[] actualTracePattern = accumulatedTraces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + string[] actualTracePattern = traces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); } @@ -1461,82 +1507,41 @@ public void JIT_invalid_opcode_results_in_failure() TestBlockChain enhancedChain = new TestBlockChain(new VMConfig { PatternMatchingThreshold = 1, - IsPatternMatchingEnabled = true, + IsPatternMatchingEnabled = false, JittingThreshold = 1, IsJitEnabled = true, AnalysisQueueMaxSize = 1, AggressiveJitMode = false }); - byte[] bytecode = + Address main = enhancedChain.InsertCode( Prepare.EvmCode .PUSHx() // PUSH0 .POP() .STOP() - .Done; - - var accumulatedTraces = new List(); - for (int i = 0; i < RepeatCount; i++) - { - var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - enhancedChain.Execute(bytecode, tracer); - var traces = tracer.BuildResult(); - accumulatedTraces.AddRange(traces); - } - - var HasIlvmTraces = accumulatedTraces.SelectMany(tr => tr.Entries).Where(tr => tr.SegmentID is not null).Any(); - var allFailed = accumulatedTraces.All(tr => tr.Failed); - Assert.That(HasIlvmTraces, Is.True); - Assert.That(allFailed, Is.True); - } - - [Test] - public void Execution_Swap_Happens_When_Segments_are_compiled() - { - TestBlockChain enhancedChain = new TestBlockChain(new VMConfig - { - PatternMatchingThreshold = int.MaxValue, - IsPatternMatchingEnabled = false, - AnalysisQueueMaxSize = 1, - JittingThreshold = 1, - IsJitEnabled = true, - AggressiveJitMode = false - }); + .Done); - byte[] bytecode = + byte[] driver = Prepare.EvmCode - .JUMPDEST() - .PushSingle(1000) - .GAS() - .LT() - .PUSHx([0, 26]) - .JUMPI() - .PushSingle(23) - .PushSingle(7) - .ADD() - .POP() - .PushSingle(42) - .PushSingle(5) - .ADD() - .POP() - .PUSHx([0, 0]) - .JUMP() + .Call(main, 1000) + .JUMPI(17) + .INVALID() .JUMPDEST() .STOP() .Done; - var accumulatedTraces = new List(); - for (int i = 0; i <= RepeatCount; i++) - { - var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - enhancedChain.Execute(bytecode, tracer); - var traces = tracer.BuildResult().Entries.Where(tr => tr.IsPrecompiled is not null && tr.IsPrecompiled.Value).ToList(); - accumulatedTraces.AddRange(traces); - } + enhancedChain.ForceRunAnalysis(main); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(driver, tracer); + var traces = tracer.BuildResult(); - Assert.That(accumulatedTraces.Count, Is.GreaterThan(0)); + var HasIlvmTraces = traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var hasFailed = traces.Failed; + Assert.That(HasIlvmTraces, Is.True); + Assert.That(hasFailed, Is.True); } + [Test] public void Pure_Opcode_Emition_Coveraga() { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 021a25786c1..7fee9f2417e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -3,6 +3,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.Config; using Nethermind.Evm.IL; @@ -660,8 +661,9 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ break; case Instruction.CODESIZE: { + var lastOpcode = code[^1]; method.CleanAndLoadWord(stack, head); - method.LoadConstant(code.Length); + method.LoadConstant(lastOpcode.ProgramCounter + lastOpcode.Metadata.AdditionalBytes + 1); method.Call(Word.SetInt0); method.StackPush(head); } @@ -1083,19 +1085,16 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Or(); method.BranchIfTrue(pushZeroLabel); - using Local tempValue = method.DeclareLocal(); - - method.CleanAndLoadWord(stack, head); method.LoadLocalAddress(localReadonOnlySpan); method.LoadLocal(uint32A); method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); method.LoadIndirect(); - method.Duplicate(); - method.StoreLocal(tempValue); - - method.Print(tempValue); + method.Convert(); + method.StoreLocal(uint32A); - method.Call(Word.SetByte0); + method.CleanAndLoadWord(stack, head); + method.LoadLocal(uint32A); + method.Call(Word.SetUInt0); method.StackPush(head); method.Branch(endOfInstructionImpl); @@ -1337,21 +1336,17 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.Call(GetPropertyInfo(typeof(BlockExecutionContext), nameof(BlockExecutionContext.Header), false, out _)); method.Duplicate(); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.IsPostMerge), false, out _)); - method.BranchIfTrue(isPostMergeBranch); + method.BranchIfFalse(isPostMergeBranch); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); - method.Call(GetPropertyInfo(typeof(Hash256), nameof(Hash256.Bytes), false, out _)); - method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); - method.StoreLocal(localReadonOnlySpan); - method.LoadLocalAddress(localReadonOnlySpan); - method.LoadConstant(BitConverter.IsLittleEndian); - method.NewObject(typeof(UInt256), typeof(ReadOnlySpan).MakeByRefType(), typeof(bool)); + method.LoadField(typeof(Hash256).GetField("_hash256", BindingFlags.Instance | BindingFlags.NonPublic)); + method.Call(Word.SetKeccak); method.Branch(endOfOpcode); method.MarkLabel(isPostMergeBranch); method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Difficulty), false, out _)); + method.Call(Word.SetUInt256); method.MarkLabel(endOfOpcode); - method.Call(Word.SetUInt256); method.StackPush(head); } break; @@ -1453,7 +1448,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.StackLoadPrevious(stack, head, 1); method.Call(Word.GetUInt0); method.StoreLocal(uint32A); - method.Print(uint32A); method.LoadLocal(uint32A); method.LoadConstant(32); @@ -1763,7 +1757,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadArgument(CODE_INFO_REPOSITORY_INDEX); method.LoadArgument(WORLD_STATE_INDEX); method.LoadLocal(address); - method.Print(address); method.LoadArgument(SPEC_INDEX); method.Call(typeof(CodeInfoRepositoryExtensions).GetMethod(nameof(CodeInfoRepositoryExtensions.GetCachedCodeInfo), [typeof(ICodeInfoRepository), typeof(IWorldState), typeof(Address), typeof(IReleaseSpec)])); method.Call(GetPropertyInfo(nameof(CodeInfo.MachineCode), false, out _)); @@ -1990,7 +1983,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ // if (jumpDest > uint.MaxValue) - method.Print(jmpDestination); method.LoadConstant(uint.MaxValue); method.LoadLocal(jmpDestination); // goto invalid address diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 506b09a448c..9d802fb83f2 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -61,7 +61,7 @@ public static class ILMode /// Represents what mode of IL-EVM is used. 0 is the default. [0 = No ILVM optimizations, 1 = Pattern matching, 2 = subsegments compiling] /// public int Mode = ILMode.NO_ILVM; - public bool IsEmpty => Chunks.Count == 0 && Segments.Count == 0; + public bool IsEmpty => Chunks.Count == 0 && Segments.Count == 0 && Mode == ILMode.NO_ILVM; /// /// No overrides. /// From a577b4d2faf0197f9708b120241e848a3e185713 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 31 Oct 2024 17:36:31 +0100 Subject: [PATCH 132/146] Prioritize JIT over PAT when starting analysis, fix failing tests --- .../CodeAnalysis/IlEvmTests.cs | 48 ++++++++++++------- .../Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 9 ++-- .../Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs | 2 +- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index c1a5c77a804..5477b3e4581 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -34,6 +34,7 @@ using Polly; using Nethermind.Core.Collections; using Nethermind.Specs.Test; +using System.Threading; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -168,8 +169,9 @@ public Address InsertCode(byte[] bytecode) public void ForceRunAnalysis(Address address) { var codeinfo = CodeInfoRepository.GetCachedCodeInfo(TestState, address, Prague.Instance); + var initialILMODE = codeinfo.IlInfo.Mode; codeinfo.NoticeExecution(config, NullLogger.Instance); - while (codeinfo.IlInfo.IsEmpty) ; // wait for analysis to finish + while (codeinfo.IlInfo.Mode == initialILMODE) ; // wait for analysis to finish } public Hash256 StateRoot @@ -1410,17 +1412,28 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .Done; enhancedChain.ForceRunAnalysis(main); + enhancedChain.ForceRunAnalysis(aux); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(driver, tracer); var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); + + // in the last stint gas is almost below 1000 + // it executes segment 0 (0..47) + // then calls address 23 (segment 0..5 since it is precompiled as well) + // then it executes segment 49..60 which ends in jump back to pc = 0 + // then it executes segment 0..46 again but this time gas is below 1000 + // it ends jumping to pc = 59 (which occurs in segment 49..60) + // so the last segment executed is (49..60) + string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", - "ILEVM_PRECOMPILED_(0x4df55fd3a4afecd4a0b4550f05b7cca2aa1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", + $"ILEVM_PRECOMPILED_({main})[0..47]", + $"ILEVM_PRECOMPILED_({aux})[0..5]", + $"ILEVM_PRECOMPILED_({main})[49..60]", + $"ILEVM_PRECOMPILED_({main})[0..47]", + $"ILEVM_PRECOMPILED_({main})[49..60]", }; string[] actualTracePattern = traces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); @@ -1438,7 +1451,8 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() IsPatternMatchingEnabled = true, JittingThreshold = 1, IsJitEnabled = true, - AggressiveJitMode = false + AggressiveJitMode = false, + BakeInTracingInJitMode = true }); var aux = enhancedChain.InsertCode(Prepare.EvmCode @@ -1474,26 +1488,28 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .Call(main, 1_000_000) .Done; - enhancedChain.ForceRunAnalysis(main); + enhancedChain.ForceRunAnalysis(main); // once for JIT + enhancedChain.ForceRunAnalysis(main); // once for PAT + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(driver, tracer); var traces = tracer.BuildResult().Entries.Where(tr => tr.SegmentID is not null).ToList(); // in the last stint gas is almost below 1000 - // it executes segment 0 (0..46) + // it executes segment 0 (0..47) // then calls address 23 (segment 0..5 since it is precompiled as well) - // then it executes segment 48..59 which ends in jump back to pc = 0 - // then it executes segment 0..46 again but this time gas is below 1000 + // then it executes segment 49..60 which ends in jump back to pc = 0 + // then it executes segment 0..47 again but this time gas is below 1000 // it ends jumping to pc = 59 (which is index of AbortDestinationPattern) // so the last segment executed is AbortDestinationPattern string[] desiredTracePattern = new[] { - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", - "ILEVM_PRECOMPILED_(0x4df55fd3a4afecd4a0b4550f05b7cca2aa1db9a1)[0..5]", - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[49..60]", - "ILEVM_PRECOMPILED_(0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358)[0..47]", - "AbortDestinationPattern", + $"ILEVM_PRECOMPILED_({main})[0..47]", + $"SomeAfterTwoPush", + $"ILEVM_PRECOMPILED_({main})[49..60]", + $"ILEVM_PRECOMPILED_({main})[0..47]", + $"AbortDestinationPattern", }; string[] actualTracePattern = traces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 358f45c8f2d..3379a4477bc 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -32,12 +32,11 @@ public void NoticeExecution(IVMConfig vmConfig, ILogger logger) Interlocked.Increment(ref _callCount); // use Interlocked just in case of concurrent execution to run it only once - ILMode mode = - vmConfig.IsPatternMatchingEnabled && _callCount == vmConfig.PatternMatchingThreshold + ILMode mode = vmConfig.IsJitEnabled && _callCount == vmConfig.JittingThreshold + ? IlInfo.ILMode.JIT_MODE + : vmConfig.IsPatternMatchingEnabled && _callCount == vmConfig.PatternMatchingThreshold ? IlInfo.ILMode.PAT_MODE - : vmConfig.IsJitEnabled && _callCount == vmConfig.JittingThreshold - ? IlInfo.ILMode.JIT_MODE - : IlInfo.ILMode.NO_ILVM; + : IlInfo.ILMode.NO_ILVM; if (mode == IlInfo.ILMode.NO_ILVM) return; diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs index 9d802fb83f2..4fad6151962 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -85,7 +85,7 @@ public bool TryExecute(ILogger logger, EvmState vmState, u where TTracingInstructions : struct, VirtualMachine.IIsTracing { result = null; - if (programCounter > ushort.MaxValue || Mode == ILMode.NO_ILVM) + if (programCounter > ushort.MaxValue || this.IsEmpty) return false; if (Mode.HasFlag(ILMode.JIT_MODE) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) From f8dbb3d484307144901e436153a02fbbbf2d8a2c Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Fri, 1 Nov 2024 14:53:37 +0100 Subject: [PATCH 133/146] Refactors --- src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs | 3 ++- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 3379a4477bc..33e8e898562 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -12,6 +12,7 @@ using Nethermind.Logging; using ILMode = int; using Nethermind.Core; +using Nethermind.Core.Extensions; namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo : IThreadPoolWorkItem @@ -38,7 +39,7 @@ public void NoticeExecution(IVMConfig vmConfig, ILogger logger) ? IlInfo.ILMode.PAT_MODE : IlInfo.ILMode.NO_ILVM; - if (mode == IlInfo.ILMode.NO_ILVM) + if (mode == IlInfo.ILMode.NO_ILVM || IlInfo.Mode.HasFlag(mode)) return; IlAnalyzer.Enqueue(this, mode, vmConfig, logger); diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs index 8c2107c8d1d..0bff32f6378 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -36,7 +36,7 @@ public class AnalysisWork(CodeInfo codeInfo, ILMode mode) public static void Enqueue(CodeInfo codeInfo, ILMode mode, IVMConfig config, ILogger logger) { _queue.Enqueue(new AnalysisWork(codeInfo, mode)); - if(config.AnalysisQueueMaxSize == _queue.Count) + if(config.AnalysisQueueMaxSize <= _queue.Count) { Task.Run(() => AnalyzeQueue(config, logger)); } @@ -44,7 +44,7 @@ public static void Enqueue(CodeInfo codeInfo, ILMode mode, IVMConfig config, ILo private static void AnalyzeQueue(IVMConfig config, ILogger logger) { - int itemsLeft = config.AnalysisQueueMaxSize; + int itemsLeft = _queue.Count; while (itemsLeft-- > 0 && _queue.TryDequeue(out AnalysisWork worklet)) { if (logger.IsInfo) logger.Info($"Starting IL-EVM analysis of code {worklet.CodeInfo.Address}"); From c3ee162a0ee2f7a36ffcce6513ba4bb323ceb184 Mon Sep 17 00:00:00 2001 From: Siddharth Vaderaa Date: Sun, 3 Nov 2024 18:19:31 +0530 Subject: [PATCH 134/146] Fix/il evm opcodes [SLOAD, MCOPY, SAR] (#7703) --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 6 ++---- src/Nethermind/Nethermind.Evm/Instruction.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 7fee9f2417e..7c31bb9a80e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -983,8 +983,6 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ method.LoadLocalAddress(uint256C); method.LoadLocalAddress(lbool); method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling), [typeof(UInt256).MakeByRefType(), typeof(bool).MakeByRefType()])); - method.LoadConstant((long)1); - method.Add(); method.LoadConstant(GasCostOf.VeryLow); method.Multiply(); method.Subtract(); @@ -2163,9 +2161,9 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); il.BranchIfTrue(skipPop); - il.LoadLocalAddress(locals[0]); - il.Call(GetAsMethodInfo()); il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[0]); il.Call(Word.GetInt0); il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index d9c0ccf5907..fa7b5a475c3 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -337,7 +337,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.MLOAD] = new(GasCostOf.VeryLow, 0, 1, 1), [Instruction.MSTORE] = new(GasCostOf.VeryLow, 0, 2, 0), [Instruction.MSTORE8] = new(GasCostOf.VeryLow, 0, 2, 0), - [Instruction.SLOAD] = new(GasCostOf.SLoad, 0, 1, 1), + [Instruction.SLOAD] = new(0, 0, 1, 1), [Instruction.SSTORE] = new(0, 0, 2, 0), [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), From dc4877464f5e77cc23eed0ac5303f7283f29d0dd Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 3 Nov 2024 13:49:58 +0100 Subject: [PATCH 135/146] remove unncessary loop in tests --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 5477b3e4581..81332a41308 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1279,13 +1279,7 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var tracer2 = new GethLikeTxMemoryTracer(tracerOptions); var bytecode = Prepare.EvmCode - .JUMPDEST() .Call(address, 25000) - .POP() - .PushData(1000) - .GAS() - .GT() - .JUMPI(0) .STOP() .Done; @@ -1332,13 +1326,7 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) var bytecode = Prepare.EvmCode - .JUMPDEST() .Call(address, 100000) - .POP() - .GAS() - .PushData(1000) - .LT() - .JUMPI(0) .STOP() .Done; From 61b01e21166226c34c8b6e2d7dbec2e328648964 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 3 Nov 2024 14:03:10 +0100 Subject: [PATCH 136/146] refactor + comment --- src/Nethermind/Nethermind.Evm/Instruction.cs | 75 +++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index fa7b5a475c3..0ece1ebe2a5 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -178,6 +178,11 @@ public enum Instruction : byte } public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehaviorPop, byte stackBehaviorPush) { + // these values are just indicators that these opcodes have extra gas handling + private const int DYNAMIC = 0; + private const int FREE = 0; + private const int MEMORY_EXPANSION = 0; + private const int ACCOUNT_ACCESS = 0; /// /// The gas cost. /// @@ -202,7 +207,7 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav new Dictionary() { [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), - [Instruction.STOP] = new(0, 0, 0, 0), + [Instruction.STOP] = new(FREE, 0, 0, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.PUSH0] = new(GasCostOf.Base, 0, 0, 1), @@ -303,22 +308,22 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.SAR] = new(GasCostOf.VeryLow, 0, 2, 1), [Instruction.BYTE] = new(GasCostOf.VeryLow, 0, 2, 1), - [Instruction.KECCAK256] = new(GasCostOf.Sha3, 0, 2, 1), + [Instruction.KECCAK256] = new(GasCostOf.Sha3 + MEMORY_EXPANSION, 0, 2, 1), [Instruction.ADDRESS] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.BALANCE] = new(0, 0, 1, 1), // we need call GetBalanceCost in ILCompiler + [Instruction.BALANCE] = new(DYNAMIC + ACCOUNT_ACCESS, 0, 1, 1), // we need call GetBalanceCost in ILCompiler [Instruction.ORIGIN] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLER] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLVALUE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.CALLDATALOAD] = new(GasCostOf.VeryLow, 0, 1, 1), [Instruction.CALLDATASIZE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.CALLDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.CALLDATACOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), [Instruction.CODESIZE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.CODECOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.CODECOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), [Instruction.GASPRICE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.EXTCODESIZE] = new(0, 0, 1, 1), - [Instruction.EXTCODECOPY] = new(0, 0, 4, 0), + [Instruction.EXTCODESIZE] = new(DYNAMIC, 0, 1, 1), + [Instruction.EXTCODECOPY] = new(DYNAMIC + MEMORY_EXPANSION + ACCOUNT_ACCESS, 0, 4, 0), [Instruction.RETURNDATASIZE] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.RETURNDATACOPY] = new(GasCostOf.VeryLow, 0, 3, 0), + [Instruction.RETURNDATACOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), [Instruction.EXTCODEHASH] = new(0, 0, 1, 1), [Instruction.BLOCKHASH] = new(GasCostOf.BlockHash, 0, 1, 1), @@ -332,39 +337,41 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.BASEFEE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.BLOBHASH] = new(GasCostOf.BlobHash, 0, 1, 1), [Instruction.BLOBBASEFEE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), - [Instruction.MLOAD] = new(GasCostOf.VeryLow, 0, 1, 1), - [Instruction.MSTORE] = new(GasCostOf.VeryLow, 0, 2, 0), - [Instruction.MSTORE8] = new(GasCostOf.VeryLow, 0, 2, 0), - [Instruction.SLOAD] = new(0, 0, 1, 1), - [Instruction.SSTORE] = new(0, 0, 2, 0), + [Instruction.MLOAD] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 1, 1), + [Instruction.MSTORE] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 2, 0), + [Instruction.MSTORE8] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 2, 0), [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.GAS] = new(GasCostOf.Base, 0, 0, 1), [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), - [Instruction.MCOPY] = new(GasCostOf.VeryLow, 0, 3, 0), - - [Instruction.LOG0] = new(GasCostOf.Log, 0, 2, 0), - [Instruction.LOG1] = new(GasCostOf.Log, 0, 3, 0), - [Instruction.LOG2] = new(GasCostOf.Log, 0, 4, 0), - [Instruction.LOG3] = new(GasCostOf.Log, 0, 5, 0), - [Instruction.LOG4] = new(GasCostOf.Log, 0, 6, 0), - - [Instruction.TLOAD] = new(GasCostOf.Base, 0, 1, 1), - [Instruction.TSTORE] = new(GasCostOf.Base, 0, 2, 0), - - [Instruction.CREATE] = new(GasCostOf.Create, 0, 3, 1), - [Instruction.CALL] = new(GasCostOf.Call, 0, 7, 1), - [Instruction.CALLCODE] = new(GasCostOf.Call, 0, 7, 1), - [Instruction.RETURN] = new(0, 0, 2, 0), // has memory costs - [Instruction.DELEGATECALL] = new(GasCostOf.Call, 0, 6, 1), - [Instruction.CREATE2] = new(GasCostOf.Create, 0, 4, 1), - [Instruction.STATICCALL] = new(GasCostOf.Call, 0, 6, 1), - [Instruction.REVERT] = new(0, 0, 2, 0), // has memory costs - [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), - [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct, 0, 1, 0), + [Instruction.MCOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), + + [Instruction.LOG0] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 2, 0), + [Instruction.LOG1] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 3, 0), + [Instruction.LOG2] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 4, 0), + [Instruction.LOG3] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 5, 0), + [Instruction.LOG4] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 6, 0), + + [Instruction.TLOAD] = new(GasCostOf.Base + DYNAMIC, 0, 1, 1), + [Instruction.TSTORE] = new(GasCostOf.Base + DYNAMIC, 0, 2, 0), + + [Instruction.SLOAD] = new(DYNAMIC, 0, 1, 1), + [Instruction.SSTORE] = new(DYNAMIC, 0, 2, 0), + + [Instruction.CREATE] = new(GasCostOf.Create + DYNAMIC, 0, 3, 1), + [Instruction.CALL] = new(GasCostOf.Call + DYNAMIC, 0, 7, 1), + [Instruction.CALLCODE] = new(GasCostOf.Call + DYNAMIC, 0, 7, 1), + [Instruction.DELEGATECALL] = new(GasCostOf.Call + DYNAMIC, 0, 6, 1), + [Instruction.CREATE2] = new(GasCostOf.Create + DYNAMIC, 0, 4, 1), + [Instruction.STATICCALL] = new(GasCostOf.Call + DYNAMIC, 0, 6, 1), + [Instruction.SELFDESTRUCT] = new(GasCostOf.SelfDestruct + DYNAMIC, 0, 1, 0), + + [Instruction.RETURN] = new(MEMORY_EXPANSION, 0, 2, 0), // has memory costs + [Instruction.REVERT] = new(MEMORY_EXPANSION, 0, 2, 0), // has memory costs }.ToFrozenDictionary(); } public struct OpcodeInfo(ushort pc, Instruction instruction, int? argumentIndex) From 5c5461ab99adc7d6b5e922cff02ddf9d224f80c8 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 3 Nov 2024 15:20:13 +0100 Subject: [PATCH 137/146] revert SAR changes --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 7c31bb9a80e..2a2f0a4ec83 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2161,9 +2161,9 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); il.BranchIfTrue(skipPop); - il.StackLoadPrevious(stack.span, stack.idx, 2); - il.Call(GetAsMethodInfo()); il.LoadLocalAddress(locals[0]); + il.Call(GetAsMethodInfo()); + il.StackLoadPrevious(stack.span, stack.idx, 2); il.Call(Word.GetInt0); il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); From 354ac36e75b6313a8e68babdad76a7947185bac4 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Sun, 3 Nov 2024 16:14:38 +0100 Subject: [PATCH 138/146] add fixes to SAR --- .../CodeAnalysis/IL/ILCompiler.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 2a2f0a4ec83..69f1ca7b9bd 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2146,7 +2146,7 @@ private static void EmitShiftUInt256Method(Emit il, Local uint256R, (Local private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, Dictionary exceptions, params Local[] locals) { - Label skipPop = il.DefineLabel(); + Label aBiggerOrEqThan256 = il.DefineLabel(); Label signIsNeg = il.DefineLabel(); Label endOfOpcode = il.DefineLabel(); @@ -2156,36 +2156,41 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(Word.GetUInt256); il.StoreLocal(locals[0]); + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.StackPop(stack.idx, 2); + il.LoadLocalAddress(locals[0]); il.LoadConstant(Word.FullSize); - il.Call(typeof(UInt256).GetMethod("op_GreaterThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); - il.BranchIfTrue(skipPop); + il.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + il.BranchIfFalse(aBiggerOrEqThan256); - il.LoadLocalAddress(locals[0]); + using Local shiftBits = il.DeclareLocal(); + + + il.LoadLocalAddress(locals[1]); il.Call(GetAsMethodInfo()); - il.StackLoadPrevious(stack.span, stack.idx, 2); - il.Call(Word.GetInt0); + il.LoadLocalAddress(locals[0]); + il.LoadField(GetFieldInfo(nameof(UInt256.u0))); + il.Convert(); il.LoadLocalAddress(uint256R); il.Call(GetAsMethodInfo()); il.Call(typeof(Int256.Int256).GetMethod(nameof(Int256.Int256.RightShift), [typeof(int), typeof(Int256.Int256).MakeByRefType()])); - il.StackPop(stack.idx, 2); il.CleanAndLoadWord(stack.span, stack.idx); il.LoadLocal(uint256R); il.Call(Word.SetUInt256); - il.StackPush(stack.idx, 1); il.Branch(endOfOpcode); - il.MarkLabel(skipPop); - il.StackPop(stack.idx, 2); + il.MarkLabel(aBiggerOrEqThan256); - il.LoadLocalAddress(locals[0]); + il.LoadLocalAddress(locals[1]); il.Call(GetAsMethodInfo()); il.Call(GetPropertyInfo(typeof(Int256.Int256), nameof(Int256.Int256.Sign), false, out _)); il.LoadConstant(0); il.BranchIfLess(signIsNeg); il.CleanWord(stack.span, stack.idx); - il.StackPush(stack.idx); il.Branch(endOfOpcode); // sign @@ -2195,10 +2200,10 @@ private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local il.Call(GetAsMethodInfo()); il.LoadObject(); il.Call(Word.SetUInt256); - il.StackPush(stack.idx); il.Branch(endOfOpcode); il.MarkLabel(endOfOpcode); + il.StackPush(stack.idx); } private static void EmitBitwiseUInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, Dictionary exceptions, params Local[] locals) From e45bd27a77af628cfde8950dca3bac5c0087082c Mon Sep 17 00:00:00 2001 From: Siddharth Vaderaa Date: Mon, 4 Nov 2024 23:15:32 +0530 Subject: [PATCH 139/146] Fix TStore, TLoad gas (#7709) --- src/Nethermind/Nethermind.Evm/Instruction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 0ece1ebe2a5..fdb50873d9f 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -356,8 +356,8 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.LOG3] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 5, 0), [Instruction.LOG4] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 6, 0), - [Instruction.TLOAD] = new(GasCostOf.Base + DYNAMIC, 0, 1, 1), - [Instruction.TSTORE] = new(GasCostOf.Base + DYNAMIC, 0, 2, 0), + [Instruction.TLOAD] = new(GasCostOf.TLoad, 0, 1, 1), + [Instruction.TSTORE] = new(GasCostOf.TStore, 0, 2, 0), [Instruction.SLOAD] = new(DYNAMIC, 0, 1, 1), [Instruction.SSTORE] = new(DYNAMIC, 0, 2, 0), From 772f565a6e35c711cc41624c8cc11d84ac8fddb0 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 4 Nov 2024 22:05:59 +0100 Subject: [PATCH 140/146] fixed tests to use Prague fork --- .../CodeAnalysis/IlEvmTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 81332a41308..11338586c55 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -152,7 +152,7 @@ public override void Setup() public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null, long gasAvailable = 1_000_000) where T : ITxTracer { - Execute(tracer, bytecode, fork, gasAvailable); + Execute(tracer, bytecode, fork ?? MainnetSpecProvider.PragueActivation, gasAvailable); } public Address InsertCode(byte[] bytecode) @@ -1279,7 +1279,7 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by var tracer2 = new GethLikeTxMemoryTracer(tracerOptions); var bytecode = Prepare.EvmCode - .Call(address, 25000) + .Call(address, 750_000) .STOP() .Done; @@ -1494,7 +1494,7 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() string[] desiredTracePattern = new[] { $"ILEVM_PRECOMPILED_({main})[0..47]", - $"SomeAfterTwoPush", + $"ILEVM_PRECOMPILED_({aux})[0..5]", $"ILEVM_PRECOMPILED_({main})[49..60]", $"ILEVM_PRECOMPILED_({main})[0..47]", $"AbortDestinationPattern", @@ -1536,7 +1536,7 @@ public void JIT_invalid_opcode_results_in_failure() enhancedChain.ForceRunAnalysis(main); var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - enhancedChain.Execute(driver, tracer); + enhancedChain.Execute(driver, tracer, (ForkActivation?)(MainnetSpecProvider.ByzantiumBlockNumber, 0)); var traces = tracer.BuildResult(); var HasIlvmTraces = traces.Entries.Where(tr => tr.SegmentID is not null).Any(); @@ -1605,7 +1605,7 @@ public void Ensure_Evm_ILvm_Compatibility((Instruction? opcode, byte[] bytecode, TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); var state = new EvmState( - 100_000_000, + 750_000, new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, Snapshot.Empty); @@ -1642,7 +1642,7 @@ public void Test_ILVM_Trace_Mode((Instruction? opcode, byte[] bytecode, EvmExcep TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); var state = new EvmState( - 1_000_000, + 750_000, new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, Snapshot.Empty); @@ -1687,7 +1687,7 @@ public void Test_ILVM_Trace_Mode_Has_0_Traces_When_TraceInstructions_Is_Off((Ins TestState.InsertCode(Address.FromNumber(1), testcase.bytecode, Prague.Instance); var state = new EvmState( - 1_000_000, + 750_000, new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, Snapshot.Empty); @@ -1731,7 +1731,7 @@ public void Extra() TestState.InsertCode(Address.FromNumber(1), bytecode, Prague.Instance); var state = new EvmState( - 1_000_000, + 750_000, new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), ExecutionType.CALL, Snapshot.Empty); From 324393f60080971ae4d307495e956581b3a33822 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Mon, 4 Nov 2024 23:19:37 +0100 Subject: [PATCH 141/146] fix Instruction Metadata dictionary --- .../CodeAnalysis/IlEvmTests.cs | 186 ++++++++++++++++-- src/Nethermind/Nethermind.Evm/Instruction.cs | 3 - 2 files changed, 175 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 11338586c55..f04da5bdf67 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -165,13 +165,19 @@ public Address InsertCode(byte[] bytecode) TestState.InsertCode(address, bytecode, spec); return address; } - - public void ForceRunAnalysis(Address address) + public void ForceRunAnalysis(Address address, int mode) { var codeinfo = CodeInfoRepository.GetCachedCodeInfo(TestState, address, Prague.Instance); var initialILMODE = codeinfo.IlInfo.Mode; - codeinfo.NoticeExecution(config, NullLogger.Instance); - while (codeinfo.IlInfo.Mode == initialILMODE) ; // wait for analysis to finish + if(mode.HasFlag(ILMode.JIT_MODE)) + { + IlAnalyzer.StartAnalysis(codeinfo, ILMode.JIT_MODE, config, NullLogger.Instance); + } + + if (mode.HasFlag(ILMode.PAT_MODE)) + { + IlAnalyzer.StartAnalysis(codeinfo, ILMode.PAT_MODE, config, NullLogger.Instance); + } } public Hash256 StateRoot @@ -1285,7 +1291,7 @@ public void ILVM_JIT_Execution_Equivalence_Tests((Instruction? opcode, byte[] by standardChain.Execute(bytecode, tracer1); - enhancedChain.ForceRunAnalysis(address); + enhancedChain.ForceRunAnalysis(address, ILMode.JIT_MODE); enhancedChain.Execute(bytecode, tracer2); @@ -1332,7 +1338,7 @@ public void ILVM_Pat_Execution_Equivalence_Tests((Type opcode, byte[] bytecode) standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); - enhancedChain.ForceRunAnalysis(address); + enhancedChain.ForceRunAnalysis(address, ILMode.PAT_MODE); enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); @@ -1399,8 +1405,8 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() .Call(main, 1_000_000) .Done; - enhancedChain.ForceRunAnalysis(main); - enhancedChain.ForceRunAnalysis(aux); + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); + enhancedChain.ForceRunAnalysis(aux, ILMode.JIT_MODE); var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(driver, tracer); @@ -1476,8 +1482,9 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() .Call(main, 1_000_000) .Done; - enhancedChain.ForceRunAnalysis(main); // once for JIT - enhancedChain.ForceRunAnalysis(main); // once for PAT + enhancedChain.ForceRunAnalysis(main, ILMode.PAT_MODE | ILMode.JIT_MODE); + + enhancedChain.ForceRunAnalysis(aux, ILMode.PAT_MODE | ILMode.JIT_MODE); var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(driver, tracer); @@ -1505,6 +1512,163 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() } + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On_Equiv() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 1, + IsPatternMatchingEnabled = false, + JittingThreshold = 1, + AnalysisQueueMaxSize = 1, + IsJitEnabled = true, + AggressiveJitMode = true, + }); + + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + + var auxCode = Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done; + + var aux = enhancedChain.InsertCode(auxCode); + standardChain.InsertCode(auxCode); + + var maincode = Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .JUMPI(59) + .PushSingle(23) + .PushSingle(7) + .ADD() + .POP() + .Call(aux, 100) + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .JUMP(0) + .JUMPDEST() + .STOP() + .Done; + + Address main = enhancedChain.InsertCode(maincode); + standardChain.InsertCode(maincode); + + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + var bytecode = + Prepare.EvmCode + .Call(main, 100000) + .STOP() + .Done; + + standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); + + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); + + enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); + + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); + + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; + + var enhancedHasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var normalHasIlvmTraces = normal_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + + Assert.That(enhancedHasIlvmTraces, Is.True); + Assert.That(normalHasIlvmTraces, Is.False); + Assert.That(actual, Is.EqualTo(expected)); + } + + + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off_Equiv() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 2, + AnalysisQueueMaxSize = 1, + IsPatternMatchingEnabled = true, + JittingThreshold = 1, + IsJitEnabled = true, + AggressiveJitMode = false, + BakeInTracingInJitMode = true + }); + + + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + + var auxCode = Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done; + + var aux = enhancedChain.InsertCode(auxCode); + standardChain.InsertCode(auxCode); + + var maincode = Prepare.EvmCode + .JUMPDEST() + .PushSingle(1000) + .GAS() + .LT() + .JUMPI(59) + .PushSingle(23) + .PushSingle(7) + .ADD() + .POP() + .Call(aux, 100) + .POP() + .PushSingle(42) + .PushSingle(5) + .ADD() + .POP() + .JUMP(0) + .JUMPDEST() + .STOP() + .Done; + + Address main = enhancedChain.InsertCode(maincode); + standardChain.InsertCode(maincode); + + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + var bytecode = + Prepare.EvmCode + .Call(main, 100000) + .STOP() + .Done; + + standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); + + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); + + enhancedChain.Execute(bytecode, tracer2, (ForkActivation)10000000000); + + var normal_traces = tracer1.BuildResult(); + var ilvm_traces = tracer2.BuildResult(); + + var actual = standardChain.StateRoot; + var expected = enhancedChain.StateRoot; + + var enhancedHasIlvmTraces = ilvm_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + var normalHasIlvmTraces = normal_traces.Entries.Where(tr => tr.SegmentID is not null).Any(); + + Assert.That(enhancedHasIlvmTraces, Is.True); + Assert.That(normalHasIlvmTraces, Is.False); + Assert.That(actual, Is.EqualTo(expected)); + } + [Test] public void JIT_invalid_opcode_results_in_failure() { @@ -1534,7 +1698,7 @@ public void JIT_invalid_opcode_results_in_failure() .STOP() .Done; - enhancedChain.ForceRunAnalysis(main); + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); enhancedChain.Execute(driver, tracer, (ForkActivation?)(MainnetSpecProvider.ByzantiumBlockNumber, 0)); var traces = tracer.BuildResult(); diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index fdb50873d9f..f2aed12e049 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -339,15 +339,12 @@ public struct OpcodeMetadata(long gasCost, byte additionalBytes, byte stackBehav [Instruction.BLOBBASEFEE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.INVALID] = new(GasCostOf.Base, 0, 0, 0), - [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), [Instruction.MLOAD] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 1, 1), [Instruction.MSTORE] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 2, 0), [Instruction.MSTORE8] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 2, 0), - [Instruction.JUMP] = new(GasCostOf.Mid, 0, 1, 0), [Instruction.PC] = new(GasCostOf.Base, 0, 0, 1), [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), [Instruction.GAS] = new(GasCostOf.Base, 0, 0, 1), - [Instruction.JUMPDEST] = new(GasCostOf.JumpDest, 0, 0, 0), [Instruction.MCOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), [Instruction.LOG0] = new(GasCostOf.Log + MEMORY_EXPANSION, 0, 2, 0), From f5f3cf7b1e8b752af127a6b2120b5b187c97a6e7 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 5 Nov 2024 18:14:16 +0100 Subject: [PATCH 142/146] fix ISZERO --- .../Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs | 9 +++++---- .../Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index f04da5bdf67..5dbc3e5f7d0 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -364,6 +364,7 @@ public override void Setup() .PushSingle(1) .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.ISZERO, Prepare.EvmCode .ISZERO(7) .PushData(7) @@ -371,7 +372,11 @@ public override void Setup() .ISZERO(0) .PushData(1) .SSTORE() + .ISZERO(UInt256.MaxValue) + .PushData(23) + .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.SUB, Prepare.EvmCode .PushSingle(23) .PushSingle(7) @@ -1434,7 +1439,6 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On() Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); } - [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() { @@ -1511,7 +1515,6 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); } - [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On_Equiv() { @@ -1589,7 +1592,6 @@ public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_On_Equ Assert.That(actual, Is.EqualTo(expected)); } - [Test] public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off_Equiv() { @@ -1709,7 +1711,6 @@ public void JIT_invalid_opcode_results_in_failure() Assert.That(hasFailed, Is.True); } - [Test] public void Pure_Opcode_Emition_Coveraga() { diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 69f1ca7b9bd..5f9d3bffdea 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -589,8 +589,14 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.ISZERO: {// we load the stack method.StackLoadPrevious(stack, head, 1); - method.Duplicate(); method.Call(Word.GetIsZero); + method.StoreLocal(lbool); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.SetToZero); + + method.StackLoadPrevious(stack, head, 1); + method.LoadLocal(lbool); method.Call(Word.SetByte0); } break; From 6632bdc5781d839de86aa18ed7946e7277089b8d Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Tue, 5 Nov 2024 18:41:06 +0100 Subject: [PATCH 143/146] minor changes to ISZERO to avoid triple loading --- src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 5f9d3bffdea..1c03e36779e 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -589,13 +589,11 @@ private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[ case Instruction.ISZERO: {// we load the stack method.StackLoadPrevious(stack, head, 1); + method.Duplicate(); + method.Duplicate(); method.Call(Word.GetIsZero); method.StoreLocal(lbool); - - method.StackLoadPrevious(stack, head, 1); method.Call(Word.SetToZero); - - method.StackLoadPrevious(stack, head, 1); method.LoadLocal(lbool); method.Call(Word.SetByte0); } From 12b5ddcd1bb44fb7e09d00e3ff8e3efa9c94ca47 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 7 Nov 2024 06:58:50 +0100 Subject: [PATCH 144/146] fix SGT and SLT --- .../CodeAnalysis/IlEvmTests.cs | 163 +++++++++++++++++- .../CodeAnalysis/IL/ILCompiler.cs | 21 ++- 2 files changed, 171 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 5dbc3e5f7d0..915b6796543 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -498,6 +498,22 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.GT, Prepare.EvmCode + .PushSingle(23) + .PushSingle(7) + .GT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.GT, Prepare.EvmCode + .PushSingle(17) + .PushSingle(17) + .GT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.LT, Prepare.EvmCode .PushSingle(23) .PushSingle(7) @@ -506,11 +522,35 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.LT, Prepare.EvmCode + .PushSingle(7) + .PushSingle(23) + .LT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.LT, Prepare.EvmCode + .PushSingle(17) + .PushSingle(17) + .LT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.NOT, Prepare.EvmCode .PushSingle(1) .NOT() .PushData(1) .SSTORE() + .PushSingle(0) + .NOT() + .PushData(1) + .SSTORE() + .PushSingle(UInt256.MaxValue) + .NOT() + .PushData(1) + .SSTORE() .Done, EvmExceptionType.None); yield return (Instruction.BLOBHASH, Prepare.EvmCode @@ -558,6 +598,9 @@ public override void Setup() .Done, EvmExceptionType.None); yield return (Instruction.PC, Prepare.EvmCode + .PC() + .PushData(1) + .SSTORE() .PC() .PushData(1) .SSTORE() @@ -716,6 +759,14 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.SHL, Prepare.EvmCode + .PushSingle(23) + .PushSingle(32) + .SHL() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.SHR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) @@ -724,14 +775,58 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.SHR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(32) + .SHR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.SAR, Prepare.EvmCode .PushSingle(23) - .PushSingle(1) + .PushSingle(0) + .SAR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(0) + .PushSingle(23) + .SAR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(23) + .PushSingle(17) .SAR() .PushData(1) .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(23) + .PushSingle((UInt256)((Int256.Int256)(-1))) + .SAR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SAR, Prepare.EvmCode + .PushSingle(23) + .PushSingle((UInt256)((Int256.Int256)(-1))) + .SAR() + .PushSingle((UInt256)((Int256.Int256)(1))) + .SAR() + .PushSingle(23) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.AND, Prepare.EvmCode .PushSingle(23) .PushSingle(1) @@ -740,6 +835,22 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.AND, Prepare.EvmCode + .PushSingle(0) + .PushSingle(UInt256.MaxValue) + .AND() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.AND, Prepare.EvmCode + .PushSingle(UInt256.MaxValue) + .PushSingle(0) + .AND() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.OR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) @@ -748,6 +859,22 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.OR, Prepare.EvmCode + .PushSingle(0) + .PushSingle(UInt256.MaxValue) + .OR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.OR, Prepare.EvmCode + .PushSingle(UInt256.MaxValue) + .PushSingle(0) + .OR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.XOR, Prepare.EvmCode .PushSingle(23) .PushSingle(1) @@ -757,8 +884,24 @@ public override void Setup() .Done, EvmExceptionType.None); yield return (Instruction.SLT, Prepare.EvmCode + .PushSingle(17) .PushData(23) - .PushSingle(4) + .SLT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SLT, Prepare.EvmCode + .PushData(23) + .PushSingle(17) + .SLT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SLT, Prepare.EvmCode + .PushData(17) + .PushSingle(17) .SLT() .PushData(1) .SSTORE() @@ -766,7 +909,23 @@ public override void Setup() yield return (Instruction.SGT, Prepare.EvmCode .PushData(23) + .PushData(17) + .SGT() .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SGT, Prepare.EvmCode + .PushData(17) + .PushData(17) + .SGT() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SGT, Prepare.EvmCode + .PushData(17) + .PushData(23) .SGT() .PushData(1) .SSTORE() diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index 1c03e36779e..fbd0910b62d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2269,7 +2269,7 @@ private static void EmitComparaisonUInt256Method(Emit il, Local uint256R, private static void EmitComparaisonInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, MethodInfo operation, bool isGreaterThan, Dictionary exceptions, params Local[] locals) { Label endOpcodeHandling = il.DefineLabel(); - Label pushZerohandling = il.DefineLabel(); + Label pushOnehandling = il.DefineLabel(); // we the two uint256 from the stack il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); @@ -2289,25 +2289,24 @@ private static void EmitComparaisonInt256Method(Emit il, Local uint256R, ( il.LoadConstant(0); if (isGreaterThan) { - il.BranchIfLess(pushZerohandling); + il.BranchIfGreater(pushOnehandling); } else { - il.BranchIfGreater(pushZerohandling); + il.BranchIfLess(pushOnehandling); } - il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.One))); - il.StoreLocal(uint256R); + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.Zero))); il.Branch(endOpcodeHandling); - il.MarkLabel(pushZerohandling); + il.MarkLabel(pushOnehandling); + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.One))); + il.Branch(endOpcodeHandling); - il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.Zero))); - il.StoreLocal(uint256R); - il.MarkLabel(endOpcodeHandling); // push the result to the stack - il.CleanAndLoadWord(stack.span, stack.idx); - il.LoadLocal(uint256R); // stack: word*, uint256 + il.MarkLabel(endOpcodeHandling); il.Call(Word.SetUInt256); il.StackPush(stack.idx); } From 3d21462fdcf361c2d5754f2740a14832dd791464 Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 7 Nov 2024 07:19:24 +0100 Subject: [PATCH 145/146] Added more tests --- .../CodeAnalysis/IlEvmTests.cs | 185 +++++++++++++----- 1 file changed, 141 insertions(+), 44 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index 915b6796543..e9608011c54 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -35,6 +35,7 @@ using Nethermind.Core.Collections; using Nethermind.Specs.Test; using System.Threading; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; namespace Nethermind.Evm.Test.CodeAnalysis { @@ -465,6 +466,41 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(123, ((UInt256)23).PaddedBytes(32)) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(32, ((UInt256)23).PaddedBytes(32)) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(0, ((UInt256)0).PaddedBytes(32)) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(123, ((UInt256)0).PaddedBytes(32)) + .MLOAD(123) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE | Instruction.MLOAD, Prepare.EvmCode + .MSTORE(32, ((UInt256)0).PaddedBytes(32)) + .MLOAD(32) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.MSTORE8, Prepare.EvmCode .MSTORE8(0, ((UInt256)23).PaddedBytes(32)) .MLOAD(0) @@ -472,6 +508,41 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(123, ((UInt256)23).PaddedBytes(32)) + .MLOAD(123) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(32, ((UInt256)23).PaddedBytes(32)) + .MLOAD(32) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(0, UInt256.MaxValue.PaddedBytes(32)) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(123, UInt256.MaxValue.PaddedBytes(32)) + .MLOAD(123) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MSTORE8, Prepare.EvmCode + .MSTORE8(32, UInt256.MaxValue.PaddedBytes(32)) + .MLOAD(32) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.MCOPY, Prepare.EvmCode .MSTORE(0, ((UInt256)23).PaddedBytes(32)) .MCOPY(32, 0, 32) @@ -482,6 +553,76 @@ public override void Setup() .SSTORE() .Done, EvmExceptionType.None); + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(123, ((UInt256)23).PaddedBytes(32)) + .MCOPY(32, 123, 32) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(32, ((UInt256)23).PaddedBytes(32)) + .MCOPY(32, 123, 32) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(0, ((UInt256)0).PaddedBytes(32)) + .MCOPY(32, 0, 32) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(123, ((UInt256)0).PaddedBytes(32)) + .MCOPY(32, 123, 32) + .MLOAD(32) + .MLOAD(123) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(32, ((UInt256)0).PaddedBytes(32)) + .MCOPY(0, 32, 32) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(32, ((UInt256)0).PaddedBytes(32)) + .MCOPY(32, 32, 32) + .MLOAD(32) + .PushSingle((UInt256)0) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MCOPY, Prepare.EvmCode + .MSTORE(32, ((UInt256)23).PaddedBytes(32)) + .MCOPY(32, 32, 32) + .MLOAD(32) + .PushSingle((UInt256)23) + .EQ() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + yield return (Instruction.EQ, Prepare.EvmCode .PushSingle(23) .PushSingle(7) @@ -2031,49 +2172,5 @@ public void Test_ILVM_Trace_Mode_Has_0_Traces_When_TraceInstructions_Is_Off((Ins Assert.That(tracedOpcodes.Count, Is.EqualTo(0)); } - - [Test] - public void Extra() - { - var bytecode = Prepare.EvmCode - .PushData("0x2213bc0b00000000000000000000000070bf6634ee8cb27d04478f184b9b8bb1") - .PushData("0xe0") - .SHR() - .Done; - - var codeInfo = new CodeInfo(bytecode, TestItem.AddressA); - var blkExCtx = new BlockExecutionContext(BuildBlock(MainnetSpecProvider.CancunActivation, SenderRecipientAndMiner.Default).Header); - var txExCtx = new TxExecutionContext(blkExCtx, TestItem.AddressA, 23, [TestItem.KeccakH.Bytes.ToArray()], CodeInfoRepository); - var envExCtx = new ExecutionEnvironment(codeInfo, Recipient, Sender, Contract, new ReadOnlyMemory([1, 2, 3, 4, 5, 6, 7]), txExCtx, 23, 7); - var stack = new byte[1024 * 32]; - var inputBuffer = envExCtx.InputData; - var returnBuffer = - new ReadOnlyMemory(Enumerable.Range(0, 32) - .Select(i => (byte)i).ToArray()); - - TestState.CreateAccount(Address.FromNumber(1), 1000000); - TestState.InsertCode(Address.FromNumber(1), bytecode, Prague.Instance); - - var state = new EvmState( - 750_000, - new ExecutionEnvironment(codeInfo, Address.FromNumber(1), Address.FromNumber(1), Address.FromNumber(1), ReadOnlyMemory.Empty, txExCtx, 0, 0), - ExecutionType.CALL, - Snapshot.Empty); - - IVirtualMachine evm = typeof(VirtualMachine).GetField("_evm", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Machine) as IVirtualMachine; - - state.InitStacks(); - - var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); - - ILEvmState iLEvmState = new ILEvmState(SpecProvider.ChainId, state, EvmExceptionType.None, 0, 100000, ref returnBuffer); - var metadata = IlAnalyzer.StripByteCode(bytecode); - var ctx = ILCompiler.CompileSegment("ILEVM_TEST", metadata.Item1, metadata.Item2, config); - ctx.PrecompiledSegment(ref iLEvmState, _blockhashProvider, TestState, CodeInfoRepository, Prague.Instance, tracer, ctx.Data); - - var tracedOpcodes = tracer.BuildResult().Entries; - - Assert.That(tracedOpcodes.Count, Is.GreaterThan(0)); - } } } From 96b15ae7fa4cf5aa96462d280b0b4e4fd33c7fcc Mon Sep 17 00:00:00 2001 From: Demuirgos Date: Thu, 7 Nov 2024 10:46:05 +0100 Subject: [PATCH 146/146] fixes for LOGx opcodes --- .../CodeAnalysis/IlEvmTests.cs | 42 +++++----- .../CodeAnalysis/IL/ILCompiler.cs | 77 ++++++++----------- 2 files changed, 49 insertions(+), 70 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs index e9608011c54..860403c6597 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -1097,52 +1097,48 @@ public override void Setup() .Done, EvmExceptionType.None); yield return (Instruction.LOG0, Prepare.EvmCode - .Log(0, 0) + .PushData(SampleHexData1.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .LOGx(0, 0, 64) .Done, EvmExceptionType.None); yield return (Instruction.LOG1, Prepare.EvmCode .PushData(SampleHexData1.PadLeft(64, '0')) .PushData(0) .Op(Instruction.MSTORE) - .Log(1, 0, [TestItem.KeccakA]) + .PushData(TestItem.KeccakA.Bytes.ToArray()) + .LOGx(1, 0, 64) .Done, EvmExceptionType.None); yield return (Instruction.LOG2, Prepare.EvmCode - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(0) - .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(64) .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(96) + .PushData(0) .Op(Instruction.MSTORE) - .Log(4, 0, [TestItem.KeccakA, TestItem.KeccakB]) + .PushData(TestItem.KeccakA.Bytes.ToArray()) + .PushData(TestItem.KeccakB.Bytes.ToArray()) + .LOGx(2, 0, 64) .Done, EvmExceptionType.None); yield return (Instruction.LOG3, Prepare.EvmCode .PushData(SampleHexData1.PadLeft(64, '0')) .PushData(0) .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .Log(2, 0, [TestItem.KeccakA, TestItem.KeccakA, TestItem.KeccakB]) + .PushData(TestItem.KeccakA.Bytes.ToArray()) + .PushData(TestItem.KeccakB.Bytes.ToArray()) + .PushData(TestItem.KeccakC.Bytes.ToArray()) + .LOGx(3, 0, 64) .Done, EvmExceptionType.None); yield return (Instruction.LOG4, Prepare.EvmCode .PushData(SampleHexData1.PadLeft(64, '0')) .PushData(0) .Op(Instruction.MSTORE) - .PushData(SampleHexData2.PadLeft(64, '0')) - .PushData(32) - .Op(Instruction.MSTORE) - .PushData(SampleHexData1.PadLeft(64, '0')) - .PushData(64) - .Op(Instruction.MSTORE) - .Log(3, 0, [TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakA, TestItem.KeccakB]) + .PushData(TestItem.KeccakA.Bytes.ToArray()) + .PushData(TestItem.KeccakB.Bytes.ToArray()) + .PushData(TestItem.KeccakC.Bytes.ToArray()) + .PushData(TestItem.KeccakD.Bytes.ToArray()) + .LOGx(4, 0, 64) .Done, EvmExceptionType.None); yield return (Instruction.TSTORE | Instruction.TLOAD, Prepare.EvmCode diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs index fbd0910b62d..610b7bc9cb1 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -2424,49 +2424,6 @@ private static void EmitLogMethod( ) { using Local logEntry = il.DeclareLocal(); - Action loadExecutingAccount = () => - { - // Executing account - il.LoadArgument(VMSTATE_INDEX); - il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); - il.LoadField( - GetFieldInfo( - typeof(ExecutionEnvironment), - nameof(ExecutionEnvironment.ExecutingAccount) - ) - ); - }; - - Action loadMemoryIntoByteArray = () => - { - // memory load - il.LoadArgument(VMSTATE_INDEX); - il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); - il.LoadLocalAddress(uint256Position); // position - il.LoadLocalAddress(uint256Length); // length - il.Call( - typeof(EvmPooledMemory).GetMethod( - nameof(EvmPooledMemory.Load), - [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()] - ) - ); - il.StoreLocal(localReadOnlyMemory); - il.LoadLocalAddress(localReadOnlyMemory); - il.Call(typeof(ReadOnlyMemory).GetMethod(nameof(ReadOnlyMemory.ToArray))); - }; - - // Pop an item off the Stack, create a Hash256 object, store it in a local - Action storeLocalHash256AtStackIndex = (int index) => - { - using (var keccak = il.DeclareLocal(typeof(ValueHash256))) - { - il.StackLoadPrevious(stack.span, stack.idx, index); - il.Call(Word.GetKeccak); - il.StoreLocal(keccak); - il.LoadLocalAddress(keccak); - il.NewObject(typeof(Hash256), typeof(ValueHash256).MakeByRefType()); - } - }; il.StackLoadPrevious(stack.span, stack.idx, 1); il.Call(Word.GetUInt256); @@ -2504,8 +2461,28 @@ private static void EmitLogMethod( il.LoadConstant((ulong)0); il.BranchIfLess(exceptions[EvmExceptionType.OutOfGas]); - loadExecutingAccount(); - loadMemoryIntoByteArray(); + il.LoadArgument(VMSTATE_INDEX); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Env))); + il.LoadField( + GetFieldInfo( + typeof(ExecutionEnvironment), + nameof(ExecutionEnvironment.ExecutingAccount) + ) + ); + + il.LoadArgument(VMSTATE_INDEX); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + il.LoadLocalAddress(uint256Position); // position + il.LoadLocalAddress(uint256Length); // length + il.Call( + typeof(EvmPooledMemory).GetMethod( + nameof(EvmPooledMemory.Load), + [typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType()] + ) + ); + il.StoreLocal(localReadOnlyMemory); + il.LoadLocalAddress(localReadOnlyMemory); + il.Call(typeof(ReadOnlyMemory).GetMethod(nameof(ReadOnlyMemory.ToArray))); il.LoadConstant(topicsCount); il.NewArray(); @@ -2513,7 +2490,14 @@ private static void EmitLogMethod( { il.Duplicate(); il.LoadConstant(i); - storeLocalHash256AtStackIndex(i); + using (var keccak = il.DeclareLocal(typeof(ValueHash256))) + { + il.StackLoadPrevious(stack.span, stack.idx, i + 1); + il.Call(Word.GetKeccak); + il.StoreLocal(keccak); + il.LoadLocalAddress(keccak); + il.NewObject(typeof(Hash256), typeof(ValueHash256).MakeByRefType()); + } il.StoreElement(); } // Creat an LogEntry Object from Items on the Stack @@ -2529,7 +2513,6 @@ private static void EmitLogMethod( il.CallVirtual( typeof(ICollection).GetMethod(nameof(ICollection.Add)) ); - } private static void EmitGasAvailabilityCheck(