diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index 7d129079f0f..095f63255ef 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -73,6 +73,7 @@ + diff --git a/src/Nethermind/Nethermind.Api/IBasicApi.cs b/src/Nethermind/Nethermind.Api/IBasicApi.cs index d6d8d320ef3..f435bb5c77d 100644 --- a/src/Nethermind/Nethermind.Api/IBasicApi.cs +++ b/src/Nethermind/Nethermind.Api/IBasicApi.cs @@ -15,6 +15,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; @@ -33,6 +34,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 b6a3574c212..9a51a0ee903 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -57,6 +57,7 @@ using Nethermind.Wallet; using Nethermind.Sockets; using Nethermind.Trie; +using Nethermind.Evm.Config; using Nethermind.Consensus.Processing.CensorshipDetector; using Nethermind.Facade.Find; @@ -114,6 +115,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.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 new file mode 100644 index 00000000000..860403c6597 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/CodeAnalysis/IlEvmTests.cs @@ -0,0 +1,2172 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +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.Tracing.GethStyle; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Specs; +using Nethermind.Specs.Forks; +using Nethermind.State; +using Nethermind.Trie; +using Nethermind.Trie.Pruning; +using NUnit.Framework; +using Sigil; +using System; +using System.Collections.Generic; +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; +using Nethermind.Evm.CodeAnalysis.IL.Patterns; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Blockchain; +using Polly; +using Nethermind.Core.Collections; +using Nethermind.Specs.Test; +using System.Threading; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; + +namespace Nethermind.Evm.Test.CodeAnalysis +{ + 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; + + 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; + + UInt256 lhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 1]; + UInt256 rhs = vmState.Env.CodeInfo.MachineCode.Span[programCounter + 3]; + stack.PushUInt256(lhs + rhs); + + programCounter += 5; + } + } + internal class TestBlockChain : VirtualMachineTestsBase + { + protected IVMConfig config; + public TestBlockChain(IVMConfig config) + { + this.config = config; + Setup(); + } + + public TestBlockChain() + { + config = new VMConfig(); + Setup(); + } + public override void Setup() + { + base.Setup(); + + ILogManager logManager = GetLogManager(); + + _blockhashProvider = new TestBlockhashProvider(SpecProvider); + Machine = new VirtualMachine(_blockhashProvider, SpecProvider, CodeInfoRepository, logManager, config); + _processor = new TransactionProcessor(SpecProvider, TestState, Machine, CodeInfoRepository, logManager); + + var code = Prepare.EvmCode + .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); + + 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(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + IlAnalyzer.AddPattern(); + } + + public void Execute(byte[] bytecode, T tracer, ForkActivation? fork = null, long gasAvailable = 1_000_000) + where T : ITxTracer + { + Execute(tracer, bytecode, fork ?? MainnetSpecProvider.PragueActivation, gasAvailable); + } + + public Address InsertCode(byte[] bytecode) + { + var hashcode = Keccak.Compute(bytecode); + var address = new Address(hashcode); + + var spec = Prague.Instance; + TestState.CreateAccount(address, 1_000_000_000); + TestState.InsertCode(address, bytecode, spec); + return address; + } + public void ForceRunAnalysis(Address address, int mode) + { + var codeinfo = CodeInfoRepository.GetCachedCodeInfo(TestState, address, Prague.Instance); + var initialILMODE = codeinfo.IlInfo.Mode; + 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 + { + get + { + TestState.Commit(Spec); + TestState.RecalculateStateRoot(); + return TestState.StateRoot; + } + } + + } + + [TestFixture] + [NonParallelizable] + internal class IlEvmTests : TestBlockChain + { + private const string AnalyzerField = "_analyzer"; + private const string PatternField = "_patterns"; + private const int RepeatCount = 256; + + [SetUp] + public override void Setup() + { + base.Setup(); + + base.config = new VMConfig() + { + IsJitEnabled = true, + IsPatternMatchingEnabled = true, + AggressiveJitMode = true, + BakeInTracingInJitMode = true, + + PatternMatchingThreshold = 4, + JittingThreshold = 256, + }; + + CodeInfoRepository.ClearCache(); + } + + public static IEnumerable<(Type, byte[])> GePatBytecodesSamples() + { + + 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, 8]) + .JUMPI() + .INVALID() + .JUMPDEST() + .Done); + yield return (typeof(EmulatedStaticJump), Prepare.EvmCode + .PUSHx([0, 6]) + .JUMP() + .INVALID() + .JUMPDEST() + .Done); + yield return (typeof(MethodSelector), Prepare.EvmCode + .PushData(23) + .PushData(32) + .MSTORE() + .CALLVALUE() + .DUPx(1) + .PushData(32) + .MLOAD() + .SSTORE() + .Done); + yield return (typeof(IsContractCheck), Prepare.EvmCode + .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) + .PushData(1) + .SSTORE() + .ISZERO(UInt256.MaxValue) + .PushData(23) + .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 + .PushSingle(23) + .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(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.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) + .PushData(1) + .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) + .MLOAD(32) + .MLOAD(0) + .EQ() + .PushData(1) + .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) + .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.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) + .LT() + .PushData(1) + .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 + .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() + .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 + .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); + + 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) + .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 + .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); + + 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); + + yield return (Instruction.SHL, Prepare.EvmCode + .PushSingle(23) + .PushSingle(1) + .SHL() + .PushData(1) + .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) + .SHR() + .PushData(1) + .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(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) + .AND() + .PushData(1) + .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) + .OR() + .PushData(1) + .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) + .XOR() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SLT, Prepare.EvmCode + .PushSingle(17) + .PushData(23) + .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() + .Done, EvmExceptionType.None); + + 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() + .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) + .INVALID() + // this assumes that the code segment is jumping to another segment beyond it's boundaries + .Done, EvmExceptionType.None); + + yield return (Instruction.LOG0, Prepare.EvmCode + .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) + .PushData(TestItem.KeccakA.Bytes.ToArray()) + .LOGx(1, 0, 64) + .Done, EvmExceptionType.None); + + yield return (Instruction.LOG2, Prepare.EvmCode + .PushData(SampleHexData2.PadLeft(64, '0')) + .PushData(0) + .Op(Instruction.MSTORE) + .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(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(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 + .PushData(23) + .PushData(7) + .TSTORE() + .PushData(7) + .TLOAD() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.SSTORE | Instruction.SLOAD, Prepare.EvmCode + .PushData(23) + .PushData(7) + .SSTORE() + .PushData(7) + .SLOAD() + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODESIZE, Prepare.EvmCode + .EXTCODESIZE(Address.FromNumber(23)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODEHASH, Prepare.EvmCode + .EXTCODEHASH(Address.FromNumber(23)) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.EXTCODECOPY, Prepare.EvmCode + .EXTCODECOPY(Address.FromNumber(23), 0, 0, 32) + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.BALANCE, Prepare.EvmCode + .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 + .PUSHx() + .POP() + .Done, EvmExceptionType.None); + + yield return (Instruction.POP | Instruction.INVALID, Prepare.EvmCode + .POP() + .POP() + .POP() + .POP() + .Done, EvmExceptionType.StackUnderflow); + + + for (byte opcode = (byte)Instruction.DUP1; opcode <= (byte)Instruction.DUP16; opcode++) + { + int n = opcode - (byte)Instruction.DUP1 + 1; + var test = Prepare.EvmCode; + for (int i = 0; i < n; i++) + { + test.PushData(i); + } + test.Op((Instruction)opcode) + .PushData(1) + .SSTORE(); + + 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) + .PushData(1) + .SSTORE() + .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) + .PushData(1) + .SSTORE(); + + yield return ((Instruction)opcode, test.Done, EvmExceptionType.None); + } + + yield return (Instruction.SDIV, Prepare.EvmCode + .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 + .PushData(0) + .PushData(32) + .PushData(7) + .CODECOPY() + .MLOAD(0) + .PushData(1) + .SSTORE() + .Done, EvmExceptionType.None); + + yield return (Instruction.MULMOD, Prepare.EvmCode + .PushData(23) + .PushData(3) + .PushData(7) + .MULMOD() + .PushData(1) + .SSTORE() + .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() + .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 + .PushData(0) + .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 + .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); + } + + [Test] + public void All_Stateless_Opcodes_Are_Covered_in_JIT_Tests() + { + 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)}]"); + } + + [Test] + public void Pattern_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, TestItem.AddressA); + + IlAnalyzer.StartAnalysis(codeInfo, ILMode.PAT_MODE, config, NullLogger.Instance); + + codeInfo.IlInfo.Chunks.Count.Should().Be(2); + } + + + [Test] + public void 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, TestItem.AddressA); + + IlAnalyzer.StartAnalysis(codeInfo, IlInfo.ILMode.JIT_MODE, config, NullLogger.Instance); + + codeInfo.IlInfo.Segments.Count.Should().Be(2); + } + + [Test] + public void Execution_Swap_Happens_When_Pattern_Occurs() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + IsJitEnabled = false, + AggressiveJitMode = false, + AnalysisQueueMaxSize = 1, + }); + + 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 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 < 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 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() + { + 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, EvmExceptionType _) testcase) + { + TestBlockChain standardChain = new TestBlockChain(new VMConfig()); + var address = standardChain.InsertCode(testcase.bytecode); + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = int.MaxValue, + IsPatternMatchingEnabled = false, + BakeInTracingInJitMode = true, + JittingThreshold = 1, + AnalysisQueueMaxSize = 1, + IsJitEnabled = true + }); + enhancedChain.InsertCode(testcase.bytecode); + + GethTraceOptions tracerOptions = new GethTraceOptions + { + EnableMemory = true, + }; + + var tracer1 = new GethLikeTxMemoryTracer(tracerOptions); + var tracer2 = new GethLikeTxMemoryTracer(tracerOptions); + + var bytecode = Prepare.EvmCode + .Call(address, 750_000) + .STOP() + .Done; + + standardChain.Execute(bytecode, tracer1); + + enhancedChain.ForceRunAnalysis(address, ILMode.JIT_MODE); + + enhancedChain.Execute(bytecode, tracer2); + + 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(); + + if (testcase.opcode is not null) + { + Assert.That(enhancedHasIlvmTraces, Is.True); + Assert.That(normalHasIlvmTraces, Is.False); + } + 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 = 1, + IsPatternMatchingEnabled = true, + JittingThreshold = int.MaxValue, + AnalysisQueueMaxSize = 1, + IsJitEnabled = false + }); + enhancedChain.InsertCode(testcase.bytecode); + + var tracer1 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + var tracer2 = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + + var bytecode = + Prepare.EvmCode + .Call(address, 100000) + .STOP() + .Done; + + standardChain.Execute(bytecode, tracer1, (ForkActivation)10000000000); + + enhancedChain.ForceRunAnalysis(address, ILMode.PAT_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(); + + if (testcase.opcode is not null) + { + 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_On() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 1, + IsPatternMatchingEnabled = false, + JittingThreshold = 1, + AnalysisQueueMaxSize = 1, + IsJitEnabled = true, + AggressiveJitMode = true, + }); + + var aux = enhancedChain.InsertCode(Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done); + + Address main = enhancedChain.InsertCode( + 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); + + byte[] driver = + Prepare.EvmCode + .Call(main, 1_000_000) + .Done; + + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); + enhancedChain.ForceRunAnalysis(aux, ILMode.JIT_MODE); + + 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_({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(); + Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); + } + + [Test] + public void JIT_Mode_Segment_Has_Jump_Into_Another_Segment_Agressive_Mode_Off() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 2, + AnalysisQueueMaxSize = 1, + IsPatternMatchingEnabled = true, + JittingThreshold = 1, + IsJitEnabled = true, + AggressiveJitMode = false, + BakeInTracingInJitMode = true + }); + + var aux = enhancedChain.InsertCode(Prepare.EvmCode + .PushData(23) + .PushData(7) + .ADD() + .STOP().Done); + + Address main = enhancedChain.InsertCode( + 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); + + byte[] driver = + Prepare.EvmCode + .Call(main, 1_000_000) + .Done; + + 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); + 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..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_({main})[0..47]", + $"ILEVM_PRECOMPILED_({aux})[0..5]", + $"ILEVM_PRECOMPILED_({main})[49..60]", + $"ILEVM_PRECOMPILED_({main})[0..47]", + $"AbortDestinationPattern", + }; + + string[] actualTracePattern = traces.TakeLast(5).Select(tr => tr.SegmentID).ToArray(); + Assert.That(actualTracePattern, Is.EqualTo(desiredTracePattern)); + } + + [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() + { + TestBlockChain enhancedChain = new TestBlockChain(new VMConfig + { + PatternMatchingThreshold = 1, + IsPatternMatchingEnabled = false, + JittingThreshold = 1, + IsJitEnabled = true, + AnalysisQueueMaxSize = 1, + AggressiveJitMode = false + }); + + Address main = enhancedChain.InsertCode( + Prepare.EvmCode + .PUSHx() // PUSH0 + .POP() + .STOP() + .Done); + + byte[] driver = + Prepare.EvmCode + .Call(main, 1000) + .JUMPI(17) + .INVALID() + .JUMPDEST() + .STOP() + .Done; + + enhancedChain.ForceRunAnalysis(main, ILMode.JIT_MODE); + var tracer = new GethLikeTxMemoryTracer(GethTraceOptions.Default); + enhancedChain.Execute(driver, tracer, (ForkActivation?)(MainnetSpecProvider.ByzantiumBlockNumber, 0)); + var traces = tracer.BuildResult(); + + 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() + { + Instruction[] instructions = + System.Enum.GetValues() + .Where(opcode => !opcode.IsStateful()) + .ToArray(); + + + 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], [], config); + } + catch (NotSupportedException nse) + { + notYetImplemented.Add((instruction, nse)); + } + catch (Exception) + { + } + } + + 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() + { + 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, 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( + 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(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); + + 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( + 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(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)); + } + } + + [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( + 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(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)); + } + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs index 33c7eb093cc..3b7b05e2b5c 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmPooledMemoryTests.cs @@ -232,6 +232,10 @@ public class MyTracer : ITxTracer, IDisposable || IsTracingFees || IsTracingLogs; + bool IILVMTracer.IsTracingPredefinedPatterns => true; + + bool IILVMTracer.IsTracingCompiledSegments => true; + public string lastmemline; public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Hash256 stateRoot = null) @@ -389,4 +393,12 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) } public void Dispose() { } + + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) + { + } } diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index d1eb75997f7..6436859dffa 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -19,23 +19,25 @@ using Nethermind.State; using Nethermind.Trie.Pruning; using NUnit.Framework; +using Nethermind.Evm.Config; 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; - 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; @@ -66,9 +68,9 @@ public virtual void Setup() ITrieStore trieStore = new TrieStore(_stateDb, logManager); TestState = new WorldState(trieStore, codeDb, logManager); _ethereumEcdsa = new EthereumEcdsa(SpecProvider.ChainId); - 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); } @@ -154,9 +156,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/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/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index e1bb762bceb..33e8e898562 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -3,27 +3,62 @@ using System; using System.Threading; - +using Nethermind.Evm.CodeAnalysis.IL; +using System.Runtime.CompilerServices; using Nethermind.Evm.Precompiles; - +using Nethermind.Evm.Tracing; +using Nethermind.Core.Crypto; +using Nethermind.Evm.Config; +using Nethermind.Logging; +using ILMode = int; +using Nethermind.Core; +using Nethermind.Core.Extensions; namespace Nethermind.Evm.CodeAnalysis { public class CodeInfo : IThreadPoolWorkItem { + public Address? Address { get; init; } public ReadOnlyMemory MachineCode { get; } public IPrecompile? Precompile { get; set; } + + + // IL-EVM + private int _callCount; + + public void NoticeExecution(IVMConfig vmConfig, ILogger logger) + { + // IL-EVM info already created + 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 + ILMode mode = vmConfig.IsJitEnabled && _callCount == vmConfig.JittingThreshold + ? IlInfo.ILMode.JIT_MODE + : vmConfig.IsPatternMatchingEnabled && _callCount == vmConfig.PatternMatchingThreshold + ? IlInfo.ILMode.PAT_MODE + : IlInfo.ILMode.NO_ILVM; + + if (mode == IlInfo.ILMode.NO_ILVM || IlInfo.Mode.HasFlag(mode)) + return; + + IlAnalyzer.Enqueue(this, mode, vmConfig, logger); + + } 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(), null); - public CodeInfo(byte[] code) + public CodeInfo(byte[] code, Address source = null) { + Address = source; MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } - public CodeInfo(ReadOnlyMemory code) + public CodeInfo(ReadOnlyMemory code, Address source = null) { + Address = source; MachineCode = code; _analyzer = code.Length == 0 ? _emptyAnalyzer : new JumpDestinationAnalyzer(code); } @@ -31,8 +66,14 @@ public CodeInfo(ReadOnlyMemory code) public bool IsPrecompile => Precompile is not null; public bool IsEmpty => ReferenceEquals(_analyzer, _emptyAnalyzer) && !IsPrecompile; - public CodeInfo(IPrecompile precompile) + /// + /// Gets information whether this code info has IL-EVM optimizations ready. + /// + internal IlInfo? IlInfo { get; set; } = IlInfo.Empty; + + public CodeInfo(IPrecompile precompile, Address source) { + Address = source; Precompile = precompile; MachineCode = Array.Empty(); _analyzer = _emptyAnalyzer; 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..610b7bc9cb1 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILCompiler.cs @@ -0,0 +1,2567 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Evm.Config; +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.Runtime.Intrinsics; +using static Nethermind.Evm.IL.EmitExtensions; +using Label = Sigil.Label; + +namespace Nethermind.Evm.CodeAnalysis.IL; +internal class ILCompiler +{ + 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 TXTRACER_INDEX = 5; + private const int IMMEDIATES_DATA_INDEX = 6; + + 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, 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? + // Note(Ayman) : verify all endianness arguments and bytes + + Emit method = Emit.NewDynamicMethod(segmentName, doVerify: true, strictBranchVerification: true); + + ushort[] jumpdests = EmitSegmentBody(method, code, config.BakeInTracingInJitMode); + ExecuteSegment dynEmitedDelegate = method.CreateDelegate(); + return new SegmentExecutionCtx + { + PrecompiledSegment = dynEmitedDelegate, + Data = data, + JumpDestinations = jumpdests + }; + } + + private static ushort[] EmitSegmentBody(Emit method, OpcodeInfo[] code, bool bakeInTracerCalls) + { + using Local jmpDestination = method.DeclareLocal(typeof(int)); + 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)); + 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)); + + 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 lbool = method.DeclareLocal(typeof(bool)); + using Local byte8B = 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)); + + 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(); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Stack))); + method.Call(GetCastMethodInfo()); + method.StoreLocal(stack); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); + method.StoreLocal(head); + + // set gas to local + 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.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); + method.StoreLocal(programCounter); + + // if last ilvmstate was a jump + method.LoadLocal(programCounter); + method.LoadConstant(code[0].ProgramCounter); + method.CompareEqual(); + method.BranchIfFalse(isContinuation); + + 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++) + { + 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); + } + + + 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 (!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(op.Metadata.GasCost); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + 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) + { + 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) + { + case Instruction.JUMPDEST: + // we do nothing + break; + case Instruction.STOP: + { + method.LoadArgument(VMSTATE_INDEX); + method.LoadConstant(true); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ShouldStop))); + method.FakeBranch(ret); + } + break; + case Instruction.CHAINID: + { + method.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ChainId))); + method.Call(Word.SetULong0); + method.StackPush(head); + } + break; + case Instruction.NOT: + { + 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.StackLoadPrevious(stack, head, 1); + method.Call(refWordToRefByteMethod); + method.Duplicate(); + method.Call(readVector256Method); + method.Call(notVector256Method); + method.Call(writeVector256Method); + } + break; + case Instruction.JUMP: + { + // we jump into the jump table + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } + method.FakeBranch(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 + + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } + method.Branch(jumpTable); + + method.MarkLabel(noJump); + method.StackPop(head, 2); + } + break; + case Instruction.PUSH0: + { + method.CleanWord(stack, head); + method.StackPush(head); + } + 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: + {// we load the stack + method.CleanAndLoadWord(stack, head); + + // we load the span of bytes + method.LoadArgument(IMMEDIATES_DATA_INDEX); + 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); + break; + case Instruction.SUB: + 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, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.MOD: + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Mod), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel(); + + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); + il.BranchIfFalse(label); + + il.LoadConstant(0); + il.Call(ConvertionExplicit()); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(label); + }, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.SMOD: + 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 bIsNotZeroOrOneLabel = il.DefineLabel(); + + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.BranchIfFalse(bIsNotZeroOrOneLabel); + + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(bIsNotZeroOrOneLabel); + }, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.DIV: + EmitBinaryUInt256Method(method, uint256R, (stack, head), typeof(UInt256).GetMethod(nameof(UInt256.Divide), BindingFlags.Public | BindingFlags.Static)!, + (il, postInstructionLabel, locals) => + { + Label label = il.DefineLabel(); + + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); + il.BranchIfFalse(label); + + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(label); + }, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.SDIV: + 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 bIsNotZero = il.DefineLabel(); + Label bIsNotMinusOneLabel = il.DefineLabel(); + + il.LoadLocalAddress(locals[1]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZero), false, out _)); + il.BranchIfFalse(bIsNotZero); + + il.LoadField(typeof(UInt256).GetField(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(bIsNotZero); + + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + 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 | BindingFlags.NonPublic).GetMethod); + il.Call(typeof(UInt256).GetMethod("op_Equality", new[] { typeof(UInt256).MakeByRefType(), typeof(UInt256).MakeByRefType() })); + il.And(); + 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(bIsNotMinusOneLabel); + }, evmExceptionLabels, 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 cIsNotZeroLabel = il.DefineLabel(); + + il.LoadLocalAddress(locals[2]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.BranchIfFalse(cIsNotZeroLabel); + + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + 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 cIsNotZeroLabel = il.DefineLabel(); + + il.LoadLocalAddress(locals[2]); + il.Call(GetPropertyInfo(typeof(UInt256), nameof(UInt256.IsZeroOrOne), false, out _)); + il.BranchIfFalse(cIsNotZeroLabel); + + il.LoadField(GetFieldInfo(nameof(UInt256.Zero), BindingFlags.Static | BindingFlags.Public)); + il.StoreLocal(uint256R); + il.Branch(postInstructionLabel); + + il.MarkLabel(cIsNotZeroLabel); + }, evmExceptionLabels, uint256A, uint256B, uint256C); + break; + case Instruction.SHL: + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: true, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.SHR: + EmitShiftUInt256Method(method, uint256R, (stack, head), isLeft: false, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.SAR: + EmitShiftInt256Method(method, uint256R, (stack, head), evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.AND: + 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(Vector256).GetMethod(nameof(Vector256.BitwiseOr), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); + break; + case Instruction.XOR: + EmitBitwiseUInt256Method(method, uint256R, (stack, head), typeof(Vector256).GetMethod(nameof(Vector256.Xor), BindingFlags.Public | BindingFlags.Static)!, evmExceptionLabels); + 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(SPEC_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadLocal(uint256R); + method.Call(Word.SetUInt256); + + method.Branch(endOfExpImpl); + + method.MarkLabel(powerIsZero); + method.CleanAndLoadWord(stack, head); + method.LoadConstant(1); + method.Call(Word.SetUInt0); + method.Branch(endOfExpImpl); + + method.MarkLabel(baseIsOneOrZero); + method.CleanAndLoadWord(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); + break; + case Instruction.GT: + 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, 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, evmExceptionLabels, uint256A, uint256B); + break; + case Instruction.EQ: + { + 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.StackPop(head, 2); + + method.Call(operationUnegenerified); + method.StoreLocal(lbool); + + method.CleanAndLoadWord(stack, head); + method.LoadLocal(lbool); + method.Convert(); + method.Call(Word.SetUInt0); + method.StackPush(head); + } + break; + case Instruction.ISZERO: + {// we load the stack + method.StackLoadPrevious(stack, head, 1); + method.Duplicate(); + method.Duplicate(); + method.Call(Word.GetIsZero); + method.StoreLocal(lbool); + method.Call(Word.SetToZero); + method.LoadLocal(lbool); + method.Call(Word.SetByte0); + } + break; + case Instruction.POP: + { + method.StackPop(head); + } + 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.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: + 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: + { + 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 + 1); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + + method.StackLoadPrevious(stack, head, count + 1); + method.LoadLocalAddress(uint256R); + method.LoadObject(typeof(Word)); + method.StoreObject(typeof(Word)); + } + break; + case Instruction.CODESIZE: + { + var lastOpcode = code[^1]; + method.CleanAndLoadWord(stack, head); + method.LoadConstant(lastOpcode.ProgramCounter + lastOpcode.Metadata.AdditionalBytes + 1); + method.Call(Word.SetInt0); + method.StackPush(head); + } + break; + case Instruction.PC: + { + method.CleanAndLoadWord(stack, head); + method.LoadConstant((uint)op.ProgramCounter); + method.Call(Word.SetUInt0); + method.StackPush(head); + } + break; + case Instruction.COINBASE: + { + 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 _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasBeneficiary), false, out _)); + method.Call(Word.SetAddress); + method.StackPush(head); + } + break; + case Instruction.TIMESTAMP: + { + 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 _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.Timestamp), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } + break; + case Instruction.NUMBER: + { + 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 _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.Number), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } + break; + case Instruction.GASLIMIT: + { + 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 _)); + method.Call(GetPropertyInfo(nameof(BlockHeader.GasLimit), false, out _)); + method.Call(Word.SetULong0); + method.StackPush(head); + } + break; + case Instruction.CALLER: + { + method.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + 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.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 _)); + method.Call(Word.SetAddress); + method.StackPush(head); + } + break; + case Instruction.CALLVALUE: + { + method.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + 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.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 _)); + 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.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(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + + method.LoadArgument(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localZeroPaddedSpan); + method.CallVirtual(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.CleanAndLoadWord(stack, head); + + method.LoadArgument(VMSTATE_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadArgument(VMSTATE_INDEX); + 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.CleanAndLoadWord(stack, head); + + method.LoadArgument(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(Word.Size); + method.Call(ConvertionExplicit()); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + 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.Call(ConvertionImplicit(typeof(Span), 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.Call(Word.GetByte0); + method.StoreLocal(byte8A); + method.StackPop(head, 2); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadConstant(1); + method.Call(ConvertionExplicit()); + method.StoreLocal(uint256C); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadLocal(byte8A); + + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.LoadSpan), [typeof(UInt256).MakeByRefType()])); + method.Call(ConvertionImplicit(typeof(Span), typeof(Span))); + method.StoreLocal(localReadonOnlySpan); + + method.CleanAndLoadWord(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.LoadLocalAddress(lbool); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling), [typeof(UInt256).MakeByRefType(), typeof(bool).MakeByRefType()])); + method.LoadConstant(GasCostOf.VeryLow); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.Memory))); + method.LoadLocalAddress(uint256A); + method.LoadArgument(VMSTATE_INDEX); + 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: + { + 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.LoadLocal(gasAvailable); + method.LoadLocalAddress(uint256B); + method.LoadLocalAddress(lbool); + method.Call(typeof(EvmPooledMemory).GetMethod(nameof(EvmPooledMemory.Div32Ceiling), [typeof(UInt256).MakeByRefType(), typeof(bool).MakeByRefType()])); + method.LoadConstant(GasCostOf.Sha3Word); + method.Multiply(); + method.Subtract(); + method.Duplicate(); + method.StoreLocal(gasAvailable); + method.LoadConstant((long)0); + method.BranchIfLess(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + + 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.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: + {// 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); + 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.LoadLocalAddress(localReadonOnlySpan); + method.LoadLocal(uint32A); + method.Call(typeof(ReadOnlySpan).GetMethod("get_Item")); + method.LoadIndirect(); + method.Convert(); + method.StoreLocal(uint32A); + + method.CleanAndLoadWord(stack, head); + method.LoadLocal(uint32A); + method.Call(Word.SetUInt0); + method.StackPush(head); + method.Branch(endOfInstructionImpl); + + method.MarkLabel(pushZeroLabel); + method.CleanWord(stack, head); + method.StackPush(head); + + method.MarkLabel(endOfInstructionImpl); + } + break; + case Instruction.CODECOPY: + { + 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.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(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadLocal(gasAvailable); + method.Call(Word.SetULong0); + + method.StackPush(head); + } + break; + case Instruction.RETURNDATASIZE: + { + 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: + { + 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(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) })); + method.Or(); + method.BranchIfTrue(evmExceptionLabels[EvmExceptionType.AccessViolation]); + + + 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]); + + // Note : check if c + b > returnData.Size + + method.LoadLocalAddress(uint256C); + method.Call(typeof(UInt256).GetProperty(nameof(UInt256.IsZero)).GetMethod!); + method.BranchIfTrue(endOfOpcode); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256B); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ReturnBuffer))); + 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.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.FakeBranch(ret); + } + break; + case Instruction.BASEFEE: + { + 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 _)); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.BaseFeePerGas), false, out _)); + method.Call(Word.SetUInt256); + method.StackPush(head); + } + break; + case Instruction.BLOBBASEFEE: + { + using Local uint256Nullable = method.DeclareLocal(typeof(UInt256?)); + 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 _)); + 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(); + Label endOfOpcode = method.DefineLabel(); + 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 _)); + method.Duplicate(); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.IsPostMerge), false, out _)); + method.BranchIfFalse(isPostMergeBranch); + method.Call(GetPropertyInfo(typeof(BlockHeader), nameof(BlockHeader.Random), false, out _)); + 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.StackPush(head); + } + break; + case Instruction.BLOBHASH: + { + 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(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.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.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.CleanAndLoadWord(stack, head); + method.LoadLocal(localArray); + method.Call(Word.SetArray); + method.Branch(endOfOpcode); + + method.MarkLabel(blobVersionedHashNotFound); + method.CleanWord(stack, head); + + 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(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); + 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(ConvertionImplicit(typeof(Span), typeof(Span))); + method.StoreLocal(localReadonOnlySpan); + method.Branch(pushToStackRegion); + // equal to null + + method.MarkLabel(blockHashReturnedNull); + + method.LoadField(GetFieldInfo(typeof(VirtualMachine), nameof(VirtualMachine.BytesZero32))); + method.Call(ConvertionImplicit(typeof(ReadOnlySpan), typeof(byte[]))); + method.StoreLocal(localReadonOnlySpan); + + method.MarkLabel(pushToStackRegion); + method.CleanAndLoadWord(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(); + Label argumentGt32 = method.DefineLabel(); + + method.StackLoadPrevious(stack, head, 1); + method.Call(Word.GetUInt0); + method.StoreLocal(uint32A); + + 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); + method.StackPop(head, 1); + } + break; + case Instruction.LOG0: + case Instruction.LOG1: + case Instruction.LOG2: + case Instruction.LOG3: + 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; + case Instruction.TSTORE: + { + 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]); + + 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(VMSTATE_INDEX); + 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(WORLD_STATE_INDEX); + 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(VMSTATE_INDEX); + 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(WORLD_STATE_INDEX); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.GetTransientState), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanAndLoadWord(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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(localReadonOnlySpan); + method.LoadArgument(SPEC_INDEX); + 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(VMSTATE_INDEX); + 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(SPEC_INDEX); + 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(VMSTATE_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(storageCell); + method.LoadConstant((int)VirtualMachine.StorageAccessType.SLOAD); + method.LoadArgument(SPEC_INDEX); + 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(WORLD_STATE_INDEX); + method.LoadLocalAddress(storageCell); + method.CallVirtual(typeof(IWorldState).GetMethod(nameof(IWorldState.Get), [typeof(StorageCell).MakeByRefType()])); + method.StoreLocal(localReadonOnlySpan); + + method.CleanAndLoadWord(stack, head); + method.LoadLocal(localReadonOnlySpan); + method.Call(Word.SetSpan); + method.StackPush(head); + } + break; + case Instruction.EXTCODESIZE: + { + method.LoadLocal(gasAvailable); + method.LoadArgument(SPEC_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadConstant(true); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadArgument(SPEC_INDEX); + 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.CleanAndLoadWord(stack, head); + + method.LoadArgument(CODE_INFO_REPOSITORY_INDEX); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadLocal(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 _)); + 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: + { + 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); + 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.StackPop(head, 4); + + method.LoadLocalAddress(gasAvailable); + method.LoadArgument(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadConstant(true); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadArgument(SPEC_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocalAddress(gasAvailable); + method.LoadLocalAddress(uint256A); + method.LoadLocalAddress(uint256C); + method.Call(typeof(VirtualMachine).GetMethod(nameof(VirtualMachine.UpdateMemoryCost))); + method.BranchIfFalse(evmExceptionLabels[EvmExceptionType.OutOfGas]); + + method.LoadArgument(CODE_INFO_REPOSITORY_INDEX); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadLocal(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 _)); + + 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(VMSTATE_INDEX); + 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: + { + Label endOfOpcode = method.DefineLabel(); + + method.LoadLocal(gasAvailable); + method.LoadArgument(SPEC_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadConstant(true); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadArgument(SPEC_INDEX); + 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(WORLD_STATE_INDEX); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.AccountExists))); + method.LoadConstant(false); + method.CompareEqual(); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadLocal(address); + method.CallVirtual(typeof(IReadOnlyStateProvider).GetMethod(nameof(IWorldState.IsDeadAccount))); + method.Or(); + method.BranchIfTrue(endOfOpcode); + + method.CleanAndLoadWord(stack, head); + method.LoadArgument(WORLD_STATE_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadArgument(WORLD_STATE_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))); + method.Call(Word.SetUInt256); + method.StackPush(head); + } + break; + case Instruction.BALANCE: + { + method.LoadLocal(gasAvailable); + method.LoadArgument(SPEC_INDEX); + 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(VMSTATE_INDEX); + method.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + method.LoadLocal(address); + method.LoadConstant(false); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadArgument(SPEC_INDEX); + 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.CleanAndLoadWord(stack, head); + method.LoadArgument(WORLD_STATE_INDEX); + method.LoadLocal(address); + method.CallVirtual(typeof(IAccountStateProvider).GetMethod(nameof(IWorldState.GetBalance))); + method.Call(Word.SetUInt256); + method.StackPush(head); + } + break; + default: + { + method.FakeBranch(evmExceptionLabels[EvmExceptionType.BadInstruction]); + } + break; + } + + if (bakeInTracerCalls) + { + EmitCallToEndInstructionTrace(method, gasAvailable); + } + } + + Label skipProgramCounterSetting = method.DefineLabel(); + Local isEphemeralJump = method.DeclareLocal(); + // prepare ILEvmState + // check if returnState is null + method.MarkLabel(ret); + // we get stack size + method.LoadArgument(VMSTATE_INDEX); + method.LoadLocal(head); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.StackHead))); + + // set stack + 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.LoadLocal(gasAvailable); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.GasAvailable))); + + // set program counter + method.LoadLocal(isEphemeralJump); + method.BranchIfTrue(skipProgramCounterSetting); + + method.LoadArgument(VMSTATE_INDEX); + method.LoadLocal(programCounter); + method.LoadConstant(1); + method.Add(); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.ProgramCounter))); + + method.MarkLabel(skipProgramCounterSetting); + + + // set exception + method.LoadArgument(VMSTATE_INDEX); + method.LoadConstant((int)EvmExceptionType.None); + method.StoreField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmException))); + + // 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); + method.Call(Word.GetInt0); + method.StoreLocal(jmpDestination); + method.StackPop(head); + + method.StackPop(head, consumeJumpCondition); + method.LoadConstant(0); + method.StoreLocal(consumeJumpCondition); + + //check if jump crosses segment boundaies + int maxJump = code[^1].ProgramCounter + code[^1].Metadata.AdditionalBytes; + int minJump = code[0].ProgramCounter; + + // if (jumpDest <= maxJump) + method.LoadLocal(jmpDestination); + method.LoadConstant(maxJump); + method.BranchIfGreater(jumpIsNotLocal); + + // if (jumpDest >= minJump) + method.LoadLocal(jmpDestination); + method.LoadConstant(minJump); + method.BranchIfLess(jumpIsNotLocal); + + method.Branch(jumpIsLocal); + + method.MarkLabel(jumpIsNotLocal); + method.LoadArgument(VMSTATE_INDEX); + 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); + + + // if (jumpDest > uint.MaxValue) + method.LoadConstant(uint.MaxValue); + method.LoadLocal(jmpDestination); + // goto invalid address + method.BranchIfGreater(evmExceptionLabels[EvmExceptionType.InvalidJumpDestination]); + // else + + 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 < length; i++) + { + method.MarkLabel(jumps[i]); + // for each destination matching the bit mask emit check for the equality + foreach (ushort dest in jumpDestinations.Keys.Where(dest => (dest & bitMask) == i)) + { + method.LoadLocal(jmpDestination); + method.LoadConstant(dest); + method.Duplicate(); + method.StoreLocal(uint32A); + 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.InvalidJumpDestination]); + } + + foreach (var kvp in evmExceptionLabels) + { + method.MarkLabel(kvp.Value); + 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); + } + + // return + method.MarkLabel(exit); + method.Return(); + + return jumpDestinations.Keys.ToArray(); + } + + 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); + 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)); + + method.MarkLabel(skipTracing); + } + + 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(); + Label endOfOpcode = il.DefineLabel(); + + // 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.FullSize); + il.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + il.BranchIfFalse(skipPop); + + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(locals[1]); + il.LoadLocalAddress(locals[1]); + + il.LoadLocal(shiftBit); + + il.LoadLocalAddress(uint256R); + + il.Call(shiftOp); + + 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.CleanWord(stack.span, stack.idx); + il.StackPush(stack.idx, 1); + + il.MarkLabel(endOfOpcode); + } + + private static void EmitShiftInt256Method(Emit il, Local uint256R, (Local span, Local idx) stack, Dictionary exceptions, params Local[] locals) + { + Label aBiggerOrEqThan256 = 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 + 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); + + il.LoadLocalAddress(locals[0]); + il.LoadConstant(Word.FullSize); + il.Call(typeof(UInt256).GetMethod("op_LessThan", new[] { typeof(UInt256).MakeByRefType(), typeof(int) })); + il.BranchIfFalse(aBiggerOrEqThan256); + + using Local shiftBits = il.DeclareLocal(); + + + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + 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.CleanAndLoadWord(stack.span, stack.idx); + il.LoadLocal(uint256R); + il.Call(Word.SetUInt256); + il.Branch(endOfOpcode); + + il.MarkLabel(aBiggerOrEqThan256); + + 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.Branch(endOfOpcode); + + // sign + il.MarkLabel(signIsNeg); + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadFieldAddress(GetFieldInfo(typeof(Int256.Int256), nameof(Int256.Int256.MinusOne))); + il.Call(GetAsMethodInfo()); + il.LoadObject(); + il.Call(Word.SetUInt256); + 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) + { + // 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 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); + 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.Call(operation); + + // convert to conv_i + il.Convert(); + il.Call(ConvertionExplicit()); + il.StoreLocal(uint256R); + + // push the result to the stack + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + 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) + { + Label endOpcodeHandling = il.DefineLabel(); + Label pushOnehandling = il.DefineLabel(); + // 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.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[1]); + il.Call(GetAsMethodInfo()); + il.LoadObject(); + il.Call(operation); + il.LoadConstant(0); + if (isGreaterThan) + { + il.BranchIfGreater(pushOnehandling); + } + else + { + il.BranchIfLess(pushOnehandling); + } + + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.Zero))); + il.Branch(endOpcodeHandling); + + il.MarkLabel(pushOnehandling); + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadField(GetFieldInfo(typeof(UInt256), nameof(UInt256.One))); + il.Branch(endOpcodeHandling); + + // push the result to the stack + il.MarkLabel(endOpcodeHandling); + il.Call(Word.SetUInt256); + 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) + { + Label label = il.DefineLabel(); + + // 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[0]); + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(uint256R); + il.Call(operation); + + // skip the main handling + il.MarkLabel(label); + + // push the result to the stack + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + 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) + { + Label label = il.DefineLabel(); + + // 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[0]); + il.Call(GetAsMethodInfo()); + il.LoadLocalAddress(locals[1]); + 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.CleanAndLoadWord(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + 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) + { + Label label = il.DefineLabel(); + + // 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[0]); + il.LoadLocalAddress(locals[1]); + il.LoadLocalAddress(locals[2]); + il.LoadLocalAddress(uint256R); + il.Call(operation); + + // skip the main handling + il.MarkLabel(label); + + // push the result to the stack + il.CleanAndLoadWord(stack.span, stack.idx); + il.LoadLocal(uint256R); // stack: word*, uint256 + il.Call(Word.SetUInt256); + il.StackPush(stack.idx); + } + + + private static void EmitLogMethod( + Emit il, + (Local span, Local idx) stack, + sbyte topicsCount, + Dictionary exceptions, + Local uint256Position, Local uint256Length, Local int64A, Local gasAvailable, Local hash256, Local localReadOnlyMemory + ) + { + using Local logEntry = il.DeclareLocal(); + + il.StackLoadPrevious(stack.span, stack.idx, 1); + il.Call(Word.GetUInt256); + il.StoreLocal(uint256Position); // position + il.StackLoadPrevious(stack.span, stack.idx, 2); + il.Call(Word.GetUInt256); + il.StoreLocal(uint256Length); // length + il.StackPop(stack.idx, 2); + // UpdateMemoryCost + il.LoadArgument(VMSTATE_INDEX); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + il.LoadLocalAddress(gasAvailable); + il.LoadLocalAddress(uint256Position); // position + il.LoadLocalAddress(uint256Length); // length + il.Call( + typeof(VirtualMachine).GetMethod( + nameof(VirtualMachine.UpdateMemoryCost) + ) + ); + il.BranchIfFalse(exceptions[EvmExceptionType.OutOfGas]); + + // update gasAvailable + il.LoadLocal(gasAvailable); + il.LoadConstant(topicsCount * GasCostOf.LogTopic); + il.Convert(); + 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(); + il.Add(); + il.Subtract(); + il.Duplicate(); + il.StoreLocal(gasAvailable); // gasAvailable -= gasCost + il.LoadConstant((ulong)0); + il.BranchIfLess(exceptions[EvmExceptionType.OutOfGas]); + + 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(); + for (int i = 0; i < topicsCount; i++) + { + il.Duplicate(); + il.LoadConstant(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 + il.NewObject(typeof(LogEntry), typeof(Address), typeof(byte[]), typeof(Hash256[])); + il.StoreLocal(logEntry); + il.StackPop(stack.idx, topicsCount); + + il.LoadArgument(0); + il.LoadField(GetFieldInfo(typeof(ILEvmState), nameof(ILEvmState.EvmState))); + 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( + typeof(ICollection).GetMethod(nameof(ICollection.Add)) + ); + } + + 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(); + int costStart = code[0].ProgramCounter; + long coststack = 0; + + for (int pc = 0; pc < code.Length; pc++) + { + + OpcodeInfo op = code[pc]; + switch (op.Operation) + { + case Instruction.JUMPDEST: + costs[costStart] = coststack; // remember the stack chain of opcodes + 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: + coststack += op.Metadata.GasCost; + costs[costStart] = coststack; // remember the stack chain of opcodes + costStart = op.ProgramCounter + 1; // start with the next again + coststack = 0; + break; + default: + coststack += op.Metadata.GasCost; + break; + } + } + + if (coststack > 0) + { + 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 new file mode 100644 index 00000000000..9aaa5a8efd1 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILEvmState.cs @@ -0,0 +1,76 @@ +// 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; +using System.Linq; +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 ulong ChainId; + + public ReadOnlyMemory MachineCode; + public EvmState EvmState; + // static arguments + // * 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 + public ushort ProgramCounter; + public long GasAvailable; + // in case STOP is executed + public bool ShouldStop; + public bool ShouldRevert; + public bool ShouldReturn; + public bool ShouldJump; + + // * vmState.DataStackHead : + public int StackHead; + // * vmState.DataStack : + public Span Stack; + + // * vmState.Memory : + public ref EvmPooledMemory Memory; + + 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; + // 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 = 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; + } +} 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..c719423644c --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/ILExtensions.cs @@ -0,0 +1,275 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Db; +using Nethermind.Evm.CodeAnalysis.IL; +using Nethermind.Int256; +using Org.BouncyCastle.Tls; +using Sigil; +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; + +namespace Nethermind.Evm.IL; + +/// +/// Extensions for . +/// +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); + 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 + { + 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 + { + 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 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 flags is null ? TypeInstance.GetField(name) : TypeInstance.GetField(name, flags.Value); + } + 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 = flags is null ? typeInstance.GetProperty(name) : typeInstance.GetProperty(name, flags.Value); + 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); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); + } + + 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, Local idx) + { + il.LoadLocalAddress(local); + il.LoadLocal(idx); + il.Call(typeof(Span).GetMethod("get_Item")); + + il.InitializeObject(typeof(Word)); + } + + + public static void CleanAndLoadWord(this Emit il, Local local, 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) + { + il.Load(local, idx); + il.Call(typeof(Word).GetMethod(nameof(Word.SetToZero))); + } + + /// + /// Advances the stack one word up. + /// + public static void StackPush(this Emit il, Local idx, int count = 1) + { + il.LoadLocal(idx); + il.LoadConstant(count); + il.Add(); + 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. + /// + public static void StackPop(this Emit il, Local idx, int count = 1) + { + il.LoadLocal(idx); + il.LoadConstant(count); + il.Subtract(); + 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. + /// + public static void StackPop(this Emit il, Local local, Local count) + { + il.LoadLocal(local); + il.LoadLocal(count); + il.Subtract(); + il.StoreLocal(local); + } + + 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); + + // if cond + il.LoadLocal(cond); + il.BranchIfFalse(end); + + // emit body of loop + action(il, cond); + + // jump to start of the loop + il.Branch(start); + + // end of the loop + il.MarkLabel(end); + } + + public static void ForBranch(this Emit il, Local count, Action, Local> action) + { + var start = il.DefineLabel(); + var end = il.DefineLabel(); + + // 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.BranchIfEqual(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); + } + + /// + /// Loads the previous EVM stack value on top of .NET stack. + /// + public static void StackLoadPrevious(this Emit il, Local src, Local idx, int count = 1) + { + 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) + { + il.LoadConstant(value.Length); + il.NewArray(); + + // get methodInfo of AsSpan from int[] it is a public instance method + + for (int i = 0; i < value.Length; i++) + { + il.Duplicate(); + il.LoadConstant(i); + il.LoadConstant(value[i]); + il.StoreElement(); + } + + 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 new file mode 100644 index 00000000000..0bff32f6378 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlAnalyzer.cs @@ -0,0 +1,218 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Evm.Config; +using Nethermind.Evm.Tracing; +using Nethermind.Int256; +using Nethermind.Logging; +using System; +using System.Collections.Concurrent; +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 static Org.BouncyCastle.Crypto.Engines.SM2Engine; +using ILMode = int; + +namespace Nethermind.Evm.CodeAnalysis.IL; + +/// +/// Provides +/// +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 = _queue.Count; + 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) + { + lock (_patterns) + { + _patterns[handler.GetType()] = handler; + } + } + internal static void AddPattern() where T : InstructionChunk + { + var handler = Activator.CreateInstance(); + lock (_patterns) + { + _patterns[typeof(T)] = handler; + } + } + internal static T GetPatternHandler() where T : InstructionChunk + { + lock (_patterns) + { + 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); + } + } + + 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]; + int? argsIndex = null; + ushort pc = i; + if (opcode is > Instruction.PUSH0 and <= Instruction.PUSH32) + { + ushort immediatesCount = opcode - Instruction.PUSH0; + data.Add(machineCode.SliceWithZeroPadding((UInt256)i + 1, immediatesCount).ToArray()); + argsIndex = data.Count - 1; + i += immediatesCount; + } + opcodes[j] = new OpcodeInfo(pc, opcode, argsIndex); + } + return (opcodes[..j], data.ToArray()); + } + + /// + /// For now, return null always to default to EVM. + /// + 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) + { + if (codeData.Item1.Length == 0) + { + return; + } + + string GenerateName(Range segmentRange) => $"ILEVM_PRECOMPILED_({codeInfo.Address.ToString()})[{segmentRange.Start}..{segmentRange.End}]"; + + int[] statefulOpcodeindex = new int[1 + (codeData.Item1.Length / 5)]; + + int j = 0; + for (int i = 0; i < codeData.Item1.Length; i++) + { + if (codeData.Item1[i].Operation.IsStateful()) + { + statefulOpcodeindex[j++] = i; + } + } + + long segmentAvgSize = 0; + 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]; + 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)); + + segmentAvgSize += segment.Length; + + if(segment.Length <= 1) + { + continue; + } + + var segmentExecutionCtx = CompileSegment(segmentName, segment, codeData.Item2, vmConfig); + if (vmConfig.AggressiveJitMode) + { + ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); + for (int k = 0; k < segmentExecutionCtx.JumpDestinations.Length; k++) + { + ilinfo.Segments.GetOrAdd(segmentExecutionCtx.JumpDestinations[k], segmentExecutionCtx); + } + } + else + { + ilinfo.Segments.GetOrAdd(segment[0].ProgramCounter, segmentExecutionCtx); + } + } + + Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.JIT_MODE); + } + + static void CheckPatterns(ReadOnlyMemory machineCode, IlInfo ilinfo) + { + var (strippedBytecode, data) = StripByteCode(machineCode.Span); + foreach ((Type _, InstructionChunk chunkHandler) in _patterns) + { + for (int i = 0; i < strippedBytecode.Length - chunkHandler.Pattern.Length + 1; i++) + { + bool found = true; + for (int j = 0; j < chunkHandler.Pattern.Length && found; j++) + { + found = ((byte)strippedBytecode[i + j].Operation == chunkHandler.Pattern[j]); + } + + if (found) + { + ilinfo.Chunks.GetOrAdd((ushort)strippedBytecode[i].ProgramCounter, chunkHandler); + i += chunkHandler.Pattern.Length - 1; + } + } + } + + Interlocked.Or(ref ilinfo.Mode, IlInfo.ILMode.PAT_MODE); + } + + switch (mode) + { + case IlInfo.ILMode.PAT_MODE: + 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.Address}"); + SegmentCode(codeInfo, StripByteCode(machineCode.Span), codeInfo.IlInfo, vmConfig); + break; + } + } +} 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..4fad6151962 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/IlInfo.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Numerics; +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; +using Nethermind.State; +using NonBlocking; +using static Nethermind.Evm.CodeAnalysis.IL.ILCompiler; +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm.CodeAnalysis.IL; +/// +/// Represents the IL-EVM information about the contract. +/// +internal class IlInfo +{ + internal struct ILChunkExecutionResult + { + public readonly bool ShouldFail => ExceptionType != EvmExceptionType.None; + public bool ShouldJump; + public bool ShouldStop; + public bool ShouldRevert; + 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 + { + public const int NO_ILVM = 0; + public const int PAT_MODE = 1; + public const int JIT_MODE = 2; + } + + /// + /// Represents an information about IL-EVM being not able to optimize the given . + /// + public static IlInfo Empty => new(); + + /// + /// 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 && Mode == ILMode.NO_ILVM; + /// + /// No overrides. + /// + private IlInfo() + { + Chunks = new ConcurrentDictionary(); + Segments = new ConcurrentDictionary(); + } + + public IlInfo(ConcurrentDictionary mappedOpcodes, ConcurrentDictionary segments) + { + Chunks = mappedOpcodes; + Segments = segments; + } + + // assumes small number of ILed + public ConcurrentDictionary Chunks { get; } = new(); + public ConcurrentDictionary Segments { get; } = new(); + + 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; + if (programCounter > ushort.MaxValue || this.IsEmpty) + return false; + + if (Mode.HasFlag(ILMode.JIT_MODE) && Segments.TryGetValue((ushort)programCounter, out SegmentExecutionCtx ctx)) + { + 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, tracer, ctx.Data); + + gasAvailable = ilvmState.GasAvailable; + programCounter = ilvmState.ProgramCounter; + 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)) + 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); + + if (typeof(TTracingInstructions) == typeof(IsTracing)) + tracer.ReportOperationRemainingGas(gasAvailable); + + result = executionResult; + return true; + } + return false; + } + + 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/InstructionChunk.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs new file mode 100644 index 00000000000..7501abfe6c7 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/InstructionChunk.cs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Specs; +using Nethermind.State; +using static Nethermind.Evm.CodeAnalysis.IL.IlInfo; +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. +/// +/// +internal interface InstructionChunk +{ + string Name { get; } + byte[] Pattern { get; } + 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/PredefinedPatterns.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs new file mode 100644 index 00000000000..a45c9e31d61 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/PredefinedPatterns.cs @@ -0,0 +1,607 @@ +// 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; +using Nethermind.Evm.Tracing; + +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(vmState, ref gasAvailable, location, 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.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; + + Address address = stack.PopAddress(); + + if (!VirtualMachine.ChargeAccountAccessGas(ref gasAvailable, vmState, address, false, worldState, spec, NullTxTracer.Instance, true)) + result.ExceptionType = EvmExceptionType.OutOfGas; + + int contractCodeSize = codeInfoRepository.GetCachedCodeInfo(worldState, address, spec).MachineCode.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; + } + } +} +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; + + } +} 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..f8f4d8d7111 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/IL/Word.cs @@ -0,0 +1,345 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; +using Nethermind.Trie; +using Newtonsoft.Json.Linq; +using Org.BouncyCastle.Utilities; +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +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; + public const int FullSize = 256; + + [FieldOffset(0)] public unsafe fixed byte _buffer[Size]; + + [FieldOffset(Size - sizeof(byte))] + public byte _uByte0; + + [FieldOffset(Size - sizeof(int))] + private int _sInt0; + + [FieldOffset(Size - sizeof(int))] + private uint _uInt0; + + [FieldOffset(Size - 2 * sizeof(int))] + private uint _uInt1; + + [FieldOffset(Size - 1 * sizeof(ulong))] + private ulong _ulong0; + + [FieldOffset(Size - 2 * sizeof(ulong))] + private ulong _ulong1; + + [FieldOffset(Size - 3 * sizeof(ulong))] + private ulong _ulong2; + + [FieldOffset(Size - 4 * sizeof(ulong))] + private ulong _ulong3; + + public bool IsZero => (_ulong0 | _ulong1 | _ulong2 | _ulong3) == 0; + public void ToZero() + { + _ulong0 = 0; _ulong1 = 0; + _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 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); + } + } + } + + public unsafe ValueHash256 Keccak + { + get + { + fixed (byte* ptr = _buffer) + { + return new ValueHash256(new Span(ptr, 32)); + } + } + set + { + ReadOnlySpan buffer = value.Bytes; + fixed (byte* src = buffer, dest = _buffer) + { + Buffer.MemoryCopy(src, dest, 32, 32); + } + } + } + + public unsafe Address Address + { + get + { + byte[] buffer = new byte[20]; + for (int i = 0; i < 20; i++) + { + buffer[i] = _buffer[12 + i]; + } + + return new Address(buffer); + } + set + { + byte[] buffer = value.Bytes; + for (int i = 12; i < 32; i++) + { + _buffer[i] = buffer[i - 12]; + } + } + } + + 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 uint UInt0 + { + get + { + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_uInt0); + } + else + { + return _uInt0; + } + } + set + { + if (BitConverter.IsLittleEndian) + { + _uInt0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _uInt0 = value; + } + } + } + + 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 + { + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_sInt0); + } + else + { + return _sInt0; + } + } + set + { + if (BitConverter.IsLittleEndian) + { + _sInt0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _sInt0 = value; + } + } + } + + public ulong ULong0 + { + get + { + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(_ulong0); + } + else + { + return _ulong0; + } + } + set + { + if (BitConverter.IsLittleEndian) + { + _ulong0 = BinaryPrimitives.ReverseEndianness(value); + } + else + { + _ulong0 = value; + } + } + } + + + public unsafe long LeadingZeros + { + get + { + fixed (byte* ptr = _buffer) + { + byte* end = ptr + 32; + byte* current = ptr; + while (current < end && *current == 0) + { + current++; + } + + return current - ptr; + } + } + } + + public static readonly MethodInfo LeadingZeroProp = typeof(Word).GetProperty(nameof(LeadingZeros))!.GetMethod; + + 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; + + 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 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))!; + + 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; + + 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; + + public static explicit operator Word(Span span) + { + unsafe + { + var result = new Word(); + result.Span = span; + return result; + } + } + +} diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 939352a20ac..6e82b908ac1 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); + 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); @@ -143,7 +138,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)); } /// @@ -186,11 +181,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, @@ -252,6 +252,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/Config/IVMConfig.cs b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs new file mode 100644 index 00000000000..ac1f97d9ab2 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Config/IVMConfig.cs @@ -0,0 +1,50 @@ +// 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 JittingThreshold { get; set; } + + [ConfigItem( + Description = "Threshold for enabling n-gram pattern optimizations", + DefaultValue = "32")] + public int PatternMatchingThreshold { get; set; } + + [ConfigItem( + Description = "Activates or Deactivates aggressive JIT optimizations", + DefaultValue = "false")] + public bool AggressiveJitMode { get; set; } + + [ConfigItem( + Description = "Activates or Deactivates traces in JIT optimizations", + 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 new file mode 100644 index 00000000000..8c9bc74b132 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Config/VMConfig.cs @@ -0,0 +1,21 @@ +// 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 class VMConfig : IVMConfig +{ + public bool IsPatternMatchingEnabled { get; set; } = false; + public bool IsJitEnabled { get; set; } = false; + public int PatternMatchingThreshold { get; set; } = 32; + public int JittingThreshold { get; set; } = 128; + public bool AggressiveJitMode { get; set; } = false; + public bool BakeInTracingInJitMode { get; set; } = false; + public int AnalysisQueueMaxSize { get; set; } = 8; +} 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/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 96fcf3f3595..f2aed12e049 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,7 +1,11 @@ // 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.Diagnostics.CodeAnalysis; +using System.Linq; using FastEnumUtility; using Nethermind.Core.Specs; @@ -172,9 +176,307 @@ public enum Instruction : byte INVALID = 0xfe, SELFDESTRUCT = 0xff, } + 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. + /// + 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 Dictionary() + { + [Instruction.POP] = new(GasCostOf.Base, 0, 1, 0), + [Instruction.STOP] = new(FREE, 0, 0, 0), + [Instruction.PC] = new(GasCostOf.Base, 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), + [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.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, 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), + [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.KECCAK256] = new(GasCostOf.Sha3 + MEMORY_EXPANSION, 0, 2, 1), + [Instruction.ADDRESS] = new(GasCostOf.Base, 0, 0, 1), + [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 + MEMORY_EXPANSION, 0, 3, 0), + [Instruction.CODESIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.CODECOPY] = new(GasCostOf.VeryLow + MEMORY_EXPANSION, 0, 3, 0), + [Instruction.GASPRICE] = new(GasCostOf.Base, 0, 0, 1), + [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 + MEMORY_EXPANSION, 0, 3, 0), + [Instruction.EXTCODEHASH] = new(0, 0, 1, 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.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.INVALID] = new(GasCostOf.Base, 0, 0, 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.PC] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.MSIZE] = new(GasCostOf.Base, 0, 0, 1), + [Instruction.GAS] = new(GasCostOf.Base, 0, 0, 1), + [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.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), + + [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) + { + 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; + } public static class InstructionExtensions { + public static bool IsEnabled(this IReleaseSpec? spec, Instruction instruction) => 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, + Instruction.CALL or Instruction.CALLCODE or Instruction.DELEGATECALL or Instruction.STATICCALL => true, + Instruction.SELFDESTRUCT => true, + _ => false, + }; + public static string? GetName(this Instruction instruction, bool isPostMerge = false, IReleaseSpec? spec = null) => instruction switch { 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; + } +} diff --git a/src/Nethermind/Nethermind.Evm/Metrics.cs b/src/Nethermind/Nethermind.Evm/Metrics.cs index b965e96c1c3..3cb984d49bd 100644 --- a/src/Nethermind/Nethermind.Evm/Metrics.cs +++ b/src/Nethermind/Nethermind.Evm/Metrics.cs @@ -112,6 +112,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(); diff --git a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj index 8e7b1a629a8..106b334f75a 100644 --- a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj +++ b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Nethermind/Nethermind.Evm/StateOverridesExtensions.cs b/src/Nethermind/Nethermind.Evm/StateOverridesExtensions.cs index d34ef57f238..33f4f1fd53e 100644 --- a/src/Nethermind/Nethermind.Evm/StateOverridesExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/StateOverridesExtensions.cs @@ -81,7 +81,7 @@ private static void UpdateCode( stateProvider, currentSpec, address, - new CodeInfo(accountOverride.Code), + new CodeInfo(accountOverride.Code, address), accountOverride.MovePrecompileToAddress); } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs index 603cec0f6e2..cf076f84031 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/AlwaysCancelTxTracer.cs @@ -42,6 +42,8 @@ public static AlwaysCancelTxTracer Instance public bool IsTracingAccess => true; public bool IsTracingFees => true; public bool IsTracingLogs => 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); @@ -100,4 +102,12 @@ 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 ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) => 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 822c8277424..942de0213a4 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/BlockReceiptsTracer.cs @@ -204,6 +204,9 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) public ITxTracer InnerTracer => _currentTxTracer; + public bool IsTracingPredefinedPatterns => _currentTxTracer.IsTracingPredefinedPatterns; + + public bool IsTracingCompiledSegments => _currentTxTracer.IsTracingCompiledSegments; public int TakeSnapshot() => _txReceipts.Count; public void Restore(int snapshot) @@ -272,4 +275,14 @@ public void Dispose() { _currentTxTracer.Dispose(); } + + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + _currentTxTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) + { + _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 45a5f903a37..e9def9d2aca 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CancellationTxTracer.cs @@ -26,6 +26,8 @@ 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; public ITxTracer InnerTracer => innerTracer; @@ -116,6 +118,17 @@ public bool IsTracingLogs init => _isTracingOpLevelLogs = value; } + public bool IsTracingPredefinedPatterns + { + get => _isTracingEvmChunks || innerTracer.IsTracingPredefinedPatterns; + init => _isTracingEvmChunks = value; + } + + public bool IsTracingCompiledSegments + { + get => _isTracingEvmSegments || innerTracer.IsTracingCompiledSegments; + init => _isTracingEvmSegments = value; + } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -454,4 +467,22 @@ public void Dispose() { innerTracer.Dispose(); } + + public void ReportPredefinedPatternExecution(long gas, int pc, string segmentID, in ExecutionEnvironment env) + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + InnerTracer.ReportPredefinedPatternExecution(gas, pc, segmentID, in env); + } + } + + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) + { + token.ThrowIfCancellationRequested(); + if (innerTracer.IsTracingFees) + { + 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 85e30708cf4..09d6e8ced9e 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/CompositeTxTracer.cs @@ -53,6 +53,8 @@ public CompositeTxTracer(IList txTracers) public bool IsTracingAccess { get; } public bool IsTracingFees { get; } public bool IsTracingLogs { get; } + public bool IsTracingPredefinedPatterns { get; } + public bool IsTracingCompiledSegments { get; } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -517,4 +519,27 @@ public void Dispose() _txTracers[index].Dispose(); } } + 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, in env); + } + } + } + + 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, in env); + } + } + } } diff --git a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs index e37a5ce52b5..954b1d8d854 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/Debugger/DebugTracer.cs @@ -61,6 +61,10 @@ public DebugTracer(ITxTracer tracer) public bool IsTracingLogs => InnerTracer.IsTracingLogs; + public bool IsTracingPredefinedPatterns => InnerTracer.IsTracingPredefinedPatterns; + + public bool IsTracingCompiledSegments => InnerTracer.IsTracingCompiledSegments; + public bool IsBreakpoitnSet(int depth, int programCounter) => _breakPoints.ContainsKey((depth, programCounter)); public void SetBreakPoint((int depth, int pc) point, Func condition = null) @@ -287,8 +291,12 @@ public void ReportStorageRead(in StorageCell storageCell) => InnerTracer.ReportStorageRead(storageCell); public void Dispose() - { - _autoResetEvent.Dispose(); - } + => _autoResetEvent.Dispose(); + + 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, in ExecutionEnvironment env) + => InnerTracer.ReportCompiledSegmentExecution(gas, pc, segmentId, in env); } #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/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..f3acfec6172 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 new file mode 100644 index 00000000000..58d0e0c876a --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxILVMTracer.cs @@ -0,0 +1,37 @@ +// 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 IsTracingPredefinedPatterns { get; } + + /// + /// Traces EVM precompiled segments + /// + /// + /// Controls + /// - + /// + bool IsTracingCompiledSegments { get; } + + 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/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index 876844c3673..251b8e0769b 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/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); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index dee699912cc..e32e4993d9c 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 + || IsTracingPredefinedPatterns + || IsTracingCompiledSegments; } public bool IsTracing { get; protected set; } public virtual bool IsTracingState { get; protected set; } @@ -43,6 +45,9 @@ 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 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) { } public virtual void ReportNonceChange(Address address, UInt256? before, UInt256? after) { } @@ -76,5 +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, 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/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index f914553a832..1b2a23c3d20 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -552,7 +552,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); codeInfo.AnalyseInBackgroundIfRequired(); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 101b88d120c..151a9dacf6b 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -16,6 +16,7 @@ using Nethermind.State; using System.Diagnostics.CodeAnalysis; using System.Runtime.Intrinsics; +using Nethermind.Evm.CodeAnalysis.IL; using static Nethermind.Evm.VirtualMachine; using static System.Runtime.CompilerServices.Unsafe; @@ -27,6 +28,8 @@ namespace Nethermind.Evm; using Int256; +using Nethermind.Evm.Config; +using System.Diagnostics; public class VirtualMachine : IVirtualMachine @@ -35,11 +38,11 @@ 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]; - 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, @@ -47,7 +50,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, @@ -60,16 +63,19 @@ 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, logger) - : new VirtualMachine(blockhashProvider, specProvider, logger); + ? new VirtualMachine(blockhashProvider, specProvider, _config, logger) + : new VirtualMachine(blockhashProvider, specProvider, _config, logger); } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -144,16 +150,19 @@ internal sealed class VirtualMachine : IVirtualMachine where TLogger : private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; + private readonly IVMConfig _vmConfig; public VirtualMachine( IBlockhashProvider? blockhashProvider, ISpecProvider? specProvider, + IVMConfig vmConfig, ILogger? logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); + _vmConfig = vmConfig; } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -476,7 +485,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) { @@ -487,12 +496,12 @@ 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; } - private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, bool chargeForDelegation, IReleaseSpec spec, bool chargeForWarm = true) + public static bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, bool chargeForDelegation, IWorldState state, IReleaseSpec spec, ITxTracer tracer, bool chargeForWarm = true) { if (!spec.UseHotAndColdStorage) { @@ -501,14 +510,14 @@ private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Add bool notOutOfGas = ChargeAccountGas(ref gasAvailable, vmState, address, spec); return notOutOfGas && chargeForDelegation - && vmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(_state, address, out Address delegated) + && vmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(state, address, out Address delegated) ? ChargeAccountGas(ref gasAvailable, vmState, delegated, spec) : notOutOfGas; bool ChargeAccountGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec) { bool result = true; - 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.AccessTracker.WarmUp(address); } @@ -526,25 +535,26 @@ bool ChargeAccountGas(ref long gasAvailable, EvmState vmState, Address address, } } - 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.AccessTracker.WarmUp(in storageCell); } @@ -652,6 +662,11 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM { _state.IncrementNonce(env.ExecutingAccount); } + + if (_vmConfig.IsVmOptimizationEnabled) + { + vmState.Env.CodeInfo.NoticeExecution(_vmConfig, _logger); + } } if (env.CodeInfo.MachineCode.Length == 0) @@ -663,6 +678,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM goto Empty; } + vmState.InitStacks(); EvmStack stack = new(vmState.DataStack.AsSpan(), vmState.DataStackHead, _txTracer); long gasAvailable = vmState.GasAvailable; @@ -719,6 +735,9 @@ private CallResult ExecuteCode code = env.CodeInfo.MachineCode.Span; EvmExceptionType exceptionType = EvmExceptionType.None; + IlInfo? ilInfo = env.CodeInfo.IlInfo; + + bool isRevert = false; #if DEBUG DebugTracer? debugger = _txTracer.GetTracer(); @@ -728,7 +747,8 @@ private CallResult ExecuteCode)chunkExecutionResult.Value.ReturnData).ToArray(); + goto DataReturn; + } + if (chunkExecutionResult.Value.ShouldRevert) + { + isRevert = true; + returnData = ((ReadOnlyMemory)chunkExecutionResult.Value.ReturnData).ToArray(); + goto DataReturn; + } + if (chunkExecutionResult.Value.ShouldStop) + { + goto EmptyReturn; + } + if (chunkExecutionResult.Value.ShouldJump && !vmState.Env.CodeInfo.ValidateJump(programCounter)) + { + exceptionType = EvmExceptionType.InvalidJumpDestination; + goto InvalidJumpDestination; + } + if (chunkExecutionResult.Value.ShouldFail) + { + exceptionType = chunkExecutionResult.Value.ExceptionType; + switch (exceptionType) + { + case EvmExceptionType.StackOverflow: + goto StackOverflow; + case EvmExceptionType.StackUnderflow: + goto StackUnderflow; + case EvmExceptionType.InvalidJumpDestination: + goto InvalidJumpDestination; + case EvmExceptionType.BadInstruction: + goto InvalidInstruction; + case EvmExceptionType.AccessViolation: + goto AccessViolation; + case EvmExceptionType.OutOfGas: + goto OutOfGas; + case EvmExceptionType.StaticCallViolation: + goto StaticCallViolation; + } + } + + if (programCounter >= codeLength) + { + goto EmptyReturnNoTrace; + } + } + Instruction instruction = (Instruction)code[programCounter]; if (isCancelable && _txTracer.IsCancelled) @@ -746,7 +819,7 @@ private CallResult ExecuteCode bytes; @@ -1176,7 +1249,7 @@ private CallResult ExecuteCode(vmState, ref stack, ref gasAvailable, spec); + if (!stack.PopUInt256(out result)) goto StackUnderflow; + ReadOnlySpan bytesSpan = stack.PopWord256(); + exceptionType = InstructionSStore(vmState, _state, ref gasAvailable, ref result, ref bytesSpan, spec, _txTracer); if (exceptionType != EvmExceptionType.None) goto ReturnFailure; break; @@ -1920,7 +1995,7 @@ private CallResult ExecuteCode( Address codeSource = stack.PopAddress(); if (codeSource is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, true, spec)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, true, _state, spec, _txTracer)) return EvmExceptionType.OutOfGas; UInt256 callValue; switch (instruction) @@ -2319,7 +2397,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref Address inheritor = stack.PopAddress(); if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, false, spec, false)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, false, _state, spec, _txTracer, false)) return EvmExceptionType.OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.AccessTracker.CreateList.Contains(executingAccount); @@ -2422,7 +2500,7 @@ private EvmExceptionType InstructionSelfDestruct(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; @@ -2463,7 +2541,7 @@ private EvmExceptionType InstructionSelfDestruct(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 @@ -2540,7 +2618,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); @@ -2553,7 +2632,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 @@ -2567,12 +2646,10 @@ private EvmExceptionType InstructionSStore bytes = stack.PopWord256(); bool newIsZero = bytes.IsZero(); bytes = !newIsZero ? bytes.WithoutLeadingZeros() : BytesZero; @@ -2583,9 +2660,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(); @@ -2599,7 +2677,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); @@ -2632,7 +2710,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); } } @@ -2700,7 +2778,7 @@ private 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 { @@ -2724,7 +2802,7 @@ 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(EvmState vmState, ref long gasAvailable, in UInt256 position, in UInt256 length) { long memoryCost = vmState.Memory.CalculateMemoryCost(in position, length, out bool outOfGas); if (outOfGas) return false; @@ -2753,34 +2831,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) => diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 446e3887769..296fc59f6e2 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -22,6 +22,8 @@ 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; using Nethermind.State; @@ -191,11 +193,17 @@ 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) + { + IlAnalyzer.Initialize(); + } + VirtualMachine virtualMachine = new( blockhashProvider, _api.SpecProvider, codeInfoRepository, - _api.LogManager); + _api.LogManager, + _api.VMConfig!); return virtualMachine; } diff --git a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs index 2d00f475f49..cb8c79357db 100644 --- a/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs +++ b/src/Nethermind/Nethermind.Test.Runner/StateTestTxTracer.cs @@ -33,8 +33,13 @@ 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 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) { @@ -273,5 +278,28 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) throw new NotImplementedException(); } + public void ReportCompiledSegmentExecution(long gas, int pc, string segmentId, in ExecutionEnvironment env) + { + _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, in ExecutionEnvironment env) + { + _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; }