-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add RISC-V VM which can run the imperative fib benchmark
- Loading branch information
Showing
8 changed files
with
527 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) | ||
} | ||
} | ||
} |
Oops, something went wrong.