The Þog of 2023-06-16 — Plugin VM
As of 2023-07-05, this document is unfinished!
Support for unaligned loads and stores is implementation-defined.
The byte order is little-endian for the file format, instructions, and data values.
Instruction and data memory are in separate spaces. Only the data memory is read-write accessible to the ISA. Instruction space pointers are only word-addressable, data space is octet-addressable.
File Format
struct HeaderSymbol {
offset: uint32, // the instruction word this symbol
// points to, must not be zero
name: [uint8; 60], // zero-padded string
}
struct Header {
magic: [uint8; 4] = "Plg\x00",
data_len: uint32, // must be a multiple of 16
bss_len: uint32, // ditto, bss space is appended to the end
// of data space but is not stored in the file
text_count: uint32, // length in 32 bit words, not bytes
sym_count: uint32,
// what pc will be set to at startup for in-vm initialization
// if zero, the 0th instruction word must be 'eret'
reset_vector: uint32,
// 40 free bytes for implementations to use
userdata: [uint8; 40],
// symbols for the host to call plugin functions
syms: [HeaderSymbol; sym_count],
// these two sections are separate in the ISA,
// data being general purpose read-write space,
// and text being host-read-only instruction memory
data: [uint8; data_len],
text: [uint32; text_count],
}
Registers
There are 32 general purpose 32-bit integer registers. Writes to the zero register are ignored, and reading from it always returns zero.
Floating-point operations share the 32 integer registers.
Instruction Encoding
All instructions are a fixed 32 bits in length, with 128 available opcodes.
Immediate fields must be zero if an instruction does not use them.
RRR Encoding
31 22 17 12 07 04 00 Bit
├───────┼─────┼─────┼─────┼───────┼───────┤
│ imm10 │ rs2 │ rs1 │ rd │ opc │ opf │
└───────┴─────┴─────┴─────┴───────┴───────┘
RRI Encoding
31 17 12 07 04 00 Bit
├─────────────┼─────┼─────┼───────┼───────┤
│ imm15 │ rs1 │ rd │ opc │ opf │
└─────────────┴─────┴─────┴───────┴───────┘
RI Encoding
31 12 07 03 00 Bit
├───────────────────┼─────┼───────┼───────┤
│ imm20 │ rd │ opc │ opf │
└───────────────────┴─────┴───────┴───────┘
Instructions
Instructions either listed as 'reserved' or not listed at all should cause an invalid instruction exception.
An instruction field must be zero if the instruction does not use it.
System (RRI)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 00 │ 00 │ break │ Debugger breakpoint │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 00 │ 01 │ eret │ Halt execution and return to host │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 00 │ 02 │ ecall │ Call host function (imm15) │
└────┴────┴───────┴──────────────────────────────────────────────┘
Memory Ops (RRI)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 00 │ │ Reserved │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 01 │ sw │ Store 32b word (rd) to (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 02 │ sh │ Store 16b short (rd) to (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 03 │ sb │ Store 8b byte (rd) to (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 04 │ │ Reserved │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 05 │ lw │ Load s-ext 32b word (rd) from (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 06 │ lh │ Load s-ext 16b short (rd) from (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 07 │ lb │ Load s-ext 8b byte (rd) from (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 08 │ │ Reserved │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 09 │ lhu │ Load z-ext 16b short (rd) from (rs1 + imm15) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 01 │ 0A │ lbu │ Load z-ext 8b byte (rd) from (rs1 + imm15) │
└────┴────┴───────┴──────────────────────────────────────────────┘
Immediate Arithmetic (RRI)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 00 │ addi │ rd := rs1 + (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 01 │ subi │ rd := rs1 - (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 02 │ andi │ rd := rs1 & (imm15 s-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 03 │ xori │ rd := rs1 ^ (imm15 s-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 04 │ ori │ rd := rs1 | (imm15 s-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 05 │ slli │ rd := rs1 << (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 06 │ srli │ rd := rs1 z-ext>> (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 07 │ srai │ rd := rs1 s-ext>> (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 08 │ muli │ rd := rs1 * (imm15 s-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 09 │ divi │ rd := rs1 /trunc (imm15 s-ext) (0 illegal) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0A │ remi │ rd := rs1 %trunc (imm15 s-ext) (0 illegal) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0B │ icf │ rd := (float)rs1 truncated to int32 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0C │ fci │ rd := rs1 converted to float │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0D │roundf │ rd := round((float)rs1) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0E │ ceilf │ rd := ceil((float)rs1) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 02 │ 0F │floorf │ rd := floor((float)rs1) │
└────┴────┴───────┴──────────────────────────────────────────────┘
Register Arithmetic (RRR)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 00 │ add │ rd := rs1 + rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 01 │ sub │ rd := rs1 - rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 02 │ and │ rd := rs1 & rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 03 │ xor │ rd := rs1 ^ rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 04 │ or │ rd := rs1 | rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 05 │ sll │ rd := rs1 << rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 06 │ srl │ rd := rs1 z-ext>> rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 07 │ sra │ rd := rs1 s-ext>> rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 08 │ mul │ rd := rs1 * rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 09 │ div │ rd := rs1 /trunc rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0A │ rem │ rd := rs1 %trunc rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0B │ addf │ rd := (float)rs1 + (float)rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0C │ subf │ rd := (float)rs1 - (float)rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0D │ mulf │ rd := (float)rs1 * (float)rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0E │ divf │ rd := (float)rs1 / (float)rs2 │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 03 │ 0F │ sqrtf │ rd := sqrt((float)rs1) │
└────┴────┴───────┴──────────────────────────────────────────────┘
JALR and Branches (RRI)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 00 │ jalr │ rd := pc + 1; pc := rs1 + (imm15 z-ext) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 01 │ beq │ if rs1 == rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 02 │ bne │ if rs1 != rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 03 │ blt │ if rs1 <s rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 04 │ bge │ if rs1 >=s rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 05 │ bltu │ if rs1 <u rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 06 │ bgeu │ if rs1 >=u rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 07 │ bltf │ if rs1 <f rs2 { pc += (imm15 s-ext) } │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 04 │ 08 │ bgef │ if rs1 >=f rs2 { pc += (imm15 s-ext) } │
└────┴────┴───────┴──────────────────────────────────────────────┘
RI Stuff (RI)
┌────┬────┬───────┬──────────────────────────────────────────────┐
│ opc│ opf│ Name│ Description│
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 05 │ 00 │ lui │ rd := (imm20 << 12) │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 05 │ 01 │ lif │ rd := (imm20 s-ext) converted to float │
├────┼────┼───────┼──────────────────────────────────────────────┤
│ 05 │ 02 │ jal │ rd := pc + 1; pc = (imm20 z-ext) │
└────┴────┴───────┴──────────────────────────────────────────────┘
Pseudo-instructions
┌─────────────────┬──────────────────────────────────────────────┐
│ Example│ Description│
├─────────────────┼──────────────────────────────────────────────┤
│ nop │ addi zero zero 0 │
├─────────────────┼──────────────────────────────────────────────┤
│ mv rd rs1 │ addi rd rs1 0 │
├─────────────────┼──────────────────────────────────────────────┤
│ not rd rs1 │ xori rd rs1 -1 │
├─────────────────┼──────────────────────────────────────────────┤
│ TODO │ │
└─────────────────┴──────────────────────────────────────────────┘
ABI
This ABI is used both for function calls inside the VM and for host-VM syscalls.
Floats are in the IEEE 754-2008 single-precision format.
The first 256 bytes of data memory is reserved for host-VM interop.
Function Argument Passing
Integers and floats share the same 32-register file.
Signed 8- and 16-bit integers are passed sign-extended.
Unsigned 8- and 16-bit integers are passed zero-extended.
Structure arguments larger than two 32-bit words are passed by reference.
Stack
The stack grows downward and the stack pointer is always 16-byte aligned.
Registers
┌────┬────┬──────────────────────────────────┬────────┐
│ Rn│Name│ Description│ Saver│
├────┼────┼──────────────────────────────────┼────────┤
│ 00 │zero│ Always zero │ ---- │
├────┼────┼──────────────────────────────────┼────────┤
│ 01 │ ra │ Return address │ Caller │
├────┼────┼──────────────────────────────────┼────────┤
│ 02 │ sp │ Stack pointer │ Callee │
├────┼────┼──────────────────────────────────┼────────┤
│ 03 │ fp │ Frame pointer │ Callee │
├────┼────┼──────────────────────────────────┼────────┤
│ 04 │ a0 │ Function arguments/Return values │ Caller │
│ 05 │ a1 │ │ │
│ 06 │ a2 │ │ │
│ 07 │ a3 │ │ │
│ 08 │ a4 │ │ │
│ 09 │ a5 │ │ │
│ 0A │ a6 │ │ │
│ 0B │ a7 │ │ │
├────┼────┼──────────────────────────────────┼────────┤
│ 0C │ t0 │ Temporary registers │ Caller │
│ 0D │ t1 │ │ │
│ 0E │ t2 │ │ │
│ 0F │ t3 │ │ │
│ 10 │ t4 │ │ │
│ 11 │ t5 │ │ │
│ 12 │ t6 │ │ │
│ 13 │ t7 │ │ │
│ 14 │ t8 │ │ │
│ 15 │ t9 │ │ │
├────┼────┼──────────────────────────────────┼────────┤
│ 16 │ s0 │ Saved registers │ Callee │
│ 17 │ s1 │ │ │
│ 18 │ s2 │ │ │
│ 19 │ s3 │ │ │
│ 1A │ s4 │ │ │
│ 1B │ s5 │ │ │
│ 1C │ s6 │ │ │
│ 1D │ s7 │ │ │
│ 1E │ s8 │ │ │
│ 1F │ s9 │ │ │
└────┴────┴──────────────────────────────────┴────────┘
C Types
┌─────────────┬───────┐
│ C Type Name │ Bytes │
├─────────────┼───────┤
│ char │ 1 │
├─────────────┼───────┤
│ short │ 2 │
├─────────────┼───────┤
│ int │ 4 │
├─────────────┼───────┤
│ long │ 4 │
├─────────────┼───────┤
│ long long │ 8 │
├─────────────┼───────┤
│ void * │ 4 │
├─────────────┼───────┤
│ float │ 4 │
├─────────────┼───────┤
│ double │ --- │ The semantics of 'double' and
├─────────────┼───────┤ 'long double' are undefined by this spec.
│ long double │ --- │
└─────────────┴───────┘