a super regular RISC that encodes constants in immediate blocks.
Figure 1 - instruction (pc-relative) and constant (ib-relative) streams
glyph is a super regular RISC architecture that encodes constants in a secondary constant stream accessed via an immediate base register. the immediate base register branches like the program counter and the call instructions sets (pc,ib) together for procedure calls and returns.
glyph uses relative addresses in its link register which is different to typical RISC architectures. glyph needs to do this for the branch instruction to fit (pc,ib) into the link register for compatibility. glyph achieves this by packing together two 32-bit relative (pc,ib) displacements in an i32x2 vector.
immediate blocks can be linked together using relative displacements and switched using the constant branch instruction detailed below. immediate blocks, unlike typical RISC architectures, mean that most relocations are word sized like CISC architectures, and can use C-style structure packing and alignment rules.
this list of points outlines the primary differentiating elements of the super regular RISC architecture:
- variable length instruction format supporting 16, 32, 64, and 128-bit instructions.
- 1-bit predicate for compare, branch, add with carry, and subtract with borrow.
- 16-bit compressed instruction packets that can access 8x64-bit registers.
- (pc,ib) is a special program counter and immediate base register address vector.
- link register contains packed i32x2 relative address vector to function entry.
ibj(immediate-block-jump) adds a relative address to the immediate base register.movw(move-word-immediate-block) uses unsigned displacement to access constants.jalib(jump-and-link-immediate-block) or (call) links address vector and adds constants to (pc,ib).jtlib(jump-to-link-immediate-block) or (ret) subtracts link vector from and adds constants to (pc,ib).pin(pack-indirect) packs two absolute addresses as relative address vector from (pc,ib).
compiled versions of the architecture specification are available from the following URLs:
- current: glyph.pdf
- latest: glyph-20251105.pdf
this list outlines instructions, opcodes, and descriptions used in the 16-bit compressed instruction packet:
| nr | instruction | form | code | description |
|---|---|---|---|---|
| 00 | break | op0r_imm9_16 | 00000 | break uimm9 |
| 01 | j | op0r_imm9_16 | 00001 | jump simm9*2 |
| 02 | b | op0r_imm9_16 | 00010 | branch simm9*2 |
| 03 | ibj | op0r_imm9_16 | 00011 | ib-jump simm9*64 |
| 04 | link.i64 | op1r_imm6_16 | 00100 | link fun3,ib64(uimm6*8) |
| fun3[2:1]=jib,jalib,jtlib,jalalib | ||||
| 05 | movh.i64 | op1r_imm6_16 | 00101 | move-half rc,ib32(uimm6*8) |
| 06 | movw.i64 | op1r_imm6_16 | 00110 | move-word rc,ib64(uimm6*8) |
| 07 | movi.i64 | op1r_imm6_16 | 00111 | mov-imm6 rc,simm6 |
| 08 | addi.i64 | op1r_imm6_16 | 01000 | add-imm6 rc,simm6 |
| 09 | srli.i64 | op1r_imm6_16 | 01001 | shift-right-logical-imm rc,uimm6 |
| 10 | srai.i64 | op1r_imm6_16 | 01010 | shift-right-arith-imm rc,uimm6 |
| 11 | slli.i64 | op1r_imm6_16 | 01011 | shift-left-logical-imm rc,uimm6 |
| 12 | addh.i64 | op1r_imm6_16 | 01100 | add-half rc,ib32(uimm6*4) |
| 13 | leapc.i64 | op1r_imm6_16 | 01101 | lea-pc rc,ib32(uimm6*4)(pc) |
| 14 | loadpc.i64 | op1r_imm6_16 | 01110 | load-pc rc,ib32(uimm6*4)(pc) |
| 15 | storepc.i64 | op1r_imm6_16 | 01111 | store-pc rc,ib32(uimm6*4)(pc) |
| 16 | load.i64 | op2r_imm3_16 | 10000 | load rc,(uimm3*8)(rb) |
| 17 | store.i64 | op2r_imm3_16 | 10001 | store rc,(uimm3*8)(rb) |
| 18 | compare.i64 | op2r_fun3_16 | 10010 | compare rc,rb |
| fun3=lt,ge,eq,ne,ltu,geu,cmov,ncmov | ||||
| 19 | logic.i64 | op2r_fun3_16 | 10011 | logic rc,rb |
| fun3=mov,not,neg,bswap,ctz,clz,ctpop,sext | ||||
| 20 | pin.i64 | op3r_16 | 10100 | pack-indirect rc,rb,ra |
| i32x2(rc) = (pc-ra+2,ib-rb); | ||||
| 21 | and.i64 | op3r_16 | 10101 | and rc,rb,ra |
| 22 | or.i64 | op3r_16 | 10110 | or rc,rb,ra |
| 23 | xor.i64 | op3r_16 | 10111 | xor rc,rb,ra |
| 24 | add.i64 | op3r_16 | 11000 | add rc,rb,ra |
| 25 | srl.i64 | op3r_16 | 11001 | shift-right-logical rc,rb,ra |
| 26 | sra.i64 | op3r_16 | 11010 | shift-right-arith rc,rb,ra |
| 27 | sll.i64 | op3r_16 | 11011 | shift-left-logical rc,rb,ra |
| 28 | sub.i64 | op3r_16 | 11100 | sub rc,rb,ra |
| 29 | mul.i64 | op3r_16 | 11101 | mul rc,rb,ra |
| 30 | div.i64 | op3r_16 | 11110 | div rc,rb,ra |
| 31 | illegal | op0r_imm9_16 | 11111 | illegal uimm9 |
the 16-bit instruction packet, while intended to be used in conjunction with the 32-bit opcodes, is designed as a complete subset, so there is an ABI variant that targets a subset using only the 16-bit opcodes.
the register assignment for the 16-bit subset was chosen with this rationale:
- 2 blocks of 4 contiguous non-volatile callee-save and volatile caller-save registers.
- 3 special registers, 2 argument registers, 1 temporary register, and 3 save registers.
- 3 save registers to avoid excessive spilling around function calls.
- 1 temporary register to avoid spilling arguments to free a temporary.
the calling convention for the 16-bit subset is as follows:
- immediate base
ibis set bycallinstructions and must point to a valid immediate block on function entry. function symbols are exported with two labels; one in the.textsection, and one in the.constsection. immediate base must be restored to the entry value in the function epilogue before it can be restored byret. - argument registers
a0anda1are used for the first two arguments, and the remaining arguments are passed on the stack. return value is places ina0anda1, temporary registert0is a volatile register, and frame pointer (if enabled) usess0. there are two more non-volatile callee-save registers,s1ands2.
the following table outlines the 16-bit register assignment, showing register name alias, description, and non-volatile callee-save or volatile caller-save status.
| name | alias | description | save |
|---|---|---|---|
| r0 | sp | stack pointer | callee |
| r1 | s0/fp | saved register 0 / frame pointer | callee |
| r2 | s1 | saved register 1 | callee |
| r3 | s2 | saved register 2 | callee |
| r4 | t0 | temporary register 0 | caller |
| r5 | a0 | argument register 0 | caller |
| r6 | a1 | argument register 1 | caller |
| r7 | ra | return address / (pc,ib) link vector | caller |
glyph uses a super regular RISC encoding designed for vectorized decoders. the variable length instruction encoding supports 16-bit, 32-bit, 64-bit, and 128-bit instruction packets. each 16-bit packet has 2-bits for size. in contrast, RISC-V currently has a variable size field which requires up to 7-bits for 64-bit instruction packets. glyph size decoding logic is simpler and easily supports 16-wide decoders (256-bits) with 8x32-bit instructions. for this reason, glyph does not support 48-bit instructions.
Figure 2 - one, two, and three operand 16-bit instruction formats
Figure 3 - one, two, and three operand 32-bit instruction formats
Figure 4 - one, two, and three operand 64-bit instruction formats
this repository contains three implementations of the super regular RISC architecture. there is a simple interpreter written in C, another one written in Go, and a reference interpreter written in Python.
the simple interpreter in C can be built using CMake. presently the implementation is constrained to inline functions in one header.
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build
the simple interpreter in C, the Go interpreter and the reference interpreter in Python all support the 16-bit compressed opcodes and can be launched using the test script which runs all three then diffs the output of the tests to perform differential testing between them.
python3 scripts/test.py
this Python script allows one to explore the combinatorial decode window for various widths using 16-bit alignment for instructions. the following invocation prints the decode offsets for a 4-wide 64-bit decoder.
./scripts/combo.py --print-decode -w 4
Figure 5 - screenshot showing partial output from combos.py




