Commit 5913140b6b
Changed files (7)
src
src/codegen/c.zig
@@ -91,55 +91,76 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
return .{ .data = ident };
}
-/// This data is available when outputting .c code for a Module.
+/// This data is available when outputting .c code for a `*Module.Fn`.
/// It is not available when generating .h file.
-pub const Object = struct {
- dg: DeclGen,
+pub const Function = struct {
air: Air,
liveness: Liveness,
- gpa: *mem.Allocator,
- code: std.ArrayList(u8),
value_map: CValueMap,
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),
+ object: Object,
+ func: *Module.Fn,
- fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue {
- if (o.air.value(inst)) |_| {
+ fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
+ if (f.air.value(inst)) |_| {
return CValue{ .constant = inst };
}
const index = Air.refToIndex(inst).?;
- return o.value_map.get(index).?; // Assertion means instruction does not dominate usage.
+ return f.value_map.get(index).?; // Assertion means instruction does not dominate usage.
}
- fn allocLocalValue(o: *Object) CValue {
- const result = o.next_local_index;
- o.next_local_index += 1;
+ fn allocLocalValue(f: *Function) CValue {
+ const result = f.next_local_index;
+ f.next_local_index += 1;
return .{ .local = result };
}
- fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue {
- const local_value = o.allocLocalValue();
- try o.renderTypeAndName(o.writer(), ty, local_value, mutability);
+ fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
+ const local_value = f.allocLocalValue();
+ try f.object.renderTypeAndName(f.object.writer(), ty, local_value, mutability);
return local_value;
}
+ fn writeCValue(f: *Function, w: anytype, c_value: CValue) !void {
+ switch (c_value) {
+ .constant => |inst| {
+ const ty = f.air.typeOf(inst);
+ const val = f.air.value(inst).?;
+ return f.object.dg.renderValue(w, ty, val);
+ },
+ else => return Object.writeCValue(w, c_value),
+ }
+ }
+
+ fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
+ return f.object.dg.fail(format, args);
+ }
+
+ fn renderType(f: *Function, w: anytype, t: Type) !void {
+ return f.object.dg.renderType(w, t);
+ }
+};
+
+/// This data is available when outputting .c code for a `Module`.
+/// It is not available when generating .h file.
+pub const Object = struct {
+ dg: DeclGen,
+ code: std.ArrayList(u8),
+ indent_writer: IndentWriter(std.ArrayList(u8).Writer),
+
fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
return o.indent_writer.writer();
}
- fn writeCValue(o: *Object, w: anytype, c_value: CValue) !void {
+ fn writeCValue(w: anytype, c_value: CValue) !void {
switch (c_value) {
.none => unreachable,
.local => |i| return w.print("t{d}", .{i}),
.local_ref => |i| return w.print("&t{d}", .{i}),
- .constant => |inst| {
- const ty = o.air.typeOf(inst);
- const val = o.air.value(inst).?;
- return o.dg.renderValue(w, ty, val);
- },
+ .constant => unreachable,
.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}),
@@ -153,7 +174,7 @@ pub const Object = struct {
name: CValue,
mutability: Mutability,
) error{ OutOfMemory, AnalysisFail }!void {
- var suffix = std.ArrayList(u8).init(o.gpa);
+ var suffix = std.ArrayList(u8).init(o.dg.gpa);
defer suffix.deinit();
var render_ty = ty;
@@ -177,7 +198,7 @@ pub const Object = struct {
.Const => try w.writeAll("const "),
.Mut => {},
}
- try o.writeCValue(w, name);
+ try writeCValue(w, name);
try w.writeAll(")(");
const param_len = render_ty.fnParamLen();
const is_var_args = render_ty.fnIsVarArgs();
@@ -205,7 +226,7 @@ pub const Object = struct {
.Mut => "",
};
try w.print(" {s}", .{const_prefix});
- try o.writeCValue(w, name);
+ try writeCValue(w, name);
}
try w.writeAll(suffix.items);
}
@@ -213,11 +234,14 @@ pub const Object = struct {
/// This data is available both when outputting .c code and when outputting an .h file.
pub const DeclGen = struct {
+ gpa: *std.mem.Allocator,
module: *Module,
decl: *Decl,
fwd_decl: std.ArrayList(u8),
error_msg: ?*Module.ErrorMsg,
+ /// The key of this map is Type which has references to typedefs_arena.
typedefs: TypedefMap,
+ typedefs_arena: *std.mem.Allocator,
fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
@setCold(true);
@@ -545,7 +569,10 @@ pub const DeclGen = struct {
try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
} else {
try dg.renderType(w, t.elemType());
try w.writeAll(" *");
@@ -586,7 +613,10 @@ pub const DeclGen = struct {
try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
},
.ErrorSet => {
comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);
@@ -626,7 +656,10 @@ pub const DeclGen = struct {
try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
},
.Struct => {
if (dg.typedefs.get(t)) |some| {
@@ -659,7 +692,10 @@ pub const DeclGen = struct {
try dg.typedefs.ensureUnusedCapacity(1);
try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
},
.Enum => {
// For enums, we simply use the integer tag type.
@@ -724,6 +760,29 @@ pub const DeclGen = struct {
}
};
+pub fn genFunc(f: *Function) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const o = &f.object;
+ const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl);
+ const fwd_decl_writer = o.dg.fwd_decl.writer();
+ if (is_global) {
+ try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
+ }
+ try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
+ try fwd_decl_writer.writeAll(";\n");
+
+ try o.indent_writer.insertNewline();
+ try o.dg.renderFunctionSignature(o.writer(), is_global);
+
+ try o.writer().writeByte(' ');
+ const main_body = f.air.getMainBody();
+ try genBody(f, main_body);
+
+ try o.indent_writer.insertNewline();
+}
+
pub fn genDecl(o: *Object) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -732,28 +791,6 @@ pub fn genDecl(o: *Object) !void {
.ty = o.dg.decl.ty,
.val = o.dg.decl.val,
};
- if (tv.val.castTag(.function)) |func_payload| {
- const func: *Module.Fn = func_payload.data;
- if (func.owner_decl == o.dg.decl) {
- const is_global = o.dg.declIsGlobal(tv);
- const fwd_decl_writer = o.dg.fwd_decl.writer();
- if (is_global) {
- try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
- }
- try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
- try fwd_decl_writer.writeAll(";\n");
-
- try o.indent_writer.insertNewline();
- try o.dg.renderFunctionSignature(o.writer(), is_global);
-
- try o.writer().writeByte(' ');
- const main_body = o.air.getMainBody();
- try genBody(o, main_body);
-
- try o.indent_writer.insertNewline();
- return;
- }
- }
if (tv.val.tag() == .extern_fn) {
const writer = o.writer();
try writer.writeAll("ZIG_EXTERN_C ");
@@ -821,250 +858,250 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
}
}
-fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
- const writer = o.writer();
+fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
+ const writer = f.object.writer();
if (body.len == 0) {
try writer.writeAll("{}");
return;
}
try writer.writeAll("{\n");
- o.indent_writer.pushIndent();
+ f.object.indent_writer.pushIndent();
- const air_tags = o.air.instructions.items(.tag);
+ const air_tags = f.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),
+ .arg => airArg(f),
- .breakpoint => try airBreakpoint(o),
- .unreach => try airUnreach(o),
- .fence => try airFence(o, inst),
+ .breakpoint => try airBreakpoint(f),
+ .unreach => try airUnreach(f),
+ .fence => try airFence(f, inst),
// TODO use a different strategy for add that communicates to the optimizer
// that wrapping is UB.
- .add, .ptr_add => try airBinOp( o, inst, " + "),
- .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
+ .add, .ptr_add => try airBinOp( f, inst, " + "),
+ .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
// TODO use a different strategy for sub that communicates to the optimizer
// that wrapping is UB.
- .sub, .ptr_sub => try airBinOp( o, inst, " - "),
- .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
+ .sub, .ptr_sub => try airBinOp( f, inst, " - "),
+ .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
// TODO use a different strategy for mul that communicates to the optimizer
// that wrapping is UB.
- .mul => try airBinOp( o, inst, " * "),
- .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
+ .mul => try airBinOp( f, inst, " * "),
+ .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
// TODO use a different strategy for div that communicates to the optimizer
// that wrapping is UB.
- .div => try airBinOp( o, inst, " / "),
- .rem => try airBinOp( o, inst, " % "),
+ .div => try airBinOp( f, inst, " / "),
+ .rem => try airBinOp( f, 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, " != "),
+ .cmp_eq => try airBinOp(f, inst, " == "),
+ .cmp_gt => try airBinOp(f, inst, " > "),
+ .cmp_gte => try airBinOp(f, inst, " >= "),
+ .cmp_lt => try airBinOp(f, inst, " < "),
+ .cmp_lte => try airBinOp(f, inst, " <= "),
+ .cmp_neq => try airBinOp(f, inst, " != "),
// bool_and and bool_or are non-short-circuit operations
- .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, " ^ "),
-
- .shr => try airBinOp(o, inst, " >> "),
- .shl => 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),
- .trunc => try airTrunc(o, inst),
- .bool_to_int => try airBoolToInt(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),
- .struct_field_ptr => try airStructFieldPtr(o, inst),
- .array_to_slice => try airArrayToSlice(o, inst),
- .cmpxchg_weak => try airCmpxchg(o, inst, "weak"),
- .cmpxchg_strong => try airCmpxchg(o, inst, "strong"),
- .atomic_rmw => try airAtomicRmw(o, inst),
- .atomic_load => try airAtomicLoad(o, inst),
-
- .int_to_float, .float_to_int => try airSimpleCast(o, inst),
-
- .atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)),
- .atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)),
- .atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)),
- .atomic_store_seq_cst => try airAtomicStore(o, inst, toMemoryOrder(.SeqCst)),
-
- .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
- .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
- .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2),
- .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3),
-
- .struct_field_val => try airStructFieldVal(o, inst),
- .slice_ptr => try airSliceField(o, inst, ".ptr;\n"),
- .slice_len => try airSliceField(o, inst, ".len;\n"),
-
- .ptr_elem_val => try airPtrElemVal(o, inst, "["),
- .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),
- .ptr_elem_ptr => try airPtrElemPtr(o, inst),
- .slice_elem_val => try airSliceElemVal(o, inst, "["),
- .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),
-
- .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", .{}),
+ .bool_and => try airBinOp(f, inst, " & "),
+ .bool_or => try airBinOp(f, inst, " | "),
+ .bit_and => try airBinOp(f, inst, " & "),
+ .bit_or => try airBinOp(f, inst, " | "),
+ .xor => try airBinOp(f, inst, " ^ "),
+
+ .shr => try airBinOp(f, inst, " >> "),
+ .shl => try airBinOp(f, inst, " << "),
+
+ .not => try airNot( f, inst),
+
+ .optional_payload => try airOptionalPayload(f, inst),
+ .optional_payload_ptr => try airOptionalPayload(f, inst),
+
+ .is_err => try airIsErr(f, inst, "", ".", "!="),
+ .is_non_err => try airIsErr(f, inst, "", ".", "=="),
+ .is_err_ptr => try airIsErr(f, inst, "*", "->", "!="),
+ .is_non_err_ptr => try airIsErr(f, inst, "*", "->", "=="),
+
+ .is_null => try airIsNull(f, inst, "==", ""),
+ .is_non_null => try airIsNull(f, inst, "!=", ""),
+ .is_null_ptr => try airIsNull(f, inst, "==", "[0]"),
+ .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"),
+
+ .alloc => try airAlloc(f, inst),
+ .assembly => try airAsm(f, inst),
+ .block => try airBlock(f, inst),
+ .bitcast => try airBitcast(f, inst),
+ .call => try airCall(f, inst),
+ .dbg_stmt => try airDbgStmt(f, inst),
+ .intcast => try airIntCast(f, inst),
+ .trunc => try airTrunc(f, inst),
+ .bool_to_int => try airBoolToInt(f, inst),
+ .load => try airLoad(f, inst),
+ .ret => try airRet(f, inst),
+ .store => try airStore(f, inst),
+ .loop => try airLoop(f, inst),
+ .cond_br => try airCondBr(f, inst),
+ .br => try airBr(f, inst),
+ .switch_br => try airSwitchBr(f, inst),
+ .wrap_optional => try airWrapOptional(f, inst),
+ .struct_field_ptr => try airStructFieldPtr(f, inst),
+ .array_to_slice => try airArrayToSlice(f, inst),
+ .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
+ .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
+ .atomic_rmw => try airAtomicRmw(f, inst),
+ .atomic_load => try airAtomicLoad(f, inst),
+
+ .int_to_float, .float_to_int => try airSimpleCast(f, inst),
+
+ .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)),
+ .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)),
+ .atomic_store_release => try airAtomicStore(f, inst, toMemoryOrder(.Release)),
+ .atomic_store_seq_cst => try airAtomicStore(f, inst, toMemoryOrder(.SeqCst)),
+
+ .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0),
+ .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1),
+ .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2),
+ .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3),
+
+ .struct_field_val => try airStructFieldVal(f, inst),
+ .slice_ptr => try airSliceField(f, inst, ".ptr;\n"),
+ .slice_len => try airSliceField(f, inst, ".len;\n"),
+
+ .ptr_elem_val => try airPtrElemVal(f, inst, "["),
+ .ptr_ptr_elem_val => try airPtrElemVal(f, inst, "[0]["),
+ .ptr_elem_ptr => try airPtrElemPtr(f, inst),
+ .slice_elem_val => try airSliceElemVal(f, inst, "["),
+ .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["),
+
+ .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),
+ .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),
+ .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst),
+ .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),
+ .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
+ .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
+
+ .ptrtoint => return f.fail("TODO: C backend: implement codegen for ptrtoint", .{}),
+ .floatcast => return f.fail("TODO: C backend: implement codegen for floatcast", .{}),
// zig fmt: on
};
switch (result_value) {
.none => {},
- else => try o.value_map.putNoClobber(inst, result_value),
+ else => try f.value_map.putNoClobber(inst, result_value),
}
}
- o.indent_writer.popIndent();
+ f.object.indent_writer.popIndent();
try writer.writeAll("}");
}
-fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {
- if (o.liveness.isUnused(inst))
+fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const operand = try o.resolveInst(ty_op.operand);
- const writer = o.writer();
- const local = try o.allocLocal(Type.initTag(.usize), .Const);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
+ const writer = f.object.writer();
+ const local = try f.allocLocal(Type.initTag(.usize), .Const);
try writer.writeAll(" = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(suffix);
return local;
}
-fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
+fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
const is_volatile = false; // TODO
- if (!is_volatile and o.liveness.isUnused(inst))
+ if (!is_volatile and f.liveness.isUnused(inst))
return CValue.none;
_ = prefix;
- return o.dg.fail("TODO: C backend: airPtrElemVal", .{});
+ return f.fail("TODO: C backend: airPtrElemVal", .{});
}
-fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- return o.dg.fail("TODO: C backend: airPtrElemPtr", .{});
+ return f.fail("TODO: C backend: airPtrElemPtr", .{});
}
-fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
+fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
const is_volatile = false; // TODO
- if (!is_volatile and o.liveness.isUnused(inst))
+ if (!is_volatile and f.liveness.isUnused(inst))
return CValue.none;
- const bin_op = o.air.instructions.items(.data)[inst].bin_op;
- const slice = try o.resolveInst(bin_op.lhs);
- const index = try o.resolveInst(bin_op.rhs);
- const writer = o.writer();
- const local = try o.allocLocal(o.air.typeOfIndex(inst), .Const);
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const slice = try f.resolveInst(bin_op.lhs);
+ const index = try f.resolveInst(bin_op.rhs);
+ const writer = f.object.writer();
+ const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
try writer.writeAll(" = ");
- try o.writeCValue(writer, slice);
+ try f.writeCValue(writer, slice);
try writer.writeAll(prefix);
- try o.writeCValue(writer, index);
+ try f.writeCValue(writer, index);
try writer.writeAll("];\n");
return local;
}
-fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
+fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
// First line: the variable used as data storage.
const elem_type = inst_ty.elemType();
const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
- const local = try o.allocLocal(elem_type, mutability);
+ const local = try f.allocLocal(elem_type, mutability);
try writer.writeAll(";\n");
return CValue{ .local_ref = local.local };
}
-fn airArg(o: *Object) CValue {
- const i = o.next_arg_index;
- o.next_arg_index += 1;
+fn airArg(f: *Function) CValue {
+ const i = f.next_arg_index;
+ f.next_arg_index += 1;
return .{ .arg = i };
}
-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))
+fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
+ if (!is_volatile and f.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_ty, .Const);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const operand = try f.resolveInst(ty_op.operand);
+ const writer = f.object.writer();
+ const local = try f.allocLocal(inst_ty, .Const);
switch (operand) {
.local_ref => |i| {
const wrapped: CValue = .{ .local = i };
try writer.writeAll(" = ");
- try o.writeCValue(writer, wrapped);
+ try f.writeCValue(writer, wrapped);
try writer.writeAll(";\n");
},
.decl_ref => |decl| {
const wrapped: CValue = .{ .decl = decl };
try writer.writeAll(" = ");
- try o.writeCValue(writer, wrapped);
+ try f.writeCValue(writer, wrapped);
try writer.writeAll(";\n");
},
else => {
try writer.writeAll(" = *");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
},
}
return local;
}
-fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
- const un_op = o.air.instructions.items(.data)[inst].un_op;
- const writer = o.writer();
- if (o.air.typeOf(un_op).hasCodeGenBits()) {
- const operand = try o.resolveInst(un_op);
+fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ if (f.air.typeOf(un_op).hasCodeGenBits()) {
+ const operand = try f.resolveInst(un_op);
try writer.writeAll("return ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
} else {
try writer.writeAll("return;\n");
@@ -1072,75 +1109,75 @@ fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
return CValue.none;
}
-fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const operand = try o.resolveInst(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = (");
- try o.dg.renderType(writer, inst_ty);
+ try f.renderType(writer, inst_ty);
try writer.writeAll(")");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
-fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const operand = try o.resolveInst(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
_ = operand;
- return o.dg.fail("TODO: C backend: airTrunc", .{});
+ return f.fail("TODO: C backend: airTrunc", .{});
}
-fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const un_op = o.air.instructions.items(.data)[inst].un_op;
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
- const operand = try o.resolveInst(un_op);
- const local = try o.allocLocal(inst_ty, .Const);
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const operand = try f.resolveInst(un_op);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
-fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
+fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
// *a = b;
- 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 bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const dest_ptr = try f.resolveInst(bin_op.lhs);
+ const src_val = try f.resolveInst(bin_op.rhs);
- const writer = o.writer();
+ const writer = f.object.writer();
switch (dest_ptr) {
.local_ref => |i| {
const dest: CValue = .{ .local = i };
- try o.writeCValue(writer, dest);
+ try f.writeCValue(writer, dest);
try writer.writeAll(" = ");
- try o.writeCValue(writer, src_val);
+ try f.writeCValue(writer, src_val);
try writer.writeAll(";\n");
},
.decl_ref => |decl| {
const dest: CValue = .{ .decl = decl };
- try o.writeCValue(writer, dest);
+ try f.writeCValue(writer, dest);
try writer.writeAll(" = ");
- try o.writeCValue(writer, src_val);
+ try f.writeCValue(writer, src_val);
try writer.writeAll(";\n");
},
else => {
try writer.writeAll("*");
- try o.writeCValue(writer, dest_ptr);
+ try f.writeCValue(writer, dest_ptr);
try writer.writeAll(" = ");
- try o.writeCValue(writer, src_val);
+ try f.writeCValue(writer, src_val);
try writer.writeAll(";\n");
},
}
@@ -1148,17 +1185,17 @@ fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
}
fn airWrapOp(
- o: *Object,
+ f: *Function,
inst: Air.Inst.Index,
str_op: [*:0]const u8,
fn_op: [*:0]const u8,
) !CValue {
- if (o.liveness.isUnused(inst))
+ if (f.liveness.isUnused(inst))
return CValue.none;
- 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 bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
const bits = int_info.bits;
// if it's an unsigned int with non-arbitrary bit size then we can just add
@@ -1168,12 +1205,12 @@ fn airWrapOp(
else => false,
};
if (ok_bits or inst_ty.tag() != .int_unsigned) {
- return try airBinOp(o, inst, str_op);
+ return try airBinOp(f, inst, str_op);
}
}
if (bits > 64) {
- return o.dg.fail("TODO: C backend: airWrapOp for large integers", .{});
+ return f.fail("TODO: C backend: airWrapOp for large integers", .{});
}
var min_buf: [80]u8 = undefined;
@@ -1220,11 +1257,11 @@ fn airWrapOp(
},
};
- const lhs = try o.resolveInst(bin_op.lhs);
- const rhs = try o.resolveInst(bin_op.rhs);
- const w = o.writer();
+ const lhs = try f.resolveInst(bin_op.lhs);
+ const rhs = try f.resolveInst(bin_op.rhs);
+ const w = f.object.writer();
- const ret = try o.allocLocal(inst_ty, .Mut);
+ const ret = try f.allocLocal(inst_ty, .Mut);
try w.print(" = zig_{s}", .{fn_op});
switch (inst_ty.tag()) {
@@ -1250,71 +1287,71 @@ fn airWrapOp(
}
try w.writeByte('(');
- try o.writeCValue(w, lhs);
+ try f.writeCValue(w, lhs);
try w.writeAll(", ");
- try o.writeCValue(w, rhs);
+ try f.writeCValue(w, rhs);
if (int_info.signedness == .signed) {
try w.print(", {s}", .{min});
}
try w.print(", {s});", .{max});
- try o.indent_writer.insertNewline();
+ try f.object.indent_writer.insertNewline();
return ret;
}
-fn airNot(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const op = try o.resolveInst(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const op = try f.resolveInst(ty_op.operand);
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
if (inst_ty.zigTypeTag() == .Bool)
try writer.writeAll("!")
else
try writer.writeAll("~");
- try o.writeCValue(writer, op);
+ try f.writeCValue(writer, op);
try writer.writeAll(";\n");
return local;
}
-fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
- if (o.liveness.isUnused(inst))
+fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- 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 bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try f.resolveInst(bin_op.lhs);
+ const rhs = try f.resolveInst(bin_op.rhs);
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
- try o.writeCValue(writer, lhs);
+ try f.writeCValue(writer, lhs);
try writer.print("{s}", .{operator});
- try o.writeCValue(writer, rhs);
+ try f.writeCValue(writer, rhs);
try writer.writeAll(";\n");
return local;
}
-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]);
- const fn_ty = o.air.typeOf(pl_op.operand);
+fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
+ const pl_op = f.air.instructions.items(.data)[inst].pl_op;
+ const extra = f.air.extraData(Air.Call, pl_op.payload);
+ const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);
+ const fn_ty = f.air.typeOf(pl_op.operand);
const ret_ty = fn_ty.fnReturnType();
- const unused_result = o.liveness.isUnused(inst);
- const writer = o.writer();
+ const unused_result = f.liveness.isUnused(inst);
+ const writer = f.object.writer();
var result_local: CValue = .none;
if (unused_result) {
@@ -1322,11 +1359,11 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
try writer.print("(void)", .{});
}
} else {
- result_local = try o.allocLocal(ret_ty, .Const);
+ result_local = try f.allocLocal(ret_ty, .Const);
try writer.writeAll(" = ");
}
- if (o.air.value(pl_op.operand)) |func_val| {
+ if (f.air.value(pl_op.operand)) |func_val| {
const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|
extern_fn.data
else if (func_val.castTag(.function)) |func_payload|
@@ -1336,8 +1373,8 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
try writer.writeAll(mem.spanZ(fn_decl.name));
} else {
- const callee = try o.resolveInst(pl_op.operand);
- try o.writeCValue(writer, callee);
+ const callee = try f.resolveInst(pl_op.operand);
+ try f.writeCValue(writer, callee);
}
try writer.writeAll("(");
@@ -1345,113 +1382,113 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
if (i != 0) {
try writer.writeAll(", ");
}
- if (o.air.value(arg)) |val| {
- try o.dg.renderValue(writer, o.air.typeOf(arg), val);
+ if (f.air.value(arg)) |val| {
+ try f.object.dg.renderValue(writer, f.air.typeOf(arg), val);
} else {
- const val = try o.resolveInst(arg);
- try o.writeCValue(writer, val);
+ const val = try f.resolveInst(arg);
+ try f.writeCValue(writer, val);
}
}
try writer.writeAll(");\n");
return result_local;
}
-fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {
- const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;
- const writer = o.writer();
+fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
+ const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;
+ const writer = f.object.writer();
try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
return CValue.none;
}
-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];
+fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+ const extra = f.air.extraData(Air.Block, ty_pl.payload);
+ const body = f.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 block_id: usize = f.next_block_index;
+ f.next_block_index += 1;
+ const writer = f.object.writer();
- const inst_ty = o.air.typeOfIndex(inst);
- const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: {
+ const inst_ty = f.air.typeOfIndex(inst);
+ const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: {
// allocate a location for the result
- const local = try o.allocLocal(inst_ty, .Mut);
+ const local = try f.allocLocal(inst_ty, .Mut);
try writer.writeAll(";\n");
break :blk local;
} else CValue{ .none = {} };
- try o.blocks.putNoClobber(o.gpa, inst, .{
+ try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
.block_id = block_id,
.result = result,
});
- try genBody(o, body);
- try o.indent_writer.insertNewline();
+ try genBody(f, body);
+ try f.object.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 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).?;
+fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
+ const branch = f.air.instructions.items(.data)[inst].br;
+ const block = f.blocks.get(branch.block_inst).?;
const result = block.result;
- const writer = o.writer();
+ const writer = f.object.writer();
// If result is .none then the value of the block is unused.
if (result != .none) {
- const operand = try o.resolveInst(branch.operand);
- try o.writeCValue(writer, result);
+ const operand = try f.resolveInst(branch.operand);
+ try f.writeCValue(writer, result);
try writer.writeAll(" = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
}
- try o.writer().print("goto zig_block_{d};\n", .{block.block_id});
+ try f.object.writer().print("goto zig_block_{d};\n", .{block.block_id});
return CValue.none;
}
-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);
+fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
- const writer = o.writer();
- const inst_ty = o.air.typeOfIndex(inst);
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
if (inst_ty.zigTypeTag() == .Pointer and
- o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
+ f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
{
- const local = try o.allocLocal(inst_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = (");
- try o.dg.renderType(writer, inst_ty);
+ try f.renderType(writer, inst_ty);
try writer.writeAll(")");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
- const local = try o.allocLocal(inst_ty, .Mut);
+ const local = try f.allocLocal(inst_ty, .Mut);
try writer.writeAll(";\n");
try writer.writeAll("memcpy(&");
- try o.writeCValue(writer, local);
+ try f.writeCValue(writer, local);
try writer.writeAll(", &");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(", sizeof ");
- try o.writeCValue(writer, local);
+ try f.writeCValue(writer, local);
try writer.writeAll(");\n");
return local;
}
-fn airBreakpoint(o: *Object) !CValue {
- try o.writer().writeAll("zig_breakpoint();\n");
+fn airBreakpoint(f: *Function) !CValue {
+ try f.object.writer().writeAll("zig_breakpoint();\n");
return CValue.none;
}
-fn airFence(o: *Object, inst: Air.Inst.Index) !CValue {
- const atomic_order = o.air.instructions.items(.data)[inst].fence;
- const writer = o.writer();
+fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
+ const atomic_order = f.air.instructions.items(.data)[inst].fence;
+ const writer = f.object.writer();
try writer.writeAll("zig_fence(");
try writeMemoryOrder(writer, atomic_order);
@@ -1460,85 +1497,85 @@ fn airFence(o: *Object, inst: Air.Inst.Index) !CValue {
return CValue.none;
}
-fn airUnreach(o: *Object) !CValue {
- try o.writer().writeAll("zig_unreachable();\n");
+fn airUnreach(f: *Function) !CValue {
+ try f.object.writer().writeAll("zig_unreachable();\n");
return CValue.none;
}
-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, body);
- try o.indent_writer.insertNewline();
+fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+ const loop = f.air.extraData(Air.Block, ty_pl.payload);
+ const body = f.air.extra[loop.end..][0..loop.data.body_len];
+ try f.object.writer().writeAll("while (true) ");
+ try genBody(f, body);
+ try f.object.indent_writer.insertNewline();
return CValue.none;
}
-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();
+fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
+ const pl_op = f.air.instructions.items(.data)[inst].pl_op;
+ const cond = try f.resolveInst(pl_op.operand);
+ const extra = f.air.extraData(Air.CondBr, pl_op.payload);
+ const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len];
+ const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+ const writer = f.object.writer();
try writer.writeAll("if (");
- try o.writeCValue(writer, cond);
+ try f.writeCValue(writer, cond);
try writer.writeAll(") ");
- try genBody(o, then_body);
+ try genBody(f, then_body);
try writer.writeAll(" else ");
- try genBody(o, else_body);
- try o.indent_writer.insertNewline();
+ try genBody(f, else_body);
+ try f.object.indent_writer.insertNewline();
return CValue.none;
}
-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();
+fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
+ const pl_op = f.air.instructions.items(.data)[inst].pl_op;
+ const condition = try f.resolveInst(pl_op.operand);
+ const condition_ty = f.air.typeOf(pl_op.operand);
+ const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload);
+ const writer = f.object.writer();
try writer.writeAll("switch (");
- try o.writeCValue(writer, condition);
+ try f.writeCValue(writer, condition);
try writer.writeAll(") {");
- o.indent_writer.pushIndent();
+ f.object.indent_writer.pushIndent();
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];
+ const case = f.air.extraData(Air.SwitchBr.Case, extra_index);
+ const items = @bitCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]);
+ const case_body = f.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 f.object.indent_writer.insertNewline();
try writer.writeAll("case ");
- try o.dg.renderValue(writer, condition_ty, o.air.value(item).?);
+ try f.object.dg.renderValue(writer, condition_ty, f.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);
+ try genBody(f, case_body);
}
- const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len];
- try o.indent_writer.insertNewline();
+ const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
+ try f.object.indent_writer.insertNewline();
try writer.writeAll("default: ");
- try genBody(o, else_body);
- try o.indent_writer.insertNewline();
+ try genBody(f, else_body);
+ try f.object.indent_writer.insertNewline();
- o.indent_writer.popIndent();
+ f.object.indent_writer.popIndent();
try writer.writeAll("}\n");
return CValue.none;
}
-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;
+fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
+ const air_datas = f.air.instructions.items(.data);
+ const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
+ const zir = f.object.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);
@@ -1547,14 +1584,14 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
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]);
+ const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);
+ const args = @bitCast([]const Air.Inst.Ref, f.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", .{});
+ return f.fail("TODO implement codegen for asm with more than 1 output", .{});
}
- if (o.liveness.isUnused(inst) and !is_volatile)
+ if (f.liveness.isUnused(inst) and !is_volatile)
return CValue.none;
var extra_i: usize = zir_extra.end;
@@ -1569,28 +1606,28 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
};
const args_extra_begin = extra_i;
- const writer = o.writer();
+ const writer = f.object.writer();
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);
+ const arg_c_value = try f.resolveInst(arg);
try writer.writeAll("register ");
- try o.dg.renderType(writer, o.air.typeOf(arg));
+ try f.renderType(writer, f.air.typeOf(arg));
try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
- try o.writeCValue(writer, arg_c_value);
+ try f.writeCValue(writer, arg_c_value);
try writer.writeAll(";\n");
} else {
- return o.dg.fail("TODO non-explicit inline asm regs", .{});
+ return f.fail("TODO non-explicit inline asm regs", .{});
}
}
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", .{});
+ return f.fail("TODO: CBE inline asm output", .{});
}
if (args.len > 0) {
if (output_constraint == null) {
@@ -1616,30 +1653,30 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
}
try writer.writeAll(");\n");
- if (o.liveness.isUnused(inst))
+ if (f.liveness.isUnused(inst))
return CValue.none;
- return o.dg.fail("TODO: C backend: inline asm expression result used", .{});
+ return f.fail("TODO: C backend: inline asm expression result used", .{});
}
fn airIsNull(
- o: *Object,
+ f: *Function,
inst: Air.Inst.Index,
operator: [*:0]const u8,
deref_suffix: [*:0]const u8,
) !CValue {
- if (o.liveness.isUnused(inst))
+ if (f.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(un_op);
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(un_op);
- const local = try o.allocLocal(Type.initTag(.bool), .Const);
+ const local = try f.allocLocal(Type.initTag(.bool), .Const);
try writer.writeAll(" = (");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
- if (o.air.typeOf(un_op).isPtrLikeOptional()) {
+ if (f.air.typeOf(un_op).isPtrLikeOptional()) {
// operand is a regular pointer, test `operand !=/== NULL`
try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
} else {
@@ -1648,14 +1685,14 @@ fn airIsNull(
return local;
}
-fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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(ty_op.operand);
- const operand_ty = o.air.typeOf(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
+ const operand_ty = f.air.typeOf(ty_op.operand);
const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
operand_ty.elemType()
@@ -1668,98 +1705,98 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
return operand;
}
- const inst_ty = o.air.typeOfIndex(inst);
+ const inst_ty = f.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_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.print(" = {s}(", .{maybe_addrof});
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print("){s}payload;\n", .{maybe_deref});
return local;
}
-fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
// TODO this @as is needed because of a stage1 bug
return @as(CValue, 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 struct_ptr = try o.resolveInst(extra.struct_operand);
- const struct_ptr_ty = o.air.typeOf(extra.struct_operand);
- return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index);
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+ const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
+ const struct_ptr = try f.resolveInst(extra.struct_operand);
+ const struct_ptr_ty = f.air.typeOf(extra.struct_operand);
+ return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index);
}
-fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue {
- if (o.liveness.isUnused(inst))
+fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
+ if (f.liveness.isUnused(inst))
// TODO this @as is needed because of a stage1 bug
return @as(CValue, CValue.none);
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const struct_ptr = try o.resolveInst(ty_op.operand);
- const struct_ptr_ty = o.air.typeOf(ty_op.operand);
- return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const struct_ptr = try f.resolveInst(ty_op.operand);
+ const struct_ptr_ty = f.air.typeOf(ty_op.operand);
+ return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index);
}
-fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
- const writer = o.writer();
+fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
+ const writer = f.object.writer();
const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
const field_name = struct_obj.fields.keys()[index];
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
switch (struct_ptr) {
.local_ref => |i| {
try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
},
else => {
try writer.writeAll(" = &");
- try o.writeCValue(writer, struct_ptr);
+ try f.writeCValue(writer, struct_ptr);
try writer.print("->{};\n", .{fmtIdent(field_name)});
},
}
return local;
}
-fn airStructFieldVal(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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_byval = try o.resolveInst(extra.struct_operand);
- const struct_ty = o.air.typeOf(extra.struct_operand);
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+ const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
+ const writer = f.object.writer();
+ const struct_byval = try f.resolveInst(extra.struct_operand);
+ const struct_ty = f.air.typeOf(extra.struct_operand);
const struct_obj = struct_ty.castTag(.@"struct").?.data;
const field_name = struct_obj.fields.keys()[extra.field_index];
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
- try o.writeCValue(writer, struct_byval);
+ try f.writeCValue(writer, struct_byval);
try writer.print(".{};\n", .{fmtIdent(field_name)});
return local;
}
// *(E!T) -> E NOT *E
-fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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(ty_op.operand);
- const operand_ty = o.air.typeOf(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
+ const operand_ty = f.air.typeOf(ty_op.operand);
const payload_ty = operand_ty.errorUnionPayload();
if (!payload_ty.hasCodeGenBits()) {
if (operand_ty.zigTypeTag() == .Pointer) {
- const local = try o.allocLocal(inst_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = *");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
} else {
@@ -1769,172 +1806,172 @@ fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
- const local = try o.allocLocal(inst_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = (");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print("){s}error;\n", .{maybe_deref});
return local;
}
-fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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(ty_op.operand);
- const operand_ty = o.air.typeOf(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
+ const operand_ty = f.air.typeOf(ty_op.operand);
const payload_ty = operand_ty.errorUnionPayload();
if (!payload_ty.hasCodeGenBits()) {
return CValue.none;
}
- const inst_ty = o.air.typeOfIndex(inst);
+ const inst_ty = f.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_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.print(" = {s}(", .{maybe_addrof});
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print("){s}payload;\n", .{maybe_deref});
return local;
}
-fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
- const inst_ty = o.air.typeOfIndex(inst);
+ const inst_ty = f.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_ty, .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = { .is_null = false, .payload =");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll("};\n");
return local;
}
-fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const writer = o.writer();
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const operand = try o.resolveInst(ty_op.operand);
+ const writer = f.object.writer();
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const operand = try f.resolveInst(ty_op.operand);
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = { .error = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(" };\n");
return local;
}
-fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.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(ty_op.operand);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = { .error = 0, .payload = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(" };\n");
return local;
}
fn airIsErr(
- o: *Object,
+ f: *Function,
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))
+ if (f.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(un_op);
- const operand_ty = o.air.typeOf(un_op);
- const local = try o.allocLocal(Type.initTag(.bool), .Const);
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(un_op);
+ const operand_ty = f.air.typeOf(un_op);
+ const local = try f.allocLocal(Type.initTag(.bool), .Const);
const payload_ty = operand_ty.errorUnionPayload();
if (!payload_ty.hasCodeGenBits()) {
try writer.print(" = {s}", .{deref_prefix});
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print(" {s} 0;\n", .{op_str});
} else {
try writer.writeAll(" = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
}
return local;
}
-fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const writer = o.writer();
- const operand = try o.resolveInst(ty_op.operand);
- const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
+ const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
try writer.writeAll(" = { .ptr = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.print(", .len = {d} }};\n", .{array_len});
return local;
}
/// Emits a local variable with the result type and initializes it
/// with the operand.
-fn airSimpleCast(o: *Object, inst: Air.Inst.Index) !CValue {
- if (o.liveness.isUnused(inst))
+fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst))
return CValue.none;
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
- const ty_op = o.air.instructions.items(.data)[inst].ty_op;
- const writer = o.writer();
- const operand = try o.resolveInst(ty_op.operand);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+ const writer = f.object.writer();
+ const operand = try f.resolveInst(ty_op.operand);
try writer.writeAll(" = ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
-fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
- const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
- const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
- const inst_ty = o.air.typeOfIndex(inst);
- const ptr = try o.resolveInst(extra.ptr);
- const expected_value = try o.resolveInst(extra.expected_value);
- const new_value = try o.resolveInst(extra.new_value);
- const local = try o.allocLocal(inst_ty, .Const);
- const writer = o.writer();
+fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+ const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const ptr = try f.resolveInst(extra.ptr);
+ const expected_value = try f.resolveInst(extra.expected_value);
+ const new_value = try f.resolveInst(extra.new_value);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
try writer.print(" = zig_cmpxchg_{s}(", .{flavor});
- try o.writeCValue(writer, ptr);
+ try f.writeCValue(writer, ptr);
try writer.writeAll(", ");
- try o.writeCValue(writer, expected_value);
+ try f.writeCValue(writer, expected_value);
try writer.writeAll(", ");
- try o.writeCValue(writer, new_value);
+ try f.writeCValue(writer, new_value);
try writer.writeAll(", ");
try writeMemoryOrder(writer, extra.successOrder());
try writer.writeAll(", ");
@@ -1944,19 +1981,19 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
return local;
}
-fn airAtomicRmw(o: *Object, inst: Air.Inst.Index) !CValue {
- const pl_op = o.air.instructions.items(.data)[inst].pl_op;
- const extra = o.air.extraData(Air.AtomicRmw, pl_op.payload).data;
- const inst_ty = o.air.typeOfIndex(inst);
- const ptr = try o.resolveInst(pl_op.operand);
- const operand = try o.resolveInst(extra.operand);
- const local = try o.allocLocal(inst_ty, .Const);
- const writer = o.writer();
+fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
+ const pl_op = f.air.instructions.items(.data)[inst].pl_op;
+ const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const ptr = try f.resolveInst(pl_op.operand);
+ const operand = try f.resolveInst(extra.operand);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
try writer.print(" = zig_atomicrmw_{s}(", .{toAtomicRmwSuffix(extra.op())});
- try o.writeCValue(writer, ptr);
+ try f.writeCValue(writer, ptr);
try writer.writeAll(", ");
- try o.writeCValue(writer, operand);
+ try f.writeCValue(writer, operand);
try writer.writeAll(", ");
try writeMemoryOrder(writer, extra.ordering());
try writer.writeAll(");\n");
@@ -1964,15 +2001,15 @@ fn airAtomicRmw(o: *Object, inst: Air.Inst.Index) !CValue {
return local;
}
-fn airAtomicLoad(o: *Object, inst: Air.Inst.Index) !CValue {
- const atomic_load = o.air.instructions.items(.data)[inst].atomic_load;
- const inst_ty = o.air.typeOfIndex(inst);
- const ptr = try o.resolveInst(atomic_load.ptr);
- const local = try o.allocLocal(inst_ty, .Const);
- const writer = o.writer();
+fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
+ const atomic_load = f.air.instructions.items(.data)[inst].atomic_load;
+ const inst_ty = f.air.typeOfIndex(inst);
+ const ptr = try f.resolveInst(atomic_load.ptr);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
try writer.writeAll(" = zig_atomic_load(");
- try o.writeCValue(writer, ptr);
+ try f.writeCValue(writer, ptr);
try writer.writeAll(", ");
try writeMemoryOrder(writer, atomic_load.order);
try writer.writeAll(");\n");
@@ -1980,18 +2017,18 @@ fn airAtomicLoad(o: *Object, inst: Air.Inst.Index) !CValue {
return local;
}
-fn airAtomicStore(o: *Object, inst: Air.Inst.Index, order: [*:0]const u8) !CValue {
- const bin_op = o.air.instructions.items(.data)[inst].bin_op;
- const ptr = try o.resolveInst(bin_op.lhs);
- const element = try o.resolveInst(bin_op.rhs);
- const inst_ty = o.air.typeOfIndex(inst);
- const local = try o.allocLocal(inst_ty, .Const);
- const writer = o.writer();
+fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue {
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const ptr = try f.resolveInst(bin_op.lhs);
+ const element = try f.resolveInst(bin_op.rhs);
+ const inst_ty = f.air.typeOfIndex(inst);
+ const local = try f.allocLocal(inst_ty, .Const);
+ const writer = f.object.writer();
try writer.writeAll(" = zig_atomic_store(");
- try o.writeCValue(writer, ptr);
+ try f.writeCValue(writer, ptr);
try writer.writeAll(", ");
- try o.writeCValue(writer, element);
+ try f.writeCValue(writer, element);
try writer.print(", {s});\n", .{order});
return local;
src/link/C.zig
@@ -21,30 +21,34 @@ base: link.File,
/// This linker backend does not try to incrementally link output C source code.
/// Instead, it tracks all declarations in this table, and iterates over it
/// in the flush function, stitching pre-rendered pieces of C code together.
-decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
+decl_table: std.AutoArrayHashMapUnmanaged(*const Module.Decl, DeclBlock) = .{},
+/// Stores Type/Value data for `typedefs` to reference.
+/// Accumulates allocations and then there is a periodic garbage collection after flush().
+arena: std.heap.ArenaAllocator,
/// Per-declaration data. For functions this is the body, and
/// the forward declaration is stored in the FnBlock.
-pub const DeclBlock = struct {
- code: std.ArrayListUnmanaged(u8),
-
- pub const empty: DeclBlock = .{
- .code = .{},
- };
-};
-
-/// Per-function data.
-pub const FnBlock = struct {
- fwd_decl: std.ArrayListUnmanaged(u8),
- typedefs: codegen.TypedefMap.Unmanaged,
-
- pub const empty: FnBlock = .{
- .fwd_decl = .{},
- .typedefs = .{},
- };
+const DeclBlock = struct {
+ code: std.ArrayListUnmanaged(u8) = .{},
+ fwd_decl: std.ArrayListUnmanaged(u8) = .{},
+ /// Each Decl stores a mapping of Zig Types to corresponding C types, for every
+ /// Zig Type used by the Decl. In flush(), we iterate over each Decl
+ /// and emit the typedef code for all types, making sure to not emit the same thing twice.
+ /// Any arena memory the Type points to lives in the `arena` field of `C`.
+ typedefs: codegen.TypedefMap.Unmanaged = .{},
+
+ fn deinit(db: *DeclBlock, gpa: *Allocator) void {
+ db.code.deinit(gpa);
+ db.fwd_decl.deinit(gpa);
+ for (db.typedefs.values()) |typedef| {
+ gpa.free(typedef.rendered);
+ }
+ db.typedefs.deinit(gpa);
+ db.* = undefined;
+ }
};
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
+pub fn openPath(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*C {
assert(options.object_format == .c);
if (options.use_llvm) return error.LLVMHasNoCBackend;
@@ -57,15 +61,16 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
});
errdefer file.close();
- var c_file = try allocator.create(C);
- errdefer allocator.destroy(c_file);
+ var c_file = try gpa.create(C);
+ errdefer gpa.destroy(c_file);
c_file.* = C{
+ .arena = std.heap.ArenaAllocator.init(gpa),
.base = .{
.tag = .c,
.options = options,
.file = file,
- .allocator = allocator,
+ .allocator = gpa,
},
};
@@ -73,38 +78,105 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
}
pub fn deinit(self: *C) void {
- for (self.decl_table.keys()) |key| {
- deinitDecl(self.base.allocator, key);
+ const gpa = self.base.allocator;
+
+ for (self.decl_table.values()) |*db| {
+ db.deinit(gpa);
}
- self.decl_table.deinit(self.base.allocator);
-}
+ self.decl_table.deinit(gpa);
-pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {
- _ = self;
- _ = decl;
+ self.arena.deinit();
}
pub fn freeDecl(self: *C, decl: *Module.Decl) void {
- _ = self.decl_table.swapRemove(decl);
- deinitDecl(self.base.allocator, decl);
+ const gpa = self.base.allocator;
+ if (self.decl_table.fetchSwapRemove(decl)) |*kv| {
+ kv.value.deinit(gpa);
+ }
}
-fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {
- decl.link.c.code.deinit(gpa);
- decl.fn_link.c.fwd_decl.deinit(gpa);
- for (decl.fn_link.c.typedefs.values()) |value| {
- gpa.free(value.rendered);
+pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const decl = func.owner_decl;
+ const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{};
+ }
+ const fwd_decl = &gop.value_ptr.fwd_decl;
+ const typedefs = &gop.value_ptr.typedefs;
+ const code = &gop.value_ptr.code;
+ fwd_decl.shrinkRetainingCapacity(0);
+ {
+ for (typedefs.values()) |value| {
+ module.gpa.free(value.rendered);
+ }
+ }
+ typedefs.clearRetainingCapacity();
+ code.shrinkRetainingCapacity(0);
+
+ var function: codegen.Function = .{
+ .value_map = codegen.CValueMap.init(module.gpa),
+ .air = air,
+ .liveness = liveness,
+ .func = func,
+ .object = .{
+ .dg = .{
+ .gpa = module.gpa,
+ .module = module,
+ .error_msg = null,
+ .decl = decl,
+ .fwd_decl = fwd_decl.toManaged(module.gpa),
+ .typedefs = typedefs.promote(module.gpa),
+ .typedefs_arena = &self.arena.allocator,
+ },
+ .code = code.toManaged(module.gpa),
+ .indent_writer = undefined, // set later so we can get a pointer to object.code
+ },
+ };
+
+ function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
+ defer {
+ function.value_map.deinit();
+ function.blocks.deinit(module.gpa);
+ function.object.code.deinit();
+ function.object.dg.fwd_decl.deinit();
+ for (function.object.dg.typedefs.values()) |value| {
+ module.gpa.free(value.rendered);
+ }
+ function.object.dg.typedefs.deinit();
}
- decl.fn_link.c.typedefs.deinit(gpa);
+
+ codegen.genFunc(&function) catch |err| switch (err) {
+ error.AnalysisFail => {
+ try module.failed_decls.put(module.gpa, decl, function.object.dg.error_msg.?);
+ return;
+ },
+ else => |e| return e,
+ };
+
+ fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
+ typedefs.* = function.object.dg.typedefs.unmanaged;
+ function.object.dg.typedefs.unmanaged = .{};
+ code.* = function.object.code.moveToUnmanaged();
+
+ // Free excess allocated memory for this Decl.
+ fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);
+ code.shrinkAndFree(module.gpa, code.items.len);
}
-pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void {
- // Keep track of all decls so we can iterate over them on flush().
- _ = try self.decl_table.getOrPut(self.base.allocator, decl);
+pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
- const fwd_decl = &decl.fn_link.c.fwd_decl;
- const typedefs = &decl.fn_link.c.typedefs;
- const code = &decl.link.c.code;
+ const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{};
+ }
+ const fwd_decl = &gop.value_ptr.fwd_decl;
+ const typedefs = &gop.value_ptr.typedefs;
+ const code = &gop.value_ptr.code;
fwd_decl.shrinkRetainingCapacity(0);
{
for (typedefs.values()) |value| {
@@ -116,23 +188,19 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
var object: codegen.Object = .{
.dg = .{
+ .gpa = module.gpa,
.module = module,
.error_msg = null,
.decl = decl,
.fwd_decl = fwd_decl.toManaged(module.gpa),
.typedefs = typedefs.promote(module.gpa),
+ .typedefs_arena = &self.arena.allocator,
},
- .gpa = module.gpa,
.code = code.toManaged(module.gpa),
- .value_map = codegen.CValueMap.init(module.gpa),
.indent_writer = undefined, // set later so we can get a pointer to object.code
- .air = air,
- .liveness = liveness,
};
object.indent_writer = .{ .underlying_writer = object.code.writer() };
defer {
- object.value_map.deinit();
- object.blocks.deinit(module.gpa);
object.code.deinit();
object.dg.fwd_decl.deinit();
for (object.dg.typedefs.values()) |value| {
@@ -159,24 +227,12 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
code.shrinkAndFree(module.gpa, code.items.len);
}
-pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- return self.finishUpdateDecl(module, func.owner_decl, air, liveness);
-}
-
-pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- return self.finishUpdateDecl(module, decl, undefined, undefined);
-}
-
pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
// The C backend does not have the ability to fix line numbers without re-generating
// the entire Decl.
- return self.updateDecl(module, decl);
+ _ = self;
+ _ = module;
+ _ = decl;
}
pub fn flush(self: *C, comp: *Compilation) !void {
@@ -223,32 +279,42 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
var typedefs = std.HashMap(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage).init(comp.gpa);
defer typedefs.deinit();
- // Typedefs, forward decls and non-functions first.
+ // Typedefs, forward decls, and non-functions first.
// TODO: performance investigation: would keeping a list of Decls that we should
// generate, rather than querying here, be faster?
- for (self.decl_table.keys()) |decl| {
- if (!decl.has_tv) continue;
- const buf = buf: {
- if (decl.val.castTag(.function)) |_| {
- try typedefs.ensureUnusedCapacity(@intCast(u32, decl.fn_link.c.typedefs.count()));
- var it = decl.fn_link.c.typedefs.iterator();
- while (it.next()) |new| {
- const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);
- if (!gop.found_existing) {
- try err_typedef_writer.writeAll(new.value_ptr.rendered);
- }
+ const decl_keys = self.decl_table.keys();
+ const decl_values = self.decl_table.values();
+ for (decl_keys) |decl, i| {
+ if (!decl.has_tv) continue; // TODO do we really need this branch?
+
+ const decl_block = &decl_values[i];
+
+ if (decl_block.fwd_decl.items.len != 0) {
+ try typedefs.ensureUnusedCapacity(@intCast(u32, decl_block.typedefs.count()));
+ var it = decl_block.typedefs.iterator();
+ while (it.next()) |new| {
+ const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);
+ if (!gop.found_existing) {
+ try err_typedef_writer.writeAll(new.value_ptr.rendered);
}
- fn_count += 1;
- break :buf decl.fn_link.c.fwd_decl.items;
- } else {
- break :buf decl.link.c.code.items;
}
- };
- all_buffers.appendAssumeCapacity(.{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- });
- file_size += buf.len;
+ const buf = decl_block.fwd_decl.items;
+ all_buffers.appendAssumeCapacity(.{
+ .iov_base = buf.ptr,
+ .iov_len = buf.len,
+ });
+ file_size += buf.len;
+ }
+ if (decl.getFunction() != null) {
+ fn_count += 1;
+ } else if (decl_block.code.items.len != 0) {
+ const buf = decl_block.code.items;
+ all_buffers.appendAssumeCapacity(.{
+ .iov_base = buf.ptr,
+ .iov_len = buf.len,
+ });
+ file_size += buf.len;
+ }
}
err_typedef_item.* = .{
@@ -259,15 +325,17 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
// Now the function bodies.
try all_buffers.ensureUnusedCapacity(fn_count);
- for (self.decl_table.keys()) |decl| {
- if (!decl.has_tv) continue;
- if (decl.val.castTag(.function)) |_| {
- const buf = decl.link.c.code.items;
- all_buffers.appendAssumeCapacity(.{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- });
- file_size += buf.len;
+ for (decl_keys) |decl, i| {
+ if (decl.getFunction() != null) {
+ const decl_block = &decl_values[i];
+ const buf = decl_block.code.items;
+ if (buf.len != 0) {
+ all_buffers.appendAssumeCapacity(.{
+ .iov_base = buf.ptr,
+ .iov_len = buf.len,
+ });
+ file_size += buf.len;
+ }
}
}
src/Compilation.zig
@@ -2145,7 +2145,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
const module = self.bin_file.options.module.?;
const decl = func.owner_decl;
- var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
+ var tmp_arena = std.heap.ArenaAllocator.init(gpa);
+ defer tmp_arena.deinit();
+ const sema_arena = &tmp_arena.allocator;
+
+ var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
error.AnalysisFail => {
assert(func.state != .in_progress);
continue;
@@ -2207,16 +2211,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
const decl_emit_h = decl.getEmitH(module);
const fwd_decl = &decl_emit_h.fwd_decl;
fwd_decl.shrinkRetainingCapacity(0);
+ var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
+ defer typedefs_arena.deinit();
var dg: c_codegen.DeclGen = .{
+ .gpa = gpa,
.module = module,
.error_msg = null,
.decl = decl,
.fwd_decl = fwd_decl.toManaged(gpa),
- // we don't want to emit optionals and error unions to headers since they have no ABI
- .typedefs = undefined,
+ .typedefs = c_codegen.TypedefMap.init(gpa),
+ .typedefs_arena = &typedefs_arena.allocator,
};
defer dg.fwd_decl.deinit();
+ defer dg.typedefs.deinit();
c_codegen.genHeader(&dg) catch |err| switch (err) {
error.AnalysisFail => {
src/link.zig
@@ -149,7 +149,7 @@ pub const File = struct {
coff: Coff.TextBlock,
macho: MachO.TextBlock,
plan9: Plan9.DeclBlock,
- c: C.DeclBlock,
+ c: void,
wasm: Wasm.DeclBlock,
spirv: void,
};
@@ -159,7 +159,7 @@ pub const File = struct {
coff: Coff.SrcFn,
macho: MachO.SrcFn,
plan9: void,
- c: C.FnBlock,
+ c: void,
wasm: Wasm.FnData,
spirv: SpirV.FnData,
};
@@ -372,16 +372,18 @@ pub const File = struct {
/// Must be called before any call to updateDecl or updateDeclExports for
/// any given Decl.
+ /// TODO we're transitioning to deleting this function and instead having
+ /// each linker backend notice the first time updateDecl or updateFunc is called, or
+ /// a callee referenced from AIR.
pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
switch (base.tag) {
.coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
.macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
- .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
.wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
.plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
- .spirv => {},
+ .c, .spirv => {},
}
}
src/Module.zig
@@ -610,7 +610,7 @@ pub const Decl = struct {
/// If the Decl has a value and it is a function, return it,
/// otherwise null.
- pub fn getFunction(decl: *Decl) ?*Fn {
+ pub fn getFunction(decl: *const Decl) ?*Fn {
if (!decl.owns_tv) return null;
const func = (decl.val.castTag(.function) orelse return null).data;
assert(func.owner_decl == decl);
@@ -3789,7 +3789,7 @@ pub fn clearDecl(
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
- .c => .{ .c = link.File.C.DeclBlock.empty },
+ .c => .{ .c = {} },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
};
@@ -3798,7 +3798,7 @@ pub fn clearDecl(
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
.plan9 => .{ .plan9 = {} },
- .c => .{ .c = link.File.C.FnBlock.empty },
+ .c => .{ .c = {} },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
};
@@ -3828,10 +3828,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
// about the Decl in the first place.
// Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
// must call `freeDecl` in the linker backend now.
- if (decl.has_tv) {
- if (decl.ty.hasCodeGenBits()) {
- mod.comp.bin_file.freeDecl(decl);
- }
+ switch (mod.comp.bin_file.tag) {
+ .c => {}, // this linker backend has already migrated to the new API
+ else => if (decl.has_tv) {
+ if (decl.ty.hasCodeGenBits()) {
+ mod.comp.bin_file.freeDecl(decl);
+ }
+ },
}
const dependants = decl.dependants.keys();
@@ -3893,22 +3896,16 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
mod.gpa.free(kv.value);
}
-pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
+pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: *Allocator) SemaError!Air {
const tracy = trace(@src());
defer tracy.end();
const gpa = mod.gpa;
- // Use the Decl's arena for function memory.
- var arena = decl.value_arena.?.promote(gpa);
- defer decl.value_arena.?.* = arena.state;
-
- const fn_ty = decl.ty;
-
var sema: Sema = .{
.mod = mod,
.gpa = gpa,
- .arena = &arena.allocator,
+ .arena = arena,
.code = decl.namespace.file_scope.zir,
.owner_decl = decl,
.namespace = decl.namespace,
@@ -3942,6 +3939,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
// This could be a generic function instantiation, however, in which case we need to
// map the comptime parameters to constant values and only emit arg AIR instructions
// for the runtime ones.
+ const fn_ty = decl.ty;
const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
@@ -4072,7 +4070,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
- .c => .{ .c = link.File.C.DeclBlock.empty },
+ .c => .{ .c = {} },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
},
@@ -4081,7 +4079,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
.plan9 => .{ .plan9 = {} },
- .c => .{ .c = link.File.C.FnBlock.empty },
+ .c => .{ .c = {} },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
},
src/Sema.zig
@@ -2999,6 +2999,8 @@ fn analyzeCall(
// TODO: check whether any external comptime memory was mutated by the
// comptime function call. If so, then do not memoize the call here.
+ // TODO: re-evaluate whether memoized_calls needs its own arena. I think
+ // it should be fine to use the Decl arena for the function.
{
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
errdefer arena_allocator.deinit();
@@ -3009,7 +3011,7 @@ fn analyzeCall(
}
try mod.memoized_calls.put(gpa, memoized_call_key, .{
- .val = result_val,
+ .val = try result_val.copy(arena),
.arena = arena_allocator.state,
});
delete_memoized_call_key = false;
@@ -5876,10 +5878,7 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
else
try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type });
const val = try Value.Tag.array.create(anon_decl.arena(), buf);
- return sema.analyzeDeclRef(try anon_decl.finish(
- ty,
- val,
- ));
+ return sema.analyzeDeclRef(try anon_decl.finish(ty, val));
}
return sema.mod.fail(&block.base, lhs_src, "TODO array_cat more types of Values", .{});
} else {
@@ -5941,10 +5940,7 @@ fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
}
}
const val = try Value.Tag.array.create(anon_decl.arena(), buf);
- return sema.analyzeDeclRef(try anon_decl.finish(
- final_ty,
- val,
- ));
+ return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val));
}
return sema.mod.fail(&block.base, lhs_src, "TODO array_mul more types of Values", .{});
}
@@ -9979,7 +9975,7 @@ fn analyzeRef(
var anon_decl = try block.startAnonDecl();
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
- operand_ty,
+ try operand_ty.copy(anon_decl.arena()),
try val.copy(anon_decl.arena()),
));
}
src/type.zig
@@ -1366,10 +1366,6 @@ pub const Type = extern union {
.f128,
.bool,
.anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.array_u8_sentinel_0,
@@ -1397,6 +1393,12 @@ pub const Type = extern union {
.function => !self.castTag(.function).?.data.is_generic,
+ .fn_noreturn_no_args,
+ .fn_void_no_args,
+ .fn_naked_noreturn_no_args,
+ .fn_ccc_void_no_args,
+ => true,
+
.@"struct" => {
// TODO introduce lazy value mechanism
const struct_obj = self.castTag(.@"struct").?.data;