diff --git a/lib/zig.h b/lib/zig.h index d336ecb2e2..c10720d1bd 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -5,6 +5,7 @@ #endif #include #include +#include #include #include @@ -77,6 +78,32 @@ typedef char bool; #define zig_cold #endif +#if zig_has_attribute(flatten) +#define zig_maybe_flatten __attribute__((flatten)) +#else +#define zig_maybe_flatten +#endif + +#if zig_has_attribute(noinline) +#define zig_never_inline __attribute__((noinline)) zig_maybe_flatten +#elif defined(_MSC_VER) +#define zig_never_inline __declspec(noinline) zig_maybe_flatten +#else +#define zig_never_inline zig_never_inline_unavailable +#endif + +#if zig_has_attribute(not_tail_called) +#define zig_never_tail __attribute__((not_tail_called)) zig_never_inline +#else +#define zig_never_tail zig_never_tail_unavailable +#endif + +#if zig_has_attribute(always_inline) +#define zig_always_tail __attribute__((musttail)) +#else +#define zig_always_tail zig_always_tail_unavailable +#endif + #if __STDC_VERSION__ >= 199901L #define zig_restrict restrict #elif defined(__GNUC__) @@ -1049,7 +1076,7 @@ static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, \ static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ int##w##_t res; \ - if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ + if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \ return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ @@ -2383,39 +2410,47 @@ zig_msvc_atomics(i64, int64_t, 64) #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - ReprType comparand = *((ReprType*)expected); \ - ReprType initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, *((ReprType*)&desired), comparand); \ - bool exchanged = initial == comparand; \ - if (!exchanged) { \ - *expected = *((zig_##Type*)&initial); \ - } \ - return exchanged; \ + ReprType exchange; \ + ReprType comparand; \ + ReprType initial; \ + bool success; \ + memcpy(&comparand, expected, sizeof(comparand)); \ + memcpy(&exchange, &desired, sizeof(exchange)); \ + initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ + success = initial == comparand; \ + if (!success) memcpy(expected, &initial, sizeof(*expected)); \ + return success; \ } \ static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - ReprType initial = _InterlockedExchange##suffix((ReprType volatile*)obj, *((ReprType*)&value)); \ - return *((zig_##Type*)&initial); \ + ReprType repr; \ + ReprType initial; \ + zig_##Type result; \ + memcpy(&repr, &value, sizeof(repr)); \ + initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ + memcpy(&result, &initial, sizeof(result)); \ + return result; \ } \ static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev + value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected + value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } \ static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected - value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } zig_msvc_flt_atomics(f32, uint32_t, ) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index bfc6e6451b..1af14cb372 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -23,7 +23,6 @@ const libcFloatSuffix = target_util.libcFloatSuffix; const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; -const Mutability = enum { @"const", mut }; const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; @@ -39,7 +38,7 @@ pub const CValue = union(enum) { constant: Air.Inst.Ref, /// Index into the parameters arg: usize, - /// The payload field of a parameter + /// The array field of a parameter arg_array: usize, /// Index into a tuple's fields field: usize, @@ -50,6 +49,8 @@ pub const CValue = union(enum) { undef: Type, /// Render the slice as an identifier (using fmtIdent) identifier: []const u8, + /// Render the slice as an payload.identifier (using fmtIdent) + payload_identifier: []const u8, /// Render these bytes literally. /// TODO make this a [*:0]const u8 to save memory bytes: []const u8, @@ -60,21 +61,22 @@ const BlockData = struct { result: CValue, }; -const TypedefKind = enum { - Forward, - Complete, -}; - pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); pub const LazyFnKey = union(enum) { tag_name: Decl.Index, + never_tail: Decl.Index, + never_inline: Decl.Index, }; pub const LazyFnValue = struct { fn_name: []const u8, - data: union { + data: Data, + + pub const Data = union { tag_name: Type, - }, + never_tail: void, + never_inline: void, + }; }; pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); @@ -209,6 +211,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "volatile", {} }, .{ "while ", {} }, + // stdarg.h + .{ "va_start", {} }, + .{ "va_arg", {} }, + .{ "va_end", {} }, + .{ "va_copy", {} }, + + // stddef.h + .{ "offsetof", {} }, + // windows.h .{ "max", {} }, .{ "min", {} }, @@ -296,19 +307,19 @@ pub const Function = struct { const val = f.air.value(inst).?; const ty = f.air.typeOf(inst); - const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { + const result: CValue = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { const writer = f.object.code_header.writer(); const alignment = 0; const decl_c_value = try f.allocLocalValue(ty, alignment); const gpa = f.object.dg.gpa; try f.allocs.put(gpa, decl_c_value.new_local, true); try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .@"const", alignment, .Complete); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); try writer.writeAll(";\n "); break :result decl_c_value; - } else CValue{ .constant = inst }; + } else .{ .constant = inst }; gop.value_ptr.* = result; return result; @@ -333,26 +344,24 @@ pub const Function = struct { .alignment = alignment, .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), }); - return CValue{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; + return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; } fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { - const result = try f.allocAlignedLocal(ty, .mut, 0); + const result = try f.allocAlignedLocal(ty, .{}, 0); log.debug("%{d}: allocating t{d}", .{ inst, result.new_local }); return result; } /// Only allocates the local; does not print anything. - fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue { - _ = mutability; - + fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| { for (locals_list.items, 0..) |local_index, i| { const local = &f.locals.items[local_index]; if (local.alignment >= alignment) { local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); _ = locals_list.swapRemove(i); - return CValue{ .new_local = local_index }; + return .{ .new_local = local_index }; } } } @@ -416,12 +425,20 @@ pub const Function = struct { return f.object.dg.fail(format, args); } - fn renderType(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderType(w, t, .Complete); + fn indexToCType(f: *Function, idx: CType.Index) CType { + return f.object.dg.indexToCType(idx); } - fn renderTypecast(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderTypecast(w, t); + fn typeToIndex(f: *Function, ty: Type, kind: CType.Kind) !CType.Index { + return f.object.dg.typeToIndex(ty, kind); + } + + fn typeToCType(f: *Function, ty: Type, kind: CType.Kind) !CType { + return f.object.dg.typeToCType(ty, kind); + } + + fn renderType(f: *Function, w: anytype, t: Type) !void { + return f.object.dg.renderType(w, t); } fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { @@ -432,11 +449,9 @@ pub const Function = struct { return f.object.dg.fmtIntLiteral(ty, val); } - fn getTagNameFn(f: *Function, enum_ty: Type) ![]const u8 { + fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { const gpa = f.object.dg.gpa; - const owner_decl = enum_ty.getOwnerDecl(); - - const gop = try f.lazy_fns.getOrPut(gpa, .{ .tag_name = owner_decl }); + const gop = try f.lazy_fns.getOrPut(gpa, key); if (!gop.found_existing) { errdefer _ = f.lazy_fns.pop(); @@ -445,11 +460,21 @@ pub const Function = struct { const arena = promoted.arena.allocator(); gop.value_ptr.* = .{ - .fn_name = try std.fmt.allocPrint(arena, "zig_tagName_{}__{d}", .{ - fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), - @enumToInt(owner_decl), - }), - .data = .{ .tag_name = try enum_ty.copy(arena) }, + .fn_name = switch (key) { + .tag_name, + .never_tail, + .never_inline, + => |owner_decl| try std.fmt.allocPrint(arena, "zig_{s}_{}__{d}", .{ + @tagName(key), + fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), + @enumToInt(owner_decl), + }), + }, + .data = switch (key) { + .tag_name => .{ .tag_name = try data.tag_name.copy(arena) }, + .never_tail => .{ .never_tail = data.never_tail }, + .never_inline => .{ .never_inline = data.never_inline }, + }, }; } return gop.value_ptr.fn_name; @@ -518,7 +543,7 @@ pub const DeclGen = struct { // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { - return dg.writeCValue(writer, CValue{ .undef = ty }); + return dg.writeCValue(writer, .{ .undef = ty }); } // Chase function values in order to be able to reference the original function. @@ -532,7 +557,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); } else { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeAll("){ .ptr = "); } @@ -559,7 +584,7 @@ pub const DeclGen = struct { const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); if (need_typecast) { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try writer.writeByte('&'); @@ -574,7 +599,7 @@ pub const DeclGen = struct { fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void { if (!ptr_ty.isSlice()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ptr_ty); + try dg.renderType(writer, ptr_ty); try writer.writeByte(')'); } switch (ptr_val.tag()) { @@ -589,90 +614,71 @@ pub const DeclGen = struct { try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index, location); }, .field_ptr => { - const ptr_info = ptr_ty.ptrInfo(); + const target = dg.module.getTarget(); const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const container_ty = field_ptr.container_ty; - const index = field_ptr.field_index; - var container_ptr_ty_pl: Type.Payload.ElemType = .{ - .base = .{ .tag = .c_mut_pointer }, - .data = field_ptr.container_ty, - }; - const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); + // Ensure complete type definition is visible before accessing fields. + _ = try dg.typeToIndex(field_ptr.container_ty, .complete); - const FieldInfo = struct { name: []const u8, ty: Type }; - const field_info: FieldInfo = switch (container_ty.zigTypeTag()) { - .Struct => switch (container_ty.containerLayout()) { - .Auto, .Extern => FieldInfo{ - .name = container_ty.structFields().keys()[index], - .ty = container_ty.structFields().values()[index].ty, - }, - .Packed => if (ptr_info.data.host_size == 0) { - const target = dg.module.getTarget(); + var container_ptr_pl = ptr_ty.ptrInfo(); + container_ptr_pl.data.pointee_type = field_ptr.container_ty; + const container_ptr_ty = Type.initPayload(&container_ptr_pl.base); - const byte_offset = container_ty.packedStructFieldByteOffset(index, target); - var byte_offset_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = byte_offset, - }; - const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - - var u8_ptr_pl = ptr_info; - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - - try writer.writeAll("&(("); - try dg.renderTypecast(writer, u8_ptr_ty); - try writer.writeByte(')'); - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); - return writer.print(")[{}]", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); - } else { - var host_pl = Type.Payload.Bits{ - .base = .{ .tag = .int_unsigned }, - .data = ptr_info.data.host_size * 8, - }; - const host_ty = Type.initPayload(&host_pl.base); - - try writer.writeByte('('); - try dg.renderTypecast(writer, ptr_ty); - try writer.writeByte(')'); - return dg.renderParentPtr(writer, field_ptr.container_ptr, host_ty, location); - }, + switch (fieldLocation( + field_ptr.container_ty, + ptr_ty, + @intCast(u32, field_ptr.field_index), + target, + )) { + .begin => try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ), + .field => |field| { + try writer.writeAll("&("); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.writeAll(")->"); + try dg.writeCValue(writer, field); }, - .Union => switch (container_ty.containerLayout()) { - .Auto, .Extern => FieldInfo{ - .name = container_ty.unionFields().keys()[index], - .ty = container_ty.unionFields().values()[index].ty, - }, - .Packed => { - return dg.renderParentPtr(writer, field_ptr.container_ptr, ptr_ty, location); - }, - }, - .Pointer => field_info: { - assert(container_ty.isSlice()); - break :field_info switch (index) { - 0 => FieldInfo{ .name = "ptr", .ty = container_ty.childType() }, - 1 => FieldInfo{ .name = "len", .ty = Type.usize }, - else => unreachable, + .byte_offset => |byte_offset| { + var u8_ptr_pl = ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try dg.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try writer.writeAll("(("); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)}); }, - else => unreachable, - }; - - if (field_info.ty.hasRuntimeBitsIgnoreComptime()) { - // Ensure complete type definition is visible before accessing fields. - try dg.renderType(std.io.null_writer, field_ptr.container_ty, .Complete); - - try writer.writeAll("&("); - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); - try writer.writeAll(")->"); - switch (field_ptr.container_ty.tag()) { - .union_tagged, .union_safety_tagged => try writer.writeAll("payload."), - else => {}, - } - try writer.print("{ }", .{fmtIdent(field_info.name)}); - } else { - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); } }, .elem_ptr => { @@ -696,7 +702,7 @@ pub const DeclGen = struct { const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); // Ensure complete type definition is visible before accessing fields. - try dg.renderType(std.io.null_writer, payload_ptr.container_ty, .Complete); + _ = try dg.typeToIndex(payload_ptr.container_ty, .complete); try writer.writeAll("&("); try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty, location); @@ -763,18 +769,18 @@ pub const DeclGen = struct { .Pointer => if (ty.isSlice()) { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try writer.writeAll("{("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&buf); - try dg.renderTypecast(writer, ptr_ty); + try dg.renderType(writer, ptr_ty); return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); } else { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .Optional => { @@ -791,7 +797,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -805,7 +811,7 @@ pub const DeclGen = struct { .Auto, .Extern => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -827,7 +833,7 @@ pub const DeclGen = struct { .Union => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -852,7 +858,7 @@ pub const DeclGen = struct { .ErrorUnion => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -865,7 +871,7 @@ pub const DeclGen = struct { .Array, .Vector => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -874,14 +880,14 @@ pub const DeclGen = struct { var literal = stringLiteral(writer); try literal.start(); const c_len = ty.arrayLenIncludingSentinel(); - var index: usize = 0; + var index: u64 = 0; while (index < c_len) : (index += 1) try literal.writeChar(0xaa); return literal.end(); } else { try writer.writeByte('{'); const c_len = ty.arrayLenIncludingSentinel(); - var index: usize = 0; + var index: u64 = 0; while (index < c_len) : (index += 1) { if (index > 0) try writer.writeAll(", "); try dg.renderValue(writer, ty.childType(), val, initializer_type); @@ -1026,7 +1032,7 @@ pub const DeclGen = struct { return dg.renderValue(writer, ty, slice_val, location); } else { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeAll(")NULL)"); }, .variable => { @@ -1036,7 +1042,7 @@ pub const DeclGen = struct { .slice => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1059,7 +1065,7 @@ pub const DeclGen = struct { }, .int_u64, .one => { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .field_ptr, @@ -1074,15 +1080,15 @@ pub const DeclGen = struct { .Array, .Vector => { if (location == .FunctionArgument) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } // First try specific tag representations for more efficiency. switch (val.tag()) { .undef, .empty_struct_value, .empty_array => { - try writer.writeByte('{'); const ai = ty.arrayInfo(); + try writer.writeByte('{'); if (ai.sentinel) |s| { try dg.renderValue(writer, ai.elem_type, s, initializer_type); } else { @@ -1090,13 +1096,19 @@ pub const DeclGen = struct { } try writer.writeByte('}'); }, - .bytes => { - try writer.print("{s}", .{fmtStringLiteral(val.castTag(.bytes).?.data)}); - }, - .str_lit => { - const str_lit = val.castTag(.str_lit).?.data; - const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - try writer.print("{s}", .{fmtStringLiteral(bytes)}); + .bytes, .str_lit => |t| { + const bytes = switch (t) { + .bytes => val.castTag(.bytes).?.data, + .str_lit => bytes: { + const str_lit = val.castTag(.str_lit).?.data; + break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + }, + else => unreachable, + }; + const sentinel = if (ty.sentinel()) |sentinel| @intCast(u8, sentinel.toUnsignedInt(target)) else null; + try writer.print("{s}", .{ + fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen())], sentinel), + }); }, else => { // Fall back to generic implementation. @@ -1120,7 +1132,7 @@ pub const DeclGen = struct { } if (ai.sentinel) |s| { const s_u8 = @intCast(u8, s.toUnsignedInt(target)); - try literal.writeChar(s_u8); + if (s_u8 != 0) try literal.writeChar(s_u8); } try literal.end(); } else { @@ -1177,7 +1189,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1211,7 +1223,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1275,7 +1287,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1362,7 +1374,7 @@ pub const DeclGen = struct { if (!empty) try writer.writeAll(" | "); try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); if (bit_offset_val_pl.data != 0) { @@ -1385,7 +1397,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1396,11 +1408,11 @@ pub const DeclGen = struct { if (field_ty.hasRuntimeBits()) { if (field_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } else if (field_ty.zigTypeTag() == .Float) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); @@ -1457,24 +1469,31 @@ pub const DeclGen = struct { } } - fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void { + fn renderFunctionSignature( + dg: *DeclGen, + w: anytype, + fn_decl_index: Decl.Index, + kind: CType.Kind, + name: union(enum) { + export_index: u32, + string: []const u8, + }, + ) !void { const store = &dg.ctypes.set; const module = dg.module; - const fn_ty = dg.decl.?.ty; - const fn_cty_idx = try dg.typeToIndex(fn_ty, switch (kind) { - .Forward => .forward, - .Complete => .complete, - }); + const fn_decl = module.declPtr(fn_decl_index); + const fn_cty_idx = try dg.typeToIndex(fn_decl.ty, kind); - const fn_info = fn_ty.fnInfo(); + const fn_info = fn_decl.ty.fnInfo(); if (fn_info.cc == .Naked) { switch (kind) { - .Forward => try w.writeAll("zig_naked_decl "), - .Complete => try w.writeAll("zig_naked "), + .forward => try w.writeAll("zig_naked_decl "), + .complete => try w.writeAll("zig_naked "), + else => unreachable, } } - if (dg.decl.?.val.castTag(.function)) |func_payload| + if (fn_decl.val.castTag(.function)) |func_payload| if (func_payload.data.is_cold) try w.writeAll("zig_cold "); if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn "); @@ -1485,7 +1504,7 @@ pub const DeclGen = struct { w, fn_cty_idx, .suffix, - CQualifiers.init(.{}), + .{}, ); try w.print("{}", .{trailing}); @@ -1493,25 +1512,48 @@ pub const DeclGen = struct { try w.print("zig_callconv({s}) ", .{call_conv}); } - if (fn_info.alignment > 0 and kind == .Complete) { - try w.print(" zig_align_fn({})", .{fn_info.alignment}); + switch (kind) { + .forward => {}, + .complete => if (fn_info.alignment > 0) + try w.print(" zig_align_fn({})", .{fn_info.alignment}), + else => unreachable, } - try dg.renderDeclName(w, dg.decl_index.unwrap().?, export_index); + switch (name) { + .export_index => |export_index| try dg.renderDeclName(w, fn_decl_index, export_index), + .string => |string| try w.writeAll(string), + } - try renderTypeSuffix(dg.decl_index, store.*, module, w, fn_cty_idx, .suffix); + try renderTypeSuffix( + dg.decl_index, + store.*, + module, + w, + fn_cty_idx, + .suffix, + CQualifiers.init(.{ .@"const" = switch (kind) { + .forward => false, + .complete => true, + else => unreachable, + } }), + ); - if (fn_info.alignment > 0 and kind == .Forward) { - try w.print(" zig_align_fn({})", .{fn_info.alignment}); + switch (kind) { + .forward => if (fn_info.alignment > 0) + try w.print(" zig_align_fn({})", .{fn_info.alignment}), + .complete => {}, + else => unreachable, } } fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { return dg.ctypes.indexToCType(idx); } + fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind); } + fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); } @@ -1524,29 +1566,15 @@ pub const DeclGen = struct { /// There are three type formats in total that we support rendering: /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// - fn renderType( - dg: *DeclGen, - w: anytype, - t: Type, - _: TypedefKind, - ) error{ OutOfMemory, AnalysisFail }!void { + fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; const idx = try dg.typeToIndex(t, .complete); - _ = try renderTypePrefix( - dg.decl_index, - store.*, - module, - w, - idx, - .suffix, - CQualifiers.init(.{}), - ); - try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); + _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); } const IntCastContext = union(enum) { @@ -1603,17 +1631,22 @@ pub const DeclGen = struct { if (needs_cast) { try w.writeByte('('); - try dg.renderTypecast(w, dest_ty); + try dg.renderType(w, dest_ty); try w.writeByte(')'); } if (src_is_ptr) { try w.writeByte('('); - try dg.renderTypecast(w, src_eff_ty); + try dg.renderType(w, src_eff_ty); try w.writeByte(')'); } try context.writeValue(dg, w, src_ty, location); } else if (dest_bits <= 64 and src_bits > 64) { assert(!src_is_ptr); + if (dest_bits < 64) { + try w.writeByte('('); + try dg.renderType(w, dest_ty); + try w.writeByte(')'); + } try w.writeAll("zig_lo_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); try w.writeByte('('); @@ -1625,7 +1658,7 @@ pub const DeclGen = struct { try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? if (src_is_ptr) { try w.writeByte('('); - try dg.renderTypecast(w, src_eff_ty); + try dg.renderType(w, src_eff_ty); try w.writeByte(')'); } try context.writeValue(dg, w, src_ty, .FunctionArgument); @@ -1646,28 +1679,11 @@ pub const DeclGen = struct { } } - /// Renders a type in C typecast format. - /// - /// This is guaranteed to be valid in a typecast expression, but not - /// necessarily in a variable/field declaration. - /// - /// There are three type formats in total that we support rendering: - /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | - /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | - /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | - /// - fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { - try dg.renderType(w, ty, undefined); - } - /// Renders a type and name in field declaration/definition format. /// /// There are three type formats in total that we support rendering: /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// @@ -1676,9 +1692,9 @@ pub const DeclGen = struct { w: anytype, ty: Type, name: CValue, - mutability: Mutability, + qualifiers: CQualifiers, alignment: u32, - _: TypedefKind, + kind: CType.Kind, ) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; @@ -1689,71 +1705,12 @@ pub const DeclGen = struct { .gt => try w.print("zig_align({}) ", .{alignment}), }; - const idx = try dg.typeToIndex(ty, .complete); - const trailing = try renderTypePrefix( - dg.decl_index, - store.*, - module, - w, - idx, - .suffix, - CQualifiers.init(.{ .@"const" = mutability == .@"const" }), - ); + const idx = try dg.typeToIndex(ty, kind); + const trailing = + try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, qualifiers); try w.print("{}", .{trailing}); try dg.writeCValue(w, name); - try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); - } - - fn renderTagNameFn(dg: *DeclGen, w: anytype, fn_name: []const u8, enum_ty: Type) !void { - const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); - - try w.writeAll("static "); - try dg.renderType(w, name_slice_ty, .Complete); - try w.writeByte(' '); - try w.writeAll(fn_name); - try w.writeByte('('); - try dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, .@"const", 0, .Complete); - try w.writeAll(") {\n switch (tag) {\n"); - for (enum_ty.enumFields().keys(), 0..) |name, index| { - const name_z = try dg.gpa.dupeZ(u8, name); - defer dg.gpa.free(name_z); - const name_bytes = name_z[0 .. name_z.len + 1]; - - var tag_pl: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = @intCast(u32, index), - }; - const tag_val = Value.initPayload(&tag_pl.base); - - var int_pl: Value.Payload.U64 = undefined; - const int_val = tag_val.enumToInt(enum_ty, &int_pl); - - var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; - const name_ty = Type.initPayload(&name_ty_pl.base); - - var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; - const name_val = Value.initPayload(&name_pl.base); - - var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; - const len_val = Value.initPayload(&len_pl.base); - - try w.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); - try dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, .@"const", 0, .Complete); - try w.writeAll(" = "); - try dg.renderValue(w, name_ty, name_val, .Initializer); - try w.writeAll(";\n return ("); - try dg.renderTypecast(w, name_slice_ty); - try w.print("){{{}, {}}};\n", .{ - fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), - }); - - try w.writeAll(" }\n"); - } - try w.writeAll(" }\n while ("); - try dg.renderValue(w, Type.bool, Value.true, .Other); - try w.writeAll(") "); - _ = try airBreakpoint(w); - try w.writeAll("}\n"); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); } fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { @@ -1787,6 +1744,10 @@ pub const DeclGen = struct { }, .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), + .payload_identifier => |ident| return w.print("{ }.{ }", .{ + fmtIdent("payload"), + fmtIdent(ident), + }), .bytes => |bytes| return w.writeAll(bytes), } } @@ -1812,6 +1773,10 @@ pub const DeclGen = struct { .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), .undef => unreachable, .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), + .payload_identifier => |ident| return w.print("(*{ }.{ })", .{ + fmtIdent("payload"), + fmtIdent(ident), + }), .bytes => |bytes| { try w.writeAll("(*"); try w.writeAll(bytes); @@ -1829,7 +1794,7 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { .none, .constant, .field, .undef => unreachable, - .new_local, .local, .arg, .arg_array, .decl, .identifier, .bytes => { + .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier, .bytes => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); }, @@ -1958,7 +1923,8 @@ pub const DeclGen = struct { const CTypeFix = enum { prefix, suffix }; const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); -const CTypeRenderTrailing = enum { +const Const = CQualifiers.init(.{ .@"const" = true }); +const RenderCTypeTrailing = enum { no_space, maybe_space, @@ -2017,8 +1983,8 @@ fn renderTypePrefix( idx: CType.Index, parent_fix: CTypeFix, qualifiers: CQualifiers, -) @TypeOf(w).Error!CTypeRenderTrailing { - var trailing = CTypeRenderTrailing.maybe_space; +) @TypeOf(w).Error!RenderCTypeTrailing { + var trailing = RenderCTypeTrailing.maybe_space; const cty = store.indexToCType(idx); switch (cty.tag()) { @@ -2160,7 +2126,7 @@ fn renderTypePrefix( w, cty.cast(CType.Payload.Function).?.data.return_type, .suffix, - CQualifiers.init(.{}), + .{}, ); switch (parent_fix) { .prefix => { @@ -2187,6 +2153,7 @@ fn renderTypeSuffix( w: anytype, idx: CType.Index, parent_fix: CTypeFix, + qualifiers: CQualifiers, ) @TypeOf(w).Error!void { const cty = store.indexToCType(idx); switch (cty.tag()) { @@ -2233,7 +2200,15 @@ fn renderTypeSuffix( .pointer_const, .pointer_volatile, .pointer_const_volatile, - => try renderTypeSuffix(decl, store, mod, w, cty.cast(CType.Payload.Child).?.data, .prefix), + => try renderTypeSuffix( + decl, + store, + mod, + w, + cty.cast(CType.Payload.Child).?.data, + .prefix, + .{}, + ), .array, .vector, @@ -2251,6 +2226,7 @@ fn renderTypeSuffix( w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix, + .{}, ); }, @@ -2285,17 +2261,10 @@ fn renderTypeSuffix( for (data.param_types, 0..) |param_type, param_i| { if (need_comma) try w.writeAll(", "); need_comma = true; - const trailing = try renderTypePrefix( - decl, - store, - mod, - w, - param_type, - .suffix, - CQualifiers.init(.{ .@"const" = true }), - ); - try w.print("{}a{d}", .{ trailing, param_i }); - try renderTypeSuffix(decl, store, mod, w, param_type, .suffix); + const trailing = + try renderTypePrefix(decl, store, mod, w, param_type, .suffix, qualifiers); + if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_i }); + try renderTypeSuffix(decl, store, mod, w, param_type, .suffix, .{}); } switch (tag) { .function => {}, @@ -2309,7 +2278,7 @@ fn renderTypeSuffix( if (!need_comma) try w.writeAll("void"); try w.writeByte(')'); - try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix); + try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix, .{}); }, } } @@ -2329,17 +2298,9 @@ fn renderAggregateFields( .eq => {}, .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}), } - const trailing = try renderTypePrefix( - .none, - store, - mod, - writer, - field.type, - .suffix, - CQualifiers.init(.{}), - ); + const trailing = try renderTypePrefix(.none, store, mod, writer, field.type, .suffix, .{}); try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); - try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix); + try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix, .{}); try writer.writeAll(";\n"); } try writer.writeByteNTimes(' ', indent); @@ -2360,25 +2321,9 @@ pub fn genTypeDecl( switch (global_cty.tag()) { .fwd_anon_struct => if (decl != .none) { try writer.writeAll("typedef "); - _ = try renderTypePrefix( - .none, - global_store, - mod, - writer, - global_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); try writer.writeByte(' '); - _ = try renderTypePrefix( - decl, - decl_store, - mod, - writer, - decl_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(decl, decl_store, mod, writer, decl_idx, .suffix, .{}); try writer.writeAll(";\n"); }, @@ -2396,15 +2341,7 @@ pub fn genTypeDecl( .fwd_union, => { const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; - _ = try renderTypePrefix( - .none, - global_store, - mod, - writer, - global_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); try writer.writeAll("; // "); try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); try writer.writeByte('\n'); @@ -2441,9 +2378,7 @@ pub fn genTypeDecl( pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { var it = mod.global_assembly.valueIterator(); - while (it.next()) |asm_source| { - try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); - } + while (it.next()) |asm_source| try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*, null)}); } pub fn genErrDecls(o: *Object) !void { @@ -2461,26 +2396,24 @@ pub fn genErrDecls(o: *Object) !void { o.indent_writer.popIndent(); try writer.writeAll("};\n"); - const name_prefix = "zig_errorName"; - const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + "_".len + max_name_len + 1); + const array_identifier = "zig_errorName"; + const name_prefix = array_identifier ++ "_"; + const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len); defer o.dg.gpa.free(name_buf); - std.mem.copy(u8, name_buf, name_prefix ++ "_"); + std.mem.copy(u8, name_buf, name_prefix); for (o.dg.module.error_name_list.items) |name| { - std.mem.copy(u8, name_buf[name_prefix.len + "_".len ..], name); - name_buf[name_prefix.len + "_".len + name.len] = 0; - - const identifier = name_buf[0 .. name_prefix.len + "_".len + name.len :0]; - const name_z = identifier[name_prefix.len + "_".len ..]; + std.mem.copy(u8, name_buf[name_prefix.len..], name); + const identifier = name_buf[0 .. name_prefix.len + name.len]; var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .@"const", 0, .Complete); + try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, Const, 0, .complete); try writer.writeAll(" = "); try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); try writer.writeAll(";\n"); @@ -2493,7 +2426,7 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .@"const", 0, .Complete); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, 0, .complete); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items, 0..) |name, value| { if (value != 0) try writer.writeByte(','); @@ -2501,7 +2434,7 @@ pub fn genErrDecls(o: *Object) !void { var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; const len_val = Value.initPayload(&len_pl.base); - try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ + try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), }); } @@ -2516,23 +2449,91 @@ fn genExports(o: *Object) !void { if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| { for (exports.items[1..], 1..) |@"export", i| { try fwd_decl_writer.writeAll("zig_export("); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, i)); + try o.dg.renderFunctionSignature(fwd_decl_writer, o.dg.decl_index.unwrap().?, .forward, .{ .export_index = @intCast(u32, i) }); try fwd_decl_writer.print(", {s}, {s});\n", .{ - fmtStringLiteral(exports.items[0].options.name), - fmtStringLiteral(@"export".options.name), + fmtStringLiteral(exports.items[0].options.name, null), + fmtStringLiteral(@"export".options.name, null), }); } } } pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { - const writer = o.writer(); - switch (lazy_fn.key_ptr.*) { - .tag_name => _ = try o.dg.renderTagNameFn( - writer, - lazy_fn.value_ptr.fn_name, - lazy_fn.value_ptr.data.tag_name, - ), + const w = o.writer(); + const key = lazy_fn.key_ptr.*; + const val = lazy_fn.value_ptr; + const fn_name = val.fn_name; + switch (key) { + .tag_name => { + const enum_ty = val.data.tag_name; + + const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + + try w.writeAll("static "); + try o.dg.renderType(w, name_slice_ty); + try w.writeByte(' '); + try w.writeAll(fn_name); + try w.writeByte('('); + try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); + try w.writeAll(") {\n switch (tag) {\n"); + for (enum_ty.enumFields().keys(), 0..) |name, index| { + var tag_pl: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, index), + }; + const tag_val = Value.initPayload(&tag_pl.base); + + var int_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(enum_ty, &int_pl); + + var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; + const name_ty = Type.initPayload(&name_ty_pl.base); + + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; + const name_val = Value.initPayload(&name_pl.base); + + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_pl.base); + + try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)}); + try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); + try w.writeAll(" = "); + try o.dg.renderValue(w, name_ty, name_val, .Initializer); + try w.writeAll(";\n return ("); + try o.dg.renderType(w, name_slice_ty); + try w.print("){{{}, {}}};\n", .{ + fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val), + }); + + try w.writeAll(" }\n"); + } + try w.writeAll(" }\n while ("); + try o.dg.renderValue(w, Type.bool, Value.true, .Other); + try w.writeAll(") "); + _ = try airBreakpoint(w); + try w.writeAll("}\n"); + }, + .never_tail, .never_inline => |fn_decl_index| { + const fn_decl = o.dg.module.declPtr(fn_decl_index); + const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete); + const fn_info = fn_cty.cast(CType.Payload.Function).?.data; + + const fwd_decl_writer = o.dg.fwd_decl.writer(); + try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name }); + try fwd_decl_writer.writeAll(";\n"); + + try w.print("static zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ .string = fn_name }); + try w.writeAll(" {\n return "); + try o.dg.renderDeclName(w, fn_decl_index, 0); + try w.writeByte('('); + for (0..fn_info.param_types.len) |arg| { + if (arg > 0) try w.writeAll(", "); + try o.dg.writeCValue(w, .{ .arg = arg }); + } + try w.writeAll(");\n}\n"); + }, } } @@ -2542,6 +2543,7 @@ pub fn genFunc(f: *Function) !void { const o = &f.object; const gpa = o.dg.gpa; + const decl_index = o.dg.decl_index.unwrap().?; const tv: TypedValue = .{ .ty = o.dg.decl.?.ty, .val = o.dg.decl.?.val, @@ -2553,13 +2555,13 @@ pub fn genFunc(f: *Function) !void { const is_global = o.dg.declIsGlobal(tv); const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); + try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); try fwd_decl_writer.writeAll(";\n"); try genExports(o); try o.indent_writer.insertNewline(); if (!is_global) try o.writer().writeAll("static "); - try o.dg.renderFunctionSignature(o.writer(), .Complete, 0); + try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); try o.writer().writeByte(' '); // In case we need to use the header, populate it with a copy of the function @@ -2613,9 +2615,9 @@ pub fn genFunc(f: *Function) !void { w, local.ty, .{ .local = local_index }, - .mut, + .{}, local.alignment, - .Complete, + .complete, ); try w.writeAll(";\n "); } @@ -2634,14 +2636,14 @@ pub fn genDecl(o: *Object) !void { defer tracy.end(); const decl = o.dg.decl.?; - const decl_c_value: CValue = .{ .decl = o.dg.decl_index.unwrap().? }; + const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? }; const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val }; if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; if (tv.val.tag() == .extern_fn) { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll("zig_extern "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); + try o.dg.renderFunctionSignature(fwd_decl_writer, decl_c_value.decl, .forward, .{ .export_index = 0 }); try fwd_decl_writer.writeAll(";\n"); try genExports(o); } else if (tv.val.castTag(.variable)) |var_payload| { @@ -2652,7 +2654,7 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); - try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .mut, decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .{}, decl.@"align", .complete); try fwd_decl_writer.writeAll(";\n"); try genExports(o); @@ -2662,7 +2664,7 @@ pub fn genDecl(o: *Object) !void { if (!is_global) try w.writeAll("static "); if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .mut, decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.@"align", .complete); if (decl.@"linksection" != null) try w.writeAll(", read, write)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); @@ -2673,13 +2675,13 @@ pub fn genDecl(o: *Object) !void { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, decl.@"align", .complete); try fwd_decl_writer.writeAll(";\n"); const w = o.writer(); if (!is_global) try w.writeAll("static "); if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, decl.@"align", .complete); if (decl.@"linksection" != null) try w.writeAll(", read)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); @@ -2702,7 +2704,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const is_global = dg.declIsGlobal(tv); if (is_global) { try writer.writeAll("zig_extern "); - try dg.renderFunctionSignature(writer, .Complete, 0); + try dg.renderFunctionSignature(writer, dg.decl_index.unwrap().?, .complete, .{ .export_index = 0 }); try dg.fwd_decl.appendSlice(";\n"); } }, @@ -2892,10 +2894,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .dbg_block_begin, .dbg_block_end, - => CValue{ .none = {} }, + => .none, .call => try airCall(f, inst, .auto), - .call_always_tail => try airCall(f, inst, .always_tail), + .call_always_tail => .none, .call_never_tail => try airCall(f, inst, .never_tail), .call_never_inline => try airCall(f, inst, .never_inline), @@ -2974,10 +2976,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), - .c_va_arg => return f.fail("TODO implement c_va_arg", .{}), - .c_va_copy => return f.fail("TODO implement c_va_copy", .{}), - .c_va_end => return f.fail("TODO implement c_va_end", .{}), - .c_va_start => return f.fail("TODO implement c_va_start", .{}), + .c_va_start => try airCVaStart(f, inst), + .c_va_arg => try airCVaArg(f, inst), + .c_va_end => try airCVaEnd(f, inst), + .c_va_copy => try airCVaCopy(f, inst), // zig fmt: on }; if (result_value == .new_local) { @@ -2996,7 +2998,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3022,7 +3024,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr = try f.resolveInst(bin_op.lhs); @@ -3048,7 +3050,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3061,7 +3063,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3080,7 +3082,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")&("); if (ptr_ty.ptrSize() == .One) { // It's a pointer to an array, so we need to de-reference. @@ -3102,7 +3104,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const slice = try f.resolveInst(bin_op.lhs); @@ -3128,7 +3130,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3141,7 +3143,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const slice_ty = f.air.typeOf(bin_op.lhs); @@ -3171,7 +3173,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const array = try f.resolveInst(bin_op.lhs); @@ -3197,7 +3199,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3209,16 +3211,19 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const elem_type = inst_ty.elemType(); if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); + const local = try f.allocAlignedLocal( + elem_type, + CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), + inst_ty.ptrAlignment(target), + ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); - return CValue{ .local_ref = local.new_local }; + return .{ .local_ref = local.new_local }; } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3226,25 +3231,28 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { const elem_ty = inst_ty.elemType(); if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); + const local = try f.allocAlignedLocal( + elem_ty, + CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), + inst_ty.ptrAlignment(target), + ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); - return CValue{ .local_ref = local.new_local }; + return .{ .local_ref = local.new_local }; } fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); - const inst_cty = try f.object.dg.typeToIndex(inst_ty, .parameter); + const inst_cty = try f.typeToIndex(inst_ty, .parameter); const i = f.next_arg_index; f.next_arg_index += 1; - return if (inst_cty != try f.object.dg.typeToIndex(inst_ty, .complete)) + return if (inst_cty != try f.typeToIndex(inst_ty, .complete)) .{ .arg_array = i } else .{ .arg = i }; @@ -3259,7 +3267,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3281,7 +3289,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", (const char *)"); try f.writeCValue(writer, operand, .Other); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll("))"); } else if (ptr_info.host_size != 0) { var host_pl = Type.Payload.Bits{ @@ -3310,11 +3318,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll(")zig_wrap_"); try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); try writer.writeAll("(("); - try f.renderTypecast(writer, field_ty); + try f.renderType(writer, field_ty); try writer.writeByte(')'); const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; if (cant_cast) { @@ -3344,15 +3352,19 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); const target = f.object.dg.module.getTarget(); + const op_inst = Air.refToIndex(un_op); const op_ty = f.air.typeOf(un_op); const ret_ty = if (is_ptr) op_ty.childType() else op_ty; var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { - var deref = is_ptr; + if (op_inst != null and f.air.instructions.items(.tag)[op_inst.?] == .call_always_tail) { + try reap(f, inst, &.{un_op}); + _ = try airCall(f, op_inst.?, .always_tail); + } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); + var deref = is_ptr; const is_array = lowersToArray(ret_ty, target); const ret_val = if (is_array) ret_val: { const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); @@ -3365,7 +3377,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { try f.writeCValue(writer, operand, .FunctionArgument); deref = false; try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, ret_ty); + try f.renderType(writer, ret_ty); try writer.writeAll("));\n"); break :ret_val array_local; } else operand; @@ -3381,11 +3393,11 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { } } else { try reap(f, inst, &.{un_op}); - if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() != .Naked) - // Not even allowed to return void in a naked function. + // Not even allowed to return void in a naked function. + if (if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() != .Naked else true) try writer.writeAll("return;\n"); } - return CValue.none; + return .none; } fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3393,7 +3405,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3414,7 +3426,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3433,15 +3445,17 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); + if (dest_c_bits < 64) { + try writer.writeByte('('); + try f.renderType(writer, inst_ty); + try writer.writeByte(')'); + } + const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; if (needs_lo) { try writer.writeAll("zig_lo_"); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); - } else if (dest_c_bits <= 64) { - try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); - try writer.writeByte(')'); } if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { @@ -3501,7 +3515,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -3521,10 +3535,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { try writer.writeAll("memset("); try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); - try f.renderTypecast(writer, lhs_child_ty); + try f.renderType(writer, lhs_child_ty); try writer.writeAll("));\n"); } - return CValue.none; + return .none; } fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3533,7 +3547,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr_val = try f.resolveInst(bin_op.lhs); @@ -3582,7 +3596,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { if (!is_array) try writer.writeByte('&'); try f.writeCValue(writer, array_src, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll("))"); if (src_val == .constant) { try freeLocal(f, inst, array_src.new_local, 0); @@ -3641,13 +3655,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("(0, "); } else { try writer.writeByte('('); - try f.renderTypecast(writer, host_ty); + try f.renderType(writer, host_ty); try writer.writeByte(')'); } if (src_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.writeCValue(writer, src_val, .Other); @@ -3659,7 +3673,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, src_val, .Other); } try writer.writeAll(";\n"); - return CValue.none; + return .none; } fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { @@ -3668,7 +3682,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -3724,7 +3738,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const op = try f.resolveInst(ty_op.operand); @@ -3759,7 +3773,7 @@ fn airBinOp( try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const inst_ty = f.air.typeOfIndex(inst); @@ -3782,7 +3796,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const operand_ty = f.air.typeOf(bin_op.lhs); @@ -3822,7 +3836,7 @@ fn airEquality( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const operand_ty = f.air.typeOf(bin_op.lhs); @@ -3878,7 +3892,7 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3899,7 +3913,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -3919,7 +3933,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { // results in a NULL pointer, or if LHS is NULL. The operation is only UB // if the result is NULL and then dereferenced. try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")(((uintptr_t)"); try f.writeCValue(writer, lhs, .Other); try writer.writeAll(") "); @@ -3927,7 +3941,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { try writer.writeAll(" ("); try f.writeCValue(writer, rhs, .Other); try writer.writeAll("*sizeof("); - try f.renderTypecast(writer, elem_ty); + try f.renderType(writer, elem_ty); try writer.writeAll(")))"); } else try f.writeCValue(writer, lhs, .Initializer); @@ -3940,7 +3954,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3979,7 +3993,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr = try f.resolveInst(bin_op.lhs); @@ -3992,7 +4006,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(".ptr = ("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); + try f.renderType(writer, inst_ty.slicePtrFieldType(&buf)); try writer.writeByte(')'); try f.writeCValue(writer, ptr, .Other); try writer.writeAll("; "); @@ -4017,13 +4031,6 @@ fn airCall( const target = module.getTarget(); const writer = f.object.writer(); - switch (modifier) { - .auto => {}, - .always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}), - .never_tail => return f.fail("TODO: C backend: call with never_tail attribute", .{}), - .never_inline => return f.fail("TODO: C backend: call with never_inline attribute", .{}), - else => unreachable, - } const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); @@ -4032,13 +4039,13 @@ fn airCall( defer gpa.free(resolved_args); for (resolved_args, args) |*resolved_arg, arg| { const arg_ty = f.air.typeOf(arg); - const arg_cty = try f.object.dg.typeToIndex(arg_ty, .parameter); - if (f.object.dg.indexToCType(arg_cty).tag() == .void) { + const arg_cty = try f.typeToIndex(arg_ty, .parameter); + if (f.indexToCType(arg_cty).tag() == .void) { resolved_arg.* = .none; continue; } resolved_arg.* = try f.resolveInst(arg); - if (arg_cty != try f.object.dg.typeToIndex(arg_ty, .complete)) { + if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { var lowered_arg_buf: LowerFnRetTyBuffer = undefined; const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target); @@ -4048,7 +4055,7 @@ fn airCall( try writer.writeAll(", "); try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, lowered_arg_ty); + try f.renderType(writer, lowered_arg_ty); try writer.writeAll("));\n"); resolved_arg.* = array_local; } @@ -4073,11 +4080,14 @@ fn airCall( var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - const result_local: CValue = if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) + const result_local = if (modifier == .always_tail) r: { + try writer.writeAll("zig_always_tail return "); + break :r .none; + } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) .none else if (f.liveness.isUnused(inst)) r: { try writer.writeByte('('); - try f.renderTypecast(writer, Type.void); + try f.renderType(writer, Type.void); try writer.writeByte(')'); break :r .none; } else r: { @@ -4087,26 +4097,33 @@ fn airCall( break :r local; }; - var is_extern = false; - var name: [*:0]const u8 = ""; callee: { known: { const fn_decl = fn_decl: { const callee_val = f.air.value(pl_op.operand) orelse break :known; break :fn_decl switch (callee_val.tag()) { - .extern_fn => blk: { - is_extern = true; - break :blk callee_val.castTag(.extern_fn).?.data.owner_decl; - }, + .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, .function => callee_val.castTag(.function).?.data.owner_decl, .decl_ref => callee_val.castTag(.decl_ref).?.data, else => break :known, }; }; - name = module.declPtr(fn_decl).name; - try f.object.dg.renderDeclName(writer, fn_decl, 0); + switch (modifier) { + .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl, 0), + inline .never_tail, .never_inline => |mod| try writer.writeAll(try f.getLazyFnName( + @unionInit(LazyFnKey, @tagName(mod), fn_decl), + @unionInit(LazyFnValue.Data, @tagName(mod), {}), + )), + else => unreachable, + } break :callee; } + switch (modifier) { + .auto, .always_tail => {}, + .never_tail => return f.fail("CBE: runtime callee with never_tail attribute unsupported", .{}), + .never_inline => return f.fail("CBE: runtime callee with never_inline attribute unsupported", .{}), + else => unreachable, + } // Fall back to function pointer call. try f.writeCValue(writer, callee, .Other); } @@ -4132,7 +4149,7 @@ fn airCall( try writer.writeAll(", "); try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, ret_ty); + try f.renderType(writer, ret_ty); try writer.writeAll("));\n"); try freeLocal(f, inst, result_local.new_local, 0); break :r array_local; @@ -4153,7 +4170,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { // Perhaps an additional compilation option is in order? //try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); - return CValue.none; + return .none; } fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4162,7 +4179,7 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { const function = f.air.values[ty_pl.payload].castTag(.function).?.data; const mod = f.object.dg.module; try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); - return CValue.none; + return .none; } fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4174,7 +4191,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{pl_op.operand}); const writer = f.object.writer(); try writer.print("/* var:{s} */\n", .{name}); - return CValue.none; + return .none; } fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4190,7 +4207,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) try f.allocLocal(inst, inst_ty) else - CValue{ .none = {} }; + .none; try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ .block_id = block_id, @@ -4199,8 +4216,9 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { try genBodyInner(f, body); try f.object.indent_writer.insertNewline(); + // label might be unused, add a dummy goto // label must be followed by an expression, add an empty one. - try writer.print("zig_block_{d}:;\n", .{block_id}); + try writer.print("goto zig_block_{d};\nzig_block_{d}: (void)0;\n", .{ block_id, block_id }); return result; } @@ -4259,7 +4277,7 @@ fn lowerTry( if (!payload_has_bits) { if (!operand_is_ptr) { - return CValue.none; + return .none; } else { return err_union; } @@ -4268,7 +4286,7 @@ fn lowerTry( try reap(f, inst, &.{operand}); if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -4280,7 +4298,7 @@ fn lowerTry( try writer.writeAll(", "); try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } else { try f.writeCValue(writer, local, .Other); @@ -4313,7 +4331,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, operand_ty); + try f.renderType(writer, operand_ty); try writer.writeAll("))"); } else { try f.writeCValue(writer, result, .Other); @@ -4324,7 +4342,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.print("goto zig_block_{d};\n", .{block.block_id}); - return CValue.none; + return .none; } fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4334,7 +4352,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { // https://github.com/ziglang/zig/issues/13410 if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -4362,7 +4380,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, dest_ty); + try f.renderType(writer, dest_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); @@ -4383,7 +4401,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", &"); try f.writeCValue(writer, operand_lval, .Other); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, dest_ty); + try f.renderType(writer, dest_ty); try writer.writeAll("));\n"); // Ensure padding bits have the expected value. @@ -4406,27 +4424,27 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { fn airBreakpoint(writer: anytype) !CValue { try writer.writeAll("zig_breakpoint();\n"); - return CValue.none; + return .none; } fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeAll(")zig_return_address();\n"); return local; } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeAll(")zig_frame_address();\n"); return local; } @@ -4439,7 +4457,7 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { try writeMemoryOrder(writer, atomic_order); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airUnreach(f: *Function) !CValue { @@ -4447,7 +4465,7 @@ fn airUnreach(f: *Function) !CValue { if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; try f.object.writer().writeAll("zig_unreachable();\n"); - return CValue.none; + return .none; } fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4479,7 +4497,7 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { deinitFreeLocalsMap(gpa, new_free_locals); new_free_locals.* = old_free_locals.move(); - return CValue.none; + return .none; } fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4544,7 +4562,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.indent_writer.insertNewline(); - return CValue.none; + return .none; } fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4558,11 +4576,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("switch ("); if (condition_ty.zigTypeTag() == .Bool) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.u1); + try f.renderType(writer, Type.u1); try writer.writeByte(')'); } else if (condition_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.writeCValue(writer, condition, .Other); @@ -4579,8 +4597,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); var extra_index: usize = switch_br.end; - var case_i: u32 = 0; - while (case_i < switch_br.data.cases_len) : (case_i += 1) { + for (0..switch_br.data.cases_len) |case_i| { const case = f.air.extraData(Air.SwitchBr.Case, extra_index); const items = @ptrCast([]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]; @@ -4591,7 +4608,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("case "); if (condition_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); @@ -4657,7 +4674,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { f.object.indent_writer.popIndent(); try writer.writeAll("}\n"); - return CValue.none; + return .none; } fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { @@ -4679,8 +4696,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; - const result: CValue = r: { - if (!is_volatile and f.liveness.isUnused(inst)) break :r CValue.none; + const result = r: { + if (!is_volatile and f.liveness.isUnused(inst)) break :r .none; const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); @@ -4717,14 +4734,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(output_ty, alignment); - try f.object.dg.renderTypeAndName( - writer, - output_ty, - local_value, - .mut, - alignment, - .Complete, - ); + try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); @@ -4756,14 +4766,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (is_reg) try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(input_ty, alignment); - try f.object.dg.renderTypeAndName( - writer, - input_ty, - local_value, - .@"const", - alignment, - .Complete, - ); + try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); if (is_reg) { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); @@ -4774,14 +4777,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); } } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - } + for (0..clobbers_len) |_| { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; } { @@ -4836,7 +4836,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("__asm"); if (is_volatile) try writer.writeAll(" volatile"); - try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); + try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); } extra_i = constraints_extra_begin; @@ -4854,7 +4854,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(' '); if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); const is_reg = constraint[1] == '{'; - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); if (is_reg) { try f.writeCValue(writer, .{ .local = locals_index }, .Other); locals_index += 1; @@ -4880,28 +4880,25 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[0] == '{'; const input_val = try f.resolveInst(input); - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { - const input_local = CValue{ .local = locals_index }; + const input_local = .{ .local = locals_index }; locals_index += 1; break :local input_local; } else input_val, .Other); try writer.writeByte(')'); } try writer.writeByte(':'); - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; + for (0..clobbers_len) |clobber_i| { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; - if (clobber.len == 0) continue; + if (clobber.len == 0) continue; - if (clobber_i > 0) try writer.writeByte(','); - try writer.print(" {s}", .{fmtStringLiteral(clobber)}); - } + if (clobber_i > 0) try writer.writeByte(','); + try writer.print(" {s}", .{fmtStringLiteral(clobber, null)}); } try writer.writeAll(");\n"); @@ -4918,7 +4915,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[1] == '{'; if (is_reg) { try f.writeCValueDeref(writer, if (output == .none) - CValue{ .local_ref = local.new_local } + .{ .local_ref = local.new_local } else try f.resolveInst(output)); try writer.writeAll(" = "); @@ -4953,7 +4950,7 @@ fn airIsNull( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5003,7 +5000,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5014,7 +5011,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { const payload_ty = opt_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5043,7 +5040,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -5055,7 +5052,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5066,7 +5063,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } const local = try f.allocLocal(inst, inst_ty); @@ -5098,7 +5095,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { if (opt_ty.optionalReprIsPayload()) { if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const local = try f.allocLocal(inst, inst_ty); // The payload and the optional are the same value. @@ -5115,7 +5112,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const local = try f.allocLocal(inst, inst_ty); @@ -5127,6 +5124,62 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } } +fn fieldLocation( + container_ty: Type, + field_ptr_ty: Type, + field_index: u32, + target: std.Target, +) union(enum) { + begin: void, + field: CValue, + byte_offset: u32, + end: void, +} { + return switch (container_ty.zigTypeTag()) { + .Struct => switch (container_ty.containerLayout()) { + .Auto, .Extern => for (field_index..container_ty.structFieldCount()) |next_field_index| { + if (container_ty.structFieldIsComptime(next_field_index)) continue; + const field_ty = container_ty.structFieldType(next_field_index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + break .{ .field = if (container_ty.isSimpleTuple()) + .{ .field = next_field_index } + else + .{ .identifier = container_ty.structFieldName(next_field_index) } }; + } else if (container_ty.hasRuntimeBitsIgnoreComptime()) .end else .begin, + .Packed => if (field_ptr_ty.ptrInfo().data.host_size == 0) + .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, target) } + else + .begin, + }, + .Union => switch (container_ty.containerLayout()) { + .Auto, .Extern => { + const field_ty = container_ty.structFieldType(field_index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) + return if (container_ty.unionTagTypeSafety() != null and + !container_ty.unionHasAllZeroBitFieldTypes()) + .{ .field = .{ .identifier = "payload" } } + else + .begin; + const field_name = container_ty.unionFields().keys()[field_index]; + return .{ .field = if (container_ty.unionTagTypeSafety()) |_| + .{ .payload_identifier = field_name } + else + .{ .identifier = field_name } }; + }, + .Packed => .begin, + }, + .Pointer => switch (container_ty.ptrSize()) { + .Slice => switch (field_index) { + 0 => .{ .field = .{ .identifier = "ptr" } }, + 1 => .{ .field = .{ .identifier = "len" } }, + else => unreachable, + }, + .One, .Many, .C => unreachable, + }, + else => unreachable, + }; +} + fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; @@ -5136,10 +5189,10 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { return .none; } - const struct_ptr = try f.resolveInst(extra.struct_operand); + const container_ptr_val = try f.resolveInst(extra.struct_operand); try reap(f, inst, &.{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); + const container_ptr_ty = f.air.typeOf(extra.struct_operand); + return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, extra.field_index); } fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { @@ -5150,10 +5203,10 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue return .none; } - const struct_ptr = try f.resolveInst(ty_op.operand); + const container_ptr_val = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const struct_ptr_ty = f.air.typeOf(ty_op.operand); - return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index); + const container_ptr_ty = f.air.typeOf(ty_op.operand); + return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, index); } fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -5162,133 +5215,119 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.field_ptr}); - return CValue.none; + return .none; } - const struct_ptr_ty = f.air.typeOfIndex(inst); + const target = f.object.dg.module.getTarget(); + const container_ptr_ty = f.air.typeOfIndex(inst); + const container_ty = container_ptr_ty.childType(); const field_ptr_ty = f.air.typeOf(extra.field_ptr); const field_ptr_val = try f.resolveInst(extra.field_ptr); try reap(f, inst, &.{extra.field_ptr}); - const target = f.object.dg.module.getTarget(); - const struct_ty = struct_ptr_ty.childType(); - - if (struct_ty.zigTypeTag() == .Union) { - return f.fail("TODO: CBE: @fieldParentPtr for unions", .{}); - } - - const field_offset = struct_ty.structFieldOffset(extra.field_index, target); - - var field_offset_pl = Value.Payload.I64{ - .base = .{ .tag = .int_i64 }, - .data = -@intCast(i64, field_offset), - }; - const field_offset_val = Value.initPayload(&field_offset_pl.base); - - var u8_ptr_pl = field_ptr_ty.ptrInfo(); - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - const writer = f.object.writer(); - const local = try f.allocLocal(inst, struct_ptr_ty); + const local = try f.allocLocal(inst, container_ptr_ty); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, struct_ptr_ty); - try writer.writeAll(")&(("); - try f.renderTypecast(writer, u8_ptr_ty); + try f.renderType(writer, container_ptr_ty); try writer.writeByte(')'); - try f.writeCValue(writer, field_ptr_val, .Other); - try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.isize, field_offset_val)}); + + switch (fieldLocation(container_ty, field_ptr_ty, extra.field_index, target)) { + .begin => try f.writeCValue(writer, field_ptr_val, .Initializer), + .field => |field| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.writeAll(" - offsetof("); + try f.renderType(writer, container_ty); + try writer.writeAll(", "); + try f.writeCValue(writer, field, .Other); + try writer.writeAll("))"); + }, + .byte_offset => |byte_offset| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.print(" - {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.print(" - {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + }, + } + + try writer.writeAll(";\n"); return local; } -fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { - const writer = f.object.writer(); +fn fieldPtr( + f: *Function, + inst: Air.Inst.Index, + container_ptr_ty: Type, + container_ptr_val: CValue, + field_index: u32, +) !CValue { + const target = f.object.dg.module.getTarget(); + const container_ty = container_ptr_ty.elemType(); const field_ptr_ty = f.air.typeOfIndex(inst); - const field_ptr_info = field_ptr_ty.ptrInfo(); - const struct_ty = struct_ptr_ty.elemType(); - const field_ty = struct_ty.structFieldType(index); // Ensure complete type definition is visible before accessing fields. - try f.renderType(std.io.null_writer, struct_ty); + _ = try f.typeToIndex(container_ty, .complete); + const writer = f.object.writer(); const local = try f.allocLocal(inst, field_ptr_ty); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, field_ptr_ty); + try f.renderType(writer, field_ptr_ty); try writer.writeByte(')'); - const extra_name: CValue = switch (struct_ty.tag()) { - .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, - else => .none, - }; - - const field_loc: union(enum) { - begin: void, - field: CValue, - end: void, - } = switch (struct_ty.tag()) { - .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { - .Auto, .Extern => for (index..struct_ty.structFieldCount()) |field_i| { - if (!struct_ty.structFieldIsComptime(field_i) and - struct_ty.structFieldType(field_i).hasRuntimeBitsIgnoreComptime()) - break .{ .field = if (struct_ty.isSimpleTuple()) - .{ .field = field_i } - else - .{ .identifier = struct_ty.structFieldName(field_i) } }; - } else .end, - .Packed => if (field_ptr_info.data.host_size == 0) { - const target = f.object.dg.module.getTarget(); - - const byte_offset = struct_ty.packedStructFieldByteOffset(index, target); - var byte_offset_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = byte_offset, - }; - const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - - var u8_ptr_pl = field_ptr_info; - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - - if (!std.mem.isAligned(byte_offset, field_ptr_ty.ptrAlignment(target))) { - return f.fail("TODO: CBE: unaligned packed struct field pointer", .{}); - } - - try writer.writeAll("&(("); - try f.renderTypecast(writer, u8_ptr_ty); - try writer.writeByte(')'); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); - return local; - } else .begin, + switch (fieldLocation(container_ty, field_ptr_ty, field_index, target)) { + .begin => try f.writeCValue(writer, container_ptr_val, .Initializer), + .field => |field| { + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, container_ptr_val, field); }, - .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { - try f.writeCValue(writer, struct_ptr, .Other); - try writer.writeAll(";\n"); - return local; - } else if (field_ty.hasRuntimeBitsIgnoreComptime()) .{ .field = .{ - .identifier = struct_ty.unionFields().keys()[index], - } } else .end, - else => unreachable, - }; + .byte_offset => |byte_offset| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, container_ptr_val, .Other); + try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try writer.writeByte('('); + try f.writeCValue(writer, container_ptr_val, .Other); + try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + }, + } - if (struct_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.writeByte('&'); - switch (field_loc) { - .begin, .end => { - try writer.writeByte('('); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.print(")[{}]", .{@boolToInt(field_loc == .end)}); - }, - .field => |field| if (extra_name != .none) { - try f.writeCValueDerefMember(writer, struct_ptr, extra_name); - try writer.writeByte('.'); - try f.writeCValue(writer, field, .Other); - } else try f.writeCValueDerefMember(writer, struct_ptr, field), - } - } else try f.writeCValue(writer, struct_ptr, .Other); try writer.writeAll(";\n"); return local; } @@ -5299,13 +5338,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.struct_operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{extra.struct_operand}); - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -5315,12 +5354,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); // Ensure complete type definition is visible before accessing fields. - try f.renderType(std.io.null_writer, struct_ty); - - const extra_name: CValue = switch (struct_ty.tag()) { - .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, - else => .none, - }; + _ = try f.typeToIndex(struct_ty, .complete); const field_name: CValue = switch (struct_ty.tag()) { .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { @@ -5362,7 +5396,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = zig_wrap_"); try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); try writer.writeAll("(("); - try f.renderTypecast(writer, field_int_ty); + try f.renderType(writer, field_int_ty); try writer.writeByte(')'); const cant_cast = int_info.bits > 64; if (cant_cast) { @@ -5389,7 +5423,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("));\n"); try freeLocal(f, inst, temp_local.new_local, 0); return local; @@ -5411,7 +5445,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", &"); try f.writeCValue(writer, operand_lval, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("));\n"); if (struct_byval == .constant) { @@ -5419,31 +5453,29 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { } return local; - } else .{ - .identifier = struct_ty.unionFields().keys()[extra.field_index], + } else field_name: { + const name = struct_ty.unionFields().keys()[extra.field_index]; + break :field_name if (struct_ty.unionTagTypeSafety()) |_| + .{ .payload_identifier = name } + else + .{ .identifier = name }; }, else => unreachable, }; - const is_array = lowersToArray(inst_ty, target); const local = try f.allocLocal(inst, inst_ty); - if (is_array) { + if (lowersToArray(inst_ty, target)) { try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); + try f.writeCValueMember(writer, struct_byval, field_name); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("))"); } else { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); - } - if (extra_name != .none) { - try f.writeCValueMember(writer, struct_byval, extra_name); - try writer.writeByte('.'); - try f.writeCValue(writer, field_name, .Other); - } else try f.writeCValueMember(writer, struct_byval, field_name); - if (is_array) { - try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); - try writer.writeAll("))"); + try f.writeCValueMember(writer, struct_byval, field_name); } try writer.writeAll(";\n"); return local; @@ -5456,7 +5488,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5493,7 +5525,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5504,13 +5536,13 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { - if (!is_ptr) return CValue.none; + if (!is_ptr) return .none; const w = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); - try f.renderTypecast(w, inst_ty); + try f.renderType(w, inst_ty); try w.writeByte(')'); try f.writeCValue(w, operand, .Initializer); try w.writeAll(";\n"); @@ -5535,7 +5567,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5571,7 +5603,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, payload, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } return local; @@ -5581,7 +5613,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5635,7 +5667,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); // Then return the payload pointer (only if it is used) - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); try f.writeCValue(writer, local, .Other); @@ -5646,7 +5678,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); } @@ -5664,7 +5696,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5691,7 +5723,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, payload, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } return local; @@ -5702,7 +5734,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5740,7 +5772,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5756,7 +5788,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { // &(*(void *)p)[0], although LLVM does via GetElementPtr if (operand == .undef) { var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); + try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); } else if (array_ty.hasRuntimeBitsIgnoreComptime()) { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); @@ -5778,7 +5810,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5826,7 +5858,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); @@ -5837,7 +5869,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); @@ -5854,7 +5886,7 @@ fn airUnBuiltinCall( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5886,7 +5918,7 @@ fn airBinBuiltinCall( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -5959,7 +5991,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(";\n"); try writer.writeAll("if ("); try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderTypecast(writer, ptr_ty.childType()); + try f.renderType(writer, ptr_ty.childType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -5988,7 +6020,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(";\n"); try f.writeCValue(writer, local, .Other); try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderTypecast(writer, ptr_ty.childType()); + try f.renderType(writer, ptr_ty.childType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6009,7 +6041,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue if (f.liveness.isUnused(inst)) { try freeLocal(f, inst, local.new_local, 0); - return CValue.none; + return .none; } return local; @@ -6031,12 +6063,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { switch (extra.op()) { else => { try writer.writeAll("zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); }, .Nand, .Min, .Max => { // These are missing from stdatomic.h, so no atomic types for now. - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); }, } if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); @@ -6052,7 +6084,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try freeLocal(f, inst, local.new_local, 0); - return CValue.none; + return .none; } return local; @@ -6064,7 +6096,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{atomic_load.ptr}); const ptr_ty = f.air.typeOf(atomic_load.ptr); if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6073,7 +6105,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_atomic_load((zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6096,7 +6128,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const writer = f.object.writer(); try writer.writeAll("zig_atomic_store((zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6107,7 +6139,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa try f.object.dg.renderTypeForBuiltinFnName(writer, ptr_ty.childType()); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6138,7 +6170,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" += "); try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); try writer.writeAll(") (("); - try f.renderTypecast(writer, u8_ptr_ty); + try f.renderType(writer, u8_ptr_ty); try writer.writeByte(')'); try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.writeAll(")["); @@ -6150,7 +6182,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); try freeLocal(f, inst, index.new_local, 0); - return CValue.none; + return .none; } try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); @@ -6162,7 +6194,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6182,7 +6214,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6195,7 +6227,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const union_ty = f.air.typeOf(bin_op.lhs).childType(); const target = f.object.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); - if (layout.tag_size == 0) return CValue.none; + if (layout.tag_size == 0) return .none; try writer.writeByte('('); try f.writeCValue(writer, union_ptr, .Other); @@ -6203,7 +6235,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, new_tag, .Other); try writer.writeAll(";\n"); - return CValue.none; + return .none; } fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6211,7 +6243,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -6221,7 +6253,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const target = f.object.dg.module.getTarget(); const layout = un_ty.unionGetLayout(target); - if (layout.tag_size == 0) return CValue.none; + if (layout.tag_size == 0) return .none; const inst_ty = f.air.typeOfIndex(inst); const writer = f.object.writer(); @@ -6239,7 +6271,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6250,7 +6282,9 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(writer, local, .Other); - try writer.print(" = {s}(", .{try f.getTagNameFn(enum_ty)}); + try writer.print(" = {s}(", .{ + try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl() }, .{ .tag_name = enum_ty }), + }); try f.writeCValue(writer, operand, .Other); try writer.writeAll(");\n"); @@ -6262,7 +6296,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -6282,7 +6316,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6298,13 +6332,13 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { } fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airSelect", .{}); } fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airShuffle", .{}); } @@ -6314,7 +6348,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{reduce.operand}); - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -6390,10 +6424,9 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { // // Equivalent to: // reduce: { - // var i: usize = 0; // var accum: T = init; - // while (i < vec.len) : (i += 1) { - // accum = func(accum, vec[i]); + // for (vec) : (elem) { + // accum = func(accum, elem); // } // break :reduce accum; // } @@ -6488,7 +6521,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } } - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const target = f.object.dg.module.getTarget(); @@ -6514,7 +6547,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { .Auto, .Extern => { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")"); try writer.writeByte('{'); var empty = true; @@ -6533,7 +6566,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const element_ty = f.air.typeOf(element); try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { - .Array => CValue{ .undef = element_ty }, + .Array => .{ .undef = element_ty }, else => resolved_element, }, .Initializer); empty = false; @@ -6557,7 +6590,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, resolved_element, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, element_ty); + try f.renderType(writer, element_ty); try writer.writeAll("));\n"); } }, @@ -6602,11 +6635,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); } else { try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); if (field_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, switch (int_info.signedness) { + try f.renderType(writer, switch (int_info.signedness) { .unsigned => Type.usize, .signed => Type.isize, }); @@ -6640,7 +6673,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.init}); - return CValue.none; + return .none; } const union_ty = f.air.typeOfIndex(inst); @@ -6660,7 +6693,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { return local; } - if (union_ty.unionTagTypeSafety()) |tag_ty| { + const field: CValue = if (union_ty.unionTagTypeSafety()) |tag_ty| field: { const layout = union_ty.unionGetLayout(target); if (layout.tag_size != 0) { const field_index = tag_ty.enumFieldIndex(field_name).?; @@ -6677,18 +6710,13 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)}); } - try f.writeCValue(writer, local, .Other); - try writer.print(".payload.{ } = ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, payload, .Other); - try writer.writeAll(";\n"); - return local; - } + break :field .{ .payload_identifier = field_name }; + } else .{ .identifier = field_name }; - try f.writeCValue(writer, local, .Other); - try writer.print(".{ } = ", .{fmtIdent(field_name)}); + try f.writeCValueMember(writer, local, field); + try writer.writeAll(" = "); try f.writeCValue(writer, payload, .Other); try writer.writeAll(";\n"); - return local; } @@ -6699,7 +6727,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { // The available prefetch intrinsics do not accept a cache argument; only // address, rw, and locality. So unless the cache is data, we do not lower // this instruction. - .instruction => return CValue.none, + .instruction => return .none, } const ptr = try f.resolveInst(prefetch.ptr); try reap(f, inst, &.{prefetch.ptr}); @@ -6709,11 +6737,11 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { try writer.print(", {d}, {d});\n", .{ @enumToInt(prefetch.rw), prefetch.locality, }); - return CValue.none; + return .none; } fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; @@ -6750,7 +6778,7 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); @@ -6772,7 +6800,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -6794,7 +6822,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa const bin_op = f.air.instructions.items(.data)[inst].bin_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -6821,7 +6849,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); const mulend1 = try f.resolveInst(bin_op.lhs); @@ -6843,6 +6871,81 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return .none; + + const inst_ty = f.air.typeOfIndex(inst); + const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); + const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_start(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + if (param_len > 0) { + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + } + try writer.writeAll(");\n"); + return local; +} + +fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = va_arg(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(", "); + try f.renderType(writer, f.air.getRefType(ty_op.ty)); + try writer.writeAll(");\n"); + return local; +} + +fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { + const un_op = f.air.instructions.items(.data)[inst].un_op; + + const va_list = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + + const writer = f.object.writer(); + try writer.writeAll("va_end(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return .none; +} + +fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_copy(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", *(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return local; +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { // Note: unordered is actually even less atomic than relaxed @@ -7028,8 +7131,9 @@ fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) { return .{ .counting_writer = std.io.countingWriter(child_stream) }; } +const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; fn formatStringLiteral( - str: []const u8, + data: FormatStringContext, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype, @@ -7038,13 +7142,13 @@ fn formatStringLiteral( var literal = stringLiteral(writer); try literal.start(); - for (str) |c| - try literal.writeChar(c); + for (data.str) |c| try literal.writeChar(c); + if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel); try literal.end(); } -fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { - return .{ .data = str }; +fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { + return .{ .data = .{ .str = str, .sentinel = sentinel } }; } fn undefPattern(comptime IntType: type) IntType { diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index bf148a8b87..bd4b6d9a8d 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1056,7 +1056,7 @@ pub const CType = extern union { } }, - .Struct, .Union => |zig_tag| if (ty.containerLayout() == .Packed) { + .Struct, .Union => |zig_ty_tag| if (ty.containerLayout() == .Packed) { if (ty.castTag(.@"struct")) |struct_obj| { try self.initType(struct_obj.data.backing_int_ty, kind, lookup); } else { @@ -1068,9 +1068,13 @@ pub const CType = extern union { } } else if (ty.isTupleOrAnonStruct()) { if (lookup.isMutable()) { - for (0..ty.structFieldCount()) |field_i| { + for (0..switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; _ = try lookup.typeToIndex(field_ty, switch (kind) { .forward, .forward_parameter => .forward, @@ -1086,14 +1090,22 @@ pub const CType = extern union { } } self.init(switch (kind) { - .forward, .forward_parameter => .fwd_anon_struct, - .complete, .parameter, .global => .anon_struct, + .forward, .forward_parameter => switch (zig_ty_tag) { + .Struct => .fwd_anon_struct, + .Union => .fwd_anon_union, + else => unreachable, + }, + .complete, .parameter, .global => switch (zig_ty_tag) { + .Struct => .anon_struct, + .Union => .anon_union, + else => unreachable, + }, .payload => unreachable, }); } else { const tag_ty = ty.unionTagTypeSafety(); const is_tagged_union_wrapper = kind != .payload and tag_ty != null; - const is_struct = zig_tag == .Struct or is_tagged_union_wrapper; + const is_struct = zig_ty_tag == .Struct or is_tagged_union_wrapper; switch (kind) { .forward, .forward_parameter => { self.storage = .{ .fwd = .{ @@ -1138,7 +1150,7 @@ pub const CType = extern union { self.init(.void); } else { var is_packed = false; - for (0..switch (zig_tag) { + for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(), .Union => ty.unionFields().count(), else => unreachable, @@ -1181,10 +1193,10 @@ pub const CType = extern union { } }, - .Array, .Vector => |zig_tag| { + .Array, .Vector => |zig_ty_tag| { switch (kind) { .forward, .complete, .global => { - const t: Tag = switch (zig_tag) { + const t: Tag = switch (zig_ty_tag) { .Array => .array, .Vector => .vector, else => unreachable, @@ -1296,19 +1308,21 @@ pub const CType = extern union { .Fn => { const info = ty.fnInfo(); - if (lookup.isMutable()) { - const param_kind: Kind = switch (kind) { - .forward, .forward_parameter => .forward_parameter, - .complete, .parameter, .global => .parameter, - .payload => unreachable, - }; - _ = try lookup.typeToIndex(info.return_type, param_kind); - for (info.param_types) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - _ = try lookup.typeToIndex(param_type, param_kind); + if (!info.is_generic) { + if (lookup.isMutable()) { + const param_kind: Kind = switch (kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, + }; + _ = try lookup.typeToIndex(info.return_type, param_kind); + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(param_type, param_kind); + } } - } - self.init(if (info.is_var_args) .varargs_function else .function); + self.init(if (info.is_var_args) .varargs_function else .function); + } else self.init(.void); }, } } @@ -1499,126 +1513,95 @@ pub const CType = extern union { .@"union", .packed_struct, .packed_union, - => switch (ty.zigTypeTag()) { - .Struct => { - const fields_len = ty.structFieldCount(); + => { + const zig_ty_tag = ty.zigTypeTag(); + const fields_len = switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }; - var c_fields_len: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or - !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - c_fields_len += 1; - } + var c_fields_len: usize = 0; + for (0..fields_len) |field_i| { + const field_ty = ty.structFieldType(field_i); + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + c_fields_len += 1; + } - const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); - var c_field_i: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or - !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); + var c_field_i: usize = 0; + for (0..fields_len) |field_i| { + const field_ty = ty.structFieldType(field_i); + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - fields_pl[c_field_i] = .{ - .name = try if (ty.isSimpleTuple()) - std.fmt.allocPrintZ(arena, "f{}", .{field_i}) - else - arena.dupeZ(u8, ty.structFieldName(field_i)), - .type = store.set.typeToIndex(field_ty, target, switch (kind) { - .forward, .forward_parameter => .forward, - .complete, .parameter => .complete, - .global => .global, - .payload => unreachable, - }).?, - .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), - }; - c_field_i += 1; - } + defer c_field_i += 1; + fields_pl[c_field_i] = .{ + .name = try if (ty.isSimpleTuple()) + std.fmt.allocPrintZ(arena, "f{}", .{field_i}) + else + arena.dupeZ(u8, switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }), + .type = store.set.typeToIndex(field_ty, target, switch (kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter, .payload => .complete, + .global => .global, + }).?, + .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), + }; + } - switch (t) { - .fwd_anon_struct => { - const anon_pl = try arena.create(Payload.Fields); - anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; - return initPayload(anon_pl); - }, + switch (t) { + .fwd_anon_struct, + .fwd_anon_union, + => { + const anon_pl = try arena.create(Payload.Fields); + anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; + return initPayload(anon_pl); + }, - .anon_struct, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => { - const struct_pl = try arena.create(Payload.Aggregate); - struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(struct_pl); - }, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => { + const unnamed_pl = try arena.create(Payload.Unnamed); + unnamed_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .owner_decl = ty.getOwnerDecl(), + .id = if (ty.unionTagTypeSafety()) |_| 0 else unreachable, + } }; + return initPayload(unnamed_pl); + }, - else => unreachable, - } - }, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => { + const struct_pl = try arena.create(Payload.Aggregate); + struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(struct_pl); + }, - .Union => { - const union_fields = ty.unionFields(); - const fields_len = union_fields.count(); - - var c_fields_len: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; - c_fields_len += 1; - } - - const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); - var field_i: usize = 0; - var c_field_i: usize = 0; - var field_it = union_fields.iterator(); - while (field_it.next()) |field| { - defer field_i += 1; - if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue; - - fields_pl[c_field_i] = .{ - .name = try arena.dupeZ(u8, field.key_ptr.*), - .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) { - .forward, .forward_parameter => unreachable, - .complete, .parameter, .payload => .complete, - .global => .global, - }).?, - .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), - }; - c_field_i += 1; - } - - switch (kind) { - .forward, .forward_parameter => unreachable, - .complete, .parameter, .global => { - const union_pl = try arena.create(Payload.Aggregate); - union_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(union_pl); - }, - .payload => if (ty.unionTagTypeSafety()) |_| { - const union_pl = try arena.create(Payload.Unnamed); - union_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .owner_decl = ty.getOwnerDecl(), - .id = 0, - } }; - return initPayload(union_pl); - } else unreachable, - } - }, - - else => unreachable, + else => unreachable, + } }, .function, .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, @@ -1707,14 +1690,19 @@ pub const CType = extern union { ]u8 = undefined; const c_fields = cty.cast(Payload.Fields).?.data; + const zig_ty_tag = ty.zigTypeTag(); var c_field_i: usize = 0; - for (0..ty.structFieldCount()) |field_i| { + for (0..switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + defer c_field_i += 1; const c_field = &c_fields[c_field_i]; - c_field_i += 1; if (!self.eqlRecurse(field_ty, c_field.type, switch (self.kind) { .forward, .forward_parameter => .forward, @@ -1725,8 +1713,11 @@ pub const CType = extern union { u8, if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable - else - ty.structFieldName(field_i), + else switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }, mem.span(c_field.name), ) or Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align" != c_field.alignas.@"align") return false; @@ -1764,6 +1755,7 @@ pub const CType = extern union { if (ty.zigTypeTag() != .Fn) return false; const info = ty.fnInfo(); + assert(!info.is_generic); const data = cty.cast(Payload.Function).?.data; const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, @@ -1824,29 +1816,30 @@ pub const CType = extern union { var name_buf: [ std.fmt.count("f{}", .{std.math.maxInt(usize)}) ]u8 = undefined; + + const zig_ty_tag = ty.zigTypeTag(); for (0..switch (ty.zigTypeTag()) { .Struct => ty.structFieldCount(), .Union => ty.unionFields().count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - self.updateHasherRecurse( - hasher, - ty.structFieldType(field_i), - switch (self.kind) { - .forward, .forward_parameter => .forward, - .complete, .parameter => .complete, - .global => .global, - .payload => unreachable, - }, - ); + self.updateHasherRecurse(hasher, field_ty, switch (self.kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, + .global => .global, + .payload => unreachable, + }); hasher.update(if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable - else - ty.structFieldName(field_i)); + else switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }); autoHash( hasher, Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align", @@ -1878,6 +1871,7 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, diff --git a/src/link/C.zig b/src/link/C.zig index 262e4e4923..5663ba71e2 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -247,8 +247,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const abi_define = abiDefine(comp); - // Covers defines, zig.h, ctypes, asm, lazy fwd, lazy code. - try f.all_buffers.ensureUnusedCapacity(gpa, 6); + // Covers defines, zig.h, ctypes, asm, lazy fwd. + try f.all_buffers.ensureUnusedCapacity(gpa, 5); if (abi_define) |buf| f.appendBufAssumeCapacity(buf); f.appendBufAssumeCapacity(zig_h); @@ -263,8 +263,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) f.appendBufAssumeCapacity(asm_buf.items); } - const lazy_indices = f.all_buffers.items.len; - f.all_buffers.items.len += 2; + const lazy_index = f.all_buffers.items.len; + f.all_buffers.items.len += 1; try self.flushErrDecls(&f.lazy_db); @@ -297,6 +297,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) { // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes. + // This ensures that every lazy CType.Index exactly matches the global CType.Index. assert(f.ctypes.count() == 0); try self.flushCTypes(&f, .none, f.lazy_db.ctypes); @@ -305,30 +306,22 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) try self.flushCTypes(&f, entry.key_ptr.toOptional(), entry.value_ptr.ctypes); } - { - f.all_buffers.items[lazy_indices + 0] = .{ - .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", - .iov_len = f.lazy_db.fwd_decl.items.len, - }; - f.file_size += f.lazy_db.fwd_decl.items.len; - - f.all_buffers.items[lazy_indices + 1] = .{ - .iov_base = if (f.lazy_db.code.items.len > 0) f.lazy_db.code.items.ptr else "", - .iov_len = f.lazy_db.code.items.len, - }; - f.file_size += f.lazy_db.code.items.len; - } - f.all_buffers.items[ctypes_index] = .{ .iov_base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", .iov_len = f.ctypes_buf.items.len, }; f.file_size += f.ctypes_buf.items.len; + f.all_buffers.items[lazy_index] = .{ + .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", + .iov_len = f.lazy_db.fwd_decl.items.len, + }; + f.file_size += f.lazy_db.fwd_decl.items.len; + // Now the code. - try f.all_buffers.ensureUnusedCapacity(gpa, decl_values.len); - for (decl_values) |decl| - f.appendBufAssumeCapacity(decl.code.items); + try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len); + f.appendBufAssumeCapacity(f.lazy_db.code.items); + for (decl_values) |decl| f.appendBufAssumeCapacity(decl.code.items); const file = self.base.file.?; try file.setEndPos(f.file_size); diff --git a/src/target.zig b/src/target.zig index 001adad7c2..6d6933e9e7 100644 --- a/src/target.zig +++ b/src/target.zig @@ -723,6 +723,7 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { switch (backend) { .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), + .stage2_c => return true, else => return false, } } diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index b9e171db57..6bbd6ad7ef 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -48,7 +48,6 @@ fn testParentFieldPtrFirst(a: *const bool) !void { test "@fieldParentPtr untagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrUnion(&bar.c); @@ -75,7 +74,6 @@ fn testFieldParentPtrUnion(c: *const i32) !void { test "@fieldParentPtr tagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrTaggedUnion(&bar_tagged.c); @@ -102,7 +100,6 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { test "@fieldParentPtr extern union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrExternUnion(&bar_extern.c); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index e1237a578b..85214bd7d8 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -603,7 +603,6 @@ test "packed struct initialized in bitcast" { test "pointer to container level packed struct field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 2d55292916..ec4ff332cf 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -507,7 +507,6 @@ test "ptrCast comptime known slice to C pointer" { } test "ptrToInt on a generic function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 97f90b559d..cdfbcc9188 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -96,10 +96,9 @@ fn doNothingWithFirstArg(args: anytype) void { test "simple variadic function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -112,6 +111,12 @@ test "simple variadic function" { return @cVaArg(&ap, c_int); } + fn compatible(_: c_int, ...) callconv(.C) c_int { + var ap = @cVaStart(); + defer @cVaEnd(&ap); + return @cVaArg(&ap, c_int); + } + fn add(count: c_int, ...) callconv(.C) c_int { var ap = @cVaStart(); defer @cVaEnd(&ap); @@ -124,8 +129,13 @@ test "simple variadic function" { } }; - try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); - try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + if (builtin.zig_backend != .stage2_c) { + // pre C23 doesn't support varargs without a preceding runtime arg. + try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + } + try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024))); try std.testing.expectEqual(@as(c_int, 0), S.add(0)); try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); @@ -134,10 +144,9 @@ test "simple variadic function" { test "variadic functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -178,10 +187,9 @@ test "variadic functions" { test "copy VaList" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; }