Files
Odin/core/rexcode/ir/print.odin
Brendan Punsky daa5b7cb79 rexcode: add core:rexcode/ir — the IR API layer (no concrete IR yet)
A sibling to core:rexcode/isa for the intermediate representations (WASM,
SPIR-V, LLVM bitcode + the LLVM dialects AIR/DXIL). Holds the shared
vocabulary every IR package builds on, implements no specific IR.

Design stance (see docs/ir_design.md): keep the ISA layer's spirit, but
where IRs are structurally MORE uniform than ISAs (SSA + a type system
regularize the operand/module shape), the shared core is richer. ir/ owns:

  status.odin  Error/Error_Code (shape-identical to isa.Error)
  refs.odin    Id/Ref/Ref_Space/Symbol_Table (the label analog: structural
               id references, not PC-relative byte offsets)
  types.odin   Type/Type_Ref/Type_Kind (the type table -- no ISA analog)
  module.odin  Module/Function/Block/Operation/Operand/Result/Dataflow
               (the structured model; Operation = isa.Instruction + an
               optional typed Result, opcode a u16 like Mnemonic)
  print.odin   token kinds + options + num-fmt (parallels isa.print)

Three honest concessions vs the ISA API, made explicit not inert: a
structured Module replaces the flat []Instruction; a first-class type
system; id-based entity refs replace labels. The encode/decode verbs take
a Module and drop label_defs/resolve/base_address. Dataflow hosts both the
WASM value stack and SSA; the codec is pluggable (table for WASM/SPIR-V,
bitstream for the LLVM family -- AIR/DXIL are LLVM dialects, not peers).

Package compiles; a hand-built SSA module round-trips through the types.
2026-06-18 19:03:27 -04:00

131 lines
3.8 KiB
Odin

// rexcode · Brendan Punsky (dotbmp@github), original author
package rexcode_ir
// =============================================================================
// PRINTER FRAMEWORK (shared scaffolding -- parallels isa.print)
// =============================================================================
//
// Same role as isa.print: the universal pieces of textual output (token kinds
// for highlighting, print options, the result type, number-formatting helpers).
// A concrete IR's printer (WAT / SPIR-V disasm / LLVM `.ll`) owns the syntax of
// types, value names, blocks, and the output-sink procedures, and calls these
// helpers for hex/decimal. Kept independent of isa.print so the two siblings do
// not couple; the `Token_Kind` set adds the IR-only categories.
import "core:strings"
import "core:reflect"
Token_Kind :: enum u8 {
WHITESPACE,
NEWLINE,
OFFSET, // byte/word offset prefix
KEYWORD, // `func` / `block` / `define` / `OpLabel` style keywords
OPCODE, // the operation mnemonic
TYPE, // a type reference / spelling (IR-only)
VALUE_REF, // a use of an SSA value / local (IR-only)
RESULT, // a value definition (`%3 =`) (IR-only)
BLOCK_LABEL, // a basic-block / branch-target label (IR-only)
GLOBAL_REF, // function / global / symbol reference (IR-only)
IMMEDIATE, // literal constant
ATTRIBUTE, // dialect attribute / decoration / flag (IR-only)
PUNCTUATION, // `(`, `)`, `,`, `=`, `:`
COMMENT,
}
Token :: struct {
offset: u32, // byte offset in the output string
length: u16,
kind: Token_Kind,
operation_index: u16, // which operation (0xFFFF for module-level / whitespace)
}
@(require_results)
token_kind_to_string :: proc(k: Token_Kind) -> string {
if name, ok := reflect.enum_name_from_value(k); ok {
return name
}
return "???"
}
// -----------------------------------------------------------------------------
// Print options & result (same shape as isa, IR-flavoured defaults)
// -----------------------------------------------------------------------------
Print_Options :: struct {
uppercase: bool,
hex_prefix: string, // default "0x"
hex_lowercase: bool,
value_prefix: string, // SSA value sigil, default "%"
block_prefix: string, // block-label sigil, default "^"
show_offsets: bool,
indent: string, // default " "
separator: string, // default "\n"
}
DEFAULT_PRINT_OPTIONS :: Print_Options{
uppercase = false,
hex_prefix = "0x",
hex_lowercase = true,
value_prefix = "%",
block_prefix = "^",
show_offsets = false,
indent = " ",
separator = "\n",
}
Print_Result :: struct {
text: string,
tokens: []Token, // nil unless requested
}
// -----------------------------------------------------------------------------
// Number formatting helpers (used by every IR printer -- arch/IR-agnostic)
// -----------------------------------------------------------------------------
print_hex :: proc(sb: ^strings.Builder, value: u64, options: ^Print_Options) {
strings.write_string(sb, options.hex_prefix)
print_hex_digits(sb, value, options)
}
print_hex_digits :: proc(sb: ^strings.Builder, value: u64, options: ^Print_Options) {
if value == 0 {
strings.write_byte(sb, '0')
return
}
buf: [16]u8
i := 0
v := value
for v > 0 {
digit := u8(v & 0xF)
buf[i] = digit < 10 ? '0' + digit : 'a' + digit - 10
v >>= 4
i += 1
}
for j := i - 1; j >= 0; j -= 1 {
c := buf[j]
if options.uppercase && c >= 'a' && c <= 'f' {
c -= 32
}
strings.write_byte(sb, c)
}
}
print_decimal :: proc(sb: ^strings.Builder, value: u32) {
if value == 0 {
strings.write_byte(sb, '0')
return
}
buf: [10]u8
i := 0
v := value
for v > 0 {
buf[i] = '0' + u8(v % 10)
v /= 10
i += 1
}
for j := i - 1; j >= 0; j -= 1 {
strings.write_byte(sb, buf[j])
}
}