## LIR → C Backend ## Emits clean, well-structured C code from LIR instructions. ## Since LIR is already linear and low-level, C emission is straightforward. import std/[strutils, strformat, tables, sequtils, sets] import lir, hir, types, token type LirCBackend* = object output*: string indent*: int tempTypes*: Table[string, string] ## Track C types of temp variables proc initLirCBackend*(): LirCBackend = result = LirCBackend( indent: 0, tempTypes: initTable[string, string](), ) proc emitIndent(be: var LirCBackend) = for i in 0 ..< be.indent: be.output.add(" ") proc emitLine(be: var LirCBackend, s: string) = be.emitIndent() be.output.add(s) be.output.add("\n") proc valToC(be: var LirCBackend, v: LirValue): string = ## Convert a LirValue to its C representation. case v.kind of lvkVoid: "" 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 proc cParamDecl(cType, name: string): string = ## Emit a C parameter declaration, handling function-pointer syntax. if cType.contains("(*)"): return cType.replace("(*)", "(*" & name & ")") else: return cType & " " & name # ── Per-instruction emission ── proc emitInstr(be: var LirCBackend, instr: LirInstr) = template v(x: LirValue): string = valToC(be, x) case instr.kind # ── Data movement ── of lirMov: be.emitLine(&"{v(instr.dst)} = {v(instr.src)};") of lirLoad: # dst = *(base + offset) or dst = base->src2 (if src2 is a field name) if instr.src2.kind == lvkField: be.emitLine(&"{v(instr.dst)} = {v(instr.src)}.{v(instr.src2)};") elif instr.src2.kind == lvkInt and instr.src2.intVal == 0: be.emitLine(&"{v(instr.dst)} = *{v(instr.src)};") elif instr.src2.kind == lvkTemp or instr.src2.kind == lvkVar: be.emitLine(&"{v(instr.dst)} = {v(instr.src)}[{v(instr.src2)}];") else: be.emitLine(&"{v(instr.dst)} = {v(instr.src)}[{v(instr.src2)}];") of lirStore: # *(base + offset) = src if instr.src2.kind == lvkField: be.emitLine(&"{v(instr.src2)}.{v(instr.dst)} = {v(instr.src)};") elif instr.dst.kind == lvkInt and instr.dst.intVal == 0: be.emitLine(&"*{v(instr.src2)} = {v(instr.src)};") elif instr.src2.kind == lvkTemp or instr.src2.kind == lvkVar: be.emitLine(&"{v(instr.src2)}[{v(instr.dst)}] = {v(instr.src)};") else: be.emitLine(&"*({v(instr.src2)} + {v(instr.dst)}) = {v(instr.src)};") of lirLoadGlobal: be.emitLine(&"{v(instr.dst)} = {v(instr.src)};") # ── Arithmetic ── of lirAdd, lirSub, lirMul, lirDiv, lirMod, lirAnd, lirOr, lirXor, lirShl, lirShr: let op = case instr.kind of lirAdd: "+" of lirSub: "-" of lirMul: "*" of lirDiv: "/" of lirMod: "%" of lirAnd: "&" of lirOr: "|" of lirXor: "^" of lirShl: "<<" of lirShr: ">>" else: "?" be.emitLine(&"{v(instr.dst)} = {v(instr.src)} {op} {v(instr.src2)};") of lirNeg: be.emitLine(&"{v(instr.dst)} = -{v(instr.src)};") of lirNot: be.emitLine(&"{v(instr.dst)} = !{v(instr.src)};") of lirBNot: be.emitLine(&"{v(instr.dst)} = ~{v(instr.src)};") # ── Comparison ── of lirCmpEq, lirCmpNe, lirCmpLt, lirCmpLe, lirCmpGt, lirCmpGe: let op = case instr.kind of lirCmpEq: "==" of lirCmpNe: "!=" of lirCmpLt: "<" of lirCmpLe: "<=" of lirCmpGt: ">" of lirCmpGe: ">=" else: "==" be.emitLine(&"{v(instr.dst)} = ({v(instr.src)} {op} {v(instr.src2)});") # ── Control flow ── of lirLabel: be.emitLine(&"{v(instr.src)}:;") # C requires statement after label # Add a null statement to avoid "label at end of compound statement" warnings # Handled by the next instruction naturally of lirJmp: be.emitLine(&"goto {v(instr.src)};") of lirJz: be.emitLine(&"if (!{v(instr.src2)}) goto {v(instr.src)};") of lirJnz: be.emitLine(&"if ({v(instr.src2)}) goto {v(instr.src)};") # ── Calls ── of lirCall: var argsStr = "" for i, arg in instr.extra: if i > 0: argsStr.add(", ") argsStr.add(v(arg)) be.emitLine(&"{v(instr.dst)} = {v(instr.src)}({argsStr});") of lirCallVoid: var argsStr = "" for i, arg in instr.extra: if i > 0: argsStr.add(", ") argsStr.add(v(arg)) be.emitLine(&"{v(instr.src)}({argsStr});") of lirCallIndirect: var argsStr = "" for i, arg in instr.extra: if i > 0: argsStr.add(", ") argsStr.add(v(arg)) if instr.dst.kind != lvkVoid: be.emitLine(&"{v(instr.dst)} = ({v(instr.src)})({argsStr});") else: be.emitLine(&"({v(instr.src)})({argsStr});") # ── Return ── of lirRet: if instr.src.kind != lvkVoid: be.emitLine(&"return {v(instr.src)};") else: be.emitLine("return;") # ── Alloca ── of lirAlloca: var ct = v(instr.src) if instr.dst.strVal.len > 0 and be.tempTypes.hasKey(instr.dst.strVal): let inferred = be.tempTypes[instr.dst.strVal] if inferred != "" and inferred != ct: ct = inferred be.emitLine(cParamDecl(ct, v(instr.dst)) & ";") # ── Pointers ── of lirAddrOf: be.emitLine(&"{v(instr.dst)} = &{v(instr.src)};") of lirFieldPtr: be.emitLine(&"{v(instr.dst)} = &({v(instr.src)}.{v(instr.src2)});") of lirArrowFieldPtr: be.emitLine(&"{v(instr.dst)} = &({v(instr.src)}->{v(instr.src2)});") of lirIndexPtr: be.emitLine(&"{v(instr.dst)} = &({v(instr.src)}[{v(instr.src2)}]);") of lirPtrAdd: be.emitLine(&"{v(instr.dst)} = ({v(instr.src)} + {v(instr.src2)});") # ── Cast ── of lirCast: be.emitLine(&"{v(instr.dst)} = ({v(instr.src2)}){v(instr.src)};") # ── StructInit ── of lirStructInit: let structType = v(instr.extra[0]) var fieldPairs = "" var i = 1 while i < instr.extra.len: let fieldName = v(instr.extra[i]) # e.g. "width" let fieldVal = v(instr.extra[i + 1]) # e.g. "10" if i > 1: fieldPairs.add(", ") fieldPairs.add(&".{fieldName} = {fieldVal}") i += 2 be.emitLine(&"{v(instr.dst)} = ({structType}){{{fieldPairs}}};") # ── SliceInit ── of lirSliceInit: let elemType = v(instr.extra[0]) be.emitLine(&"{v(instr.dst)} = (Slice_{elemType}){{.data = ({elemType}*){v(instr.src)}, .len = {v(instr.src2)}}};") # ── Select (ternary) ── of lirSelect: let elseVal = if instr.extra.len > 0: v(instr.extra[0]) else: "0" be.emitLine(&"{v(instr.dst)} = ({v(instr.src)}) ? {v(instr.src2)} : {elseVal};") # ── Raw C ── of lirRawC: let code = v(instr.src) if code.len > 0: be.emitLine(code) # ── Comment ── of lirComment: let text = v(instr.src) be.emitLine(&"/* {text} */") # ── Function emission ── proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string], funcPtrTypes: Table[string, string]) = var paramsStr = "" for i, p in f.params: if i > 0: paramsStr.add(", ") paramsStr.add(cParamDecl(p.cType, p.name)) if f.params.len == 0: paramsStr = "void" be.emitLine(&"{f.retType} {f.name}({paramsStr}) {{") be.indent += 1 # ── Pass 1: collect types from allocas, params, and instructions ── var varTypes = initTable[string, string]() var tempsSet: seq[string] = @[] for p in f.params: varTypes[p.name] = p.cType be.tempTypes[p.name] = p.cType for instr in f.instrs: if instr.kind == lirAlloca and instr.dst.kind == lvkVar and instr.src.kind == lvkType: varTypes[instr.dst.strVal] = instr.src.strVal be.tempTypes[instr.dst.strVal] = instr.src.strVal if instr.dst.strVal notin tempsSet: tempsSet.add(instr.dst.strVal) # ── Pass 2: iterative type inference for temps ── var changed = true while changed: changed = false for instr in f.instrs: if instr.dst.kind != lvkTemp or instr.dst.strVal.len == 0: continue let name = instr.dst.strVal let oldType = if be.tempTypes.hasKey(name): be.tempTypes[name] else: "" var newType = oldType case instr.kind of lirStructInit: if instr.extra.len > 0 and instr.extra[0].kind == lvkType: newType = instr.extra[0].strVal of lirSliceInit: if instr.extra.len > 0 and instr.extra[0].kind == lvkType: newType = "Slice_" & instr.extra[0].strVal of lirCast: if instr.src2.kind == lvkType: newType = instr.src2.strVal of lirCall: if instr.src.kind == lvkGlobal and funcRetTypes.hasKey(instr.src.strVal): newType = funcRetTypes[instr.src.strVal] of lirCallIndirect: # Conservative; try to infer from dst usage in later passes discard of lirMov: if instr.src.kind == lvkTemp and be.tempTypes.hasKey(instr.src.strVal): newType = be.tempTypes[instr.src.strVal] elif instr.src.kind == lvkVar and varTypes.hasKey(instr.src.strVal): newType = varTypes[instr.src.strVal] of lirLoad, lirLoadGlobal: # Try to deduce pointee type from pointer vars/temps if instr.src.kind == lvkVar and varTypes.hasKey(instr.src.strVal): let srcType = varTypes[instr.src.strVal] if srcType.endsWith("*"): newType = srcType[0 ..< srcType.len - 1] elif srcType.startsWith("Slice_"): newType = srcType[6 ..< srcType.len] elif instr.src.kind == lvkTemp and be.tempTypes.hasKey(instr.src.strVal): let srcType = be.tempTypes[instr.src.strVal] if srcType.endsWith("*"): newType = srcType[0 ..< srcType.len - 1] elif srcType.startsWith("Slice_"): newType = srcType[6 ..< srcType.len] of lirSelect: if instr.src2.kind == lvkTemp and be.tempTypes.hasKey(instr.src2.strVal): newType = be.tempTypes[instr.src2.strVal] elif instr.extra.len > 0 and instr.extra[0].kind == lvkTemp and be.tempTypes.hasKey(instr.extra[0].strVal): newType = be.tempTypes[instr.extra[0].strVal] elif instr.src2.kind == lvkVar and varTypes.hasKey(instr.src2.strVal): newType = varTypes[instr.src2.strVal] of lirAddrOf: if funcPtrTypes.hasKey(instr.src.strVal): newType = funcPtrTypes[instr.src.strVal] else: newType = "void*"; of lirFieldPtr, lirArrowFieldPtr, lirIndexPtr, lirPtrAdd: newType = "void*" of lirAdd, lirSub, lirMul, lirDiv, lirMod, lirNeg, lirCmpEq, lirCmpNe, lirCmpLt, lirCmpLe, lirCmpGt, lirCmpGe, lirAnd, lirOr, lirXor, lirShl, lirShr, lirNot, lirBNot: newType = "int" else: discard if newType != "" and newType != oldType: be.tempTypes[name] = newType changed = true # ── Pass 3: declare temps that were inferred ── var declared: seq[string] = @[] for instr in f.instrs: if instr.kind == lirAlloca and instr.dst.strVal.len > 0 and instr.dst.strVal notin declared: declared.add(instr.dst.strVal) continue if instr.dst.kind == lvkTemp and instr.dst.strVal.len > 0 and instr.dst.strVal notin declared: if be.tempTypes.hasKey(instr.dst.strVal): let ct = be.tempTypes[instr.dst.strVal] if ct != "": declared.add(instr.dst.strVal) be.emitLine(cParamDecl(ct, instr.dst.strVal) & ";") # ── Pass 4: emit instructions ── for instr in f.instrs: be.emitInstr(instr) be.indent -= 1 be.emitLine("}") be.emitLine("") # ── Struct/Enum emission (from HIR module) ── proc typeToCStr(typ: Type): string = ## Duplicate from lir_lower for self-containedness if typ == nil: return "int" case typ.kind of tkVoid: return "void" of tkBool, tkBool8, tkBool16, tkBool32: return "bool" of tkChar8: return "char" of tkChar16: return "char16_t" of tkChar32: return "char32_t" of tkStr: return "const char*" of tkInt8: return "int8_t" of tkInt16: return "int16_t" of tkInt32: return "int32_t" of tkInt64: return "int64_t" of tkInt: return "int" of tkUInt8: return "uint8_t" of tkUInt16: return "uint16_t" of tkUInt32: return "uint32_t" of tkUInt64: return "uint64_t" of tkUInt: return "unsigned int" of tkFloat32: return "float" of tkFloat64: return "double" of tkPointer, tkRef, tkMutRef: if typ.inner.len > 0: return typeToCStr(typ.inner[0]) & "*" return "void*" of tkDynRef: return typ.name & "_FatPtr" of tkSlice: let elem = if typ.inner.len > 0: typeToCStr(typ.inner[0]) else: "void" return "Slice_" & elem.replace(" ", "_").replace("*", "Ptr") of tkNamed: case typ.name of "String", "str": return "const char*" of "int": return "int" of "int8": return "int8_t" of "int16": return "int16_t" of "int32": return "int32_t" of "int64": return "int64_t" of "uint": return "unsigned int" of "uint8": return "uint8_t" of "uint16": return "uint16_t" of "uint32": return "uint32_t" of "uint64": return "uint64_t" of "float32": return "float" of "float64": return "double" of "bool": return "bool" else: return typ.name of tkFunc: if typ.inner.len == 0: return "void (*)(void)" let params = typ.inner[0..^2].mapIt(typeToCStr(it)).join(", ") let ret = typeToCStr(typ.inner[^1]) return ret & " (*)(" & params & ")" else: return "int" proc emitStructDef(be: var LirCBackend, name: string, fields: seq[tuple[name: string, typ: Type]]) = be.emitLine(&"typedef struct {name} {{") be.indent += 1 for f in fields: be.emitLine(&"{typeToCStr(f.typ)} {f.name};") be.indent -= 1 be.emitLine(&"}} {name};") be.emitLine("") proc emitEnumDef(be: var LirCBackend, name: string, variants: seq[HirEnumVariant]) = var hasData = false for v in variants: if v.fields.len > 0 or v.namedFields.len > 0: hasData = true break if not hasData: # Simple enum be.emitLine(&"typedef enum {{") be.indent += 1 for i, v in variants: if i < variants.len - 1: be.emitLine(&"{name}_{v.name},") else: be.emitLine(&"{name}_{v.name}") be.indent -= 1 be.emitLine(&"}} {name};") be.emitLine("") else: # Tagged union be.emitLine(&"typedef enum {{") be.indent += 1 for i, v in variants: if i < variants.len - 1: be.emitLine(&"{name}_{v.name},") else: be.emitLine(&"{name}_{v.name}") be.indent -= 1 be.emitLine(&"}} {name}_Tag;") be.emitLine("") be.emitLine(&"typedef union {{") be.indent += 1 for v in variants: if v.fields.len > 0: for i, f in v.fields: be.emitLine(&"{typeToCStr(f)} {v.name}_{i};") elif v.namedFields.len > 0: be.emitLine(&"struct {{") be.indent += 1 for nf in v.namedFields: be.emitLine(&"{typeToCStr(nf.typ)} {nf.name};") be.indent -= 1 be.emitLine(&"}} {v.name};") be.indent -= 1 be.emitLine(&"}} {name}_Data;") be.emitLine("") be.emitLine(&"typedef struct {{") be.indent += 1 be.emitLine(&"{name}_Tag tag;") be.emitLine(&"{name}_Data data;") be.indent -= 1 be.emitLine(&"}} {name};") be.emitLine("") # ── Type dependency ordering ── proc collectValueDeps(typ: Type): seq[string] = ## Return type names that must be fully defined before a value of `typ` ## can be declared. Pointers/refs only need a forward declaration, so ## they do not introduce a dependency. if typ == nil: return @[] case typ.kind of tkNamed: return @[typ.name] of tkSlice: return @[typeToCStr(typ)] of tkPointer, tkRef, tkMutRef, tkTuple, tkFunc: return @[] else: return @[] proc emitSliceTypeDef(be: var LirCBackend, name: string, elem: string) = be.emitLine(&"typedef struct {{ {elem}* data; size_t len; }} {name};") # ── Module emission ── proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): string = ## Emit full C source from LIR builder + HIR module metadata. be.output = "" # Build function return type lookup table var funcRetTypes = initTable[string, string]() for f in module.funcs: funcRetTypes[f.name] = typeToCStr(f.retType) for f in module.externFuncs: funcRetTypes[f.name] = typeToCStr(f.retType) # Build function-pointer type lookup table (for address-of) var funcPtrTypes = initTable[string, string]() for f in module.funcs: let params = f.params.mapIt(typeToCStr(it.typ)).join(", ") let ret = typeToCStr(f.retType) funcPtrTypes[f.name] = ret & " (*)(" & params & ")" for f in module.externFuncs: let params = f.params.mapIt(typeToCStr(it.typ)).join(", ") let ret = typeToCStr(f.retType) funcPtrTypes[f.name] = ret & " (*)(" & params & ")" # Header be.emitLine("/* Generated by Bux Compiler (LIR backend) */") be.emitLine("#include ") be.emitLine("#include ") be.emitLine("#include ") be.emitLine("#include ") be.emitLine("#include ") be.emitLine("") # Forward struct declarations for s in module.structs: be.emitLine(&"typedef struct {s.name} {s.name};") if module.structs.len > 0: be.emitLine("") # Forward trait object declarations for iface in module.interfaces: if not iface.hasAssocTypes: be.emitLine(&"typedef struct {iface.name}_FatPtr {iface.name}_FatPtr;") if module.interfaces.len > 0: be.emitLine("") # Extern declarations if module.externFuncs.len > 0: be.emitLine("/* Extern function declarations */") for ef in module.externFuncs: let rt = typeToCStr(ef.retType) var params: seq[string] = @[] for p in ef.params: params.add(cParamDecl(typeToCStr(p.typ), p.name)) if params.len == 0: params.add("void") be.emitLine(&"extern {rt} {ef.name}({params.join(\", \")});") be.emitLine("") # Constants as #define if module.consts.len > 0: be.emitLine("/* Constants */") for c in module.consts: if c.value != nil and c.value.kind == hLit: case c.value.litToken.kind of tkIntLiteral: be.emitLine(&"#define {c.name} {c.value.litToken.text}") of tkStringLiteral: be.emitLine(&"#define {c.name} \"{c.value.litToken.text}\"") of tkBoolLiteral: be.emitLine(&"#define {c.name} {c.value.litToken.text}") else: discard be.emitLine("") # Collect local type names (structs and enums defined in this module). var localTypeNames: HashSet[string] for s in module.structs: localTypeNames.incl(s.name) for e in module.enums: localTypeNames.incl(e.name) # Collect slice types used in struct fields and enum payloads. var sliceTypes: seq[tuple[name: string, elem: string]] = @[] var sliceNames: HashSet[string] proc registerSlice(t: Type) = if t == nil or t.kind != tkSlice: return let name = typeToCStr(t) if sliceNames.contains(name): return sliceNames.incl(name) let elem = if t.inner.len > 0: typeToCStr(t.inner[0]) else: "void" sliceTypes.add((name, elem)) for s in module.structs: for f in s.fields: registerSlice(f.typ) for e in module.enums: for v in e.variants: for ft in v.fields: registerSlice(ft) for nf in v.namedFields: registerSlice(nf.typ) # Build dependency graph among structs, enums, and slice types. # Edge A -> B means "A depends on B, so B must be emitted before A". var deps: Table[string, seq[string]] for s in module.structs: deps[s.name] = @[] for e in module.enums: deps[e.name] = @[] for st in sliceTypes: deps[st.name] = @[] proc addDeps(node: string, t: Type) = for dep in collectValueDeps(t): if dep == node: continue if localTypeNames.contains(dep) or sliceNames.contains(dep): if dep notin deps[node]: deps[node].add(dep) for s in module.structs: for f in s.fields: addDeps(s.name, f.typ) for e in module.enums: for v in e.variants: for ft in v.fields: addDeps(e.name, ft) for nf in v.namedFields: addDeps(e.name, nf.typ) # Topological sort (Kahn's algorithm). var inDegree: Table[string, int] var dependents: Table[string, seq[string]] for node in deps.keys: inDegree[node] = 0 for node, nodeDeps in deps: for d in nodeDeps: if not inDegree.hasKey(d): inDegree[d] = 0 inDegree[node] += 1 dependents.mgetOrPut(d, @[]).add(node) var queue: seq[string] = @[] for node, deg in inDegree: if deg == 0: queue.add(node) var sorted: seq[string] = @[] while queue.len > 0: let node = queue.pop() sorted.add(node) for depNode in dependents.getOrDefault(node): inDegree[depNode] -= 1 if inDegree[depNode] == 0: queue.add(depNode) if sorted.len < deps.len: # Cycle detected; fall back to a safe deterministic order. sorted = @[] for s in module.structs: sorted.add(s.name) for e in module.enums: sorted.add(e.name) for st in sliceTypes: sorted.add(st.name) # Map type names back to their definitions. var structMap: Table[string, seq[tuple[name: string, typ: Type]]] for s in module.structs: structMap[s.name] = s.fields var enumMap: Table[string, seq[HirEnumVariant]] for e in module.enums: enumMap[e.name] = e.variants var sliceMap: Table[string, string] for st in sliceTypes: sliceMap[st.name] = st.elem # Emit type definitions in dependency order. for name in sorted: if structMap.hasKey(name): be.emitStructDef(name, structMap[name]) elif enumMap.hasKey(name): be.emitEnumDef(name, enumMap[name]) elif sliceMap.hasKey(name): be.emitSliceTypeDef(name, sliceMap[name]) # Forward function declarations for f in module.funcs: let rt = typeToCStr(f.retType) var params: seq[string] = @[] for p in f.params: params.add(cParamDecl(typeToCStr(p.typ), p.name)) if params.len == 0: params.add("void") be.emitLine(&"{rt} {f.name}({params.join(\", \")});") be.emitLine("") # VTable and fat pointer structs for iface in module.interfaces: if iface.hasAssocTypes: continue let iname = iface.name be.emitLine(&"typedef struct {iname}_VTable {{") be.indent += 1 for m in iface.methods: var paramCTypes: seq[string] = @["void* self"] for i in 1 ..< m.params.len: paramCTypes.add(cParamDecl(typeToCStr(m.params[i]), "param")) let rt = typeToCStr(m.ret) be.emitLine(&"{rt} (*{m.name})({paramCTypes.join(\", \")});") be.indent -= 1 be.emitLine(&"}} {iname}_VTable;") be.emitLine(&"typedef struct {iname}_FatPtr {{") be.indent += 1 be.emitLine("void* data;") be.emitLine(&"{iname}_VTable* vtable;") be.indent -= 1 be.emitLine(&"}} {iname}_FatPtr;") be.emitLine("") # VTable instances for vt in module.vtables: if vt.hasAssocTypes: continue let varName = vt.concreteType & "_" & vt.interfaceName & "_VTable" be.emitLine(&"{vt.interfaceName}_VTable {varName} = {{") be.indent += 1 for m in vt.methodNames: be.emitLine(&".{m} = (void*){vt.concreteType}_{m},") be.indent -= 1 be.emitLine("};") be.emitLine("") # Emit env structs for closures with captures for f in module.funcs: if f.captureNames.len > 0 and f.envStructName != "": be.emitLine(&"struct {f.envStructName} {{") be.indent += 1 for i in 0 ..< f.captureNames.len: let capName = f.captureNames[i] let capType = if i < f.captureTypes.len: typeToCStr(f.captureTypes[i]) else: "int" be.emitLine(&"{capType} {capName};") be.indent -= 1 be.emitLine("};") be.emitLine(&"static struct {f.envStructName} {f.envInstanceName};") be.emitLine("") # Emit all LIR functions for f in builder.funcs: be.emitFunc(f, funcRetTypes, funcPtrTypes) # C main wrapper var hasMain = false for f in module.funcs: if f.name == "Main": hasMain = true break if hasMain: be.emitLine("/* C entry point wrapper */") be.emitLine("extern int g_argc;") be.emitLine("extern char** g_argv;") be.emitLine("int main(int argc, char** argv) {") be.emitLine(" g_argc = argc;") be.emitLine(" g_argv = argv;") be.emitLine(" return Main();") be.emitLine("}") return be.output