diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-22 09:34:44 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-22 09:34:44 +0200 |
| commit | def135918740846d9b206c3188563cb77333f3a9 (patch) | |
| tree | 2d054e8bca8b70ab2d38e384ff4eaac665b86422 /src/codegen/c.zig | |
| parent | d0edd37f690c3e6cf3f8a7fc7a27016ba9b010ce (diff) | |
| parent | 8d0671157cdf8bc8b89d047138b42227420a5388 (diff) | |
| download | zig-def135918740846d9b206c3188563cb77333f3a9.tar.gz zig-def135918740846d9b206c3188563cb77333f3a9.zip | |
Merge remote-tracking branch 'origin/master' into zld-incremental-2
Diffstat (limited to 'src/codegen/c.zig')
| -rw-r--r-- | src/codegen/c.zig | 712 |
1 files changed, 436 insertions, 276 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 391375c709..71714cc1b8 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6,8 +6,6 @@ const log = std.log.scoped(.c); const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); -const ir = @import("../air.zig"); -const Inst = ir.Inst; const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); @@ -15,6 +13,9 @@ const C = link.File.C; const Decl = Module.Decl; const trace = @import("../tracy.zig").trace; const LazySrcLoc = Module.LazySrcLoc; +const Air = @import("../Air.zig"); +const Zir = @import("../Zir.zig"); +const Liveness = @import("../Liveness.zig"); const Mutability = enum { Const, Mut }; @@ -25,7 +26,7 @@ pub const CValue = union(enum) { /// Index into local_names, but take the address. local_ref: usize, /// A constant instruction, to be rendered inline. - constant: *Inst, + constant: Air.Inst.Ref, /// Index into the parameters arg: usize, /// By-value @@ -38,7 +39,7 @@ const BlockData = struct { result: CValue, }; -pub const CValueMap = std.AutoHashMap(*Inst, CValue); +pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue); pub const TypedefMap = std.ArrayHashMap( Type, struct { name: []const u8, rendered: []u8 }, @@ -94,20 +95,23 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { /// It is not available when generating .h file. pub const Object = struct { dg: DeclGen, + air: Air, + liveness: Liveness, gpa: *mem.Allocator, code: std.ArrayList(u8), value_map: CValueMap, - blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{}, + blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, next_arg_index: usize = 0, next_local_index: usize = 0, next_block_index: usize = 0, indent_writer: IndentWriter(std.ArrayList(u8).Writer), - fn resolveInst(o: *Object, inst: *Inst) !CValue { - if (inst.value()) |_| { + fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue { + if (o.air.value(inst)) |_| { return CValue{ .constant = inst }; } - return o.value_map.get(inst).?; // Instruction does not dominate all uses! + const index = Air.refToIndex(inst).?; + return o.value_map.get(index).?; // Assertion means instruction does not dominate usage. } fn allocLocalValue(o: *Object) CValue { @@ -131,7 +135,11 @@ pub const Object = struct { .none => unreachable, .local => |i| return w.print("t{d}", .{i}), .local_ref => |i| return w.print("&t{d}", .{i}), - .constant => |inst| return o.dg.renderValue(w, inst.ty, inst.value().?), + .constant => |inst| { + const ty = o.air.typeOf(inst); + const val = o.air.value(inst).?; + return o.dg.renderValue(w, ty, val); + }, .arg => |i| return w.print("a{d}", .{i}), .decl => |decl| return w.writeAll(mem.span(decl.name)), .decl_ref => |decl| return w.print("&{s}", .{decl.name}), @@ -211,8 +219,9 @@ pub const DeclGen = struct { error_msg: ?*Module.ErrorMsg, typedefs: TypedefMap, - fn fail(dg: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { + fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); + const src: LazySrcLoc = .{ .node_offset = 0 }; const src_loc = src.toSrcLocWithDecl(dg.decl); dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args); return error.AnalysisFail; @@ -228,7 +237,7 @@ pub const DeclGen = struct { // This should lower to 0xaa bytes in safe modes, and for unsafe modes should // lower to leaving variables uninitialized (that might need to be implemented // outside of this function). - return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement renderValue undef", .{}); + return dg.fail("TODO: C backend: implement renderValue undef", .{}); } switch (t.zigTypeTag()) { .Int => { @@ -438,7 +447,7 @@ pub const DeclGen = struct { }, else => unreachable, }, - else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ + else => |e| return dg.fail("TODO: C backend: implement value {s}", .{ @tagName(e), }), } @@ -517,14 +526,14 @@ pub const DeclGen = struct { break; } } else { - return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 128 bits", .{}); + return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); } }, else => unreachable, } }, - .Float => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Float", .{}), + .Float => return dg.fail("TODO: C backend: implement type Float", .{}), .Pointer => { if (t.isSlice()) { @@ -679,7 +688,7 @@ pub const DeclGen = struct { try dg.renderType(w, int_tag_ty); }, - .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), + .Union => return dg.fail("TODO: C backend: implement type Union", .{}), .Fn => { try dg.renderType(w, t.fnReturnType()); try w.writeAll(" (*)("); @@ -702,10 +711,10 @@ pub const DeclGen = struct { } try w.writeByte(')'); }, - .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), - .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), - .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), - .Vector => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Vector", .{}), + .Opaque => return dg.fail("TODO: C backend: implement type Opaque", .{}), + .Frame => return dg.fail("TODO: C backend: implement type Frame", .{}), + .AnyFrame => return dg.fail("TODO: C backend: implement type AnyFrame", .{}), + .Vector => return dg.fail("TODO: C backend: implement type Vector", .{}), .Null, .Undefined, @@ -758,7 +767,8 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderFunctionSignature(o.writer(), is_global); try o.writer().writeByte(' '); - try genBody(o, func.body); + const main_body = o.air.getMainBody(); + try genBody(o, main_body); try o.indent_writer.insertNewline(); return; @@ -831,9 +841,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { } } -pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void { +fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { const writer = o.writer(); - if (body.instructions.len == 0) { + if (body.len == 0) { try writer.writeAll("{}"); return; } @@ -841,82 +851,92 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi try writer.writeAll("{\n"); o.indent_writer.pushIndent(); - for (body.instructions) |inst| { - const result_value = switch (inst.tag) { + const air_tags = o.air.instructions.items(.tag); + + for (body) |inst| { + const result_value = switch (air_tags[inst]) { + // zig fmt: off + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .arg => airArg(o), + + .breakpoint => try airBreakpoint(o), + .unreach => try airUnreach(o), + // TODO use a different strategy for add that communicates to the optimizer // that wrapping is UB. - .add => try genBinOp(o, inst.castTag(.add).?, " + "), - .addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"), + .add => try airBinOp( o, inst, " + "), + .addwrap => try airWrapOp(o, inst, " + ", "addw_"), // TODO use a different strategy for sub that communicates to the optimizer // that wrapping is UB. - .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), - .subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"), + .sub => try airBinOp( o, inst, " - "), + .subwrap => try airWrapOp(o, inst, " - ", "subw_"), // TODO use a different strategy for mul that communicates to the optimizer // that wrapping is UB. - .mul => try genBinOp(o, inst.castTag(.sub).?, " * "), - .mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"), + .mul => try airBinOp( o, inst, " * "), + .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"), // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. - .div => try genBinOp(o, inst.castTag(.div).?, " / "), + .div => try airBinOp( o, inst, " / "), + + .cmp_eq => try airBinOp(o, inst, " == "), + .cmp_gt => try airBinOp(o, inst, " > "), + .cmp_gte => try airBinOp(o, inst, " >= "), + .cmp_lt => try airBinOp(o, inst, " < "), + .cmp_lte => try airBinOp(o, inst, " <= "), + .cmp_neq => try airBinOp(o, inst, " != "), - .constant => unreachable, // excluded from function bodies - .alloc => try genAlloc(o, inst.castTag(.alloc).?), - .arg => genArg(o), - .assembly => try genAsm(o, inst.castTag(.assembly).?), - .block => try genBlock(o, inst.castTag(.block).?), - .bitcast => try genBitcast(o, inst.castTag(.bitcast).?), - .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?), - .call => try genCall(o, inst.castTag(.call).?), - .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "), - .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "), - .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "), - .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "), - .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "), - .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "), - .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?), - .intcast => try genIntCast(o, inst.castTag(.intcast).?), - .load => try genLoad(o, inst.castTag(.load).?), - .ret => try genRet(o, inst.castTag(.ret).?), - .retvoid => try genRetVoid(o), - .store => try genStore(o, inst.castTag(.store).?), - .unreach => try genUnreach(o, inst.castTag(.unreach).?), - .loop => try genLoop(o, inst.castTag(.loop).?), - .condbr => try genCondBr(o, inst.castTag(.condbr).?), - .br => try genBr(o, inst.castTag(.br).?), - .br_void => try genBrVoid(o, inst.castTag(.br_void).?.block), - .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), // bool_and and bool_or are non-short-circuit operations - .bool_and => try genBinOp(o, inst.castTag(.bool_and).?, " & "), - .bool_or => try genBinOp(o, inst.castTag(.bool_or).?, " | "), - .bit_and => try genBinOp(o, inst.castTag(.bit_and).?, " & "), - .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "), - .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), - .not => try genUnOp(o, inst.castTag(.not).?, "!"), - .is_null => try genIsNull(o, inst.castTag(.is_null).?), - .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?), - .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?), - .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?), - .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), - .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), - .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), - .ref => try genRef(o, inst.castTag(.ref).?), - .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), - - .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="), - .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="), - .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="), - .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="), - - .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), - .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), - .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), - .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), - .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), - .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), - .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}), - .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}), - .varptr => try genVarPtr(o, inst.castTag(.varptr).?), - .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}), + .bool_and => try airBinOp(o, inst, " & "), + .bool_or => try airBinOp(o, inst, " | "), + .bit_and => try airBinOp(o, inst, " & "), + .bit_or => try airBinOp(o, inst, " | "), + .xor => try airBinOp(o, inst, " ^ "), + + .not => try airNot( o, inst), + + .optional_payload => try airOptionalPayload(o, inst), + .optional_payload_ptr => try airOptionalPayload(o, inst), + + .is_err => try airIsErr(o, inst, "", ".", "!="), + .is_non_err => try airIsErr(o, inst, "", ".", "=="), + .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="), + .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="), + + .is_null => try airIsNull(o, inst, "==", ""), + .is_non_null => try airIsNull(o, inst, "!=", ""), + .is_null_ptr => try airIsNull(o, inst, "==", "[0]"), + .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"), + + .alloc => try airAlloc(o, inst), + .assembly => try airAsm(o, inst), + .block => try airBlock(o, inst), + .bitcast => try airBitcast(o, inst), + .call => try airCall(o, inst), + .dbg_stmt => try airDbgStmt(o, inst), + .intcast => try airIntCast(o, inst), + .load => try airLoad(o, inst), + .ret => try airRet(o, inst), + .store => try airStore(o, inst), + .loop => try airLoop(o, inst), + .cond_br => try airCondBr(o, inst), + .br => try airBr(o, inst), + .switch_br => try airSwitchBr(o, inst), + .wrap_optional => try airWrapOptional(o, inst), + .ref => try airRef(o, inst), + .struct_field_ptr => try airStructFieldPtr(o, inst), + .varptr => try airVarPtr(o, inst), + + .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst), + .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst), + .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst), + .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst), + .wrap_errunion_payload => try airWrapErrUnionPay(o, inst), + .wrap_errunion_err => try airWrapErrUnionErr(o, inst), + + .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}), + .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}), + // zig fmt: on }; switch (result_value) { .none => {}, @@ -928,38 +948,40 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi try writer.writeAll("}"); } -fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue { - _ = o; - return CValue{ .decl_ref = inst.variable.owner_decl }; +fn airVarPtr(o: *Object, inst: Air.Inst.Index) !CValue { + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const variable = o.air.variables[ty_pl.payload]; + return CValue{ .decl_ref = variable.owner_decl }; } -fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue { +fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue { const writer = o.writer(); + const inst_ty = o.air.typeOfIndex(inst); // First line: the variable used as data storage. - const elem_type = alloc.base.ty.elemType(); - const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut; + const elem_type = inst_ty.elemType(); + const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; const local = try o.allocLocal(elem_type, mutability); try writer.writeAll(";\n"); return CValue{ .local_ref = local.local }; } -fn genArg(o: *Object) CValue { +fn airArg(o: *Object) CValue { const i = o.next_arg_index; o.next_arg_index += 1; return .{ .arg = i }; } -fn genRetVoid(o: *Object) !CValue { - try o.writer().print("return;\n", .{}); - return CValue.none; -} - -fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { - const operand = try o.resolveInst(inst.operand); +fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue { + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr(); + if (!is_volatile and o.liveness.isUnused(inst)) + return CValue.none; + const inst_ty = o.air.typeOfIndex(inst); + const operand = try o.resolveInst(ty_op.operand); const writer = o.writer(); - const local = try o.allocLocal(inst.base.ty, .Const); + const local = try o.allocLocal(inst_ty, .Const); switch (operand) { .local_ref => |i| { const wrapped: CValue = .{ .local = i }; @@ -982,35 +1004,43 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genRet(o: *Object, inst: *Inst.UnOp) !CValue { - const operand = try o.resolveInst(inst.operand); +fn airRet(o: *Object, inst: Air.Inst.Index) !CValue { + const un_op = o.air.instructions.items(.data)[inst].un_op; const writer = o.writer(); - try writer.writeAll("return "); - try o.writeCValue(writer, operand); - try writer.writeAll(";\n"); + if (o.air.typeOf(un_op).hasCodeGenBits()) { + const operand = try o.resolveInst(un_op); + try writer.writeAll("return "); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); + } else { + try writer.writeAll("return;\n"); + } return CValue.none; } -fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue { - if (inst.base.isUnused()) +fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; - const from = try o.resolveInst(inst.operand); + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const from = try o.resolveInst(ty_op.operand); const writer = o.writer(); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = ("); - try o.dg.renderType(writer, inst.base.ty); + try o.dg.renderType(writer, inst_ty); try writer.writeAll(")"); try o.writeCValue(writer, from); try writer.writeAll(";\n"); return local; } -fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { +fn airStore(o: *Object, inst: Air.Inst.Index) !CValue { // *a = b; - const dest_ptr = try o.resolveInst(inst.lhs); - const src_val = try o.resolveInst(inst.rhs); + const bin_op = o.air.instructions.items(.data)[inst].bin_op; + const dest_ptr = try o.resolveInst(bin_op.lhs); + const src_val = try o.resolveInst(bin_op.rhs); const writer = o.writer(); switch (dest_ptr) { @@ -1039,11 +1069,18 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { return CValue.none; } -fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue { - if (inst.base.isUnused()) +fn airWrapOp( + o: *Object, + inst: Air.Inst.Index, + str_op: [*:0]const u8, + fn_op: [*:0]const u8, +) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; - const int_info = inst.base.ty.intInfo(o.dg.module.getTarget()); + const bin_op = o.air.instructions.items(.data)[inst].bin_op; + const inst_ty = o.air.typeOfIndex(inst); + const int_info = inst_ty.intInfo(o.dg.module.getTarget()); const bits = int_info.bits; // if it's an unsigned int with non-arbitrary bit size then we can just add @@ -1052,19 +1089,19 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c 8, 16, 32, 64, 128 => true, else => false, }; - if (ok_bits or inst.base.ty.tag() != .int_unsigned) { - return try genBinOp(o, inst, str_op); + if (ok_bits or inst_ty.tag() != .int_unsigned) { + return try airBinOp(o, inst, str_op); } } if (bits > 64) { - return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: genWrapOp for large integers", .{}); + return o.dg.fail("TODO: C backend: airWrapOp for large integers", .{}); } var min_buf: [80]u8 = undefined; const min = switch (int_info.signedness) { .unsigned => "0", - else => switch (inst.base.ty.tag()) { + else => switch (inst_ty.tag()) { .c_short => "SHRT_MIN", .c_int => "INT_MIN", .c_long => "LONG_MIN", @@ -1081,7 +1118,7 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c }; var max_buf: [80]u8 = undefined; - const max = switch (inst.base.ty.tag()) { + const max = switch (inst_ty.tag()) { .c_short => "SHRT_MAX", .c_ushort => "USHRT_MAX", .c_int => "INT_MAX", @@ -1105,14 +1142,14 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c }, }; - const lhs = try o.resolveInst(inst.lhs); - const rhs = try o.resolveInst(inst.rhs); + const lhs = try o.resolveInst(bin_op.lhs); + const rhs = try o.resolveInst(bin_op.rhs); const w = o.writer(); - const ret = try o.allocLocal(inst.base.ty, .Mut); + const ret = try o.allocLocal(inst_ty, .Mut); try w.print(" = zig_{s}", .{fn_op}); - switch (inst.base.ty.tag()) { + switch (inst_ty.tag()) { .isize => try w.writeAll("isize"), .c_short => try w.writeAll("short"), .c_int => try w.writeAll("int"), @@ -1149,53 +1186,65 @@ fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]c return ret; } -fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue { - if (inst.base.isUnused()) +fn airNot(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; - const lhs = try o.resolveInst(inst.lhs); - const rhs = try o.resolveInst(inst.rhs); + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const op = try o.resolveInst(ty_op.operand); const writer = o.writer(); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try o.writeCValue(writer, lhs); - try writer.print("{s}", .{operator}); - try o.writeCValue(writer, rhs); + if (inst_ty.zigTypeTag() == .Bool) + try writer.writeAll("!") + else + try writer.writeAll("~"); + try o.writeCValue(writer, op); try writer.writeAll(";\n"); return local; } -fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue { - if (inst.base.isUnused()) +fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; - const operand = try o.resolveInst(inst.operand); + const bin_op = o.air.instructions.items(.data)[inst].bin_op; + const lhs = try o.resolveInst(bin_op.lhs); + const rhs = try o.resolveInst(bin_op.rhs); const writer = o.writer(); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); - try writer.print(" = {s}", .{operator}); - try o.writeCValue(writer, operand); + try writer.writeAll(" = "); + try o.writeCValue(writer, lhs); + try writer.print("{s}", .{operator}); + try o.writeCValue(writer, rhs); try writer.writeAll(";\n"); return local; } -fn genCall(o: *Object, inst: *Inst.Call) !CValue { - if (inst.func.castTag(.constant)) |func_inst| { - const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn| +fn airCall(o: *Object, inst: Air.Inst.Index) !CValue { + const pl_op = o.air.instructions.items(.data)[inst].pl_op; + const extra = o.air.extraData(Air.Call, pl_op.payload); + const args = @bitCast([]const Air.Inst.Ref, o.air.extra[extra.end..][0..extra.data.args_len]); + + if (o.air.value(pl_op.operand)) |func_val| { + const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn| extern_fn.data - else if (func_inst.val.castTag(.function)) |func_payload| + else if (func_val.castTag(.function)) |func_payload| func_payload.data.owner_decl else unreachable; const fn_ty = fn_decl.ty; const ret_ty = fn_ty.fnReturnType(); - const unused_result = inst.base.isUnused(); + const unused_result = o.liveness.isUnused(inst); var result_local: CValue = .none; const writer = o.writer(); @@ -1209,41 +1258,44 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { } const fn_name = mem.spanZ(fn_decl.name); try writer.print("{s}(", .{fn_name}); - if (inst.args.len != 0) { - for (inst.args) |arg, i| { - if (i > 0) { - try writer.writeAll(", "); - } - if (arg.value()) |val| { - try o.dg.renderValue(writer, arg.ty, val); - } else { - const val = try o.resolveInst(arg); - try o.writeCValue(writer, val); - } + for (args) |arg, i| { + if (i != 0) { + try writer.writeAll(", "); + } + if (o.air.value(arg)) |val| { + try o.dg.renderValue(writer, o.air.typeOf(arg), val); + } else { + const val = try o.resolveInst(arg); + try o.writeCValue(writer, val); } } try writer.writeAll(");\n"); return result_local; } else { - return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement function pointers", .{}); + return o.dg.fail("TODO: C backend: implement function pointers", .{}); } } -fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue { - _ = o; - _ = inst; - // TODO emit #line directive here with line number and filename +fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue { + const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt; + const writer = o.writer(); + try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); return CValue.none; } -fn genBlock(o: *Object, inst: *Inst.Block) !CValue { +fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue { + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const extra = o.air.extraData(Air.Block, ty_pl.payload); + const body = o.air.extra[extra.end..][0..extra.data.body_len]; + const block_id: usize = o.next_block_index; o.next_block_index += 1; const writer = o.writer(); - const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: { + const inst_ty = o.air.typeOfIndex(inst); + const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: { // allocate a location for the result - const local = try o.allocLocal(inst.base.ty, .Mut); + const local = try o.allocLocal(inst_ty, .Mut); try writer.writeAll(";\n"); break :blk local; } else CValue{ .none = {} }; @@ -1253,42 +1305,44 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue { .result = result, }); - try genBody(o, inst.body); + try genBody(o, body); try o.indent_writer.insertNewline(); // label must be followed by an expression, add an empty one. try writer.print("zig_block_{d}:;\n", .{block_id}); return result; } -fn genBr(o: *Object, inst: *Inst.Br) !CValue { - const result = o.blocks.get(inst.block).?.result; +fn airBr(o: *Object, inst: Air.Inst.Index) !CValue { + const branch = o.air.instructions.items(.data)[inst].br; + const block = o.blocks.get(branch.block_inst).?; + const result = block.result; const writer = o.writer(); // If result is .none then the value of the block is unused. - if (inst.operand.ty.tag() != .void and result != .none) { - const operand = try o.resolveInst(inst.operand); + if (result != .none) { + const operand = try o.resolveInst(branch.operand); try o.writeCValue(writer, result); try writer.writeAll(" = "); try o.writeCValue(writer, operand); try writer.writeAll(";\n"); } - return genBrVoid(o, inst.block); -} - -fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { - try o.writer().print("goto zig_block_{d};\n", .{o.blocks.get(block).?.block_id}); + try o.writer().print("goto zig_block_{d};\n", .{block.block_id}); return CValue.none; } -fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { - const operand = try o.resolveInst(inst.operand); +fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue { + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const operand = try o.resolveInst(ty_op.operand); const writer = o.writer(); - if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) { - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + if (inst_ty.zigTypeTag() == .Pointer and + o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer) + { + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = ("); - try o.dg.renderType(writer, inst.base.ty); + try o.dg.renderType(writer, inst_ty); try writer.writeAll(")"); try o.writeCValue(writer, operand); @@ -1296,7 +1350,7 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { return local; } - const local = try o.allocLocal(inst.base.ty, .Mut); + const local = try o.allocLocal(inst_ty, .Mut); try writer.writeAll(";\n"); try writer.writeAll("memcpy(&"); @@ -1310,60 +1364,79 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue { - _ = inst; +fn airBreakpoint(o: *Object) !CValue { try o.writer().writeAll("zig_breakpoint();\n"); return CValue.none; } -fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { - _ = inst; +fn airUnreach(o: *Object) !CValue { try o.writer().writeAll("zig_unreachable();\n"); return CValue.none; } -fn genLoop(o: *Object, inst: *Inst.Loop) !CValue { +fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue { + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const loop = o.air.extraData(Air.Block, ty_pl.payload); + const body = o.air.extra[loop.end..][0..loop.data.body_len]; try o.writer().writeAll("while (true) "); - try genBody(o, inst.body); + try genBody(o, body); try o.indent_writer.insertNewline(); return CValue.none; } -fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { - const cond = try o.resolveInst(inst.condition); +fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue { + const pl_op = o.air.instructions.items(.data)[inst].pl_op; + const cond = try o.resolveInst(pl_op.operand); + const extra = o.air.extraData(Air.CondBr, pl_op.payload); + const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = o.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; const writer = o.writer(); try writer.writeAll("if ("); try o.writeCValue(writer, cond); try writer.writeAll(") "); - try genBody(o, inst.then_body); + try genBody(o, then_body); try writer.writeAll(" else "); - try genBody(o, inst.else_body); + try genBody(o, else_body); try o.indent_writer.insertNewline(); return CValue.none; } -fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue { - const target = try o.resolveInst(inst.target); +fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue { + const pl_op = o.air.instructions.items(.data)[inst].pl_op; + const condition = try o.resolveInst(pl_op.operand); + const condition_ty = o.air.typeOf(pl_op.operand); + const switch_br = o.air.extraData(Air.SwitchBr, pl_op.payload); const writer = o.writer(); try writer.writeAll("switch ("); - try o.writeCValue(writer, target); - try writer.writeAll(") {\n"); + try o.writeCValue(writer, condition); + try writer.writeAll(") {"); o.indent_writer.pushIndent(); - for (inst.cases) |case| { - try writer.writeAll("case "); - try o.dg.renderValue(writer, inst.target.ty, case.item); - try writer.writeAll(": "); - // the case body must be noreturn so we don't need to insert a break - try genBody(o, case.body); - try o.indent_writer.insertNewline(); + var extra_index: usize = switch_br.end; + var case_i: u32 = 0; + while (case_i < switch_br.data.cases_len) : (case_i += 1) { + const case = o.air.extraData(Air.SwitchBr.Case, extra_index); + const items = @bitCast([]const Air.Inst.Ref, o.air.extra[case.end..][0..case.data.items_len]); + const case_body = o.air.extra[case.end + items.len ..][0..case.data.body_len]; + extra_index = case.end + case.data.items_len + case_body.len; + + for (items) |item| { + try o.indent_writer.insertNewline(); + try writer.writeAll("case "); + try o.dg.renderValue(writer, condition_ty, o.air.value(item).?); + try writer.writeAll(": "); + } + // The case body must be noreturn so we don't need to insert a break. + try genBody(o, case_body); } + const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len]; + try o.indent_writer.insertNewline(); try writer.writeAll("default: "); - try genBody(o, inst.else_body); + try genBody(o, else_body); try o.indent_writer.insertNewline(); o.indent_writer.popIndent(); @@ -1371,39 +1444,75 @@ fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue { return CValue.none; } -fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { - if (as.base.isUnused() and !as.is_volatile) +fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue { + const air_datas = o.air.instructions.items(.data); + const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); + const zir = o.dg.decl.namespace.file_scope.zir; + const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; + const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); + const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); + const outputs_len = @truncate(u5, extended.small); + const args_len = @truncate(u5, extended.small >> 5); + const clobbers_len = @truncate(u5, extended.small >> 10); + _ = clobbers_len; // TODO honor these + const is_volatile = @truncate(u1, extended.small >> 15) != 0; + const outputs = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end..][0..outputs_len]); + const args = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end + outputs.len ..][0..args_len]); + + if (outputs_len > 1) { + return o.dg.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + if (o.liveness.isUnused(inst) and !is_volatile) return CValue.none; + var extra_i: usize = zir_extra.end; + const output_constraint: ?[]const u8 = out: { + var i: usize = 0; + while (i < outputs_len) : (i += 1) { + const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); + extra_i = output.end; + break :out zir.nullTerminatedString(output.data.constraint); + } + break :out null; + }; + const args_extra_begin = extra_i; + const writer = o.writer(); - for (as.inputs) |i, index| { - if (i[0] == '{' and i[i.len - 1] == '}') { - const reg = i[1 .. i.len - 1]; - const arg = as.args[index]; + for (args) |arg| { + const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); + extra_i = input.end; + const constraint = zir.nullTerminatedString(input.data.constraint); + if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { + const reg = constraint[1 .. constraint.len - 1]; const arg_c_value = try o.resolveInst(arg); try writer.writeAll("register "); - try o.dg.renderType(writer, arg.ty); + try o.dg.renderType(writer, o.air.typeOf(arg)); try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); try o.writeCValue(writer, arg_c_value); try writer.writeAll(";\n"); } else { - return o.dg.fail(.{ .node_offset = 0 }, "TODO non-explicit inline asm regs", .{}); + return o.dg.fail("TODO non-explicit inline asm regs", .{}); } } - const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; - try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); - if (as.output_constraint) |_| { - return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{}); + const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; + try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); + if (output_constraint) |_| { + return o.dg.fail("TODO: CBE inline asm output", .{}); } - if (as.inputs.len > 0) { - if (as.output_constraint == null) { + if (args.len > 0) { + if (output_constraint == null) { try writer.writeAll(" :"); } try writer.writeAll(": "); - for (as.inputs) |i, index| { - if (i[0] == '{' and i[i.len - 1] == '}') { - const reg = i[1 .. i.len - 1]; + extra_i = args_extra_begin; + for (args) |_, index| { + const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); + extra_i = input.end; + const constraint = zir.nullTerminatedString(input.data.constraint); + if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { + const reg = constraint[1 .. constraint.len - 1]; if (index > 0) { try writer.writeAll(", "); } @@ -1416,40 +1525,51 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { } try writer.writeAll(");\n"); - if (as.base.isUnused()) + if (o.liveness.isUnused(inst)) return CValue.none; - return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: inline asm expression result used", .{}); + return o.dg.fail("TODO: C backend: inline asm expression result used", .{}); } -fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { +fn airIsNull( + o: *Object, + inst: Air.Inst.Index, + operator: [*:0]const u8, + deref_suffix: [*:0]const u8, +) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const un_op = o.air.instructions.items(.data)[inst].un_op; const writer = o.writer(); - const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr; - const operator = if (invert_logic) "!=" else "=="; - const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else ""; - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(un_op); const local = try o.allocLocal(Type.initTag(.bool), .Const); try writer.writeAll(" = ("); try o.writeCValue(writer, operand); - if (inst.operand.ty.isPtrLikeOptional()) { + if (o.air.typeOf(un_op).isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` - try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator }); + try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); } else { - try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator }); + try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); } return local; } -fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { +fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_op = o.air.instructions.items(.data)[inst].ty_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); + const operand_ty = o.air.typeOf(ty_op.operand); - const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer) - inst.operand.ty.elemType() + const opt_ty = if (operand_ty.zigTypeTag() == .Pointer) + operand_ty.elemType() else - inst.operand.ty; + operand_ty; if (opt_ty.isPtrLikeOptional()) { // the operand is just a regular pointer, no need to do anything special. @@ -1457,10 +1577,11 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { return operand; } - const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; - const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; + const inst_ty = o.air.typeOfIndex(inst); + const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else ""; - const local = try o.allocLocal(inst.base.ty, .Const); + const local = try o.allocLocal(inst_ty, .Const); try writer.print(" = {s}(", .{maybe_addrof}); try o.writeCValue(writer, operand); @@ -1468,24 +1589,36 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genRef(o: *Object, inst: *Inst.UnOp) !CValue { +fn airRef(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_op = o.air.instructions.items(.data)[inst].ty_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); try o.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; } -fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { +fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; const writer = o.writer(); - const struct_ptr = try o.resolveInst(inst.struct_ptr); - const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data; - const field_name = struct_obj.fields.keys()[inst.field_index]; + const struct_ptr = try o.resolveInst(extra.struct_ptr); + const struct_ptr_ty = o.air.typeOf(extra.struct_ptr); + const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; + const field_name = struct_obj.fields.keys()[extra.field_index]; - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); switch (struct_ptr) { .local_ref => |i| { try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) }); @@ -1500,17 +1633,20 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { } // *(E!T) -> E NOT *E -fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { - if (inst.base.isUnused()) +fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const inst_ty = o.air.typeOfIndex(inst); const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); + const operand_ty = o.air.typeOf(ty_op.operand); - const payload_ty = inst.operand.ty.errorUnionChild(); + const payload_ty = operand_ty.errorUnionChild(); if (!payload_ty.hasCodeGenBits()) { - if (inst.operand.ty.zigTypeTag() == .Pointer) { - const local = try o.allocLocal(inst.base.ty, .Const); + if (operand_ty.zigTypeTag() == .Pointer) { + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = *"); try o.writeCValue(writer, operand); try writer.writeAll(";\n"); @@ -1520,9 +1656,9 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { } } - const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; - const local = try o.allocLocal(inst.base.ty, .Const); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = ("); try o.writeCValue(writer, operand); @@ -1530,22 +1666,25 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { - if (inst.base.isUnused()) +fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) return CValue.none; + const ty_op = o.air.instructions.items(.data)[inst].ty_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); + const operand_ty = o.air.typeOf(ty_op.operand); - const payload_ty = inst.operand.ty.errorUnionChild(); + const payload_ty = operand_ty.errorUnionChild(); if (!payload_ty.hasCodeGenBits()) { return CValue.none; } - const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; - const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; + const inst_ty = o.air.typeOfIndex(inst); + const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else ""; - const local = try o.allocLocal(inst.base.ty, .Const); + const local = try o.allocLocal(inst_ty, .Const); try writer.print(" = {s}(", .{maybe_addrof}); try o.writeCValue(writer, operand); @@ -1553,54 +1692,75 @@ fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { +fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_op = o.air.instructions.items(.data)[inst].ty_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); - if (inst.base.ty.isPtrLikeOptional()) { + const inst_ty = o.air.typeOfIndex(inst); + if (inst_ty.isPtrLikeOptional()) { // the operand is just a regular pointer, no need to do anything special. return operand; } // .wrap_optional is used to convert non-optionals into optionals so it can never be null. - const local = try o.allocLocal(inst.base.ty, .Const); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .is_null = false, .payload ="); try o.writeCValue(writer, operand); try writer.writeAll("};\n"); return local; } -fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { +fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const operand = try o.resolveInst(ty_op.operand); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .error = "); try o.writeCValue(writer, operand); try writer.writeAll(" };\n"); return local; } -fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { + +fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const ty_op = o.air.instructions.items(.data)[inst].ty_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(ty_op.operand); - const local = try o.allocLocal(inst.base.ty, .Const); + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .error = 0, .payload = "); try o.writeCValue(writer, operand); try writer.writeAll(" };\n"); return local; } -fn genIsErr( +fn airIsErr( o: *Object, - inst: *Inst.UnOp, + inst: Air.Inst.Index, deref_prefix: [*:0]const u8, deref_suffix: [*:0]const u8, op_str: [*:0]const u8, ) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const un_op = o.air.instructions.items(.data)[inst].un_op; const writer = o.writer(); - const operand = try o.resolveInst(inst.operand); + const operand = try o.resolveInst(un_op); + const operand_ty = o.air.typeOf(un_op); const local = try o.allocLocal(Type.initTag(.bool), .Const); - const payload_ty = inst.operand.ty.errorUnionChild(); + const payload_ty = operand_ty.errorUnionChild(); if (!payload_ty.hasCodeGenBits()) { try writer.print(" = {s}", .{deref_prefix}); try o.writeCValue(writer, operand); |
