aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-07-22 09:34:44 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-07-22 09:34:44 +0200
commitdef135918740846d9b206c3188563cb77333f3a9 (patch)
tree2d054e8bca8b70ab2d38e384ff4eaac665b86422 /src/codegen/c.zig
parentd0edd37f690c3e6cf3f8a7fc7a27016ba9b010ce (diff)
parent8d0671157cdf8bc8b89d047138b42227420a5388 (diff)
downloadzig-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.zig712
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);