Skip to content

Commit 335abdc

Browse files
authored
Merge pull request ethereum#14581 from holiman/byte_opt
core/vm: improve opByte
2 parents 7322730 + ac98657 commit 335abdc

File tree

4 files changed

+174
-8
lines changed

4 files changed

+174
-8
lines changed

common/math/big.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,34 @@ func PaddedBigBytes(bigint *big.Int, n int) []byte {
130130
return ret
131131
}
132132

133+
// bigEndianByteAt returns the byte at position n,
134+
// in Big-Endian encoding
135+
// So n==0 returns the least significant byte
136+
func bigEndianByteAt(bigint *big.Int, n int) byte {
137+
words := bigint.Bits()
138+
// Check word-bucket the byte will reside in
139+
i := n / wordBytes
140+
if i >= len(words) {
141+
return byte(0)
142+
}
143+
word := words[i]
144+
// Offset of the byte
145+
shift := 8 * uint(n%wordBytes)
146+
147+
return byte(word >> shift)
148+
}
149+
150+
// Byte returns the byte at position n,
151+
// with the supplied padlength in Little-Endian encoding.
152+
// n==0 returns the MSB
153+
// Example: bigint '5', padlength 32, n=31 => 5
154+
func Byte(bigint *big.Int, padlength, n int) byte {
155+
if n >= padlength {
156+
return byte(0)
157+
}
158+
return bigEndianByteAt(bigint, padlength-1-n)
159+
}
160+
133161
// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
134162
// that buf has enough space. If buf is too short the result will be incomplete.
135163
func ReadBits(bigint *big.Int, buf []byte) {

common/math/big_test.go

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"encoding/hex"
2222
"math/big"
2323
"testing"
24+
25+
"github.com/ethereum/go-ethereum/common"
2426
)
2527

2628
func TestHexOrDecimal256(t *testing.T) {
@@ -133,8 +135,44 @@ func TestPaddedBigBytes(t *testing.T) {
133135
}
134136
}
135137

136-
func BenchmarkPaddedBigBytes(b *testing.B) {
138+
func BenchmarkPaddedBigBytesLargePadding(b *testing.B) {
137139
bigint := MustParseBig256("123456789123456789123456789123456789")
140+
for i := 0; i < b.N; i++ {
141+
PaddedBigBytes(bigint, 200)
142+
}
143+
}
144+
145+
func BenchmarkPaddedBigBytesSmallPadding(b *testing.B) {
146+
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
147+
for i := 0; i < b.N; i++ {
148+
PaddedBigBytes(bigint, 5)
149+
}
150+
}
151+
152+
func BenchmarkPaddedBigBytesSmallOnePadding(b *testing.B) {
153+
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
154+
for i := 0; i < b.N; i++ {
155+
PaddedBigBytes(bigint, 32)
156+
}
157+
}
158+
159+
func BenchmarkByteAtBrandNew(b *testing.B) {
160+
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
161+
for i := 0; i < b.N; i++ {
162+
bigEndianByteAt(bigint, 15)
163+
}
164+
}
165+
166+
func BenchmarkByteAt(b *testing.B) {
167+
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
168+
for i := 0; i < b.N; i++ {
169+
bigEndianByteAt(bigint, 15)
170+
}
171+
}
172+
173+
func BenchmarkByteAtOld(b *testing.B) {
174+
175+
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
138176
for i := 0; i < b.N; i++ {
139177
PaddedBigBytes(bigint, 32)
140178
}
@@ -174,6 +212,65 @@ func TestU256(t *testing.T) {
174212
}
175213
}
176214

215+
func TestBigEndianByteAt(t *testing.T) {
216+
tests := []struct {
217+
x string
218+
y int
219+
exp byte
220+
}{
221+
{"00", 0, 0x00},
222+
{"01", 1, 0x00},
223+
{"00", 1, 0x00},
224+
{"01", 0, 0x01},
225+
{"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x30},
226+
{"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x20},
227+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0xAB},
228+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00},
229+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 500, 0x00},
230+
}
231+
for _, test := range tests {
232+
v := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
233+
actual := bigEndianByteAt(v, test.y)
234+
if actual != test.exp {
235+
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual)
236+
}
237+
238+
}
239+
}
240+
func TestLittleEndianByteAt(t *testing.T) {
241+
tests := []struct {
242+
x string
243+
y int
244+
exp byte
245+
}{
246+
{"00", 0, 0x00},
247+
{"01", 1, 0x00},
248+
{"00", 1, 0x00},
249+
{"01", 0, 0x00},
250+
{"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x00},
251+
{"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x00},
252+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0x00},
253+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00},
254+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, 0xAB},
255+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, 0xCD},
256+
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, 0x00},
257+
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, 0xCD},
258+
{"0000000000000000000000000000000000000000000000000000000000102030", 31, 0x30},
259+
{"0000000000000000000000000000000000000000000000000000000000102030", 30, 0x20},
260+
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, 0x0},
261+
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 31, 0xFF},
262+
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFF, 0x0},
263+
}
264+
for _, test := range tests {
265+
v := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
266+
actual := Byte(v, 32, test.y)
267+
if actual != test.exp {
268+
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual)
269+
}
270+
271+
}
272+
}
273+
177274
func TestS256(t *testing.T) {
178275
tests := []struct{ x, y *big.Int }{
179276
{x: big.NewInt(0), y: big.NewInt(0)},

core/vm/instructions.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,14 @@ func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac
256256
}
257257

258258
func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
259-
th, val := stack.pop(), stack.pop()
260-
if th.Cmp(big.NewInt(32)) < 0 {
261-
byte := evm.interpreter.intPool.get().SetInt64(int64(math.PaddedBigBytes(val, 32)[th.Int64()]))
262-
stack.push(byte)
259+
th, val := stack.pop(), stack.peek()
260+
if th.Cmp(common.Big32) < 0 {
261+
b := math.Byte(val, 32, int(th.Int64()))
262+
val.SetUint64(uint64(b))
263263
} else {
264-
stack.push(new(big.Int))
264+
val.SetUint64(0)
265265
}
266-
267-
evm.interpreter.intPool.put(th, val)
266+
evm.interpreter.intPool.put(th)
268267
return nil, nil
269268
}
270269
func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

core/vm/instructions_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package vm
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/ethereum/go-ethereum/common"
8+
"github.com/ethereum/go-ethereum/params"
9+
)
10+
11+
func TestByteOp(t *testing.T) {
12+
var (
13+
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
14+
stack = newstack()
15+
)
16+
tests := []struct {
17+
v string
18+
th uint64
19+
expected *big.Int
20+
}{
21+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, big.NewInt(0xAB)},
22+
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, big.NewInt(0xCD)},
23+
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, big.NewInt(0x00)},
24+
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, big.NewInt(0xCD)},
25+
{"0000000000000000000000000000000000000000000000000000000000102030", 31, big.NewInt(0x30)},
26+
{"0000000000000000000000000000000000000000000000000000000000102030", 30, big.NewInt(0x20)},
27+
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, big.NewInt(0x0)},
28+
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFFFFFFFFFFFFFF, big.NewInt(0x0)},
29+
}
30+
pc := uint64(0)
31+
for _, test := range tests {
32+
val := new(big.Int).SetBytes(common.Hex2Bytes(test.v))
33+
th := new(big.Int).SetUint64(test.th)
34+
stack.push(val)
35+
stack.push(th)
36+
opByte(&pc, env, nil, nil, stack)
37+
actual := stack.pop()
38+
if actual.Cmp(test.expected) != 0 {
39+
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual)
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)