mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 00:52:33 +00:00
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.
131 lines
3.8 KiB
Odin
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])
|
|
}
|
|
}
|