Skip to content

Commit

Permalink
add RISC-V VM which can run the imperative fib benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent committed Dec 4, 2024
1 parent 0963441 commit 9159b1a
Show file tree
Hide file tree
Showing 8 changed files with 527 additions and 0 deletions.
157 changes: 157 additions & 0 deletions experimental/go-riscv-sim/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package main

import "fmt"

type decoder struct {
code []byte
offset int
}

func (d *decoder) readUint16() uint16 {
offset := d.offset
code := d.code

lower := uint16(code[offset])
lower += uint16(code[offset+1]) << 8

d.offset += 2

return lower
}

const instHasUpperMask = 0x3

func (d *decoder) readInstruction() (instruction uint32, hasUpper bool) {
instruction = uint32(d.readUint16())

hasUpper = (instruction & instHasUpperMask) == instHasUpperMask
if hasUpper {
instruction |= uint32(d.readUint16()) << 16
}

return
}

func (d *decoder) decodeInstructions() []instruction {
var instructions []instruction

for d.offset < len(d.code) {
encodedInstruction, hasUpper := d.readInstruction()
decodedInstruction := d.decodeInstruction(encodedInstruction)
instructions = append(instructions, decodedInstruction)
if hasUpper {
instructions = append(instructions, instructionNop{})
}
}

return instructions
}

func (d *decoder) decodeInstruction(instruction uint32) instruction {

switch instruction & 0x3 {
case 0x1:
switch instruction & 0xE003 {
case 0x1:
if instruction&0xFFFF == 0x1 {
// "c.nop "
return instructionNop{}
}
if instruction&0xE003 == 0x1 {
// "c.addi $rd, $imm"
return decodeCAddi(instruction)
}
case 0x4001:
// "c.li $rd, $imm"
return decodeCLi(instruction)
}
case 0x2:
switch instruction & 0xE003 {
case 0x8002:
switch instruction & 0xF003 {
case 0x8002:
// "c.mv $rs1, $rs2"
return decodeCMv(instruction)
case 0x9002:
if instruction&0xFFFF == 0x9002 {
// "c.ebreak "
return instructionCEbreak{}
}
if instruction&0xF003 == 0x9002 {
// "c.add $rs1, $rs2"
return decodeCAdd(instruction)
}
}
}
case 0x3:
switch instruction & 0x7F {
case 0x63:
switch instruction & 0x707F {
case 0x1063:
// "bne $rs1, $rs2, $imm12"
return decodeBne(instruction)

case 0x5063:
// "bge $rs1, $rs2, $imm12"
return decodeBge(instruction)
}
}
}

panic(fmt.Sprintf("Unknown instruction: 0x%08X", instruction))
}

func decode_rs1_GPRNoX0_f11t7(instruction uint32) uint32 {
return (instruction & 0xF80) >> 7
}

func decode_rs2_GPRNoX0_f6t2(instruction uint32) uint32 {
return (instruction & 0x7C) >> 2
}

func decode_imm_simm6_f12t12f6t2(instruction uint32) uint32 {
return ((instruction & 0x1000) >> 7) | ((instruction & 0x7C) >> 2)
}

func decode_rd_GPRNoX0_f11t7(instruction uint32) uint32 {
return (instruction & 0xF80) >> 7
}
func decode_imm12_simm13_lsb0_f31t31f7t7f30t25f11t8(instruction uint32) uint32 {
return ((instruction & 0x80000000) >> 20) |
((instruction & 0x80) << 3) |
((instruction & 0x7E000000) >> 21) |
((instruction & 0xF00) >> 8)
}

func decode_rs1_GPR_f19t15(instruction uint32) uint32 {
return (instruction & 0xF8000) >> 15
}

func decode_rs2_GPR_f24t20(instruction uint32) uint32 {
return (instruction & 0x1F00000) >> 20
}

func decode_imm_simm6nonzero_f12t12f6t2(instruction uint32) uint32 {
return ((instruction & 0x1000) >> 7) |
((instruction & 0x7C) >> 2)
}

func decodeInt(bitPattern uint32, bitWidth uint8, shiftCount uint8) int32 {
var mask = (uint32(1) << bitWidth) - 1
var signMask = uint32(1) << (bitWidth - 1)
var maskedValue = bitPattern & mask
isNegative := (signMask & maskedValue) != 0
if isNegative {
maskedValue |= ^mask
}
shiftedValue := maskedValue << shiftCount
return int32(shiftedValue)
}

func decode_simm13_lsb0(value uint32) int32 {
return decodeInt(value, 12, 1)
}

func decode_simm6nonzero(value uint32) int32 {
return decodeInt(value, 6, 0)
}
5 changes: 5 additions & 0 deletions experimental/go-riscv-sim/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/onflow/cadence/experimental/go-riscv-sim

go 1.23.2

require golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
2 changes: 2 additions & 0 deletions experimental/go-riscv-sim/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
122 changes: 122 additions & 0 deletions experimental/go-riscv-sim/instruction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

type instruction interface {
isInstruction()
}

// Nop

type instructionNop struct{}

func (instructionNop) isInstruction() {}

// CLi

type instructionCLi struct {
rd uint32
imm uint32
}

func decodeCLi(instruction uint32) instructionCLi {
// "c.li $rd, $imm"
return instructionCLi{
rd: decode_rd_GPRNoX0_f11t7(instruction),
imm: decode_imm_simm6_f12t12f6t2(instruction),
}
}

func (instructionCLi) isInstruction() {}

// CMv

type instructionCMv struct {
rs1 uint32
rs2 uint32
}

func (instructionCMv) isInstruction() {}

func decodeCMv(instruction uint32) instructionCMv {
// "c.mv $rs1, $rs2"
return instructionCMv{
rs1: decode_rs1_GPRNoX0_f11t7(instruction),
rs2: decode_rs2_GPRNoX0_f6t2(instruction),
}
}

// Bge

type instructionBge struct {
rs1 uint32
rs2 uint32
imm12 int32
}

func (instructionBge) isInstruction() {}

func decodeBge(instruction uint32) instructionBge {
// "bge $rs1, $rs2, $imm12"
return instructionBge{
rs1: decode_rs1_GPR_f19t15(instruction),
rs2: decode_rs2_GPR_f24t20(instruction),
imm12: decode_simm13_lsb0(decode_imm12_simm13_lsb0_f31t31f7t7f30t25f11t8(instruction)),
}
}

// CAddi

type instructionCAddi struct {
rd uint32
imm int32
}

func (instructionCAddi) isInstruction() {}

func decodeCAddi(instruction uint32) instructionCAddi {
// "c.addi $rd, $imm"
return instructionCAddi{
rd: decode_rd_GPRNoX0_f11t7(instruction),
imm: decode_simm6nonzero(decode_imm_simm6nonzero_f12t12f6t2(instruction)),
}
}

// CAdd

type instructionCAdd struct {
rs1 uint32
rs2 uint32
}

func (instructionCAdd) isInstruction() {}

func decodeCAdd(instruction uint32) instructionCAdd {
// "c.add $rs1, $rs2"
return instructionCAdd{
rs1: decode_rs1_GPRNoX0_f11t7(instruction),
rs2: decode_rs2_GPRNoX0_f6t2(instruction),
}
}

// Bne

type instructionBne struct {
rs1 uint32
rs2 uint32
imm12 int32
}

func (instructionBne) isInstruction() {}

func decodeBne(instruction uint32) instructionBne {
return instructionBne{
rs1: decode_rs1_GPR_f19t15(instruction),
rs2: decode_rs2_GPR_f24t20(instruction),
imm12: decode_simm13_lsb0(decode_imm12_simm13_lsb0_f31t31f7t7f30t25f11t8(instruction)),
}
}

// CEbreak

type instructionCEbreak struct{}

func (instructionCEbreak) isInstruction() {}
38 changes: 38 additions & 0 deletions experimental/go-riscv-sim/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

func main() {
const input = 46

const ioRegister = 10

code := []byte{
// _Z14fib_imperativei
0x2a, 0x86, // 0: c.mv a2, a0
0x89, 0x47, // 2: c.li a5, 2
0x05, 0x45, // 4: c.li a0, 1
0x63, 0xda, 0xc7, 0x00, // 6: bge a5, a2, 20 # .L4
0x05, 0x47, // 10: c.li a4, 1
// .L3
0xaa, 0x86, // 12: c.mv a3, a0
0x85, 0x07, // 14: c.addi a5, 1
0x3a, 0x95, // 16: c.add a0, a4
0x36, 0x87, // 18: c.mv a4, a3
0xe3, 0x1c, 0xf6, 0xfe, // 20: bne a2, a5, -8 # .L3
0x02, 0x90, // c.ebreak
// .L4
0x02, 0x90, // c.ebreak
}

dec := decoder{code: code}
instructions := dec.decodeInstructions()

vm := vm{
instructions: instructions,
}

vm.registers[ioRegister] = input

vm.run(false)

println(vm.registers[ioRegister])
}
46 changes: 46 additions & 0 deletions experimental/go-riscv-sim/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import "testing"

func BenchmarkFibImperative(b *testing.B) {

const input = 46

const ioRegister = 10

code := []byte{
// _Z14fib_imperativei
0x2a, 0x86, // 0: c.mv a2, a0
0x89, 0x47, // 2: c.li a5, 2
0x05, 0x45, // 4: c.li a0, 1
0x63, 0xda, 0xc7, 0x00, // 6: bge a5, a2, 20 # .L4
0x05, 0x47, // 10: c.li a4, 1
// .L3
0xaa, 0x86, // 12: c.mv a3, a0
0x85, 0x07, // 14: c.addi a5, 1
0x3a, 0x95, // 16: c.add a0, a4
0x36, 0x87, // 18: c.mv a4, a3
0xe3, 0x1c, 0xf6, 0xfe, // 20: bne a2, a5, -8 # .L3
0x02, 0x90, // c.ebreak
// .L4
0x02, 0x90, // c.ebreak
}

dec := decoder{code: code}
instructions := dec.decodeInstructions()

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
vm := vm{instructions: instructions}

vm.registers[ioRegister] = input

vm.run(false)

if vm.registers[ioRegister] != 1836311903 {
b.Fatalf("unexpected result: %d", vm.registers[ioRegister])
}
}
}
Loading

0 comments on commit 9159b1a

Please sign in to comment.