diff options
| author | Veikka Tuominen <git@vexu.eu> | 2023-10-02 07:08:53 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-02 07:08:53 +0300 |
| commit | fc4d53e2ea6b41440e37caf32d2fd236d0f58c93 (patch) | |
| tree | be400bc7033d3f198978ad04c05c14f15b8c5324 /deps/aro/CodeGen.zig | |
| parent | 0f1652dc603ad43be733cfdd721cedf38d9e45d9 (diff) | |
| parent | 5792570197f44b2c7599fb756f5c1e9d59bd0a9a (diff) | |
| download | zig-fc4d53e2ea6b41440e37caf32d2fd236d0f58c93.tar.gz zig-fc4d53e2ea6b41440e37caf32d2fd236d0f58c93.zip | |
Merge pull request #17221 from Vexu/aro-translate-c
Aro translate-c
Diffstat (limited to 'deps/aro/CodeGen.zig')
| -rw-r--r-- | deps/aro/CodeGen.zig | 1291 |
1 files changed, 1291 insertions, 0 deletions
diff --git a/deps/aro/CodeGen.zig b/deps/aro/CodeGen.zig new file mode 100644 index 0000000000..6febbbb917 --- /dev/null +++ b/deps/aro/CodeGen.zig @@ -0,0 +1,1291 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const BuiltinFunction = @import("builtins/BuiltinFunction.zig"); +const Compilation = @import("Compilation.zig"); +const Interner = @import("Interner.zig"); +const Ir = @import("Ir.zig"); +const Builder = Ir.Builder; +const StringId = @import("StringInterner.zig").StringId; +const Tree = @import("Tree.zig"); +const NodeIndex = Tree.NodeIndex; +const Type = @import("Type.zig"); +const Value = @import("Value.zig"); + +const CodeGen = @This(); + +const WipSwitch = struct { + cases: Cases = .{}, + default: ?Ir.Ref = null, + size: u64, + + const Cases = std.MultiArrayList(struct { + val: Interner.Ref, + label: Ir.Ref, + }); +}; + +const Symbol = struct { + name: StringId, + val: Ir.Ref, +}; + +const Error = Compilation.Error; + +tree: Tree, +comp: *Compilation, +builder: Builder, +node_tag: []const Tree.Tag, +node_data: []const Tree.Node.Data, +node_ty: []const Type, +wip_switch: *WipSwitch = undefined, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +ret_nodes: std.ArrayListUnmanaged(Ir.Inst.Phi.Input) = .{}, +phi_nodes: std.ArrayListUnmanaged(Ir.Inst.Phi.Input) = .{}, +record_elem_buf: std.ArrayListUnmanaged(Interner.Ref) = .{}, +cond_dummy_ty: ?Interner.Ref = null, +bool_invert: bool = false, +bool_end_label: Ir.Ref = .none, +cond_dummy_ref: Ir.Ref = undefined, +continue_label: Ir.Ref = undefined, +break_label: Ir.Ref = undefined, +return_label: Ir.Ref = undefined, + +pub fn generateTree(comp: *Compilation, tree: Tree) Compilation.Error!void { + var c = CodeGen{ + .builder = .{ + .gpa = comp.gpa, + .arena = std.heap.ArenaAllocator.init(comp.gpa), + }, + .tree = tree, + .comp = comp, + .node_tag = tree.nodes.items(.tag), + .node_data = tree.nodes.items(.data), + .node_ty = tree.nodes.items(.ty), + }; + defer c.symbols.deinit(c.comp.gpa); + defer c.ret_nodes.deinit(c.comp.gpa); + defer c.phi_nodes.deinit(c.comp.gpa); + defer c.record_elem_buf.deinit(c.comp.gpa); + defer c.builder.deinit(); + + const node_tags = tree.nodes.items(.tag); + for (tree.root_decls) |decl| { + c.builder.arena.deinit(); + c.builder.arena = std.heap.ArenaAllocator.init(comp.gpa); + + switch (node_tags[@intFromEnum(decl)]) { + .static_assert, + .typedef, + .struct_decl_two, + .union_decl_two, + .enum_decl_two, + .struct_decl, + .union_decl, + .enum_decl, + => {}, + + .fn_proto, + .static_fn_proto, + .inline_fn_proto, + .inline_static_fn_proto, + .extern_var, + .threadlocal_extern_var, + => {}, + + .fn_def, + .static_fn_def, + .inline_fn_def, + .inline_static_fn_def, + => c.genFn(decl) catch |err| switch (err) { + error.FatalError => return error.FatalError, + error.OutOfMemory => return error.OutOfMemory, + }, + + .@"var", + .static_var, + .threadlocal_var, + .threadlocal_static_var, + => c.genVar(decl) catch |err| switch (err) { + error.FatalError => return error.FatalError, + error.OutOfMemory => return error.OutOfMemory, + }, + else => unreachable, + } + } +} + +fn genType(c: *CodeGen, base_ty: Type) !Interner.Ref { + var key: Interner.Key = undefined; + const ty = base_ty.canonicalize(.standard); + switch (ty.specifier) { + .void => return .void, + .bool => return .i1, + .@"struct" => { + key = .{ + .record = .{ + .user_ptr = ty.data.record, + .elements = undefined, // Not needed for hash lookup. + }, + }; + if (c.builder.pool.has(key)) |some| return some; + const elem_buf_top = c.record_elem_buf.items.len; + defer c.record_elem_buf.items.len = elem_buf_top; + + for (ty.data.record.fields) |field| { + if (!field.isRegularField()) { + return c.comp.diag.fatalNoSrc("TODO lower struct bitfields", .{}); + } + // TODO handle padding bits + const field_ref = try c.genType(field.ty); + try c.record_elem_buf.append(c.builder.gpa, field_ref); + } + + key.record.elements = try c.builder.arena.allocator().dupe(Interner.Ref, c.record_elem_buf.items[elem_buf_top..]); + return c.builder.pool.put(c.builder.gpa, key); + }, + .@"union" => { + return c.comp.diag.fatalNoSrc("TODO lower union types", .{}); + }, + else => {}, + } + if (ty.isPtr()) return .ptr; + if (ty.isFunc()) return .func; + if (!ty.isReal()) return c.comp.diag.fatalNoSrc("TODO lower complex types", .{}); + if (ty.isInt()) { + const bits = ty.bitSizeof(c.comp).?; + key = .{ .int = @intCast(bits) }; + } else if (ty.isFloat()) { + const bits = ty.bitSizeof(c.comp).?; + key = .{ .float = @intCast(bits) }; + } else if (ty.isArray()) { + const elem = try c.genType(ty.elemType()); + key = .{ .array = .{ .child = elem, .len = ty.arrayLen().? } }; + } else if (ty.specifier == .vector) { + const elem = try c.genType(ty.elemType()); + key = .{ .vector = .{ .child = elem, .len = @intCast(ty.data.array.len) } }; + } else if (ty.is(.nullptr_t)) { + return c.comp.diag.fatalNoSrc("TODO lower nullptr_t", .{}); + } + return c.builder.pool.put(c.builder.gpa, key); +} + +fn genFn(c: *CodeGen, decl: NodeIndex) Error!void { + const name = c.tree.tokSlice(c.node_data[@intFromEnum(decl)].decl.name); + const func_ty = c.node_ty[@intFromEnum(decl)].canonicalize(.standard); + c.ret_nodes.items.len = 0; + + try c.builder.startFn(); + + for (func_ty.data.func.params) |param| { + // TODO handle calling convention here + const arg = try c.builder.addArg(try c.genType(param.ty)); + + const size: u32 = @intCast(param.ty.sizeof(c.comp).?); // TODO add error in parser + const @"align" = param.ty.alignof(c.comp); + const alloc = try c.builder.addAlloc(size, @"align"); + try c.builder.addStore(alloc, arg); + try c.symbols.append(c.comp.gpa, .{ .name = param.name, .val = alloc }); + } + + // Generate body + c.return_label = try c.builder.makeLabel("return"); + try c.genStmt(c.node_data[@intFromEnum(decl)].decl.node); + + // Relocate returns + if (c.ret_nodes.items.len == 0) { + _ = try c.builder.addInst(.ret, .{ .un = .none }, .noreturn); + } else if (c.ret_nodes.items.len == 1) { + c.builder.body.items.len -= 1; + _ = try c.builder.addInst(.ret, .{ .un = c.ret_nodes.items[0].value }, .noreturn); + } else { + try c.builder.startBlock(c.return_label); + const phi = try c.builder.addPhi(c.ret_nodes.items, try c.genType(func_ty.returnType())); + _ = try c.builder.addInst(.ret, .{ .un = phi }, .noreturn); + } + + var res = Ir{ + .pool = c.builder.pool, + .instructions = c.builder.instructions, + .arena = c.builder.arena.state, + .body = c.builder.body, + .strings = c.tree.strings, + }; + res.dump(c.builder.gpa, name, c.comp.diag.color, std.io.getStdOut().writer()) catch {}; +} + +fn addUn(c: *CodeGen, tag: Ir.Inst.Tag, operand: Ir.Ref, ty: Type) !Ir.Ref { + return c.builder.addInst(tag, .{ .un = operand }, try c.genType(ty)); +} + +fn addBin(c: *CodeGen, tag: Ir.Inst.Tag, lhs: Ir.Ref, rhs: Ir.Ref, ty: Type) !Ir.Ref { + return c.builder.addInst(tag, .{ .bin = .{ .lhs = lhs, .rhs = rhs } }, try c.genType(ty)); +} + +fn addBranch(c: *CodeGen, cond: Ir.Ref, true_label: Ir.Ref, false_label: Ir.Ref) !void { + if (true_label == c.bool_end_label) { + if (false_label == c.bool_end_label) { + try c.phi_nodes.append(c.comp.gpa, .{ .label = c.builder.current_label, .value = cond }); + return; + } + try c.addBoolPhi(!c.bool_invert); + } + if (false_label == c.bool_end_label) { + try c.addBoolPhi(c.bool_invert); + } + return c.builder.addBranch(cond, true_label, false_label); +} + +fn addBoolPhi(c: *CodeGen, value: bool) !void { + const val = try c.builder.addConstant(Value.int(@intFromBool(value)), .i1); + try c.phi_nodes.append(c.comp.gpa, .{ .label = c.builder.current_label, .value = val }); +} + +fn genStmt(c: *CodeGen, node: NodeIndex) Error!void { + _ = try c.genExpr(node); +} + +fn genExpr(c: *CodeGen, node: NodeIndex) Error!Ir.Ref { + std.debug.assert(node != .none); + const ty = c.node_ty[@intFromEnum(node)]; + if (c.tree.value_map.get(node)) |val| { + return c.builder.addConstant(val, try c.genType(ty)); + } + const data = c.node_data[@intFromEnum(node)]; + switch (c.node_tag[@intFromEnum(node)]) { + .enumeration_ref, + .bool_literal, + .int_literal, + .char_literal, + .float_literal, + .double_literal, + .imaginary_literal, + .string_literal_expr, + .alignof_expr, + => unreachable, // These should have an entry in value_map. + .fn_def, + .static_fn_def, + .inline_fn_def, + .inline_static_fn_def, + .invalid, + .threadlocal_var, + => unreachable, + .static_assert, + .fn_proto, + .static_fn_proto, + .inline_fn_proto, + .inline_static_fn_proto, + .extern_var, + .threadlocal_extern_var, + .typedef, + .struct_decl_two, + .union_decl_two, + .enum_decl_two, + .struct_decl, + .union_decl, + .enum_decl, + .enum_field_decl, + .record_field_decl, + .indirect_record_field_decl, + .struct_forward_decl, + .union_forward_decl, + .enum_forward_decl, + .null_stmt, + => {}, + .static_var, + .implicit_static_var, + .threadlocal_static_var, + => try c.genVar(node), // TODO + .@"var" => { + const size: u32 = @intCast(ty.sizeof(c.comp).?); // TODO add error in parser + const @"align" = ty.alignof(c.comp); + const alloc = try c.builder.addAlloc(size, @"align"); + const name = try c.comp.intern(c.tree.tokSlice(data.decl.name)); + try c.symbols.append(c.comp.gpa, .{ .name = name, .val = alloc }); + if (data.decl.node != .none) { + try c.genInitializer(alloc, ty, data.decl.node); + } + }, + .labeled_stmt => { + const label = try c.builder.makeLabel("label"); + try c.builder.startBlock(label); + try c.genStmt(data.decl.node); + }, + .compound_stmt_two => { + const old_sym_len = c.symbols.items.len; + c.symbols.items.len = old_sym_len; + + if (data.bin.lhs != .none) try c.genStmt(data.bin.lhs); + if (data.bin.rhs != .none) try c.genStmt(data.bin.rhs); + }, + .compound_stmt => { + const old_sym_len = c.symbols.items.len; + c.symbols.items.len = old_sym_len; + + for (c.tree.data[data.range.start..data.range.end]) |stmt| try c.genStmt(stmt); + }, + .if_then_else_stmt => { + const then_label = try c.builder.makeLabel("if.then"); + const else_label = try c.builder.makeLabel("if.else"); + const end_label = try c.builder.makeLabel("if.end"); + + try c.genBoolExpr(data.if3.cond, then_label, else_label); + + try c.builder.startBlock(then_label); + try c.genStmt(c.tree.data[data.if3.body]); // then + try c.builder.addJump(end_label); + + try c.builder.startBlock(else_label); + try c.genStmt(c.tree.data[data.if3.body + 1]); // else + + try c.builder.startBlock(end_label); + }, + .if_then_stmt => { + const then_label = try c.builder.makeLabel("if.then"); + const end_label = try c.builder.makeLabel("if.end"); + + try c.genBoolExpr(data.bin.lhs, then_label, end_label); + + try c.builder.startBlock(then_label); + try c.genStmt(data.bin.rhs); // then + try c.builder.startBlock(end_label); + }, + .switch_stmt => { + var wip_switch = WipSwitch{ + .size = c.node_ty[@intFromEnum(data.bin.lhs)].sizeof(c.comp).?, + }; + defer wip_switch.cases.deinit(c.builder.gpa); + + const old_wip_switch = c.wip_switch; + defer c.wip_switch = old_wip_switch; + c.wip_switch = &wip_switch; + + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + const end_ref = try c.builder.makeLabel("switch.end"); + c.break_label = end_ref; + + const cond = try c.genExpr(data.bin.lhs); + const switch_index = c.builder.instructions.len; + _ = try c.builder.addInst(.@"switch", undefined, .noreturn); + + try c.genStmt(data.bin.rhs); // body + + const default_ref = wip_switch.default orelse end_ref; + try c.builder.startBlock(end_ref); + + const a = c.builder.arena.allocator(); + const switch_data = try a.create(Ir.Inst.Switch); + switch_data.* = .{ + .target = cond, + .cases_len = @intCast(wip_switch.cases.len), + .case_vals = (try a.dupe(Interner.Ref, wip_switch.cases.items(.val))).ptr, + .case_labels = (try a.dupe(Ir.Ref, wip_switch.cases.items(.label))).ptr, + .default = default_ref, + }; + c.builder.instructions.items(.data)[switch_index] = .{ .@"switch" = switch_data }; + }, + .case_stmt => { + const val = c.tree.value_map.get(data.bin.lhs).?; + const label = try c.builder.makeLabel("case"); + try c.builder.startBlock(label); + try c.wip_switch.cases.append(c.builder.gpa, .{ + .val = try c.builder.pool.put(c.builder.gpa, .{ .value = val }), + .label = label, + }); + try c.genStmt(data.bin.rhs); + }, + .default_stmt => { + const default = try c.builder.makeLabel("default"); + try c.builder.startBlock(default); + c.wip_switch.default = default; + try c.genStmt(data.un); + }, + .while_stmt => { + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + + const old_continue_label = c.continue_label; + defer c.continue_label = old_continue_label; + + const cond_label = try c.builder.makeLabel("while.cond"); + const then_label = try c.builder.makeLabel("while.then"); + const end_label = try c.builder.makeLabel("while.end"); + + c.continue_label = cond_label; + c.break_label = end_label; + + try c.builder.startBlock(cond_label); + try c.genBoolExpr(data.bin.lhs, then_label, end_label); + + try c.builder.startBlock(then_label); + try c.genStmt(data.bin.rhs); + try c.builder.addJump(cond_label); + try c.builder.startBlock(end_label); + }, + .do_while_stmt => { + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + + const old_continue_label = c.continue_label; + defer c.continue_label = old_continue_label; + + const then_label = try c.builder.makeLabel("do.then"); + const cond_label = try c.builder.makeLabel("do.cond"); + const end_label = try c.builder.makeLabel("do.end"); + + c.continue_label = cond_label; + c.break_label = end_label; + + try c.builder.startBlock(then_label); + try c.genStmt(data.bin.rhs); + + try c.builder.startBlock(cond_label); + try c.genBoolExpr(data.bin.lhs, then_label, end_label); + + try c.builder.startBlock(end_label); + }, + .for_decl_stmt => { + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + + const old_continue_label = c.continue_label; + defer c.continue_label = old_continue_label; + + const for_decl = data.forDecl(c.tree); + for (for_decl.decls) |decl| try c.genStmt(decl); + + const then_label = try c.builder.makeLabel("for.then"); + var cond_label = then_label; + const cont_label = try c.builder.makeLabel("for.cont"); + const end_label = try c.builder.makeLabel("for.end"); + + c.continue_label = cont_label; + c.break_label = end_label; + + if (for_decl.cond != .none) { + cond_label = try c.builder.makeLabel("for.cond"); + try c.builder.startBlock(cond_label); + try c.genBoolExpr(for_decl.cond, then_label, end_label); + } + try c.builder.startBlock(then_label); + try c.genStmt(for_decl.body); + if (for_decl.incr != .none) { + _ = try c.genExpr(for_decl.incr); + } + try c.builder.addJump(cond_label); + try c.builder.startBlock(end_label); + }, + .forever_stmt => { + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + + const old_continue_label = c.continue_label; + defer c.continue_label = old_continue_label; + + const then_label = try c.builder.makeLabel("for.then"); + const end_label = try c.builder.makeLabel("for.end"); + + c.continue_label = then_label; + c.break_label = end_label; + + try c.builder.startBlock(then_label); + try c.genStmt(data.un); + try c.builder.startBlock(end_label); + }, + .for_stmt => { + const old_break_label = c.break_label; + defer c.break_label = old_break_label; + + const old_continue_label = c.continue_label; + defer c.continue_label = old_continue_label; + + const for_stmt = data.forStmt(c.tree); + if (for_stmt.init != .none) _ = try c.genExpr(for_stmt.init); + + const then_label = try c.builder.makeLabel("for.then"); + var cond_label = then_label; + const cont_label = try c.builder.makeLabel("for.cont"); + const end_label = try c.builder.makeLabel("for.end"); + + c.continue_label = cont_label; + c.break_label = end_label; + + if (for_stmt.cond != .none) { + cond_label = try c.builder.makeLabel("for.cond"); + try c.builder.startBlock(cond_label); + try c.genBoolExpr(for_stmt.cond, then_label, end_label); + } + try c.builder.startBlock(then_label); + try c.genStmt(for_stmt.body); + if (for_stmt.incr != .none) { + _ = try c.genExpr(for_stmt.incr); + } + try c.builder.addJump(cond_label); + try c.builder.startBlock(end_label); + }, + .continue_stmt => try c.builder.addJump(c.continue_label), + .break_stmt => try c.builder.addJump(c.break_label), + .return_stmt => { + if (data.un != .none) { + const operand = try c.genExpr(data.un); + try c.ret_nodes.append(c.comp.gpa, .{ .value = operand, .label = c.builder.current_label }); + } + try c.builder.addJump(c.return_label); + }, + .implicit_return => { + if (data.return_zero) { + const operand = try c.builder.addConstant(Value.int(0), try c.genType(ty)); + try c.ret_nodes.append(c.comp.gpa, .{ .value = operand, .label = c.builder.current_label }); + } + // No need to emit a jump since implicit_return is always the last instruction. + }, + .case_range_stmt, + .goto_stmt, + .computed_goto_stmt, + .nullptr_literal, + => return c.comp.diag.fatalNoSrc("TODO CodeGen.genStmt {}\n", .{c.node_tag[@intFromEnum(node)]}), + .comma_expr => { + _ = try c.genExpr(data.bin.lhs); + return c.genExpr(data.bin.rhs); + }, + .assign_expr => { + const rhs = try c.genExpr(data.bin.rhs); + const lhs = try c.genLval(data.bin.lhs); + try c.builder.addStore(lhs, rhs); + return rhs; + }, + .mul_assign_expr => return c.genCompoundAssign(node, .mul), + .div_assign_expr => return c.genCompoundAssign(node, .div), + .mod_assign_expr => return c.genCompoundAssign(node, .mod), + .add_assign_expr => return c.genCompoundAssign(node, .add), + .sub_assign_expr => return c.genCompoundAssign(node, .sub), + .shl_assign_expr => return c.genCompoundAssign(node, .bit_shl), + .shr_assign_expr => return c.genCompoundAssign(node, .bit_shr), + .bit_and_assign_expr => return c.genCompoundAssign(node, .bit_and), + .bit_xor_assign_expr => return c.genCompoundAssign(node, .bit_xor), + .bit_or_assign_expr => return c.genCompoundAssign(node, .bit_or), + .bit_or_expr => return c.genBinOp(node, .bit_or), + .bit_xor_expr => return c.genBinOp(node, .bit_xor), + .bit_and_expr => return c.genBinOp(node, .bit_and), + .equal_expr => { + const cmp = try c.genComparison(node, .cmp_eq); + return c.addUn(.zext, cmp, ty); + }, + .not_equal_expr => { + const cmp = try c.genComparison(node, .cmp_ne); + return c.addUn(.zext, cmp, ty); + }, + .less_than_expr => { + const cmp = try c.genComparison(node, .cmp_lt); + return c.addUn(.zext, cmp, ty); + }, + .less_than_equal_expr => { + const cmp = try c.genComparison(node, .cmp_lte); + return c.addUn(.zext, cmp, ty); + }, + .greater_than_expr => { + const cmp = try c.genComparison(node, .cmp_gt); + return c.addUn(.zext, cmp, ty); + }, + .greater_than_equal_expr => { + const cmp = try c.genComparison(node, .cmp_gte); + return c.addUn(.zext, cmp, ty); + }, + .shl_expr => return c.genBinOp(node, .bit_shl), + .shr_expr => return c.genBinOp(node, .bit_shr), + .add_expr => { + if (ty.isPtr()) { + const lhs_ty = c.node_ty[@intFromEnum(data.bin.lhs)]; + if (lhs_ty.isPtr()) { + const ptr = try c.genExpr(data.bin.lhs); + const offset = try c.genExpr(data.bin.rhs); + const offset_ty = c.node_ty[@intFromEnum(data.bin.rhs)]; + return c.genPtrArithmetic(ptr, offset, offset_ty, ty); + } else { + const offset = try c.genExpr(data.bin.lhs); + const ptr = try c.genExpr(data.bin.rhs); + const offset_ty = lhs_ty; + return c.genPtrArithmetic(ptr, offset, offset_ty, ty); + } + } + return c.genBinOp(node, .add); + }, + .sub_expr => { + if (ty.isPtr()) { + const ptr = try c.genExpr(data.bin.lhs); + const offset = try c.genExpr(data.bin.rhs); + const offset_ty = c.node_ty[@intFromEnum(data.bin.rhs)]; + return c.genPtrArithmetic(ptr, offset, offset_ty, ty); + } + return c.genBinOp(node, .sub); + }, + .mul_expr => return c.genBinOp(node, .mul), + .div_expr => return c.genBinOp(node, .div), + .mod_expr => return c.genBinOp(node, .mod), + .addr_of_expr => return try c.genLval(data.un), + .deref_expr => { + const un_data = c.node_data[@intFromEnum(data.un)]; + if (c.node_tag[@intFromEnum(data.un)] == .implicit_cast and un_data.cast.kind == .function_to_pointer) { + return c.genExpr(data.un); + } + const operand = try c.genLval(data.un); + return c.addUn(.load, operand, ty); + }, + .plus_expr => return c.genExpr(data.un), + .negate_expr => { + const zero = try c.builder.addConstant(Value.int(0), try c.genType(ty)); + const operand = try c.genExpr(data.un); + return c.addBin(.sub, zero, operand, ty); + }, + .bit_not_expr => { + const operand = try c.genExpr(data.un); + return c.addUn(.bit_not, operand, ty); + }, + .bool_not_expr => { + const zero = try c.builder.addConstant(Value.int(0), try c.genType(ty)); + const operand = try c.genExpr(data.un); + return c.addBin(.cmp_ne, zero, operand, ty); + }, + .pre_inc_expr => { + const operand = try c.genLval(data.un); + const val = try c.addUn(.load, operand, ty); + const one = try c.builder.addConstant(Value.int(1), try c.genType(ty)); + const plus_one = try c.addBin(.add, val, one, ty); + try c.builder.addStore(operand, plus_one); + return plus_one; + }, + .pre_dec_expr => { + const operand = try c.genLval(data.un); + const val = try c.addUn(.load, operand, ty); + const one = try c.builder.addConstant(Value.int(1), try c.genType(ty)); + const plus_one = try c.addBin(.sub, val, one, ty); + try c.builder.addStore(operand, plus_one); + return plus_one; + }, + .post_inc_expr => { + const operand = try c.genLval(data.un); + const val = try c.addUn(.load, operand, ty); + const one = try c.builder.addConstant(Value.int(1), try c.genType(ty)); + const plus_one = try c.addBin(.add, val, one, ty); + try c.builder.addStore(operand, plus_one); + return val; + }, + .post_dec_expr => { + const operand = try c.genLval(data.un); + const val = try c.addUn(.load, operand, ty); + const one = try c.builder.addConstant(Value.int(1), try c.genType(ty)); + const plus_one = try c.addBin(.sub, val, one, ty); + try c.builder.addStore(operand, plus_one); + return val; + }, + .paren_expr => return c.genExpr(data.un), + .decl_ref_expr => unreachable, // Lval expression. + .explicit_cast, .implicit_cast => switch (data.cast.kind) { + .no_op => return c.genExpr(data.cast.operand), + .to_void => { + _ = try c.genExpr(data.cast.operand); + return .none; + }, + .lval_to_rval => { + const operand = try c.genLval(data.cast.operand); + return c.addUn(.load, operand, ty); + }, + .function_to_pointer, .array_to_pointer => { + return c.genLval(data.cast.operand); + }, + .int_cast => { + const operand = try c.genExpr(data.cast.operand); + const src_ty = c.node_ty[@intFromEnum(data.cast.operand)]; + const src_bits = src_ty.bitSizeof(c.comp).?; + const dest_bits = ty.bitSizeof(c.comp).?; + if (src_bits == dest_bits) { + return operand; + } else if (src_bits < dest_bits) { + if (src_ty.isUnsignedInt(c.comp)) + return c.addUn(.zext, operand, ty) + else + return c.addUn(.sext, operand, ty); + } else { + return c.addUn(.trunc, operand, ty); + } + }, + .bool_to_int => { + const operand = try c.genExpr(data.cast.operand); + return c.addUn(.zext, operand, ty); + }, + .pointer_to_bool, .int_to_bool, .float_to_bool => { + const lhs = try c.genExpr(data.cast.operand); + const rhs = try c.builder.addConstant(Value.int(0), try c.genType(c.node_ty[@intFromEnum(node)])); + return c.builder.addInst(.cmp_ne, .{ .bin = .{ .lhs = lhs, .rhs = rhs } }, .i1); + }, + .bitcast, + .pointer_to_int, + .bool_to_float, + .bool_to_pointer, + .int_to_float, + .complex_int_to_complex_float, + .int_to_pointer, + .float_to_int, + .complex_float_to_complex_int, + .complex_int_cast, + .complex_int_to_real, + .real_to_complex_int, + .float_cast, + .complex_float_cast, + .complex_float_to_real, + .real_to_complex_float, + .null_to_pointer, + .union_cast, + .vector_splat, + => return c.comp.diag.fatalNoSrc("TODO CodeGen gen CastKind {}\n", .{data.cast.kind}), + }, + .binary_cond_expr => { + if (c.tree.value_map.get(data.if3.cond)) |cond| { + if (cond.getBool()) { + c.cond_dummy_ref = try c.genExpr(data.if3.cond); + return c.genExpr(c.tree.data[data.if3.body]); // then + } else { + return c.genExpr(c.tree.data[data.if3.body + 1]); // else + } + } + + const then_label = try c.builder.makeLabel("ternary.then"); + const else_label = try c.builder.makeLabel("ternary.else"); + const end_label = try c.builder.makeLabel("ternary.end"); + const cond_ty = c.node_ty[@intFromEnum(data.if3.cond)]; + { + const old_cond_dummy_ty = c.cond_dummy_ty; + defer c.cond_dummy_ty = old_cond_dummy_ty; + c.cond_dummy_ty = try c.genType(cond_ty); + + try c.genBoolExpr(data.if3.cond, then_label, else_label); + } + + try c.builder.startBlock(then_label); + if (c.builder.instructions.items(.ty)[@intFromEnum(c.cond_dummy_ref)] == .i1) { + c.cond_dummy_ref = try c.addUn(.zext, c.cond_dummy_ref, cond_ty); + } + const then_val = try c.genExpr(c.tree.data[data.if3.body]); // then + try c.builder.addJump(end_label); + const then_exit = c.builder.current_label; + + try c.builder.startBlock(else_label); + const else_val = try c.genExpr(c.tree.data[data.if3.body + 1]); // else + const else_exit = c.builder.current_label; + + try c.builder.startBlock(end_label); + + var phi_buf: [2]Ir.Inst.Phi.Input = .{ + .{ .value = then_val, .label = then_exit }, + .{ .value = else_val, .label = else_exit }, + }; + return c.builder.addPhi(&phi_buf, try c.genType(ty)); + }, + .cond_dummy_expr => return c.cond_dummy_ref, + .cond_expr => { + if (c.tree.value_map.get(data.if3.cond)) |cond| { + if (cond.getBool()) { + return c.genExpr(c.tree.data[data.if3.body]); // then + } else { + return c.genExpr(c.tree.data[data.if3.body + 1]); // else + } + } + + const then_label = try c.builder.makeLabel("ternary.then"); + const else_label = try c.builder.makeLabel("ternary.else"); + const end_label = try c.builder.makeLabel("ternary.end"); + + try c.genBoolExpr(data.if3.cond, then_label, else_label); + + try c.builder.startBlock(then_label); + const then_val = try c.genExpr(c.tree.data[data.if3.body]); // then + try c.builder.addJump(end_label); + const then_exit = c.builder.current_label; + + try c.builder.startBlock(else_label); + const else_val = try c.genExpr(c.tree.data[data.if3.body + 1]); // else + const else_exit = c.builder.current_label; + + try c.builder.startBlock(end_label); + + var phi_buf: [2]Ir.Inst.Phi.Input = .{ + .{ .value = then_val, .label = then_exit }, + .{ .value = else_val, .label = else_exit }, + }; + return c.builder.addPhi(&phi_buf, try c.genType(ty)); + }, + .call_expr_one => if (data.bin.rhs == .none) { + return c.genCall(data.bin.lhs, &.{}, ty); + } else { + return c.genCall(data.bin.lhs, &.{data.bin.rhs}, ty); + }, + .call_expr => { + return c.genCall(c.tree.data[data.range.start], c.tree.data[data.range.start + 1 .. data.range.end], ty); + }, + .bool_or_expr => { + if (c.tree.value_map.get(data.bin.lhs)) |lhs| { + const cond = lhs.getBool(); + if (!cond) { + return c.builder.addConstant(Value.int(1), try c.genType(ty)); + } + return c.genExpr(data.bin.rhs); + } + + const false_label = try c.builder.makeLabel("bool_false"); + const exit_label = try c.builder.makeLabel("bool_exit"); + + const old_bool_end_label = c.bool_end_label; + defer c.bool_end_label = old_bool_end_label; + c.bool_end_label = exit_label; + + const phi_nodes_top = c.phi_nodes.items.len; + defer c.phi_nodes.items.len = phi_nodes_top; + + try c.genBoolExpr(data.bin.lhs, exit_label, false_label); + + try c.builder.startBlock(false_label); + try c.genBoolExpr(data.bin.rhs, exit_label, exit_label); + + try c.builder.startBlock(exit_label); + + const phi = try c.builder.addPhi(c.phi_nodes.items[phi_nodes_top..], .i1); + return c.addUn(.zext, phi, ty); + }, + .bool_and_expr => { + if (c.tree.value_map.get(data.bin.lhs)) |lhs| { + const cond = lhs.getBool(); + if (!cond) { + return c.builder.addConstant(Value.int(0), try c.genType(ty)); + } + return c.genExpr(data.bin.rhs); + } + + const true_label = try c.builder.makeLabel("bool_true"); + const exit_label = try c.builder.makeLabel("bool_exit"); + + const old_bool_end_label = c.bool_end_label; + defer c.bool_end_label = old_bool_end_label; + c.bool_end_label = exit_label; + + const phi_nodes_top = c.phi_nodes.items.len; + defer c.phi_nodes.items.len = phi_nodes_top; + + try c.genBoolExpr(data.bin.lhs, true_label, exit_label); + + try c.builder.startBlock(true_label); + try c.genBoolExpr(data.bin.rhs, exit_label, exit_label); + + try c.builder.startBlock(exit_label); + + const phi = try c.builder.addPhi(c.phi_nodes.items[phi_nodes_top..], .i1); + return c.addUn(.zext, phi, ty); + }, + .builtin_choose_expr => { + const cond = c.tree.value_map.get(data.if3.cond).?; + if (cond.getBool()) { + return c.genExpr(c.tree.data[data.if3.body]); + } else { + return c.genExpr(c.tree.data[data.if3.body + 1]); + } + }, + .generic_expr_one => { + const index = @intFromEnum(data.bin.rhs); + switch (c.node_tag[index]) { + .generic_association_expr, .generic_default_expr => { + return c.genExpr(c.node_data[index].un); + }, + else => unreachable, + } + }, + .generic_expr => { + const index = @intFromEnum(c.tree.data[data.range.start + 1]); + switch (c.node_tag[index]) { + .generic_association_expr, .generic_default_expr => { + return c.genExpr(c.node_data[index].un); + }, + else => unreachable, + } + }, + .generic_association_expr, .generic_default_expr => unreachable, + .stmt_expr => switch (c.node_tag[@intFromEnum(data.un)]) { + .compound_stmt_two => { + const old_sym_len = c.symbols.items.len; + c.symbols.items.len = old_sym_len; + + const stmt_data = c.node_data[@intFromEnum(data.un)]; + if (stmt_data.bin.rhs == .none) return c.genExpr(stmt_data.bin.lhs); + try c.genStmt(stmt_data.bin.lhs); + return c.genExpr(stmt_data.bin.rhs); + }, + .compound_stmt => { + const old_sym_len = c.symbols.items.len; + c.symbols.items.len = old_sym_len; + + const stmt_data = c.node_data[@intFromEnum(data.un)]; + for (c.tree.data[stmt_data.range.start .. stmt_data.range.end - 1]) |stmt| try c.genStmt(stmt); + return c.genExpr(c.tree.data[stmt_data.range.end]); + }, + else => unreachable, + }, + .builtin_call_expr_one => { + const name = c.tree.tokSlice(data.decl.name); + const builtin = c.comp.builtins.lookup(name).builtin; + if (data.decl.node == .none) { + return c.genBuiltinCall(builtin, &.{}, ty); + } else { + return c.genBuiltinCall(builtin, &.{data.decl.node}, ty); + } + }, + .builtin_call_expr => { + const name_node_idx = c.tree.data[data.range.start]; + const name = c.tree.tokSlice(@intFromEnum(name_node_idx)); + const builtin = c.comp.builtins.lookup(name).builtin; + return c.genBuiltinCall(builtin, c.tree.data[data.range.start + 1 .. data.range.end], ty); + }, + .addr_of_label, + .imag_expr, + .real_expr, + .sizeof_expr, + .special_builtin_call_one, + => return c.comp.diag.fatalNoSrc("TODO CodeGen.genExpr {}\n", .{c.node_tag[@intFromEnum(node)]}), + else => unreachable, // Not an expression. + } + return .none; +} + +fn genLval(c: *CodeGen, node: NodeIndex) Error!Ir.Ref { + std.debug.assert(node != .none); + assert(Tree.isLval(c.tree.nodes, c.tree.data, c.tree.value_map, node)); + const data = c.node_data[@intFromEnum(node)]; + switch (c.node_tag[@intFromEnum(node)]) { + .string_literal_expr => { + const val = c.tree.value_map.get(node).?; + return c.builder.addConstant(val, .ptr); + }, + .paren_expr => return c.genLval(data.un), + .decl_ref_expr => { + const slice = c.tree.tokSlice(data.decl_ref); + const name = try c.comp.intern(slice); + var i = c.symbols.items.len; + while (i > 0) { + i -= 1; + if (c.symbols.items[i].name == name) { + return c.symbols.items[i].val; + } + } + + const duped_name = try c.builder.arena.allocator().dupeZ(u8, slice); + const ref: Ir.Ref = @enumFromInt(c.builder.instructions.len); + try c.builder.instructions.append(c.builder.gpa, .{ .tag = .symbol, .data = .{ .label = duped_name }, .ty = .ptr }); + return ref; + }, + .deref_expr => return c.genExpr(data.un), + .compound_literal_expr => { + const ty = c.node_ty[@intFromEnum(node)]; + const size: u32 = @intCast(ty.sizeof(c.comp).?); // TODO add error in parser + const @"align" = ty.alignof(c.comp); + const alloc = try c.builder.addAlloc(size, @"align"); + try c.genInitializer(alloc, ty, data.un); + return alloc; + }, + .builtin_choose_expr => { + const cond = c.tree.value_map.get(data.if3.cond).?; + if (cond.getBool()) { + return c.genLval(c.tree.data[data.if3.body]); + } else { + return c.genLval(c.tree.data[data.if3.body + 1]); + } + }, + .member_access_expr, + .member_access_ptr_expr, + .array_access_expr, + => return c.comp.diag.fatalNoSrc("TODO CodeGen.genLval {}\n", .{c.node_tag[@intFromEnum(node)]}), + else => unreachable, // Not an lval expression. + } +} + +fn genBoolExpr(c: *CodeGen, base: NodeIndex, true_label: Ir.Ref, false_label: Ir.Ref) Error!void { + var node = base; + while (true) switch (c.node_tag[@intFromEnum(node)]) { + .paren_expr => { + node = c.node_data[@intFromEnum(node)].un; + }, + else => break, + }; + + const data = c.node_data[@intFromEnum(node)]; + switch (c.node_tag[@intFromEnum(node)]) { + .bool_or_expr => { + if (c.tree.value_map.get(data.bin.lhs)) |lhs| { + const cond = lhs.getBool(); + if (cond) { + if (true_label == c.bool_end_label) { + return c.addBoolPhi(!c.bool_invert); + } + return c.builder.addJump(true_label); + } + return c.genBoolExpr(data.bin.rhs, true_label, false_label); + } + + const new_false_label = try c.builder.makeLabel("bool_false"); + try c.genBoolExpr(data.bin.lhs, true_label, new_false_label); + try c.builder.startBlock(new_false_label); + + if (c.cond_dummy_ty) |ty| c.cond_dummy_ref = try c.builder.addConstant(Value.int(1), ty); + return c.genBoolExpr(data.bin.rhs, true_label, false_label); + }, + .bool_and_expr => { + if (c.tree.value_map.get(data.bin.lhs)) |lhs| { + const cond = lhs.getBool(); + if (!cond) { + if (false_label == c.bool_end_label) { + return c.addBoolPhi(c.bool_invert); + } + return c.builder.addJump(false_label); + } + return c.genBoolExpr(data.bin.rhs, true_label, false_label); + } + + const new_true_label = try c.builder.makeLabel("bool_true"); + try c.genBoolExpr(data.bin.lhs, new_true_label, false_label); + try c.builder.startBlock(new_true_label); + + if (c.cond_dummy_ty) |ty| c.cond_dummy_ref = try c.builder.addConstant(Value.int(1), ty); + return c.genBoolExpr(data.bin.rhs, true_label, false_label); + }, + .bool_not_expr => { + c.bool_invert = !c.bool_invert; + defer c.bool_invert = !c.bool_invert; + + if (c.cond_dummy_ty) |ty| c.cond_dummy_ref = try c.builder.addConstant(Value.int(0), ty); + return c.genBoolExpr(data.un, false_label, true_label); + }, + .equal_expr => { + const cmp = try c.genComparison(node, .cmp_eq); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .not_equal_expr => { + const cmp = try c.genComparison(node, .cmp_ne); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .less_than_expr => { + const cmp = try c.genComparison(node, .cmp_lt); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .less_than_equal_expr => { + const cmp = try c.genComparison(node, .cmp_lte); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .greater_than_expr => { + const cmp = try c.genComparison(node, .cmp_gt); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .greater_than_equal_expr => { + const cmp = try c.genComparison(node, .cmp_gte); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + return c.addBranch(cmp, true_label, false_label); + }, + .explicit_cast, .implicit_cast => switch (data.cast.kind) { + .bool_to_int => { + const operand = try c.genExpr(data.cast.operand); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = operand; + return c.addBranch(operand, true_label, false_label); + }, + else => {}, + }, + .binary_cond_expr => { + if (c.tree.value_map.get(data.if3.cond)) |cond| { + if (cond.getBool()) { + return c.genBoolExpr(c.tree.data[data.if3.body], true_label, false_label); // then + } else { + return c.genBoolExpr(c.tree.data[data.if3.body + 1], true_label, false_label); // else + } + } + + const new_false_label = try c.builder.makeLabel("ternary.else"); + try c.genBoolExpr(data.if3.cond, true_label, new_false_label); + + try c.builder.startBlock(new_false_label); + if (c.cond_dummy_ty) |ty| c.cond_dummy_ref = try c.builder.addConstant(Value.int(1), ty); + return c.genBoolExpr(c.tree.data[data.if3.body + 1], true_label, false_label); // else + }, + .cond_expr => { + if (c.tree.value_map.get(data.if3.cond)) |cond| { + if (cond.getBool()) { + return c.genBoolExpr(c.tree.data[data.if3.body], true_label, false_label); // then + } else { + return c.genBoolExpr(c.tree.data[data.if3.body + 1], true_label, false_label); // else + } + } + + const new_true_label = try c.builder.makeLabel("ternary.then"); + const new_false_label = try c.builder.makeLabel("ternary.else"); + try c.genBoolExpr(data.if3.cond, new_true_label, new_false_label); + + try c.builder.startBlock(new_true_label); + try c.genBoolExpr(c.tree.data[data.if3.body], true_label, false_label); // then + try c.builder.startBlock(new_false_label); + if (c.cond_dummy_ty) |ty| c.cond_dummy_ref = try c.builder.addConstant(Value.int(1), ty); + return c.genBoolExpr(c.tree.data[data.if3.body + 1], true_label, false_label); // else + }, + else => {}, + } + + if (c.tree.value_map.get(node)) |value| { + if (value.getBool()) { + if (true_label == c.bool_end_label) { + return c.addBoolPhi(!c.bool_invert); + } + return c.builder.addJump(true_label); + } else { + if (false_label == c.bool_end_label) { + return c.addBoolPhi(c.bool_invert); + } + return c.builder.addJump(false_label); + } + } + + // Assume int operand. + const lhs = try c.genExpr(node); + const rhs = try c.builder.addConstant(Value.int(0), try c.genType(c.node_ty[@intFromEnum(node)])); + const cmp = try c.builder.addInst(.cmp_ne, .{ .bin = .{ .lhs = lhs, .rhs = rhs } }, .i1); + if (c.cond_dummy_ty != null) c.cond_dummy_ref = cmp; + try c.addBranch(cmp, true_label, false_label); +} + +fn genBuiltinCall(c: *CodeGen, builtin: BuiltinFunction, arg_nodes: []const NodeIndex, ty: Type) Error!Ir.Ref { + _ = arg_nodes; + _ = ty; + return c.comp.diag.fatalNoSrc("TODO CodeGen.genBuiltinCall {s}\n", .{@tagName(builtin.tag)}); +} + +fn genCall(c: *CodeGen, fn_node: NodeIndex, arg_nodes: []const NodeIndex, ty: Type) Error!Ir.Ref { + // Detect direct calls. + const fn_ref = blk: { + const data = c.node_data[@intFromEnum(fn_node)]; + if (c.node_tag[@intFromEnum(fn_node)] != .implicit_cast or data.cast.kind != .function_to_pointer) { + break :blk try c.genExpr(fn_node); + } + + var cur = @intFromEnum(data.cast.operand); + while (true) switch (c.node_tag[cur]) { + .paren_expr, .addr_of_expr, .deref_expr => { + cur = @intFromEnum(c.node_data[cur].un); + }, + .implicit_cast => { + const cast = c.node_data[cur].cast; + if (cast.kind != .function_to_pointer) { + break :blk try c.genExpr(fn_node); + } + cur = @intFromEnum(cast.operand); + }, + .decl_ref_expr => { + const slice = c.tree.tokSlice(c.node_data[cur].decl_ref); + const name = try c.comp.intern(slice); + var i = c.symbols.items.len; + while (i > 0) { + i -= 1; + if (c.symbols.items[i].name == name) { + break :blk try c.genExpr(fn_node); + } + } + + const duped_name = try c.builder.arena.allocator().dupeZ(u8, slice); + const ref: Ir.Ref = @enumFromInt(c.builder.instructions.len); + try c.builder.instructions.append(c.builder.gpa, .{ .tag = .symbol, .data = .{ .label = duped_name }, .ty = .ptr }); + break :blk ref; + }, + else => break :blk try c.genExpr(fn_node), + }; + }; + + const args = try c.builder.arena.allocator().alloc(Ir.Ref, arg_nodes.len); + for (arg_nodes, args) |node, *arg| { + // TODO handle calling convention here + arg.* = try c.genExpr(node); + } + // TODO handle variadic call + const call = try c.builder.arena.allocator().create(Ir.Inst.Call); + call.* = .{ + .func = fn_ref, + .args_len = @intCast(args.len), + .args_ptr = args.ptr, + }; + return c.builder.addInst(.call, .{ .call = call }, try c.genType(ty)); +} + +fn genCompoundAssign(c: *CodeGen, node: NodeIndex, tag: Ir.Inst.Tag) Error!Ir.Ref { + const bin = c.node_data[@intFromEnum(node)].bin; + const ty = c.node_ty[@intFromEnum(node)]; + const rhs = try c.genExpr(bin.rhs); + const lhs = try c.genLval(bin.lhs); + const res = try c.addBin(tag, lhs, rhs, ty); + try c.builder.addStore(lhs, res); + return res; +} + +fn genBinOp(c: *CodeGen, node: NodeIndex, tag: Ir.Inst.Tag) Error!Ir.Ref { + const bin = c.node_data[@intFromEnum(node)].bin; + const ty = c.node_ty[@intFromEnum(node)]; + const lhs = try c.genExpr(bin.lhs); + const rhs = try c.genExpr(bin.rhs); + return c.addBin(tag, lhs, rhs, ty); +} + +fn genComparison(c: *CodeGen, node: NodeIndex, tag: Ir.Inst.Tag) Error!Ir.Ref { + const bin = c.node_data[@intFromEnum(node)].bin; + const lhs = try c.genExpr(bin.lhs); + const rhs = try c.genExpr(bin.rhs); + + return c.builder.addInst(tag, .{ .bin = .{ .lhs = lhs, .rhs = rhs } }, .i1); +} + +fn genPtrArithmetic(c: *CodeGen, ptr: Ir.Ref, offset: Ir.Ref, offset_ty: Type, ty: Type) Error!Ir.Ref { + // TODO consider adding a getelemptr instruction + const size = ty.elemType().sizeof(c.comp).?; + if (size == 1) { + return c.builder.addInst(.add, .{ .bin = .{ .lhs = ptr, .rhs = offset } }, try c.genType(ty)); + } + + const size_inst = try c.builder.addConstant(Value.int(size), try c.genType(offset_ty)); + const offset_inst = try c.addBin(.mul, offset, size_inst, offset_ty); + return c.addBin(.add, ptr, offset_inst, offset_ty); +} + +fn genInitializer(c: *CodeGen, ptr: Ir.Ref, dest_ty: Type, initializer: NodeIndex) Error!void { + std.debug.assert(initializer != .none); + switch (c.node_tag[@intFromEnum(initializer)]) { + .array_init_expr_two, + .array_init_expr, + .struct_init_expr_two, + .struct_init_expr, + .union_init_expr, + .array_filler_expr, + .default_init_expr, + => return c.comp.diag.fatalNoSrc("TODO CodeGen.genInitializer {}\n", .{c.node_tag[@intFromEnum(initializer)]}), + .string_literal_expr => { + const val = c.tree.value_map.get(initializer).?; + const str_ptr = try c.builder.addConstant(val, .ptr); + if (dest_ty.isArray()) { + return c.comp.diag.fatalNoSrc("TODO memcpy\n", .{}); + } else { + try c.builder.addStore(ptr, str_ptr); + } + }, + else => { + const res = try c.genExpr(initializer); + try c.builder.addStore(ptr, res); + }, + } +} + +fn genVar(c: *CodeGen, decl: NodeIndex) Error!void { + _ = decl; + return c.comp.diag.fatalNoSrc("TODO CodeGen.genVar\n", .{}); +} |
