mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
core/vm: improved segments by adding native calls
When segments are build native functions call will be used next time round instead of the large switch loop.
This commit is contained in:
parent
e92fce8a03
commit
27bf7b6c65
2 changed files with 94 additions and 30 deletions
|
|
@ -44,6 +44,10 @@ var (
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "output full trace logs",
|
Usage: "output full trace logs",
|
||||||
}
|
}
|
||||||
|
SegmentationFlag = cli.BoolFlag{
|
||||||
|
Name: "nosegmentation",
|
||||||
|
Usage: "Disabled VM segmentation",
|
||||||
|
}
|
||||||
CodeFlag = cli.StringFlag{
|
CodeFlag = cli.StringFlag{
|
||||||
Name: "code",
|
Name: "code",
|
||||||
Usage: "EVM code",
|
Usage: "EVM code",
|
||||||
|
|
@ -81,6 +85,7 @@ func init() {
|
||||||
app = utils.NewApp("0.2", "the evm command line interface")
|
app = utils.NewApp("0.2", "the evm command line interface")
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
DebugFlag,
|
DebugFlag,
|
||||||
|
SegmentationFlag,
|
||||||
SysStatFlag,
|
SysStatFlag,
|
||||||
CodeFlag,
|
CodeFlag,
|
||||||
GasFlag,
|
GasFlag,
|
||||||
|
|
@ -94,6 +99,7 @@ func init() {
|
||||||
|
|
||||||
func run(ctx *cli.Context) {
|
func run(ctx *cli.Context) {
|
||||||
vm.Debug = ctx.GlobalBool(DebugFlag.Name)
|
vm.Debug = ctx.GlobalBool(DebugFlag.Name)
|
||||||
|
vm.DisableSegmentation = ctx.GlobalBool(SegmentationFlag.Name)
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := state.New(common.Hash{}, db)
|
statedb := state.New(common.Hash{}, db)
|
||||||
|
|
@ -119,7 +125,9 @@ func run(ctx *cli.Context) {
|
||||||
fmt.Println(e)
|
fmt.Println(e)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println("no analysis", time.Since(vmstart))
|
fmt.Println("no segmentation", time.Since(vmstart))
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
vmstart = time.Now()
|
vmstart = time.Now()
|
||||||
ret, e = vmenv.Call(
|
ret, e = vmenv.Call(
|
||||||
|
|
@ -135,7 +143,7 @@ func run(ctx *cli.Context) {
|
||||||
fmt.Println(e)
|
fmt.Println(e)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println("with analysis", time.Since(vmstart))
|
fmt.Println("with segmentation", time.Since(vmstart))
|
||||||
|
|
||||||
if ctx.GlobalBool(DumpFlag.Name) {
|
if ctx.GlobalBool(DumpFlag.Name) {
|
||||||
fmt.Println(string(statedb.Dump()))
|
fmt.Println(string(statedb.Dump()))
|
||||||
|
|
@ -156,12 +164,6 @@ num gc: %d
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("OUT: 0x%x\n", ret)
|
fmt.Printf("OUT: 0x%x\n", ret)
|
||||||
|
|
||||||
for codehash, chunks := range vm.Chunks {
|
|
||||||
for _, chunk := range chunks {
|
|
||||||
fmt.Printf("%x => %v\n", codehash, chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
106
core/vm/vm.go
106
core/vm/vm.go
|
|
@ -2,6 +2,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -11,14 +12,17 @@ import (
|
||||||
"github.com/hashicorp/golang-lru"
|
"github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
type segment struct {
|
type operation struct {
|
||||||
cstart, cend uint64
|
fn func(*stack, []byte)
|
||||||
msize uint64
|
data []byte
|
||||||
gas *big.Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *segment) String() string {
|
func Add(s *stack, data []byte) {
|
||||||
return fmt.Sprintf("{%d %d %d %v}", c.cstart, c.cend, c.msize, c.gas)
|
s.push(new(big.Int).Add(s.pop(), s.pop()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Push(s *stack, data []byte) {
|
||||||
|
s.push(common.BytesToBig(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
type codeSegments map[uint64]*segment
|
type codeSegments map[uint64]*segment
|
||||||
|
|
@ -29,6 +33,22 @@ func init() {
|
||||||
segments, _ = lru.New(256)
|
segments, _ = lru.New(256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DisableSegmentation bool
|
||||||
|
|
||||||
|
type segment struct {
|
||||||
|
cstart, cend, next uint64
|
||||||
|
msize uint64
|
||||||
|
gas *big.Int
|
||||||
|
|
||||||
|
ssize int
|
||||||
|
|
||||||
|
ops []operation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *segment) String() string {
|
||||||
|
return fmt.Sprintf("{%d %d %d %v}", c.cstart, c.cend, c.msize, c.gas)
|
||||||
|
}
|
||||||
|
|
||||||
// Vm implements VirtualMachine
|
// Vm implements VirtualMachine
|
||||||
type Vm struct {
|
type Vm struct {
|
||||||
env Environment
|
env Environment
|
||||||
|
|
@ -70,12 +90,17 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
statedb = self.env.State() // current state
|
statedb = self.env.State() // current state
|
||||||
// For optimisation reason we're using uint64 as the program counter.
|
// For optimisation reason we're using uint64 as the program counter.
|
||||||
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
|
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
|
||||||
pc = uint64(0) // program counter
|
pc = uint64(0) // program counter
|
||||||
ppc = uint64(0) // previous program counter
|
ppc = uint64(0) // previous program counter
|
||||||
csegment *segment // current code segment
|
csegment *segment // current code segment
|
||||||
csegments codeSegments // program segments
|
csegments codeSegments // program segments
|
||||||
nopay uint64 = 0
|
nopay uint64 = 0
|
||||||
disableOptim bool
|
|
||||||
|
addop = func(op operation) {
|
||||||
|
if csegment != nil && !DisableSegmentation {
|
||||||
|
csegment.ops = append(csegment.ops, op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// jump evaluates and checks whether the given jump destination is a valid one
|
// jump evaluates and checks whether the given jump destination is a valid one
|
||||||
// if valid move the `pc` otherwise return an error.
|
// if valid move the `pc` otherwise return an error.
|
||||||
|
|
@ -116,6 +141,12 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
delete(csegments, csegment.cstart)
|
delete(csegments, csegment.cstart)
|
||||||
} else if csegment != nil && err == nil && csegment.cend == 0 {
|
} else if csegment != nil && err == nil && csegment.cend == 0 {
|
||||||
csegment.cend = pc
|
csegment.cend = pc
|
||||||
|
csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
t, _ := segments.Get(codehash)
|
||||||
|
for _, segment := range t.(codeSegments) {
|
||||||
|
fmt.Println(segment)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -133,26 +164,52 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
for {
|
for {
|
||||||
// Get the current operation location of pc
|
// Get the current operation location of pc
|
||||||
op = context.GetOp(pc)
|
op = context.GetOp(pc)
|
||||||
if isDynamic(op) && csegment != nil {
|
if isDynamic(op) && csegment != nil && !DisableSegmentation {
|
||||||
|
// end of segment bound
|
||||||
csegment.cend = ppc
|
csegment.cend = ppc
|
||||||
|
csegment.next = pc
|
||||||
|
// write out the required required elements on the stack. i.e. the amount of items
|
||||||
|
// required outside of the segment itself.
|
||||||
|
// 0: P, 1
|
||||||
|
// 1: J, 2
|
||||||
|
// 2: D -+
|
||||||
|
// 3: P, 2 |- M,0 = M,0 pushes 1. Requires 2. 1 outside bounds of M,0
|
||||||
|
// 4: ADD -+
|
||||||
|
// 5: J, 2
|
||||||
|
csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0)))
|
||||||
csegment = nil
|
csegment = nil
|
||||||
nopay = 0
|
nopay = 0
|
||||||
} else if csegment == nil && pc != 0 {
|
} else if csegment == nil && pc != 0 && !DisableSegmentation {
|
||||||
// allocate a new segment for tracking information
|
// check if a segment is available to us and set it.
|
||||||
csegment = &segment{pc, 0, 0, new(big.Int)}
|
if seg, ok := csegments[pc]; ok {
|
||||||
csegments[pc] = csegment
|
nopay = seg.cend
|
||||||
} else {
|
|
||||||
if segment, ok := csegments[pc]; ok && !disableOptim {
|
// make sure there's enough stack items to complete the segment
|
||||||
nopay = segment.cend
|
if err := stack.require(seg.ssize); err != nil {
|
||||||
if !context.UseGas(segment.gas) {
|
return context.Return(nil), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// use required gas for this segment
|
||||||
|
if !context.UseGas(seg.gas) {
|
||||||
context.UseGas(context.Gas)
|
context.UseGas(context.Gas)
|
||||||
|
|
||||||
return context.Return(nil), OutOfGasError{}
|
return context.Return(nil), OutOfGasError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, operation := range seg.ops {
|
||||||
|
operation.fn(stack, operation.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc = seg.next
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// allocate a new segment for tracking information
|
||||||
|
csegment = &segment{pc, 0, 0, 0, new(big.Int), stack.len(), nil}
|
||||||
|
csegments[pc] = csegment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pc > nopay || pc == 0 || disableOptim {
|
if pc > nopay || pc == 0 || DisableSegmentation {
|
||||||
// calculate the new memory size and gas price for the current executing opcode
|
// calculate the new memory size and gas price for the current executing opcode
|
||||||
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
newMemSize, cost, err = self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -190,6 +247,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
|
|
||||||
U256(base)
|
U256(base)
|
||||||
|
|
||||||
|
addop(operation{Add, nil})
|
||||||
|
|
||||||
// pop result back on the stack
|
// pop result back on the stack
|
||||||
stack.push(base)
|
stack.push(base)
|
||||||
case SUB:
|
case SUB:
|
||||||
|
|
@ -522,6 +581,9 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||||
size := uint64(op - PUSH1 + 1)
|
size := uint64(op - PUSH1 + 1)
|
||||||
byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
|
byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
|
||||||
|
|
||||||
|
addop(operation{Push, byts})
|
||||||
|
|
||||||
// push value to stack
|
// push value to stack
|
||||||
stack.push(common.Bytes2Big(byts))
|
stack.push(common.Bytes2Big(byts))
|
||||||
pc += size
|
pc += size
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue