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.

Function Argument Passing

Stack

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 │  ---  │
└─────────────┴───────┘