Skip to content

Commit

Permalink
Add instruction decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
ElijahVlasov committed Aug 1, 2023
1 parent 7d9105d commit 6f25809
Show file tree
Hide file tree
Showing 5 changed files with 414 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
bin/
.DS_Store
vendor/
12 changes: 4 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@ module github.com/NethermindEth/cairo-vm-go

go 1.20

require github.com/fxamacker/cbor/v2 v2.4.0
require github.com/NethermindEth/juno v0.4.1

require (
github.com/NethermindEth/juno v0.4.1 // indirect
github.com/bits-and-blooms/bitset v1.7.0 // indirect
github.com/consensys/gnark-crypto v0.10.1-0.20230414110055-e500f2f0ff3a // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
github.com/stretchr/testify v1.8.4
github.com/x448/float16 v0.8.4 // indirect
)
require github.com/stretchr/testify v1.8.4
12 changes: 2 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,20 @@ github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHl
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/consensys/gnark-crypto v0.10.1-0.20230414110055-e500f2f0ff3a h1:hUUl++56+8w2aG2NXdQGfU8cBT9ZZ8UP4R3s47dSFdQ=
github.com/consensys/gnark-crypto v0.10.1-0.20230414110055-e500f2f0ff3a/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
247 changes: 247 additions & 0 deletions pkg/vm/instruction.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,252 @@
package vm

import (
"fmt"
"math/big"

f "github.com/NethermindEth/juno/core/felt"
)

type Register uint8

const (
Ap Register = iota
Fp
)

type Op1Addr uint8

const (
Imm Op1Addr = iota
ApPlusOff2
FpPlustOff2
Op0
)

type ResLogic uint8

const (
Op1 ResLogic = iota
AddOperands
MulOperands
Unconstrained
)

type PcUpdate uint8

const (
NextInstr PcUpdate = iota
Jump
JumpRel
Jnz
)

type ApUpdate uint8

const (
SameAp ApUpdate = iota
AddImm
Add1
Add2
)

type FpUpdate uint8

const (
SameFp FpUpdate = iota
ApPlus2
Dst
)

type Opcode uint8

const (
Nop Opcode = iota
AssertEq
Call
Ret
)

type Instruction struct {
Off0 f.Felt
Off1 f.Felt
Off2 f.Felt

Imm *f.Felt

DstRegister Register
Op0Register Register

Op1Addr Op1Addr

Res ResLogic
PcUpdate PcUpdate
ApUpdate ApUpdate
FpUpdate FpUpdate
Opcode Opcode
}

func (instr Instruction) Size() uint8 {
if instr.Imm != nil {
return 2
}
return 1
}

var (
dstRegBit uint = 0
op0RegBit uint = 1
op1ImmBit uint = 2
op1FpBit uint = 3
op1ApBit uint = 4
resAddBit uint = 5
resMulBit uint = 6
pcJumpAbsBit uint = 7
pcJumpRelBit uint = 8
pcJnzBit uint = 9
apAddBit uint = 10
apAdd1Bit uint = 11
opcodeCallBit uint = 12
opcodeRetBit uint = 13
opcodeAssertEqBit uint = 14
//reservedBit uint = 15
offsetBits uint = 16
numberOfFlags uint = 15
)

func decodeInstructionValues(encoding *big.Int) (flags *big.Int, off0_enc *big.Int, off1_enc *big.Int, off2_enc *big.Int, err error) {
if encoding.Cmp(new(big.Int).Lsh(big.NewInt(1), 3*offsetBits+numberOfFlags)) >= 0 {
return nil, nil, nil, nil, fmt.Errorf("unsupported instruction")
}

off0_enc = big.NewInt(0)
off1_enc = big.NewInt(0)
off2_enc = big.NewInt(0)
flags = big.NewInt(0)

off0_enc.And(encoding, big.NewInt(1<<offsetBits-1))
off1_enc.And(encoding.Rsh(encoding, offsetBits), big.NewInt(1<<offsetBits-1))
off2_enc.And(encoding.Rsh(encoding, offsetBits), big.NewInt(1<<offsetBits-1))
flags.Rsh(encoding, offsetBits)
err = nil

return
}

func DecodeInstruction(instruction *f.Felt, imm *f.Felt) (*Instruction, error) {
var instr *Instruction = new(Instruction)

var encoding big.Int
instruction.BigInt(&encoding)

flags, off0_enc, off1_enc, off2_enc, err := decodeInstructionValues(new(big.Int).Set(&encoding))

if err != nil {
return nil, fmt.Errorf("error during decoding an instruction: %w", err)
}

var flag big.Int
if flag.And(flag.Rsh(flags, dstRegBit), big.NewInt(1)).Cmp(big.NewInt(1)) == 0 {
instr.DstRegister = Fp
} else {
instr.DstRegister = Ap
}

if flag.And(flag.Rsh(flags, op0RegBit), big.NewInt(1)).Cmp(big.NewInt(1)) == 0 {
instr.Op0Register = Fp
} else {
instr.Op0Register = Ap
}

instr.Op1Addr = map[[3]uint64]Op1Addr{
[...]uint64{1, 0, 0}: Imm,
[...]uint64{0, 1, 0}: ApPlusOff2,
[...]uint64{0, 0, 1}: FpPlustOff2,
[...]uint64{0, 0, 0}: Op0,
}[[...]uint64{
flag.And(flag.Rsh(flags, op1ImmBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, op1ApBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, op1FpBit), big.NewInt(1)).Uint64()}]

if instr.Op1Addr == Imm {
if imm == nil {
return nil, fmt.Errorf("op1_addr is Op1Addr.IMM, but no immediate given")
} else {
var immFelt f.Felt
instr.Imm = immFelt.Set(imm)
}
} else {
instr.Imm = nil
}

instr.PcUpdate = map[[3]uint64]PcUpdate{
[...]uint64{1, 0, 0}: Jump,
[...]uint64{0, 1, 0}: JumpRel,
[...]uint64{0, 0, 1}: Jnz,
[...]uint64{0, 0, 0}: NextInstr,
}[[...]uint64{
flag.And(flag.Rsh(flags, pcJumpAbsBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, pcJumpRelBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, pcJnzBit), big.NewInt(1)).Uint64()}]

var defaultResLogic ResLogic

if instr.PcUpdate == Jnz {
defaultResLogic = Unconstrained
} else {
defaultResLogic = Op1
}

instr.Res = map[[2]uint64]ResLogic{
[...]uint64{1, 0}: AddOperands,
[...]uint64{0, 1}: MulOperands,
[...]uint64{0, 0}: defaultResLogic,
}[[...]uint64{
flag.And(flag.Rsh(flags, resAddBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, resMulBit), big.NewInt(1)).Uint64()}]

if instr.PcUpdate == Jnz && instr.Res != Unconstrained {
return nil, fmt.Errorf("jnz opcode must have Unconstrained res logic")
}

instr.ApUpdate = map[[2]uint64]ApUpdate{
[...]uint64{1, 0}: AddImm,
[...]uint64{0, 1}: Add1,
[...]uint64{0, 0}: SameAp,
}[[...]uint64{
flag.And(flag.Rsh(flags, apAddBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, apAdd1Bit), big.NewInt(1)).Uint64()}]

instr.Opcode = map[[3]uint64]Opcode{
[...]uint64{1, 0, 0}: Call,
[...]uint64{0, 1, 0}: Ret,
[...]uint64{0, 0, 1}: AssertEq,
[...]uint64{0, 0, 0}: Nop,
}[[...]uint64{
flag.And(flag.Rsh(flags, opcodeCallBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, opcodeRetBit), big.NewInt(1)).Uint64(),
flag.And(flag.Rsh(flags, opcodeAssertEqBit), big.NewInt(1)).Uint64()}]

if instr.Opcode == Call {
if instr.ApUpdate != SameAp {
return nil, fmt.Errorf("CALL must have update_ap is ADD2")
}
instr.ApUpdate = Add2
}

switch instr.Opcode {
case Call:
instr.FpUpdate = ApPlus2
case Ret:
instr.FpUpdate = Dst
default:
instr.FpUpdate = SameFp
}

var offset f.Felt
instr.Off0 = *offset.SetBigInt(off0_enc.Sub(off0_enc, big.NewInt(1<<(offsetBits-1))))
instr.Off1 = *offset.SetBigInt(off1_enc.Sub(off1_enc, big.NewInt(1<<(offsetBits-1))))
instr.Off2 = *offset.SetBigInt(off2_enc.Sub(off2_enc, big.NewInt(1<<(offsetBits-1))))

return instr, nil
}
Loading

0 comments on commit 6f25809

Please sign in to comment.