@@ -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}
0 commit comments