Skip to content

Commit c01e6d8

Browse files
committed
implementation with reverse
1 parent 5249263 commit c01e6d8

File tree

4 files changed

+147
-162
lines changed

4 files changed

+147
-162
lines changed

src/Nethermind/Nethermind.Evm/EvmStack.cs

Lines changed: 102 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -51,73 +51,82 @@ public ref Word PushRef()
5151
EvmStack.ThrowEvmStackOverflowException();
5252
}
5353

54-
return ref Unsafe.Add(ref MemoryMarshal.GetReference(_words), head * WordSize);
54+
return ref _words[head];
5555
}
5656

57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public ref UInt256 PushRefAsUInt256() => ref Unsafe.As<Word, UInt256>(ref PushRef());
59+
5760
public void PushBytes(scoped ReadOnlySpan<byte> value)
5861
{
5962
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value);
6063

61-
PushRef() = new UInt256(value, true);
64+
ref Word top = ref PushRef();
65+
top = Unsafe.As<byte, Word>(ref MemoryMarshal.GetReference(value));
66+
Reshuffle(ref top);
6267
}
6368

6469
public void PushBytes(scoped in ZeroPaddedSpan value)
6570
{
6671
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value);
6772

68-
ref byte bytes = ref PushRef();
73+
ref Word top = ref PushRef();
6974
ReadOnlySpan<byte> valueSpan = value.Span;
7075
if (valueSpan.Length != WordSize)
7176
{
72-
// Not full entry, clear first
73-
Unsafe.As<byte, Word>(ref bytes) = default;
74-
valueSpan.CopyTo(MemoryMarshal.CreateSpan(ref bytes, value.Length));
77+
// Not full entry, clear first.
78+
top = default;
79+
valueSpan.CopyTo(MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref top), value.Length));
7580
}
7681
else
7782
{
78-
Unsafe.As<byte, Word>(ref bytes) = Unsafe.As<byte, Word>(ref MemoryMarshal.GetReference(valueSpan));
83+
top = Unsafe.As<byte, Word>(ref MemoryMarshal.GetReference(valueSpan));
7984
}
85+
86+
// Reshuffle top
87+
Reshuffle(ref top);
8088
}
8189

8290
public void PushLeftPaddedBytes(ReadOnlySpan<byte> value, int paddingLength)
8391
{
8492
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value);
8593

86-
ref byte bytes = ref PushRef();
94+
ref Word top = ref PushRef();
8795
if (value.Length != WordSize)
8896
{
8997
// Not full entry, clear first
90-
Unsafe.As<byte, Word>(ref bytes) = default;
91-
value.CopyTo(MemoryMarshal.CreateSpan(ref Unsafe.Add(ref bytes, WordSize - paddingLength), value.Length));
98+
top = default;
99+
value.CopyTo(MemoryMarshal.CreateSpan(ref Unsafe.Add(ref Unsafe.As<Word, byte>(ref top), WordSize - paddingLength), value.Length));
92100
}
93101
else
94102
{
95-
Unsafe.As<byte, Word>(ref bytes) = Unsafe.As<byte, Word>(ref MemoryMarshal.GetReference(value));
103+
top = Unsafe.As<byte, Word>(ref MemoryMarshal.GetReference(value));
96104
}
97105
}
98106

99107
public void PushByte(byte value)
100108
{
101109
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value);
102-
PushRef() = new UInt256(value);
110+
111+
PushRefAsUInt256() = new UInt256(value);
103112
}
104113

105114
public void PushOne()
106115
{
107116
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(Bytes.OneByteSpan);
108-
PushRef() = UInt256.One;
117+
118+
PushRefAsUInt256() = UInt256.One;
109119
}
110120

111121
public void PushZero()
112122
{
113123
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(Bytes.ZeroByteSpan);
114-
PushRef() = UInt256.Zero;
124+
PushRef() = default;
115125
}
116126

117127
public void PushUInt32(int value)
118128
{
119-
// Little only for now
120-
PushRef() = new UInt256(unchecked((uint)BinaryPrimitives.ReverseEndianness(value)));
129+
PushRefAsUInt256() = new UInt256(unchecked((uint)value));
121130

122131
// TODO: trace
123132
//if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(intPlace);
@@ -131,8 +140,14 @@ public void PushUInt32(int value)
131140
/// </remarks>
132141
public void PushUInt256(in UInt256 value)
133142
{
134-
PushRef() = value;
135-
if (typeof(TTracing) == typeof(IsTracing)) _tracer.ReportStackPush(value);
143+
PushRefAsUInt256() = value;
144+
145+
if (typeof(TTracing) == typeof(IsTracing))
146+
{
147+
Word trace = Unsafe.As<UInt256, Word>(ref Unsafe.AsRef(in value));
148+
Reshuffle(ref trace);
149+
_tracer.ReportStackPush(MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref trace), WordSize));
150+
}
136151
}
137152

138153
public void PushSignedInt256(in Int256.Int256 value)
@@ -152,10 +167,6 @@ public void PopLimbo()
152167
/// <summary>
153168
/// Pops an Uint256 written in big endian.
154169
/// </summary>
155-
/// <remarks>
156-
/// This method does its own calculations to create the <paramref name="result"/>. It knows that 32 bytes were popped with <see cref="PopRef"/>. It doesn't have to check the size of span or slice it.
157-
/// All it does is <see cref="Unsafe.ReadUnaligned{T}(ref byte)"/> and then reverse endianness if needed. Then it creates <paramref name="result"/>.
158-
/// </remarks>
159170
/// <param name="result">The returned value.</param>
160171
public bool PopUInt256(out UInt256 result)
161172
{
@@ -164,50 +175,32 @@ public bool PopUInt256(out UInt256 result)
164175
if (Unsafe.IsNullRef(ref v)) return false;
165176
result = Unsafe.As<Word, UInt256>(ref v);
166177
return true;
178+
}
167179

168-
// if (Avx2.IsSupported)
169-
// {
170-
// Word data = Unsafe.ReadUnaligned<Word>(ref bytes);
171-
// Word shuffle = Vector256.Create(
172-
// 0x18191a1b1c1d1e1ful,
173-
// 0x1011121314151617ul,
174-
// 0x08090a0b0c0d0e0ful,
175-
// 0x0001020304050607ul).AsByte();
176-
// if (Avx512Vbmi.VL.IsSupported)
177-
// {
178-
// Word convert = Avx512Vbmi.VL.PermuteVar32x8(data, shuffle);
179-
// result = Unsafe.As<Word, UInt256>(ref convert);
180-
// }
181-
// else
182-
// {
183-
// Word convert = Avx2.Shuffle(data, shuffle);
184-
// Vector256<ulong> permute = Avx2.Permute4x64(Unsafe.As<Word, Vector256<ulong>>(ref convert), 0b_01_00_11_10);
185-
// result = Unsafe.As<Vector256<ulong>, UInt256>(ref permute);
186-
// }
187-
// }
188-
// else
189-
// {
190-
// ulong u3, u2, u1, u0;
191-
// if (BitConverter.IsLittleEndian)
192-
// {
193-
// // Combine read and switch endianness to movbe reg, mem
194-
// u3 = BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<ulong>(ref bytes));
195-
// u2 = BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, sizeof(ulong))));
196-
// u1 = BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, 2 * sizeof(ulong))));
197-
// u0 = BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, 3 * sizeof(ulong))));
198-
// }
199-
// else
200-
// {
201-
// u3 = Unsafe.ReadUnaligned<ulong>(ref bytes);
202-
// u2 = Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, sizeof(ulong)));
203-
// u1 = Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, 2 * sizeof(ulong)));
204-
// u0 = Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref bytes, 3 * sizeof(ulong)));
205-
// }
206-
//
207-
// result = new UInt256(u0, u1, u2, u3);
208-
// }
209-
210-
return true;
180+
private static void Reshuffle(ref Word word)
181+
{
182+
if (Avx2.IsSupported)
183+
{
184+
Word shuffle = Vector256.Create(
185+
0x18191a1b1c1d1e1ful,
186+
0x1011121314151617ul,
187+
0x08090a0b0c0d0e0ful,
188+
0x0001020304050607ul).AsByte();
189+
if (Avx512Vbmi.VL.IsSupported)
190+
{
191+
word = Avx512Vbmi.VL.PermuteVar32x8(word, shuffle);
192+
}
193+
else
194+
{
195+
Word convert = Avx2.Shuffle(word, shuffle);
196+
Vector256<ulong> permute = Avx2.Permute4x64(Unsafe.As<Word, Vector256<ulong>>(ref convert), 0b_01_00_11_10);
197+
word = Unsafe.As<Vector256<ulong>, Word>(ref permute);
198+
}
199+
}
200+
else
201+
{
202+
throw new NotImplementedException();
203+
}
211204
}
212205

213206
public readonly bool PeekUInt256IsZero()
@@ -218,7 +211,7 @@ public readonly bool PeekUInt256IsZero()
218211
return false;
219212
}
220213

221-
return _words[head].IsZero;
214+
return _words[head] == Word.Zero;
222215
}
223216

224217
public readonly Span<byte> PeekWord256(out UInt256 destination)
@@ -229,20 +222,30 @@ public readonly Span<byte> PeekWord256(out UInt256 destination)
229222
EvmStack.ThrowEvmStackUnderflowException();
230223
}
231224

232-
return ReshuffleToBigEndian(ref _words[head], ref destination);
225+
Unsafe.SkipInit(out destination);
226+
ref Word word = ref Unsafe.As<UInt256, Word>(ref destination);
227+
word = _words[head];
228+
Reshuffle(ref word);
229+
return MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref word), WordSize);
233230
}
234231

235-
public Address? PopAddress() => Head-- == 0
236-
? null
237-
: new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray());
232+
public Address? PopAddress()
233+
{
234+
if (Head-- == 0)
235+
return null;
236+
237+
ref Word word = ref _words[Head];
238+
Reshuffle(ref word);
239+
return new Address(MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref word), AddressSize).ToArray());
240+
}
238241

239242
[MethodImpl(MethodImplOptions.AggressiveInlining)]
240-
public ref UInt256 PopRef()
243+
public ref Word PopRef()
241244
{
242245
int head = Head;
243246
if (head == 0)
244247
{
245-
return ref Unsafe.NullRef<UInt256>();
248+
return ref Unsafe.NullRef<Word>();
246249
}
247250

248251
head--;
@@ -252,62 +255,47 @@ public ref UInt256 PopRef()
252255

253256
public Span<byte> PopWord256(out UInt256 destination)
254257
{
255-
ref UInt256 v = ref PopRef();
258+
ref Word v = ref PopRef();
256259
if (Unsafe.IsNullRef(ref v)) EvmStack.ThrowEvmStackUnderflowException();
257260

258-
return ReshuffleToBigEndian(ref v, ref destination);
261+
Unsafe.SkipInit(out destination);
262+
ref Word dest = ref Unsafe.As<UInt256, Word>(ref destination);
263+
264+
dest = v;
265+
Reshuffle(ref dest);
266+
return MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref dest), WordSize);
259267
}
260268

261-
private static Span<byte> ReshuffleToBigEndian(ref UInt256 from, ref Word to)
269+
/// <summary>
270+
/// Pops the word using the stack slot without copying.
271+
/// This means that if there's a follow push without consuming the value, it will be overwritten.
272+
/// </summary>
273+
public Span<byte> PopWord256Unsafe()
262274
{
263-
if (Avx2.IsSupported)
264-
{
265-
Word data = Unsafe.As<UInt256, Word>(ref from);
266-
Word shuffle = Vector256.Create(
267-
0x18191a1b1c1d1e1ful,
268-
0x1011121314151617ul,
269-
0x08090a0b0c0d0e0ful,
270-
0x0001020304050607ul).AsByte();
271-
272-
if (Avx512Vbmi.VL.IsSupported)
273-
{
274-
to = Avx512Vbmi.VL.PermuteVar32x8(data, shuffle);
275-
}
276-
else
277-
{
278-
Word convert = Avx2.Shuffle(data, shuffle);
279-
Vector256<ulong> permute =
280-
Avx2.Permute4x64(Unsafe.As<Word, Vector256<ulong>>(ref convert), 0b_01_00_11_10);
275+
ref Word v = ref PopRef();
276+
if (Unsafe.IsNullRef(ref v)) EvmStack.ThrowEvmStackUnderflowException();
281277

282-
result = Unsafe.As<Vector256<ulong>, UInt256>(ref permute);
283-
}
284-
}
285-
else
286-
{
287-
throw new NotImplementedException();
288-
}
278+
Reshuffle(ref v);
279+
return MemoryMarshal.CreateSpan(ref Unsafe.As<Word, byte>(ref v), WordSize);
289280
}
290281

291282
public byte PopByte()
292283
{
293-
ref UInt256 v = ref PopRef();
284+
ref Word v = ref PopRef();
294285
if (Unsafe.IsNullRef(ref v)) EvmStack.ThrowEvmStackUnderflowException();
295286

296-
if (BitConverter.IsLittleEndian == false)
297-
throw new Exception();
298-
299-
// return the smallest
300-
return (byte)(v.u0 & 0xFF);
287+
// Return the smallest
288+
return v[BitConverter.IsLittleEndian ? 0 : (WordSize - 1)];
301289
}
302290

303291
public bool Dup(in int depth)
304292
{
305293
if (!EnsureDepth(depth)) return false;
306294

307-
ref UInt256 v = ref MemoryMarshal.GetReference(_words);
295+
ref Word v = ref MemoryMarshal.GetReference(_words);
308296

309-
ref UInt256 from = ref Unsafe.Add(ref v, Head - depth);
310-
ref UInt256 to = ref Unsafe.Add(ref v, Head );
297+
ref Word from = ref Unsafe.Add(ref v, Head - depth);
298+
ref Word to = ref Unsafe.Add(ref v, Head );
311299

312300
to = from;
313301

@@ -331,12 +319,12 @@ public readonly bool Swap(int depth)
331319
{
332320
if (!EnsureDepth(depth)) return false;
333321

334-
ref UInt256 v = ref MemoryMarshal.GetReference(_words);
322+
ref Word v = ref MemoryMarshal.GetReference(_words);
335323

336-
ref UInt256 bottom = ref Unsafe.Add(ref v, Head - depth);
337-
ref UInt256 top = ref Unsafe.Add(ref v, Head );
324+
ref Word bottom = ref Unsafe.Add(ref v, Head - depth);
325+
ref Word top = ref Unsafe.Add(ref v, Head );
338326

339-
UInt256 buffer = bottom;
327+
Word buffer = bottom;
340328
bottom = top;
341329
top = buffer;
342330

@@ -352,7 +340,7 @@ private readonly void Trace(int depth)
352340
{
353341
for (int i = depth; i > 0; i--)
354342
{
355-
_tracer.ReportStackPush(_words[Head - i]);
343+
_tracer.ReportStackPush(Unsafe.As<Word, UInt256>(ref _words[Head - i]));
356344
}
357345
}
358346
}

src/Nethermind/Nethermind.Evm/EvmState.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Collections.Concurrent;
66
using System.Diagnostics;
77
using System.Diagnostics.CodeAnalysis;
8+
using System.Numerics;
9+
using System.Runtime.Intrinsics;
810
using Nethermind.Core;
911
using Nethermind.State;
10-
12+
using Word = System.Runtime.Intrinsics.Vector256<byte>;
1113
namespace Nethermind.Evm;
1214

1315
/// <summary>
@@ -19,7 +21,7 @@ public sealed class EvmState : IDisposable // TODO: rename to CallState
1921
private static readonly ConcurrentQueue<EvmState> _statePool = new();
2022
private static readonly StackPool _stackPool = new();
2123

22-
public byte[]? DataStack;
24+
public Word[]? DataStack;
2325
public int[]? ReturnStack;
2426

2527
public long GasAvailable { get; set; }

0 commit comments

Comments
 (0)