62a9c8b84a
- 42 LIR instructions in bootstrap/lir.nim - HIR→LIR lowering in bootstrap/lir_lower.nim - LIR→C emission with full struct/enum/vtable support - All 22 examples + 5 Nim unit tests passing - Updated README status and backend description
315 lines
10 KiB
Nim
315 lines
10 KiB
Nim
## LIR — Low-level Intermediate Representation
|
|
## Linear 3-address code IR, designed for straightforward C emission.
|
|
## Each HIR construct lowers to 5-30 LIR instructions.
|
|
|
|
import types
|
|
|
|
type
|
|
LirKind* = enum
|
|
# ── Data movement ──
|
|
lirMov ## dst = src
|
|
lirLoad ## dst = *(base + offset) [type: base elem type]
|
|
lirStore ## *(base + offset) = src
|
|
lirLoadGlobal ## dst = global_name
|
|
|
|
# ── Arithmetic (3-address, signed) ──
|
|
lirAdd
|
|
lirSub
|
|
lirMul
|
|
lirDiv
|
|
lirMod
|
|
|
|
# ── Bitwise ──
|
|
lirAnd
|
|
lirOr
|
|
lirXor
|
|
lirShl
|
|
lirShr
|
|
|
|
# ── Unary ──
|
|
lirNeg ## dst = -src
|
|
lirNot ## dst = !src (logical)
|
|
lirBNot ## dst = ~src (bitwise)
|
|
|
|
# ── Comparison (dst = 0 or 1) ──
|
|
lirCmpEq
|
|
lirCmpNe
|
|
lirCmpLt
|
|
lirCmpLe
|
|
lirCmpGt
|
|
lirCmpGe
|
|
|
|
# ── Control flow ──
|
|
lirLabel ## label_name:
|
|
lirJmp ## goto target
|
|
lirJz ## if (!cond) goto target
|
|
lirJnz ## if (cond) goto target
|
|
|
|
# ── Calls ──
|
|
lirCall ## dst = callee(args...)
|
|
lirCallVoid ## callee(args...) [no return]
|
|
lirCallIndirect ## dst = (*fn_ptr)(args...)
|
|
|
|
# ── Return ──
|
|
lirRet ## return [val]
|
|
|
|
# ── Stack allocation ──
|
|
lirAlloca ## type dst; (declare a C local)
|
|
|
|
# ── Pointers / addressing ──
|
|
lirAddrOf ## dst = &source_var
|
|
lirFieldPtr ## dst = &(base.field_name)
|
|
lirArrowFieldPtr ## dst = &(base->field_name)
|
|
lirIndexPtr ## dst = &base[idx]
|
|
lirPtrAdd ## dst = base + offset_bytes (raw pointer arithmetic)
|
|
|
|
# ── Type conversion ──
|
|
lirCast ## dst = (target_type)src
|
|
|
|
# ── Composite literals ──
|
|
lirStructInit ## dst = (StructType){.f1=v1, .f2=v2, ...}
|
|
lirSliceInit ## dst = (SliceType){.data=arr, .len=n}
|
|
|
|
# ── Ternary (convenience; lowered from if-expr) ──
|
|
lirSelect ## dst = cond ? a : b
|
|
|
|
# ── Inline C (for runtime calls that C handles natively) ──
|
|
lirRawC ## emit raw C line
|
|
|
|
# ── Source annotation ──
|
|
lirComment ## /* text */
|
|
|
|
LirValueKind* = enum
|
|
lvkTemp ## Virtual register / temp (e.g. "%42")
|
|
lvkVar ## Named variable / parameter
|
|
lvkInt ## Integer literal
|
|
lvkFloat ## Float literal
|
|
lvkString ## String literal (already C-escaped)
|
|
lvkGlobal ## Global variable / constant name
|
|
lvkLabel ## Label reference
|
|
lvkField ## Field name (for struct operations)
|
|
lvkType ## C type name (for casts, alloca, struct init)
|
|
lvkVoid ## No value
|
|
|
|
LirValue* = object
|
|
case kind*: LirValueKind
|
|
of lvkVoid: discard
|
|
of lvkTemp, lvkVar, lvkGlobal, lvkLabel, lvkField, lvkType, lvkString:
|
|
strVal*: string
|
|
of lvkInt:
|
|
intVal*: int64
|
|
of lvkFloat:
|
|
floatVal*: float64
|
|
|
|
LirInstr* = object
|
|
kind*: LirKind
|
|
dst*: LirValue ## Destination (temp or void)
|
|
src*: LirValue ## Source operand
|
|
src2*: LirValue ## Second source operand (for binary ops)
|
|
extra*: seq[LirValue] ## Extra operands (call args, struct fields, etc.)
|
|
locLine*: int ## Source line for debug comments (0 = none)
|
|
locFile*: string ## Source file for debug comments ("" = none)
|
|
|
|
# ── Constructor helpers ──
|
|
|
|
proc lirTemp*(name: string): LirValue =
|
|
LirValue(kind: lvkTemp, strVal: name)
|
|
|
|
proc lirVar*(name: string): LirValue =
|
|
LirValue(kind: lvkVar, strVal: name)
|
|
|
|
proc lirInt*(v: int64): LirValue =
|
|
LirValue(kind: lvkInt, intVal: v)
|
|
|
|
proc lirFloatLit*(v: float64): LirValue =
|
|
LirValue(kind: lvkFloat, floatVal: v)
|
|
|
|
proc lirStr*(v: string): LirValue =
|
|
## Already C-escaped and quoted string
|
|
LirValue(kind: lvkString, strVal: v)
|
|
|
|
proc lirGlobal*(name: string): LirValue =
|
|
LirValue(kind: lvkGlobal, strVal: name)
|
|
|
|
proc lirLabel*(name: string): LirValue =
|
|
LirValue(kind: lvkLabel, strVal: name)
|
|
|
|
proc lirField*(name: string): LirValue =
|
|
LirValue(kind: lvkField, strVal: name)
|
|
|
|
proc lirType*(name: string): LirValue =
|
|
LirValue(kind: lvkType, strVal: name)
|
|
|
|
proc lirVoid*(): LirValue =
|
|
LirValue(kind: lvkVoid)
|
|
|
|
proc `$`*(v: LirValue): string =
|
|
case v.kind
|
|
of lvkVoid: "void"
|
|
of lvkTemp: "%" & v.strVal
|
|
of lvkVar: v.strVal
|
|
of lvkInt: $v.intVal
|
|
of lvkFloat: $v.floatVal
|
|
of lvkString: v.strVal
|
|
of lvkGlobal: "@" & v.strVal
|
|
of lvkLabel: ":" & v.strVal
|
|
of lvkField: "." & v.strVal
|
|
of lvkType: "<" & v.strVal & ">"
|
|
|
|
# ── LIR function ──
|
|
|
|
type
|
|
LirFunc* = object
|
|
name*: string
|
|
params*: seq[tuple[name: string, cType: string]]
|
|
retType*: string ## C return type ("int", "void", etc.)
|
|
instrs*: seq[LirInstr]
|
|
isPublic*: bool
|
|
|
|
LirModule* = object
|
|
funcs*: seq[LirFunc]
|
|
globals*: seq[tuple[name: string, cType: string, initVal: string]]
|
|
structDefs*: seq[string] ## Raw C struct typedef strings
|
|
enumDefs*: seq[string] ## Raw C enum typedef strings
|
|
externs*: seq[string] ## Raw C extern declarations
|
|
includes*: seq[string] ## #include <...>
|
|
preamble*: string ## Raw C code at top of file
|
|
|
|
# ── Builder context (temp counter, label counter) ──
|
|
|
|
type
|
|
LirBuilder* = object
|
|
funcs*: seq[LirFunc]
|
|
tempCounter*: int
|
|
labelCounter*: int
|
|
commentLine*: int
|
|
commentFile*: string
|
|
## Current function being built
|
|
curFunc*: LirFunc
|
|
curFuncActive*: bool
|
|
|
|
proc initLirBuilder*(): LirBuilder =
|
|
result = LirBuilder()
|
|
result.tempCounter = 0
|
|
result.labelCounter = 0
|
|
|
|
proc freshTemp*(b: var LirBuilder): LirValue =
|
|
inc b.tempCounter
|
|
result = lirTemp("_t" & $b.tempCounter)
|
|
|
|
proc freshLabel*(b: var LirBuilder, prefix: string = "L"): LirValue =
|
|
inc b.labelCounter
|
|
result = lirLabel(prefix & $b.labelCounter)
|
|
|
|
# ── Instruction emitters ──
|
|
|
|
proc emit*(b: var LirBuilder, instr: LirInstr) =
|
|
var i = instr
|
|
if b.commentLine > 0:
|
|
i.locLine = b.commentLine
|
|
i.locFile = b.commentFile
|
|
b.curFunc.instrs.add(i)
|
|
|
|
proc emitMov*(b: var LirBuilder, dst, src: LirValue) =
|
|
b.emit(LirInstr(kind: lirMov, dst: dst, src: src))
|
|
|
|
proc emitLoad*(b: var LirBuilder, dst, base: LirValue, offset: int = 0) =
|
|
b.emit(LirInstr(kind: lirLoad, dst: dst, src: base, src2: lirInt(offset)))
|
|
|
|
proc emitStore*(b: var LirBuilder, base: LirValue, src: LirValue, offset: int = 0) =
|
|
b.emit(LirInstr(kind: lirStore, src: src, src2: base, dst: lirInt(offset)))
|
|
|
|
proc emitBinOp*(b: var LirBuilder, op: LirKind, dst, a, bl: LirValue) =
|
|
b.emit(LirInstr(kind: op, dst: dst, src: a, src2: bl))
|
|
|
|
proc emitUnary*(b: var LirBuilder, op: LirKind, dst, src: LirValue) =
|
|
b.emit(LirInstr(kind: op, dst: dst, src: src))
|
|
|
|
proc emitCmp*(b: var LirBuilder, op: LirKind, dst, a, bl: LirValue) =
|
|
b.emit(LirInstr(kind: op, dst: dst, src: a, src2: bl))
|
|
|
|
proc emitLabel*(b: var LirBuilder, label: LirValue) =
|
|
b.emit(LirInstr(kind: lirLabel, src: label))
|
|
|
|
proc emitJmp*(b: var LirBuilder, target: LirValue) =
|
|
b.emit(LirInstr(kind: lirJmp, src: target))
|
|
|
|
proc emitJz*(b: var LirBuilder, target, cond: LirValue) =
|
|
b.emit(LirInstr(kind: lirJz, src: target, src2: cond))
|
|
|
|
proc emitJnz*(b: var LirBuilder, target, cond: LirValue) =
|
|
b.emit(LirInstr(kind: lirJnz, src: target, src2: cond))
|
|
|
|
proc emitCall*(b: var LirBuilder, dst: LirValue, callee: string, args: seq[LirValue]) =
|
|
b.emit(LirInstr(kind: lirCall, dst: dst, src: lirGlobal(callee), extra: args))
|
|
|
|
proc emitCallVoid*(b: var LirBuilder, callee: string, args: seq[LirValue]) =
|
|
b.emit(LirInstr(kind: lirCallVoid, dst: lirVoid(), src: lirGlobal(callee), extra: args))
|
|
|
|
proc emitRet*(b: var LirBuilder, val: LirValue = lirVoid()) =
|
|
b.emit(LirInstr(kind: lirRet, src: val))
|
|
|
|
proc emitAlloca*(b: var LirBuilder, name: string, cType: string) =
|
|
b.emit(LirInstr(kind: lirAlloca, dst: lirVar(name), src: lirType(cType)))
|
|
|
|
proc emitAddrOf*(b: var LirBuilder, dst, src: LirValue) =
|
|
b.emit(LirInstr(kind: lirAddrOf, dst: dst, src: src))
|
|
|
|
proc emitFieldPtr*(b: var LirBuilder, dst, base: LirValue, field: string) =
|
|
b.emit(LirInstr(kind: lirFieldPtr, dst: dst, src: base, src2: lirField(field)))
|
|
|
|
proc emitArrowFieldPtr*(b: var LirBuilder, dst, base: LirValue, field: string) =
|
|
b.emit(LirInstr(kind: lirArrowFieldPtr, dst: dst, src: base, src2: lirField(field)))
|
|
|
|
proc emitIndexPtr*(b: var LirBuilder, dst, base, idx: LirValue) =
|
|
b.emit(LirInstr(kind: lirIndexPtr, dst: dst, src: base, src2: idx))
|
|
|
|
proc emitPtrAdd*(b: var LirBuilder, dst, base, offset: LirValue) =
|
|
b.emit(LirInstr(kind: lirPtrAdd, dst: dst, src: base, src2: offset))
|
|
|
|
proc emitCast*(b: var LirBuilder, dst, src: LirValue, targetType: string) =
|
|
b.emit(LirInstr(kind: lirCast, dst: dst, src: src, src2: lirType(targetType)))
|
|
|
|
proc emitStructInit*(b: var LirBuilder, dst: LirValue, structType: string,
|
|
fields: seq[tuple[name: string, val: LirValue]]) =
|
|
var extras: seq[LirValue] = @[lirType(structType)]
|
|
for f in fields:
|
|
extras.add(lirField(f.name))
|
|
extras.add(f.val)
|
|
b.emit(LirInstr(kind: lirStructInit, dst: dst, extra: extras))
|
|
|
|
proc emitSliceInit*(b: var LirBuilder, dst: LirValue, elemType: string,
|
|
dataPtr: LirValue, length: LirValue) =
|
|
b.emit(LirInstr(kind: lirSliceInit, dst: dst, src: dataPtr, src2: length,
|
|
extra: @[lirType(elemType)]))
|
|
|
|
proc emitSelect*(b: var LirBuilder, dst, cond, thenVal, elseVal: LirValue) =
|
|
b.emit(LirInstr(kind: lirSelect, dst: dst, src: cond, src2: thenVal,
|
|
extra: @[elseVal]))
|
|
|
|
proc emitRawC*(b: var LirBuilder, code: string) =
|
|
b.emit(LirInstr(kind: lirRawC, src: lirStr(code)))
|
|
|
|
proc emitComment*(b: var LirBuilder, text: string) =
|
|
b.emit(LirInstr(kind: lirComment, src: lirStr(text)))
|
|
|
|
# ── Function management ──
|
|
|
|
proc beginFunc*(b: var LirBuilder, name: string, params: seq[tuple[name: string, cType: string]],
|
|
retType: string, isPublic: bool = true) =
|
|
if b.curFuncActive:
|
|
b.funcs.add(b.curFunc)
|
|
b.curFunc = LirFunc(name: name, params: params, retType: retType,
|
|
isPublic: isPublic)
|
|
b.curFuncActive = true
|
|
|
|
proc endFunc*(b: var LirBuilder) =
|
|
if b.curFuncActive:
|
|
b.funcs.add(b.curFunc)
|
|
b.curFuncActive = false
|
|
b.curFunc = LirFunc()
|
|
|
|
proc setSourceLoc*(b: var LirBuilder, line: int, file: string) =
|
|
b.commentLine = line
|
|
b.commentFile = file
|