diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-09-30 21:38:04 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-09-30 21:38:04 -0700 |
| commit | 3eb729b442d6cc69bd9a9d37816e9458190bc30b (patch) | |
| tree | a412281c18914503c1566e79ab6a6f4eea187bf3 /src/codegen/c.zig | |
| parent | 1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee (diff) | |
| parent | c4cd592f0e1eeff5a4056796610d97010ae4e38c (diff) | |
| download | zig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.tar.gz zig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.zip | |
Merge remote-tracking branch 'origin/master' into llvm13
Diffstat (limited to 'src/codegen/c.zig')
| -rw-r--r-- | src/codegen/c.zig | 1331 |
1 files changed, 844 insertions, 487 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ff49b18f7b..d2ce9cc6de 100644 --- a/src/codegen/c.zig +++ b/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); @@ -251,7 +275,7 @@ pub const DeclGen = struct { try writer.writeByte('('); try dg.renderType(writer, t); try writer.writeAll("){"); - var buf: Type.Payload.ElemType = undefined; + var buf: Type.SlicePtrFieldTypeBuffer = undefined; try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); try writer.writeAll(", "); try writer.print("{d}", .{val.sliceLen()}); @@ -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,240 +858,263 @@ 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), + .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, " + "), // 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, " - "), // 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, " * "), // 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, " % "), - - .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, " != "), + .div => try airBinOp( f, inst, " / "), + .rem => try airBinOp( f, inst, " % "), + .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division + + .addwrap => try airWrapOp(f, inst, " + ", "addw_"), + .subwrap => try airWrapOp(f, inst, " - ", "subw_"), + .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), + + .add_sat => try airSatOp(f, inst, "adds_"), + .sub_sat => try airSatOp(f, inst, "subs_"), + .mul_sat => try airSatOp(f, inst, "muls_"), + .shl_sat => try airSatOp(f, inst, "shls_"), + + .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"), - - .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, .shl_exact => 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), + .memset => try airMemset(f, inst), + .memcpy => try airMemcpy(f, inst), + .set_union_tag => try airSetUnionTag(f, inst), + .get_union_tag => try airGetUnionTag(f, inst), + .clz => try airBuiltinCall(f, inst, "clz"), + .ctz => try airBuiltinCall(f, inst, "ctz"), + + .int_to_float, + .float_to_int, + .fptrunc, + .fpext, + .ptrtoint, + => 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), // 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"); @@ -1062,75 +1122,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"); }, } @@ -1138,17 +1198,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 @@ -1158,12 +1218,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; @@ -1210,11 +1270,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()) { @@ -1240,71 +1300,179 @@ 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 airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !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 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; + + switch (bits) { + 8, 16, 32, 64, 128 => {}, + else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}), + } - const writer = o.writer(); - const inst_ty = o.air.typeOfIndex(inst); - const local = try o.allocLocal(inst_ty, .Const); + // if it's an unsigned int with non-arbitrary bit size then we can just add + if (bits > 64) { + return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); + } + + var min_buf: [80]u8 = undefined; + const min = switch (int_info.signedness) { + .unsigned => "0", + else => switch (inst_ty.tag()) { + .c_short => "SHRT_MIN", + .c_int => "INT_MIN", + .c_long => "LONG_MIN", + .c_longlong => "LLONG_MIN", + .isize => "INTPTR_MIN", + else => blk: { + // compute the type minimum based on the bitcount (bits) + const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); + break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + else => |e| return e, + }; + }, + }, + }; + + var max_buf: [80]u8 = undefined; + const max = switch (inst_ty.tag()) { + .c_short => "SHRT_MAX", + .c_ushort => "USHRT_MAX", + .c_int => "INT_MAX", + .c_uint => "UINT_MAX", + .c_long => "LONG_MAX", + .c_ulong => "ULONG_MAX", + .c_longlong => "LLONG_MAX", + .c_ulonglong => "ULLONG_MAX", + .isize => "INTPTR_MAX", + .usize => "UINTPTR_MAX", + else => blk: { + const pow_bits = switch (int_info.signedness) { + .signed => bits - 1, + .unsigned => bits, + }; + const val = std.math.pow(u65, 2, pow_bits) - 1; + break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + else => |e| return e, + }; + }, + }; + + const lhs = try f.resolveInst(bin_op.lhs); + const rhs = try f.resolveInst(bin_op.rhs); + const w = f.object.writer(); + + const ret = try f.allocLocal(inst_ty, .Mut); + try w.print(" = zig_{s}", .{fn_op}); + + switch (inst_ty.tag()) { + .isize => try w.writeAll("isize"), + .c_short => try w.writeAll("short"), + .c_int => try w.writeAll("int"), + .c_long => try w.writeAll("long"), + .c_longlong => try w.writeAll("longlong"), + else => { + const prefix_byte: u8 = switch (int_info.signedness) { + .signed => 'i', + .unsigned => 'u', + }; + for ([_]u8{ 8, 16, 32, 64 }) |nbits| { + if (bits <= nbits) { + try w.print("{c}{d}", .{ prefix_byte, nbits }); + break; + } + } else { + unreachable; + } + }, + } + + try w.writeByte('('); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + + if (int_info.signedness == .signed) { + try w.print(", {s}", .{min}); + } + + try w.print(", {s});", .{max}); + try f.object.indent_writer.insertNewline(); + + return ret; +} + +fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; + + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const op = try f.resolveInst(ty_op.operand); + + 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) { @@ -1312,11 +1480,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| @@ -1326,8 +1494,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("("); @@ -1335,189 +1503,200 @@ 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(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); + try writer.writeAll(");\n"); + 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); @@ -1526,14 +1705,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; @@ -1548,28 +1727,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) { @@ -1595,30 +1774,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 { @@ -1627,14 +1806,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() @@ -1647,98 +1826,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 { @@ -1748,154 +1927,189 @@ 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; } -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(); +/// Emits a local variable with the result type and initializes it +/// with the operand. +fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; + + 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 f.writeCValue(writer, operand); + try writer.writeAll(";\n"); + return local; +} + +fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + 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); + + // TODO implement the function in zig.h and call it here + + try writer.print(" = {s}(", .{fn_name}); + try f.writeCValue(writer, operand); + try writer.writeAll(");\n"); + return local; +} + +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(", "); @@ -1905,8 +2119,134 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { return local; } -fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { - const str = switch (order) { +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 f.writeCValue(writer, ptr); + try writer.writeAll(", "); + try f.writeCValue(writer, operand); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.ordering()); + try writer.writeAll(");\n"); + + return local; +} + +fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { + const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; + const ptr = try f.resolveInst(atomic_load.ptr); + const ptr_ty = f.air.typeOf(atomic_load.ptr); + if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) + return CValue.none; + + 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_load("); + try f.writeCValue(writer, ptr); + try writer.writeAll(", "); + try writeMemoryOrder(writer, atomic_load.order); + try writer.writeAll(");\n"); + + return local; +} + +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 f.writeCValue(writer, ptr); + try writer.writeAll(", "); + try f.writeCValue(writer, element); + try writer.print(", {s});\n", .{order}); + + return local; +} + +fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const extra = f.air.extraData(Air.Bin, pl_op.payload).data; + const dest_ptr = try f.resolveInst(pl_op.operand); + const value = try f.resolveInst(extra.lhs); + const len = try f.resolveInst(extra.rhs); + const writer = f.object.writer(); + + try writer.writeAll("memset("); + try f.writeCValue(writer, dest_ptr); + try writer.writeAll(", "); + try f.writeCValue(writer, value); + try writer.writeAll(", "); + try f.writeCValue(writer, len); + try writer.writeAll(");\n"); + + return CValue.none; +} + +fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const extra = f.air.extraData(Air.Bin, pl_op.payload).data; + const dest_ptr = try f.resolveInst(pl_op.operand); + const src_ptr = try f.resolveInst(extra.lhs); + const len = try f.resolveInst(extra.rhs); + const writer = f.object.writer(); + + try writer.writeAll("memcpy("); + try f.writeCValue(writer, dest_ptr); + try writer.writeAll(", "); + try f.writeCValue(writer, src_ptr); + try writer.writeAll(", "); + try f.writeCValue(writer, len); + try writer.writeAll(");\n"); + + return CValue.none; +} + +fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const union_ptr = try f.resolveInst(bin_op.lhs); + const new_tag = try f.resolveInst(bin_op.rhs); + const writer = f.object.writer(); + + try writer.writeAll("*"); + try f.writeCValue(writer, union_ptr); + try writer.writeAll(" = "); + try f.writeCValue(writer, new_tag); + try writer.writeAll(";\n"); + + return CValue.none; +} + +fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; + + 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("get_union_tag("); + try f.writeCValue(writer, operand); + try writer.writeAll(");\n"); + return local; +} + +fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { + return switch (order) { .Unordered => "memory_order_relaxed", .Monotonic => "memory_order_consume", .Acquire => "memory_order_acquire", @@ -1914,7 +2254,24 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { .AcqRel => "memory_order_acq_rel", .SeqCst => "memory_order_seq_cst", }; - return w.writeAll(str); +} + +fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { + return w.writeAll(toMemoryOrder(order)); +} + +fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 { + return switch (order) { + .Xchg => "xchg", + .Add => "add", + .Sub => "sub", + .And => "and", + .Nand => "nand", + .Or => "or", + .Xor => "xor", + .Max => "max", + .Min => "min", + }; } fn IndentWriter(comptime UnderlyingWriter: type) type { |
