diff options
| author | Jacob Young <jacobly0@users.noreply.github.com> | 2024-03-28 20:41:58 -0400 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2024-03-30 20:50:48 -0400 |
| commit | 5a41704f7ec2c472897f955ecfe1feafa697ff68 (patch) | |
| tree | 62984e96e61c367ce7ad304fc532051c10e6921d /src/codegen/c.zig | |
| parent | 6f10b11658c002b26341bff10e1dd522f2465b5a (diff) | |
| download | zig-5a41704f7ec2c472897f955ecfe1feafa697ff68.tar.gz zig-5a41704f7ec2c472897f955ecfe1feafa697ff68.zip | |
cbe: rewrite `CType`
Closes #14904
Diffstat (limited to 'src/codegen/c.zig')
| -rw-r--r-- | src/codegen/c.zig | 1909 |
1 files changed, 926 insertions, 983 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 246f4e1e0f..cec7cdcd99 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -22,7 +22,7 @@ const Alignment = InternPool.Alignment; const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; -pub const CType = @import("c/type.zig").CType; +pub const CType = @import("c/Type.zig"); pub const CValue = union(enum) { none: void, @@ -62,7 +62,7 @@ pub const LazyFnKey = union(enum) { never_inline: InternPool.DeclIndex, }; pub const LazyFnValue = struct { - fn_name: []const u8, + fn_name: CType.String, data: Data, pub const Data = union { @@ -74,19 +74,19 @@ pub const LazyFnValue = struct { pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); const Local = struct { - cty_idx: CType.Index, + ctype: CType, flags: packed struct(u32) { alignas: CType.AlignAs, _: u20 = undefined, }, pub fn getType(local: Local) LocalType { - return .{ .cty_idx = local.cty_idx, .alignas = local.flags.alignas }; + return .{ .ctype = local.ctype, .alignas = local.flags.alignas }; } }; const LocalIndex = u16; -const LocalType = struct { cty_idx: CType.Index, alignas: CType.AlignAs }; +const LocalType = struct { ctype: CType, alignas: CType.AlignAs }; const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void); const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); @@ -193,6 +193,7 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "switch", {} }, .{ "thread_local", {} }, .{ "typedef", {} }, + .{ "typeof", {} }, .{ "uint16_t", {} }, .{ "uint32_t", {} }, .{ "uint64_t", {} }, @@ -309,12 +310,14 @@ pub const Function = struct { const result: CValue = if (lowersToArray(ty, zcu)) result: { const writer = f.object.codeHeaderWriter(); - const alignment: Alignment = .none; - const decl_c_value = try f.allocLocalValue(ty, alignment); + const decl_c_value = try f.allocLocalValue(.{ + .ctype = try f.ctypeFromType(ty, .complete), + .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(zcu)), + }); const gpa = f.object.dg.gpa; try f.allocs.put(gpa, decl_c_value.new_local, false); 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, .none, .complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, val, .StaticInitializer); try writer.writeAll(";\n "); @@ -335,42 +338,39 @@ pub const Function = struct { /// Skips the reuse logic. This function should be used for any persistent allocation, i.e. /// those which go into `allocs`. This function does not add the resulting local into `allocs`; /// that responsibility lies with the caller. - fn allocLocalValue(f: *Function, ty: Type, alignment: Alignment) !CValue { - const zcu = f.object.dg.zcu; - const gpa = f.object.dg.gpa; - try f.locals.append(gpa, .{ - .cty_idx = try f.typeToIndex(ty, .complete), - .flags = .{ - .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(zcu)), - }, + fn allocLocalValue(f: *Function, local_type: LocalType) !CValue { + try f.locals.ensureUnusedCapacity(f.object.dg.gpa, 1); + defer f.locals.appendAssumeCapacity(.{ + .ctype = local_type.ctype, + .flags = .{ .alignas = local_type.alignas }, }); - return .{ .new_local = @intCast(f.locals.items.len - 1) }; + return .{ .new_local = @intCast(f.locals.items.len) }; } fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue { - const result = try f.allocAlignedLocal(ty, .{}, .none); - if (inst) |i| { - log.debug("%{d}: allocating t{d}", .{ i, result.new_local }); - } else { - log.debug("allocating t{d}", .{result.new_local}); - } - return result; + return f.allocAlignedLocal(inst, .{ + .ctype = try f.ctypeFromType(ty, .complete), + .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(f.object.dg.zcu)), + }); } /// Only allocates the local; does not print anything. Will attempt to re-use locals, so should /// not be used for persistent locals (i.e. those in `allocs`). - fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: Alignment) !CValue { - const zcu = f.object.dg.zcu; - if (f.free_locals_map.getPtr(.{ - .cty_idx = try f.typeToIndex(ty, .complete), - .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(zcu)), - })) |locals_list| { - if (locals_list.popOrNull()) |local_entry| { - return .{ .new_local = local_entry.key }; + fn allocAlignedLocal(f: *Function, inst: ?Air.Inst.Index, local_type: LocalType) !CValue { + const result: CValue = result: { + if (f.free_locals_map.getPtr(local_type)) |locals_list| { + if (locals_list.popOrNull()) |local_entry| { + break :result .{ .new_local = local_entry.key }; + } } + break :result try f.allocLocalValue(local_type); + }; + if (inst) |i| { + log.debug("%{d}: allocating t{d}", .{ i, result.new_local }); + } else { + log.debug("allocating t{d}", .{result.new_local}); } - - return f.allocLocalValue(ty, alignment); + return result; } fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void { @@ -380,15 +380,20 @@ pub const Function = struct { .local_ref => |i| { const local = &f.locals.items[i]; if (local.flags.alignas.abiOrder().compare(.lt)) { - const zcu = f.object.dg.zcu; - const pointee_ty = try zcu.intType(.unsigned, @min( - local.flags.alignas.@"align".toByteUnitsOptional().?, - f.object.dg.mod.resolved_target.result.maxIntAlignment(), - ) * 8); - const ptr_ty = try zcu.singleMutPtrType(pointee_ty); + const gpa = f.object.dg.gpa; + const mod = f.object.dg.mod; + const ctype_pool = &f.object.dg.ctype_pool; try w.writeByte('('); - try f.renderType(w, ptr_ty); + try f.renderCType(w, try ctype_pool.getPointer(gpa, .{ + .elem_ctype = try ctype_pool.fromIntInfo(gpa, .{ + .signedness = .unsigned, + .bits = @min( + local.flags.alignas.toByteUnits(), + mod.resolved_target.result.maxIntAlignment(), + ) * 8, + }, mod, .forward), + })); try w.writeByte(')'); } try w.print("&t{d}", .{i}); @@ -460,28 +465,20 @@ pub const Function = struct { return f.object.dg.fail(format, args); } - fn indexToCType(f: *Function, idx: CType.Index) CType { - return f.object.dg.indexToCType(idx); - } - - fn typeToIndex(f: *Function, ty: Type, kind: CType.Kind) !CType.Index { - return f.object.dg.typeToIndex(ty, kind); + fn ctypeFromType(f: *Function, ty: Type, kind: CType.Kind) !CType { + return f.object.dg.ctypeFromType(ty, kind); } - fn typeToCType(f: *Function, ty: Type, kind: CType.Kind) !CType { - return f.object.dg.typeToCType(ty, kind); + fn byteSize(f: *Function, ctype: CType) u64 { + return f.object.dg.byteSize(ctype); } - fn byteSize(f: *Function, cty: CType) u64 { - return f.object.dg.byteSize(cty); + fn renderType(f: *Function, w: anytype, ctype: Type) !void { + return f.object.dg.renderType(w, ctype); } - fn renderType(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderType(w, t); - } - - fn renderCType(f: *Function, w: anytype, t: CType.Index) !void { - return f.object.dg.renderCType(w, t); + fn renderCType(f: *Function, w: anytype, ctype: CType) !void { + return f.object.dg.renderCType(w, ctype); } fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void { @@ -494,21 +491,19 @@ pub const Function = struct { fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { const gpa = f.object.dg.gpa; + const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; + const gop = try f.lazy_fns.getOrPut(gpa, key); if (!gop.found_existing) { errdefer _ = f.lazy_fns.pop(); - var promoted = f.object.dg.ctypes.promote(gpa); - defer f.object.dg.ctypes.demote(promoted); - const arena = promoted.arena.allocator(); - const zcu = f.object.dg.zcu; - gop.value_ptr.* = .{ .fn_name = switch (key) { .tag_name, .never_tail, .never_inline, - => |owner_decl| try std.fmt.allocPrint(arena, "zig_{s}_{}__{d}", .{ + => |owner_decl| try ctype_pool.fmt(gpa, "zig_{s}_{}__{d}", .{ @tagName(key), fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(owner_decl).name)), @intFromEnum(owner_decl), @@ -521,7 +516,7 @@ pub const Function = struct { }, }; } - return gop.value_ptr.fn_name; + return gop.value_ptr.fn_name.slice(ctype_pool); } pub fn deinit(f: *Function) void { @@ -532,7 +527,6 @@ pub const Function = struct { f.blocks.deinit(gpa); f.value_map.deinit(); f.lazy_fns.deinit(gpa); - f.object.dg.ctypes.deinit(gpa); } fn typeOf(f: *Function, inst: Air.Inst.Ref) Type { @@ -575,7 +569,8 @@ pub const DeclGen = struct { /// This is a borrowed reference from `link.C`. fwd_decl: std.ArrayList(u8), error_msg: ?*Zcu.ErrorMsg, - ctypes: CType.Store, + ctype_pool: CType.Pool, + scratch: std.ArrayListUnmanaged(u32), /// Keeps track of anonymous decls that need to be rendered before this /// (named) Decl in the output C code. anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, C.DeclBlock), @@ -610,6 +605,7 @@ pub const DeclGen = struct { ) error{ OutOfMemory, AnalysisFail }!void { const zcu = dg.zcu; const ip = &zcu.intern_pool; + const ctype_pool = &dg.ctype_pool; const decl_val = Value.fromInterned(anon_decl.val); const decl_ty = decl_val.typeOf(zcu); @@ -631,10 +627,10 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const child_cty = (try dg.typeToCType(ptr_ty, .complete)).cast(CType.Payload.Child).?.data; - const decl_cty = try dg.typeToIndex(decl_ty, .complete); - const need_cast = child_cty != decl_cty and - (dg.indexToCType(child_cty).tag() != .function or dg.indexToCType(decl_cty).tag() != .function); + const elem_ctype = (try dg.ctypeFromType(ptr_ty, .complete)).info(ctype_pool).pointer.elem_ctype; + const decl_ctype = try dg.ctypeFromType(decl_ty, .complete); + const need_cast = !elem_ctype.eql(decl_ctype) and + (elem_ctype.info(ctype_pool) != .function or decl_ctype.info(ctype_pool) != .function); if (need_cast) { try writer.writeAll("(("); try dg.renderType(writer, ptr_ty); @@ -655,7 +651,7 @@ pub const DeclGen = struct { const explicit_alignment = ptr_type.flags.alignment; if (explicit_alignment != .none) { const abi_alignment = Type.fromInterned(ptr_type.child).abiAlignment(zcu); - if (explicit_alignment.compareStrict(.gt, abi_alignment)) { + if (explicit_alignment.order(abi_alignment).compare(.gt)) { const aligned_gop = try dg.aligned_anon_decls.getOrPut(dg.gpa, anon_decl.val); aligned_gop.value_ptr.* = if (aligned_gop.found_existing) aligned_gop.value_ptr.maxStrict(explicit_alignment) @@ -673,6 +669,7 @@ pub const DeclGen = struct { location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const zcu = dg.zcu; + const ctype_pool = &dg.ctype_pool; const decl = zcu.declPtr(decl_index); assert(decl.has_tv); @@ -695,10 +692,10 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const child_cty = (try dg.typeToCType(ty, .complete)).cast(CType.Payload.Child).?.data; - const decl_cty = try dg.typeToIndex(decl_ty, .complete); - const need_cast = child_cty != decl_cty and - (dg.indexToCType(child_cty).tag() != .function or dg.indexToCType(decl_cty).tag() != .function); + const elem_ctype = (try dg.ctypeFromType(ty, .complete)).info(ctype_pool).pointer.elem_ctype; + const decl_ctype = try dg.ctypeFromType(decl_ty, .complete); + const need_cast = !elem_ctype.eql(decl_ctype) and + (elem_ctype.info(ctype_pool) != .function or decl_ctype.info(ctype_pool) != .function); if (need_cast) { try writer.writeAll("(("); try dg.renderType(writer, ty); @@ -720,31 +717,31 @@ pub const DeclGen = struct { const zcu = dg.zcu; const ip = &zcu.intern_pool; const ptr_ty = Type.fromInterned(ip.typeOf(ptr_val)); - const ptr_cty = try dg.typeToIndex(ptr_ty, .complete); - const ptr_child_cty = dg.indexToCType(ptr_cty).cast(CType.Payload.Child).?.data; + const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete); + const ptr_child_ctype = ptr_ctype.info(&dg.ctype_pool).pointer.elem_ctype; const ptr = ip.indexToKey(ptr_val).ptr; switch (ptr.addr) { .decl => |d| try dg.renderDeclValue(writer, Value.fromInterned(ptr_val), d, location), .anon_decl => |anon_decl| try dg.renderAnonDeclValue(writer, Value.fromInterned(ptr_val), anon_decl, location), .int => |int| { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.print("){x}", .{try dg.fmtIntLiteral(Value.fromInterned(int), .Other)}); }, .eu_payload, .opt_payload => |base| { const ptr_base_ty = Type.fromInterned(ip.typeOf(base)); const base_ty = ptr_base_ty.childType(zcu); // Ensure complete type definition is visible before accessing fields. - _ = try dg.typeToIndex(base_ty, .complete); + _ = try dg.ctypeFromType(base_ty, .complete); const payload_ty = switch (ptr.addr) { .eu_payload => base_ty.errorUnionPayload(zcu), .opt_payload => base_ty.optionalChild(zcu), else => unreachable, }; - const payload_cty = try dg.typeToIndex(payload_ty, .forward); - if (ptr_child_cty != payload_cty) { + const payload_ctype = try dg.ctypeFromType(payload_ty, .forward); + if (!ptr_child_ctype.eql(payload_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeAll("&("); @@ -754,10 +751,10 @@ pub const DeclGen = struct { .elem => |elem| { const ptr_base_ty = Type.fromInterned(ip.typeOf(elem.base)); const elem_ty = ptr_base_ty.elemType2(zcu); - const elem_cty = try dg.typeToIndex(elem_ty, .forward); - if (ptr_child_cty != elem_cty) { + const elem_ctype = try dg.ctypeFromType(elem_ty, .forward); + if (!ptr_child_ctype.eql(elem_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeAll("&("); @@ -769,14 +766,14 @@ pub const DeclGen = struct { .field => |field| { const ptr_base_ty = Type.fromInterned(ip.typeOf(field.base)); const base_ty = ptr_base_ty.childType(zcu); - // Ensure complete type definition is visible before accessing fields. - _ = try dg.typeToIndex(base_ty, .complete); + // Ensure complete type definition is available before accessing fields. + _ = try dg.ctypeFromType(base_ty, .complete); switch (fieldLocation(ptr_base_ty, ptr_ty, @as(u32, @intCast(field.index)), zcu)) { .begin => { - const ptr_base_cty = try dg.typeToIndex(ptr_base_ty, .complete); - if (ptr_cty != ptr_base_cty) { + const ptr_base_ctype = try dg.ctypeFromType(ptr_base_ty, .complete); + if (!ptr_ctype.eql(ptr_base_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try dg.renderParentPtr(writer, field.base, location); @@ -797,10 +794,10 @@ pub const DeclGen = struct { }, else => unreachable, }; - const field_cty = try dg.typeToIndex(field_ty, .forward); - if (ptr_child_cty != field_cty) { + const field_ctype = try dg.ctypeFromType(field_ty, .forward); + if (!ptr_child_ctype.eql(field_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeAll("&("); @@ -810,15 +807,15 @@ pub const DeclGen = struct { }, .byte_offset => |byte_offset| { const u8_ptr_ty = try zcu.adjustPtrTypeChild(ptr_ty, Type.u8); - const u8_ptr_cty = try dg.typeToIndex(u8_ptr_ty, .complete); + const u8_ptr_ctype = try dg.ctypeFromType(u8_ptr_ty, .complete); - if (ptr_cty != u8_ptr_cty) { + if (!ptr_ctype.eql(u8_ptr_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeAll("(("); - try dg.renderCType(writer, u8_ptr_cty); + try dg.renderCType(writer, u8_ptr_ctype); try writer.writeByte(')'); try dg.renderParentPtr(writer, field.base, location); try writer.print(" + {})", .{ @@ -826,10 +823,10 @@ pub const DeclGen = struct { }); }, .end => { - const ptr_base_cty = try dg.typeToIndex(ptr_base_ty, .complete); - if (ptr_cty != ptr_base_cty) { + const ptr_base_ctype = try dg.ctypeFromType(ptr_base_ty, .complete); + if (!ptr_ctype.eql(ptr_base_ctype)) { try writer.writeByte('('); - try dg.renderCType(writer, ptr_cty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeAll("(("); @@ -1207,8 +1204,8 @@ pub const DeclGen = struct { try writer.writeByte('}'); }, .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - switch (struct_type.layout) { + const loaded_struct = ip.loadStructType(ty.toIntern()); + switch (loaded_struct.layout) { .auto, .@"extern" => { if (!location.isInitializer()) { try writer.writeByte('('); @@ -1217,13 +1214,14 @@ pub const DeclGen = struct { } try writer.writeByte('{'); - var empty = true; - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (struct_type.fieldIsComptime(ip, field_index)) continue; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + var need_comma = false; + while (field_it.next()) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (!empty) try writer.writeByte(','); + if (need_comma) try writer.writeByte(','); + need_comma = true; const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(zcu.gpa, .{ .int = .{ .ty = field_ty.toIntern(), @@ -1233,8 +1231,6 @@ pub const DeclGen = struct { .repeated_elem => |elem| elem, }; try dg.renderValue(writer, Value.fromInterned(field_val), initializer_type); - - empty = false; } try writer.writeByte('}'); }, @@ -1247,8 +1243,8 @@ pub const DeclGen = struct { var bit_offset: u64 = 0; var eff_num_fields: usize = 0; - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + for (0..loaded_struct.field_types.len) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; eff_num_fields += 1; } @@ -1268,8 +1264,8 @@ pub const DeclGen = struct { var eff_index: usize = 0; var needs_closing_paren = false; - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + for (0..loaded_struct.field_types.len) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { @@ -1304,8 +1300,8 @@ pub const DeclGen = struct { try writer.writeByte('('); // a << a_off | b << b_off | c << c_off var empty = true; - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + for (0..loaded_struct.field_types.len) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; if (!empty) try writer.writeAll(" | "); @@ -1341,10 +1337,10 @@ pub const DeclGen = struct { else => unreachable, }, .un => |un| { - const union_obj = zcu.typeToUnion(ty).?; + const loaded_union = ip.loadUnionType(ty.toIntern()); if (un.tag == .none) { const backing_ty = try ty.unionBackingType(zcu); - switch (union_obj.getLayout(ip)) { + switch (loaded_union.getLayout(ip)) { .@"packed" => { if (!location.isInitializer()) { try writer.writeByte('('); @@ -1376,10 +1372,10 @@ pub const DeclGen = struct { try writer.writeByte(')'); } - const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; - if (union_obj.getLayout(ip) == .@"packed") { + const field_index = zcu.unionTagFieldIndex(loaded_union, Value.fromInterned(un.tag)).?; + const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); + const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index]; + if (loaded_union.getLayout(ip) == .@"packed") { if (field_ty.hasRuntimeBits(zcu)) { if (field_ty.isPtrAtRuntime(zcu)) { try writer.writeByte('('); @@ -1399,7 +1395,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); if (ty.unionTagTypeSafety(zcu)) |_| { - const layout = zcu.getUnionLayout(union_obj); + const layout = zcu.getUnionLayout(loaded_union); if (layout.tag_size != 0) { try writer.writeAll(" .tag = "); try dg.renderValue(writer, Value.fromInterned(un.tag), initializer_type); @@ -1412,8 +1408,8 @@ pub const DeclGen = struct { try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))}); try dg.renderValue(writer, Value.fromInterned(un.val), initializer_type); try writer.writeByte(' '); - } else for (0..union_obj.field_types.len) |this_field_index| { - const this_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[this_field_index]); + } else for (0..loaded_union.field_types.len) |this_field_index| { + const this_field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[this_field_index]); if (!this_field_ty.hasRuntimeBits(zcu)) continue; try dg.renderUndefValue(writer, this_field_ty, initializer_type); break; @@ -1445,12 +1441,14 @@ pub const DeclGen = struct { .ReleaseFast, .ReleaseSmall => false, }; - switch (ty.zigTypeTag(zcu)) { - .Bool => try writer.writeAll(if (safety_on) "0xaa" else "false"), - .Int, .Enum, .ErrorSet => try writer.print("{x}", .{ - try dg.fmtIntLiteral(try zcu.undefValue(ty), location), - }), - .Float => { + switch (ty.toIntern()) { + .c_longdouble_type, + .f16_type, + .f32_type, + .f64_type, + .f80_type, + .f128_type, + => { const bits = ty.floatBits(target.*); // All unsigned ints matching float types are pre-allocated. const repr_ty = zcu.intType(.unsigned, bits) catch unreachable; @@ -1468,49 +1466,90 @@ pub const DeclGen = struct { } try writer.writeAll(", "); try dg.renderUndefValue(writer, repr_ty, .FunctionArgument); - try writer.writeByte(')'); + return writer.writeByte(')'); }, - .Pointer => if (ty.isSlice(zcu)) { - if (!location.isInitializer()) { - try writer.writeByte('('); + .bool_type => try writer.writeAll(if (safety_on) "0xaa" else "false"), + else => switch (ip.indexToKey(ty.toIntern())) { + .simple_type, + .int_type, + .enum_type, + .error_set_type, + .inferred_error_set_type, + => return writer.print("{x}", .{ + try dg.fmtIntLiteral(try zcu.undefValue(ty), location), + }), + .ptr_type => if (ty.isSlice(zcu)) { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeAll("{("); + const ptr_ty = ty.slicePtrFieldType(zcu); + try dg.renderType(writer, ptr_ty); + return writer.print("){x}, {0x}}}", .{ + try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), + }); + } else { + try writer.writeAll("(("); try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + return writer.print("){x})", .{ + try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), + }); + }, + .opt_type => { + const payload_ty = ty.optionalChild(zcu); - try writer.writeAll("{("); - const ptr_ty = ty.slicePtrFieldType(zcu); - try dg.renderType(writer, ptr_ty); - try writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other)}); - } else { - try writer.writeAll("(("); - try dg.renderType(writer, ty); - try writer.print("){x})", .{try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other)}); - }, - .Optional => { - const payload_ty = ty.optionalChild(zcu); + if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + return dg.renderUndefValue(writer, Type.bool, location); + } - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return dg.renderUndefValue(writer, Type.bool, location); - } + if (ty.optionalReprIsPayload(zcu)) { + return dg.renderUndefValue(writer, payload_ty, location); + } - if (ty.optionalReprIsPayload(zcu)) { - return dg.renderUndefValue(writer, payload_ty, location); - } + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + try writer.writeAll("{ .payload = "); + try dg.renderUndefValue(writer, payload_ty, initializer_type); + try writer.writeAll(", .is_null = "); + try dg.renderUndefValue(writer, Type.bool, initializer_type); + return writer.writeAll(" }"); + }, + .struct_type => { + const loaded_struct = ip.loadStructType(ty.toIntern()); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } - try writer.writeAll("{ .payload = "); - try dg.renderUndefValue(writer, payload_ty, initializer_type); - try writer.writeAll(", .is_null = "); - try dg.renderUndefValue(writer, Type.bool, initializer_type); - try writer.writeAll(" }"); - }, - .Struct => switch (ty.containerLayout(zcu)) { - .auto, .@"extern" => { + try writer.writeByte('{'); + var field_it = loaded_struct.iterateRuntimeOrder(ip); + var need_comma = false; + while (field_it.next()) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + + if (need_comma) try writer.writeByte(','); + need_comma = true; + try dg.renderUndefValue(writer, field_ty, initializer_type); + } + return writer.writeByte('}'); + }, + .@"packed" => return writer.print("{x}", .{ + try dg.fmtIntLiteral(try zcu.undefValue(ty), .Other), + }), + } + }, + .anon_struct_type => |anon_struct_info| { if (!location.isInitializer()) { try writer.writeByte('('); try dg.renderType(writer, ty); @@ -1518,116 +1557,125 @@ pub const DeclGen = struct { } try writer.writeByte('{'); - var empty = true; - for (0..ty.structFieldCount(zcu)) |field_index| { - if (ty.structFieldIsComptime(field_index, zcu)) continue; - const field_ty = ty.structFieldType(field_index, zcu); - if (!field_ty.hasRuntimeBits(zcu)) continue; + var need_comma = false; + for (0..anon_struct_info.types.len) |field_index| { + if (anon_struct_info.values.get(ip)[field_index] != .none) continue; + const field_ty = Type.fromInterned(anon_struct_info.types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (!empty) try writer.writeByte(','); + if (need_comma) try writer.writeByte(','); + need_comma = true; try dg.renderUndefValue(writer, field_ty, initializer_type); - - empty = false; } - - try writer.writeByte('}'); + return writer.writeByte('}'); }, - .@"packed" => try writer.print("{x}", .{ - try dg.fmtIntLiteral(try zcu.undefValue(ty), .Other), - }), - }, - .Union => { - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + .union_type => { + const loaded_union = ip.loadUnionType(ty.toIntern()); + switch (loaded_union.getLayout(ip)) { + .auto, .@"extern" => { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } - try writer.writeByte('{'); - if (ty.unionTagTypeSafety(zcu)) |tag_ty| { - const layout = ty.unionGetLayout(zcu); - if (layout.tag_size != 0) { - try writer.writeAll(" .tag = "); - try dg.renderUndefValue(writer, tag_ty, initializer_type); + try writer.writeByte('{'); + if (ty.unionTagTypeSafety(zcu)) |tag_ty| { + const layout = ty.unionGetLayout(zcu); + if (layout.tag_size != 0) { + try writer.writeAll(" .tag = "); + try dg.renderUndefValue(writer, tag_ty, initializer_type); + } + if (ty.unionHasAllZeroBitFieldTypes(zcu)) return try writer.writeByte('}'); + if (layout.tag_size != 0) try writer.writeByte(','); + try writer.writeAll(" .payload = {"); + } + for (0..loaded_union.field_types.len) |field_index| { + const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + try dg.renderUndefValue(writer, field_ty, initializer_type); + break; + } + if (ty.unionTagTypeSafety(zcu)) |_| try writer.writeByte('}'); + return writer.writeByte('}'); + }, + .@"packed" => return writer.print("{x}", .{ + try dg.fmtIntLiteral(try zcu.undefValue(ty), .Other), + }), } - if (ty.unionHasAllZeroBitFieldTypes(zcu)) return try writer.writeByte('}'); - if (layout.tag_size != 0) try writer.writeByte(','); - try writer.writeAll(" .payload = {"); - } - const union_obj = zcu.typeToUnion(ty).?; - for (0..union_obj.field_types.len) |field_index| { - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBits(zcu)) continue; - try dg.renderUndefValue(writer, field_ty, initializer_type); - break; - } - if (ty.unionTagTypeSafety(zcu)) |_| try writer.writeByte('}'); - try writer.writeByte('}'); - }, - .ErrorUnion => { - const payload_ty = ty.errorUnionPayload(zcu); - const error_ty = ty.errorUnionSet(zcu); - - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return dg.renderUndefValue(writer, error_ty, location); - } + }, + .error_union_type => { + const payload_ty = ty.errorUnionPayload(zcu); + const error_ty = ty.errorUnionSet(zcu); - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + return dg.renderUndefValue(writer, error_ty, location); + } - try writer.writeAll("{ .payload = "); - try dg.renderUndefValue(writer, payload_ty, initializer_type); - try writer.writeAll(", .error = "); - try dg.renderUndefValue(writer, error_ty, initializer_type); - try writer.writeAll(" }"); - }, - .Array, .Vector => { - const ai = ty.arrayInfo(zcu); - if (ai.elem_type.eql(Type.u8, zcu)) { - const c_len = ty.arrayLenIncludingSentinel(zcu); - var literal = stringLiteral(writer, c_len); - try literal.start(); - var index: u64 = 0; - while (index < c_len) : (index += 1) - try literal.writeChar(0xaa); - try literal.end(); - } else { if (!location.isInitializer()) { try writer.writeByte('('); try dg.renderType(writer, ty); try writer.writeByte(')'); } - try writer.writeByte('{'); - const c_len = ty.arrayLenIncludingSentinel(zcu); - var index: u64 = 0; - while (index < c_len) : (index += 1) { - if (index > 0) try writer.writeAll(", "); - try dg.renderUndefValue(writer, ty.childType(zcu), initializer_type); + try writer.writeAll("{ .payload = "); + try dg.renderUndefValue(writer, payload_ty, initializer_type); + try writer.writeAll(", .error = "); + try dg.renderUndefValue(writer, error_ty, initializer_type); + return writer.writeAll(" }"); + }, + .array_type, .vector_type => { + const ai = ty.arrayInfo(zcu); + if (ai.elem_type.eql(Type.u8, zcu)) { + const c_len = ty.arrayLenIncludingSentinel(zcu); + var literal = stringLiteral(writer, c_len); + try literal.start(); + var index: u64 = 0; + while (index < c_len) : (index += 1) + try literal.writeChar(0xaa); + return literal.end(); + } else { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeByte('{'); + const c_len = ty.arrayLenIncludingSentinel(zcu); + var index: u64 = 0; + while (index < c_len) : (index += 1) { + if (index > 0) try writer.writeAll(", "); + try dg.renderUndefValue(writer, ty.childType(zcu), initializer_type); + } + return writer.writeByte('}'); } - try writer.writeByte('}'); - } + }, + .anyframe_type, + .opaque_type, + .func_type, + => unreachable, + + .undef, + .simple_value, + .variable, + .extern_func, + .func, + .int, + .err, + .error_union, + .enum_literal, + .enum_tag, + .empty_enum_value, + .float, + .ptr, + .slice, + .opt, + .aggregate, + .un, + .memoized_call, + => unreachable, }, - .ComptimeInt, - .ComptimeFloat, - .Type, - .EnumLiteral, - .Void, - .NoReturn, - .Undefined, - .Null, - .Opaque, - => unreachable, - - .Fn, - .Frame, - .AnyFrame, - => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ - @tagName(tag), - }), } } @@ -1641,13 +1689,12 @@ pub const DeclGen = struct { ident: []const u8, }, ) !void { - const store = &dg.ctypes.set; const zcu = dg.zcu; const ip = &zcu.intern_pool; const fn_decl = zcu.declPtr(fn_decl_index); const fn_ty = fn_decl.typeOf(zcu); - const fn_cty_idx = try dg.typeToIndex(fn_ty, kind); + const fn_ctype = try dg.ctypeFromType(fn_ty, kind); const fn_info = zcu.typeToFunc(fn_ty).?; if (fn_info.cc == .Naked) { @@ -1661,7 +1708,7 @@ pub const DeclGen = struct { try w.writeAll("zig_cold "); if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn "); - var trailing = try renderTypePrefix(dg.pass, store.*, zcu, w, fn_cty_idx, .suffix, .{}); + var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{}); if (toCallingConvention(fn_info.cc)) |call_conv| { try w.print("{}zig_callconv({s})", .{ trailing, call_conv }); @@ -1670,7 +1717,7 @@ pub const DeclGen = struct { switch (kind) { .forward => {}, - .complete => if (fn_decl.alignment.toByteUnitsOptional()) |a| { + .complete => if (fn_decl.alignment.toByteUnits()) |a| { try w.print("{}zig_align_fn({})", .{ trailing, a }); trailing = .maybe_space; }, @@ -1687,10 +1734,10 @@ pub const DeclGen = struct { try renderTypeSuffix( dg.pass, - store.*, + &dg.ctype_pool, zcu, w, - fn_cty_idx, + fn_ctype, .suffix, CQualifiers.init(.{ .@"const" = switch (kind) { .forward => false, @@ -1701,7 +1748,7 @@ pub const DeclGen = struct { switch (kind) { .forward => { - if (fn_decl.alignment.toByteUnitsOptional()) |a| { + if (fn_decl.alignment.toByteUnits()) |a| { try w.print(" zig_align_fn({})", .{a}); } switch (name) { @@ -1748,20 +1795,13 @@ pub const DeclGen = struct { } } - fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { - return dg.ctypes.indexToCType(idx); + fn ctypeFromType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { + defer std.debug.assert(dg.scratch.items.len == 0); + return dg.ctype_pool.fromType(dg.gpa, &dg.scratch, ty, dg.zcu, dg.mod, kind); } - fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { - return dg.ctypes.typeToIndex(dg.gpa, ty, dg.zcu, dg.mod, kind); - } - - fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { - return dg.ctypes.typeToCType(dg.gpa, ty, dg.zcu, dg.mod, kind); - } - - fn byteSize(dg: *DeclGen, cty: CType) u64 { - return cty.byteSize(dg.ctypes.set, dg.mod); + fn byteSize(dg: *DeclGen, ctype: CType) u64 { + return ctype.byteSize(&dg.ctype_pool, dg.mod); } /// Renders a type as a single identifier, generating intermediate typedefs @@ -1776,14 +1816,12 @@ pub const DeclGen = struct { /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { - try dg.renderCType(w, try dg.typeToIndex(t, .complete)); + try dg.renderCType(w, try dg.ctypeFromType(t, .complete)); } - fn renderCType(dg: *DeclGen, w: anytype, idx: CType.Index) error{ OutOfMemory, AnalysisFail }!void { - const store = &dg.ctypes.set; - const zcu = dg.zcu; - _ = try renderTypePrefix(dg.pass, store.*, zcu, w, idx, .suffix, .{}); - try renderTypeSuffix(dg.pass, store.*, zcu, w, idx, .suffix, .{}); + fn renderCType(dg: *DeclGen, w: anytype, ctype: CType) error{ OutOfMemory, AnalysisFail }!void { + _ = try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); + try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); } const IntCastContext = union(enum) { @@ -1905,32 +1943,37 @@ pub const DeclGen = struct { alignment: Alignment, kind: CType.Kind, ) error{ OutOfMemory, AnalysisFail }!void { - const zcu = dg.zcu; - const alignas = CType.AlignAs.init(alignment, ty.abiAlignment(zcu)); - try dg.renderCTypeAndName(w, try dg.typeToIndex(ty, kind), name, qualifiers, alignas); + try dg.renderCTypeAndName( + w, + try dg.ctypeFromType(ty, kind), + name, + qualifiers, + CType.AlignAs.fromAlignment(.{ + .@"align" = alignment, + .abi = ty.abiAlignment(dg.zcu), + }), + ); } fn renderCTypeAndName( dg: *DeclGen, w: anytype, - cty_idx: CType.Index, + ctype: CType, name: CValue, qualifiers: CQualifiers, alignas: CType.AlignAs, ) error{ OutOfMemory, AnalysisFail }!void { - const store = &dg.ctypes.set; - const zcu = dg.zcu; - switch (alignas.abiOrder()) { .lt => try w.print("zig_under_align({}) ", .{alignas.toByteUnits()}), .eq => {}, .gt => try w.print("zig_align({}) ", .{alignas.toByteUnits()}), } - const trailing = try renderTypePrefix(dg.pass, store.*, zcu, w, cty_idx, .suffix, qualifiers); - try w.print("{}", .{trailing}); + try w.print("{}", .{ + try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, qualifiers), + }); try dg.writeName(w, name); - try renderTypeSuffix(dg.pass, store.*, zcu, w, cty_idx, .suffix, .{}); + try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); } fn declIsGlobal(dg: *DeclGen, val: Value) bool { @@ -2094,33 +2137,31 @@ pub const DeclGen = struct { } fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { - try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete)); + try dg.renderCTypeForBuiltinFnName(writer, try dg.ctypeFromType(ty, .complete)); } - fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void { - switch (cty.tag()) { - else => try writer.print("{c}{d}", .{ - if (cty.isBool()) + fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ctype: CType) !void { + switch (ctype.info(&dg.ctype_pool)) { + else => |ctype_info| try writer.print("{c}{d}", .{ + if (ctype.isBool()) signAbbrev(.unsigned) - else if (cty.isInteger()) - signAbbrev(cty.signedness(dg.mod)) - else if (cty.isFloat()) + else if (ctype.isInteger()) + signAbbrev(ctype.signedness(dg.mod)) + else if (ctype.isFloat()) @as(u8, 'f') - else if (cty.isPointer()) + else if (ctype_info == .pointer) @as(u8, 'p') else - return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ - cty.tag(), - }), - if (cty.isFloat()) cty.floatActiveBits(dg.mod) else dg.byteSize(cty) * 8, + return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for {s} type", .{@tagName(ctype_info)}), + if (ctype.isFloat()) ctype.floatActiveBits(dg.mod) else dg.byteSize(ctype) * 8, }), .array => try writer.writeAll("big"), } } fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { - const cty = try dg.typeToCType(ty, .complete); - const is_big = cty.tag() == .array; + const ctype = try dg.ctypeFromType(ty, .complete); + const is_big = ctype.info(&dg.ctype_pool) == .array; switch (info) { .none => if (!is_big) return, .bits => {}, @@ -2155,7 +2196,7 @@ pub const DeclGen = struct { .dg = dg, .int_info = ty.intInfo(zcu), .kind = kind, - .cty = try dg.typeToCType(ty, kind), + .ctype = try dg.ctypeFromType(ty, kind), .val = val, } }; } @@ -2184,122 +2225,74 @@ const RenderCTypeTrailing = enum { } } }; -fn renderTypeName( +fn renderAlignedTypeName(w: anytype, ctype: CType) !void { + try w.print("anon__aligned_{d}", .{@intFromEnum(ctype.index)}); +} +fn renderFwdDeclTypeName( zcu: *Zcu, w: anytype, - idx: CType.Index, - cty: CType, + ctype: CType, + fwd_decl: CType.Info.FwdDecl, attributes: []const u8, ) !void { - switch (cty.tag()) { - else => unreachable, - - .fwd_anon_struct, - .fwd_anon_union, - => |tag| try w.print("{s} {s}anon__lazy_{d}", .{ - @tagName(tag)["fwd_anon_".len..], - attributes, - idx, + try w.print("{s} {s}", .{ @tagName(fwd_decl.tag), attributes }); + switch (fwd_decl.name) { + .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}), + .owner_decl => |owner_decl| try w.print("{}__{d}", .{ + fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(owner_decl).name)), + @intFromEnum(owner_decl), }), - - .fwd_struct, - .fwd_union, - => |tag| { - const owner_decl = cty.cast(CType.Payload.FwdDecl).?.data; - try w.print("{s} {s}{}__{d}", .{ - @tagName(tag)["fwd_".len..], - attributes, - fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(owner_decl).name)), - @intFromEnum(owner_decl), - }); - }, } } fn renderTypePrefix( pass: DeclGen.Pass, - store: CType.Store.Set, + ctype_pool: *const CType.Pool, zcu: *Zcu, w: anytype, - idx: CType.Index, + ctype: CType, parent_fix: CTypeFix, qualifiers: CQualifiers, ) @TypeOf(w).Error!RenderCTypeTrailing { var trailing = RenderCTypeTrailing.maybe_space; + switch (ctype.info(ctype_pool)) { + .basic => |basic_info| try w.writeAll(@tagName(basic_info)), - const cty = store.indexToCType(idx); - switch (cty.tag()) { - .void, - .char, - .@"signed char", - .short, - .int, - .long, - .@"long long", - ._Bool, - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .float, - .double, - .@"long double", - .bool, - .size_t, - .ptrdiff_t, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .uintptr_t, - .intptr_t, - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => |tag| try w.writeAll(@tagName(tag)), - - .pointer, - .pointer_const, - .pointer_volatile, - .pointer_const_volatile, - => |tag| { - const child_idx = cty.cast(CType.Payload.Child).?.data; - const child_trailing = try renderTypePrefix( + .pointer => |pointer_info| { + try w.print("{}*", .{try renderTypePrefix( pass, - store, + ctype_pool, zcu, w, - child_idx, + pointer_info.elem_ctype, .prefix, - CQualifiers.init(.{ .@"const" = switch (tag) { - .pointer, .pointer_volatile => false, - .pointer_const, .pointer_const_volatile => true, - else => unreachable, - }, .@"volatile" = switch (tag) { - .pointer, .pointer_const => false, - .pointer_volatile, .pointer_const_volatile => true, - else => unreachable, - } }), - ); - try w.print("{}*", .{child_trailing}); + CQualifiers.init(.{ + .@"const" = pointer_info.@"const", + .@"volatile" = pointer_info.@"volatile", + }), + )}); trailing = .no_space; }, - .array, - .vector, - => { - const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; - const child_trailing = - try renderTypePrefix(pass, store, zcu, w, child_idx, .suffix, qualifiers); + .aligned => switch (pass) { + .decl => |decl_index| try w.print("decl__{d}_{d}", .{ + @intFromEnum(decl_index), @intFromEnum(ctype.index), + }), + .anon => |anon_decl| try w.print("anon__{d}_{d}", .{ + @intFromEnum(anon_decl), @intFromEnum(ctype.index), + }), + .flush => try renderAlignedTypeName(w, ctype), + }, + + .array, .vector => |sequence_info| { + const child_trailing = try renderTypePrefix( + pass, + ctype_pool, + zcu, + w, + sequence_info.elem_ctype, + .suffix, + qualifiers, + ); switch (parent_fix) { .prefix => { try w.print("{}(", .{child_trailing}); @@ -2309,56 +2302,46 @@ fn renderTypePrefix( } }, - .fwd_anon_struct, - .fwd_anon_union, - => switch (pass) { - .decl => |decl_index| try w.print("decl__{d}_{d}", .{ @intFromEnum(decl_index), idx }), - .anon => |anon_decl| try w.print("anon__{d}_{d}", .{ @intFromEnum(anon_decl), idx }), - .flush => try renderTypeName(zcu, w, idx, cty, ""), + .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) { + .anon => switch (pass) { + .decl => |decl_index| try w.print("decl__{d}_{d}", .{ + @intFromEnum(decl_index), @intFromEnum(ctype.index), + }), + .anon => |anon_decl| try w.print("anon__{d}_{d}", .{ + @intFromEnum(anon_decl), @intFromEnum(ctype.index), + }), + .flush => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""), + }, + .owner_decl => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""), }, - .fwd_struct, - .fwd_union, - => try renderTypeName(zcu, w, idx, cty, ""), - - .unnamed_struct, - .unnamed_union, - .packed_unnamed_struct, - .packed_unnamed_union, - => |tag| { - try w.print("{s} {s}", .{ - @tagName(tag)["unnamed_".len..], - if (cty.isPacked()) "zig_packed(" else "", - }); - try renderAggregateFields(zcu, w, store, cty, 1); - if (cty.isPacked()) try w.writeByte(')'); + .aggregate => |aggregate_info| switch (aggregate_info.name) { + .anon => { + try w.print("{s} {s}", .{ + @tagName(aggregate_info.tag), + if (aggregate_info.@"packed") "zig_packed(" else "", + }); + try renderFields(zcu, w, ctype_pool, aggregate_info, 1); + if (aggregate_info.@"packed") try w.writeByte(')'); + }, + .fwd_decl => |fwd_decl| return renderTypePrefix( + pass, + ctype_pool, + zcu, + w, + fwd_decl, + parent_fix, + qualifiers, + ), }, - .anon_struct, - .anon_union, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => return renderTypePrefix( - pass, - store, - zcu, - w, - cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, - parent_fix, - qualifiers, - ), - - .function, - .varargs_function, - => { + .function => |function_info| { const child_trailing = try renderTypePrefix( pass, - store, + ctype_pool, zcu, w, - cty.cast(CType.Payload.Function).?.data.return_type, + function_info.return_ctype, .suffix, .{}, ); @@ -2371,170 +2354,107 @@ fn renderTypePrefix( } }, } - var qualifier_it = qualifiers.iterator(); while (qualifier_it.next()) |qualifier| { try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); trailing = .maybe_space; } - return trailing; } fn renderTypeSuffix( pass: DeclGen.Pass, - store: CType.Store.Set, + ctype_pool: *const CType.Pool, zcu: *Zcu, w: anytype, - idx: CType.Index, + ctype: CType, parent_fix: CTypeFix, qualifiers: CQualifiers, ) @TypeOf(w).Error!void { - const cty = store.indexToCType(idx); - switch (cty.tag()) { - .void, - .char, - .@"signed char", - .short, - .int, - .long, - .@"long long", - ._Bool, - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .float, - .double, - .@"long double", - .bool, - .size_t, - .ptrdiff_t, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .uintptr_t, - .intptr_t, - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - .zig_c_longdouble, - => {}, - - .pointer, - .pointer_const, - .pointer_volatile, - .pointer_const_volatile, - => try renderTypeSuffix( + switch (ctype.info(ctype_pool)) { + .basic, .aligned, .fwd_decl, .aggregate => {}, + .pointer => |pointer_info| try renderTypeSuffix( pass, - store, + ctype_pool, zcu, w, - cty.cast(CType.Payload.Child).?.data, + pointer_info.elem_ctype, .prefix, .{}, ), - - .array, - .vector, - => { + .array, .vector => |sequence_info| { switch (parent_fix) { .prefix => try w.writeByte(')'), .suffix => {}, } - try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); - try renderTypeSuffix( - pass, - store, - zcu, - w, - cty.cast(CType.Payload.Sequence).?.data.elem_type, - .suffix, - .{}, - ); + try w.print("[{}]", .{sequence_info.len}); + try renderTypeSuffix(pass, ctype_pool, zcu, w, sequence_info.elem_ctype, .suffix, .{}); }, - - .fwd_anon_struct, - .fwd_anon_union, - .fwd_struct, - .fwd_union, - .unnamed_struct, - .unnamed_union, - .packed_unnamed_struct, - .packed_unnamed_union, - .anon_struct, - .anon_union, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => {}, - - .function, - .varargs_function, - => |tag| { + .function => |function_info| { switch (parent_fix) { .prefix => try w.writeByte(')'), .suffix => {}, } - const data = cty.cast(CType.Payload.Function).?.data; - try w.writeByte('('); var need_comma = false; - for (data.param_types, 0..) |param_type, param_i| { + for (0..function_info.param_ctypes.len) |param_index| { + const param_type = function_info.param_ctypes.at(param_index, ctype_pool); if (need_comma) try w.writeAll(", "); need_comma = true; const trailing = - try renderTypePrefix(pass, store, zcu, w, param_type, .suffix, qualifiers); - if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_i }); - try renderTypeSuffix(pass, store, zcu, w, param_type, .suffix, .{}); + try renderTypePrefix(pass, ctype_pool, zcu, w, param_type, .suffix, qualifiers); + if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_index }); + try renderTypeSuffix(pass, ctype_pool, zcu, w, param_type, .suffix, .{}); } - switch (tag) { - .function => {}, - .varargs_function => { - if (need_comma) try w.writeAll(", "); - need_comma = true; - try w.writeAll("..."); - }, - else => unreachable, + if (function_info.varargs) { + if (need_comma) try w.writeAll(", "); + need_comma = true; + try w.writeAll("..."); } if (!need_comma) try w.writeAll("void"); try w.writeByte(')'); - try renderTypeSuffix(pass, store, zcu, w, data.return_type, .suffix, .{}); + try renderTypeSuffix(pass, ctype_pool, zcu, w, function_info.return_ctype, .suffix, .{}); }, } } -fn renderAggregateFields( +fn renderFields( zcu: *Zcu, writer: anytype, - store: CType.Store.Set, - cty: CType, + ctype_pool: *const CType.Pool, + aggregate_info: CType.Info.Aggregate, indent: usize, ) !void { try writer.writeAll("{\n"); - const fields = cty.fields(); - for (fields) |field| { + for (0..aggregate_info.fields.len) |field_index| { + const field_info = aggregate_info.fields.at(field_index, ctype_pool); try writer.writeByteNTimes(' ', indent + 1); - switch (field.alignas.abiOrder()) { - .lt => try writer.print("zig_under_align({}) ", .{field.alignas.toByteUnits()}), - .eq => {}, - .gt => try writer.print("zig_align({}) ", .{field.alignas.toByteUnits()}), + switch (field_info.alignas.abiOrder()) { + .lt => { + std.debug.assert(aggregate_info.@"packed"); + if (field_info.alignas.@"align" != .@"1") try writer.print("zig_under_align({}) ", .{ + field_info.alignas.toByteUnits(), + }); + }, + .eq => if (aggregate_info.@"packed" and field_info.alignas.@"align" != .@"1") + try writer.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}), + .gt => { + std.debug.assert(field_info.alignas.@"align" != .@"1"); + try writer.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}); + }, } - const trailing = try renderTypePrefix(.flush, store, zcu, writer, field.type, .suffix, .{}); - try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); - try renderTypeSuffix(.flush, store, zcu, writer, field.type, .suffix, .{}); + const trailing = try renderTypePrefix( + .flush, + ctype_pool, + zcu, + writer, + field_info.ctype, + .suffix, + .{}, + ); + try writer.print("{}{ }", .{ trailing, fmtIdent(field_info.name.slice(ctype_pool)) }); + try renderTypeSuffix(.flush, ctype_pool, zcu, writer, field_info.ctype, .suffix, .{}); try writer.writeAll(";\n"); } try writer.writeByteNTimes(' ', indent); @@ -2544,77 +2464,77 @@ fn renderAggregateFields( pub fn genTypeDecl( zcu: *Zcu, writer: anytype, - global_store: CType.Store.Set, - global_idx: CType.Index, + global_ctype_pool: *const CType.Pool, + global_ctype: CType, pass: DeclGen.Pass, - decl_store: CType.Store.Set, - decl_idx: CType.Index, + decl_ctype_pool: *const CType.Pool, + decl_ctype: CType, found_existing: bool, ) !void { - const global_cty = global_store.indexToCType(global_idx); - switch (global_cty.tag()) { - .fwd_anon_struct => if (pass != .flush) { - try writer.writeAll("typedef "); - _ = try renderTypePrefix(.flush, global_store, zcu, writer, global_idx, .suffix, .{}); - try writer.writeByte(' '); - _ = try renderTypePrefix(pass, decl_store, zcu, writer, decl_idx, .suffix, .{}); - try writer.writeAll(";\n"); - }, - - .fwd_struct, - .fwd_union, - .anon_struct, - .anon_union, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => |tag| if (!found_existing) { - switch (tag) { - .fwd_struct, - .fwd_union, - => { - const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; - _ = try renderTypePrefix( - .flush, - global_store, - zcu, - writer, - global_idx, - .suffix, - .{}, - ); - try writer.writeAll("; /* "); - try zcu.declPtr(owner_decl).renderFullyQualifiedName(zcu, writer); - try writer.writeAll(" */\n"); - }, - - .anon_struct, - .anon_union, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => { - const fwd_idx = global_cty.cast(CType.Payload.Aggregate).?.data.fwd_decl; - try renderTypeName( - zcu, - writer, - fwd_idx, - global_store.indexToCType(fwd_idx), - if (global_cty.isPacked()) "zig_packed(" else "", - ); + switch (global_ctype.info(global_ctype_pool)) { + .basic, .pointer, .array, .vector, .function => {}, + .aligned => |aligned_info| { + if (!found_existing) { + try writer.writeAll("typedef "); + try writer.print("{}", .{ + try renderTypePrefix(pass, global_ctype_pool, zcu, writer, aligned_info.ctype, .suffix, .{}), + }); + try renderAlignedTypeName(writer, global_ctype); + try renderTypeSuffix(pass, global_ctype_pool, zcu, writer, aligned_info.ctype, .suffix, .{}); + std.debug.assert(aligned_info.alignas.abiOrder().compare(.lt)); + try writer.print(" zig_under_align({d});\n", .{aligned_info.alignas.toByteUnits()}); + } + switch (pass) { + .decl, .anon => { + try writer.writeAll("typedef "); + _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{}); try writer.writeByte(' '); - try renderAggregateFields(zcu, writer, global_store, global_cty, 0); - if (global_cty.isPacked()) try writer.writeByte(')'); + _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, writer, decl_ctype, .suffix, .{}); try writer.writeAll(";\n"); }, - - else => unreachable, + .flush => {}, } }, - - else => {}, + .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) { + .anon => switch (pass) { + .decl, .anon => { + try writer.writeAll("typedef "); + _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{}); + try writer.writeByte(' '); + _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, writer, decl_ctype, .suffix, .{}); + try writer.writeAll(";\n"); + }, + .flush => {}, + }, + .owner_decl => |owner_decl_index| if (!found_existing) { + _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{}); + try writer.writeByte(';'); + const owner_decl = zcu.declPtr(owner_decl_index); + const owner_mod = zcu.namespacePtr(owner_decl.src_namespace).file_scope.mod; + if (!owner_mod.strip) { + try writer.writeAll(" /* "); + try owner_decl.renderFullyQualifiedName(zcu, writer); + try writer.writeAll(" */"); + } + try writer.writeByte('\n'); + }, + }, + .aggregate => |aggregate_info| switch (aggregate_info.name) { + .anon => {}, + .fwd_decl => |fwd_decl| if (!found_existing) { + try renderFwdDeclTypeName( + zcu, + writer, + fwd_decl, + fwd_decl.info(global_ctype_pool).fwd_decl, + if (aggregate_info.@"packed") "zig_packed(" else "", + ); + try writer.writeByte(' '); + try renderFields(zcu, writer, global_ctype_pool, aggregate_info, 0); + if (aggregate_info.@"packed") try writer.writeByte(')'); + try writer.writeAll(";\n"); + }, + }, } } @@ -2771,13 +2691,13 @@ fn genExports(o: *Object) !void { } } -pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { +pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) !void { const zcu = o.dg.zcu; const ip = &zcu.intern_pool; + const ctype_pool = &o.dg.ctype_pool; 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; @@ -2787,7 +2707,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { try w.writeAll("static "); try o.dg.renderType(w, name_slice_ty); try w.writeByte(' '); - try w.writeAll(fn_name); + try w.writeAll(val.fn_name.slice(lazy_ctype_pool)); try w.writeByte('('); try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete); try w.writeAll(") {\n switch (tag) {\n"); @@ -2829,8 +2749,9 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { }, .never_tail, .never_inline => |fn_decl_index| { const fn_decl = zcu.declPtr(fn_decl_index); - const fn_cty = try o.dg.typeToCType(fn_decl.typeOf(zcu), .complete); - const fn_info = fn_cty.cast(CType.Payload.Function).?.data; + const fn_ctype = try o.dg.ctypeFromType(fn_decl.typeOf(zcu), .complete); + const fn_info = fn_ctype.info(ctype_pool).function; + const fn_name = val.fn_name.slice(lazy_ctype_pool); const fwd_decl_writer = o.dg.fwdDeclWriter(); try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); @@ -2843,11 +2764,13 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { try fwd_decl_writer.writeAll(";\n"); try w.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ .ident = fn_name }); + try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ + .ident = 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| { + for (0..fn_info.param_ctypes.len) |arg| { if (arg > 0) try w.writeAll(", "); try o.dg.writeCValue(w, .{ .arg = arg }); } @@ -2931,7 +2854,7 @@ pub fn genFunc(f: *Function) !void { for (free_locals.values()) |list| { for (list.keys()) |local_index| { const local = f.locals.items[local_index]; - try o.dg.renderCTypeAndName(w, local.cty_idx, .{ .local = local_index }, .{}, local.flags.alignas); + try o.dg.renderCTypeAndName(w, local.ctype, .{ .local = local_index }, .{}, local.flags.alignas); try w.writeAll(";\n "); } } @@ -3451,11 +3374,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const ptr_ty = f.typeOf(bin_op.lhs); - const ptr_align = ptr_ty.ptrAlignment(zcu); - const elem_ty = ptr_ty.elemType2(zcu); - const elem_align = elem_ty.abiAlignment(zcu); - const is_under_aligned = ptr_align.compareStrict(.lt, elem_align); - const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(zcu); + const elem_has_bits = ptr_ty.elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu); const ptr = try f.resolveInst(bin_op.lhs); const index = try f.resolveInst(bin_op.rhs); @@ -3470,22 +3389,13 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { try f.renderType(writer, inst_ty); try writer.writeByte(')'); if (elem_has_bits) try writer.writeByte('&'); - if (elem_has_bits and ptr_ty.ptrSize(zcu) == .One and !is_under_aligned) { + if (elem_has_bits and ptr_ty.ptrSize(zcu) == .One) { // It's a pointer to an array, so we need to de-reference. try f.writeCValueDeref(writer, ptr); } else try f.writeCValue(writer, ptr, .Other); if (elem_has_bits) { try writer.writeByte('['); try f.writeCValue(writer, index, .Other); - if (is_under_aligned) { - const factor = @divExact(elem_align.toByteUnitsOptional().?, @min( - ptr_align.toByteUnitsOptional().?, - f.object.dg.mod.resolved_target.result.maxIntAlignment(), - )); - try writer.print(" * {}", .{ - try f.fmtIntLiteral(try zcu.intValue(Type.usize, factor)), - }); - } try writer.writeByte(']'); } try a.end(f, writer); @@ -3577,13 +3487,16 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const zcu = f.object.dg.zcu; const inst_ty = f.typeOfIndex(inst); - const elem_type = inst_ty.childType(zcu); - if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty }; + const elem_ty = inst_ty.childType(zcu); + if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty }; - const local = try f.allocLocalValue( - elem_type, - inst_ty.ptrAlignment(zcu), - ); + const local = try f.allocLocalValue(.{ + .ctype = try f.ctypeFromType(elem_ty, .complete), + .alignas = CType.AlignAs.fromAlignment(.{ + .@"align" = inst_ty.ptrInfo(zcu).flags.alignment, + .abi = elem_ty.abiAlignment(zcu), + }), + }); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.zcu.gpa; try f.allocs.put(gpa, local.new_local, true); @@ -3596,10 +3509,13 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { const elem_ty = inst_ty.childType(zcu); if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty }; - const local = try f.allocLocalValue( - elem_ty, - inst_ty.ptrAlignment(zcu), - ); + const local = try f.allocLocalValue(.{ + .ctype = try f.ctypeFromType(elem_ty, .complete), + .alignas = CType.AlignAs.fromAlignment(.{ + .@"align" = inst_ty.ptrInfo(zcu).flags.alignment, + .abi = elem_ty.abiAlignment(zcu), + }), + }); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.zcu.gpa; try f.allocs.put(gpa, local.new_local, true); @@ -3608,14 +3524,14 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); - const inst_cty = try f.typeToIndex(inst_ty, .parameter); + const inst_ctype = try f.ctypeFromType(inst_ty, .parameter); const i = f.next_arg_index; f.next_arg_index += 1; - const result: CValue = if (inst_cty != try f.typeToIndex(inst_ty, .complete)) - .{ .arg_array = i } + const result: CValue = if (inst_ctype.eql(try f.ctypeFromType(inst_ty, .complete))) + .{ .arg = i } else - .{ .arg = i }; + .{ .arg_array = i }; if (f.liveness.isUnused(inst)) { const writer = f.object.writer(); @@ -3649,7 +3565,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); const is_aligned = if (ptr_info.flags.alignment != .none) - ptr_info.flags.alignment.compare(.gte, src_ty.abiAlignment(zcu)) + ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte) else true; const is_array = lowersToArray(src_ty, zcu); @@ -3724,18 +3640,21 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const op_inst = un_op.toIndex(); const op_ty = f.typeOf(un_op); const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty; - const lowered_ret_ty = try lowerFnRetTy(ret_ty, zcu); + const ret_ctype = try f.ctypeFromType(ret_ty, .parameter); if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) { try reap(f, inst, &.{un_op}); _ = try airCall(f, op_inst.?, .always_tail); - } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (ret_ctype.index != .void) { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); var deref = is_ptr; const is_array = lowersToArray(ret_ty, zcu); const ret_val = if (is_array) ret_val: { - const array_local = try f.allocLocal(inst, lowered_ret_ty); + const array_local = try f.allocAlignedLocal(inst, .{ + .ctype = ret_ctype, + .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(f.object.dg.zcu)), + }); try writer.writeAll("memcpy("); try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); try writer.writeAll(", "); @@ -3921,7 +3840,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { } const is_aligned = if (ptr_info.flags.alignment != .none) - ptr_info.flags.alignment.compare(.gte, src_ty.abiAlignment(zcu)) + ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte) else true; const is_array = lowersToArray(Type.fromInterned(ptr_info.child), zcu); @@ -4397,22 +4316,23 @@ fn airCall( defer gpa.free(resolved_args); for (resolved_args, args) |*resolved_arg, arg| { const arg_ty = f.typeOf(arg); - const arg_cty = try f.typeToIndex(arg_ty, .parameter); - if (f.indexToCType(arg_cty).tag() == .void) { + const arg_ctype = try f.ctypeFromType(arg_ty, .parameter); + if (arg_ctype.index == .void) { resolved_arg.* = .none; continue; } resolved_arg.* = try f.resolveInst(arg); - if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { - const lowered_arg_ty = try lowerFnRetTy(arg_ty, zcu); - - const array_local = try f.allocLocal(inst, lowered_arg_ty); + if (!arg_ctype.eql(try f.ctypeFromType(arg_ty, .complete))) { + const array_local = try f.allocAlignedLocal(inst, .{ + .ctype = arg_ctype, + .alignas = CType.AlignAs.fromAbiAlignment(arg_ty.abiAlignment(zcu)), + }); try writer.writeAll("memcpy("); try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); try writer.writeAll(", "); try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderType(writer, lowered_arg_ty); + try f.renderCType(writer, arg_ctype); try writer.writeAll("));\n"); resolved_arg.* = array_local; } @@ -4433,21 +4353,27 @@ fn airCall( else => unreachable, }).?; const ret_ty = Type.fromInterned(fn_info.return_type); - const lowered_ret_ty = try lowerFnRetTy(ret_ty, zcu); + const ret_ctype: CType = if (ret_ty.isNoReturn(zcu)) + .{ .index = .void } + else + try f.ctypeFromType(ret_ty, .parameter); const result_local = result: { if (modifier == .always_tail) { try writer.writeAll("zig_always_tail return "); break :result .none; - } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + } else if (ret_ctype.index == .void) { break :result .none; } else if (f.liveness.isUnused(inst)) { try writer.writeByte('('); - try f.renderType(writer, Type.void); + try f.renderCType(writer, .{ .index = .void }); try writer.writeByte(')'); break :result .none; } else { - const local = try f.allocLocal(inst, lowered_ret_ty); + const local = try f.allocAlignedLocal(inst, .{ + .ctype = ret_ctype, + .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)), + }); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); break :result local; @@ -4767,6 +4693,7 @@ const LocalResult = struct { fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !LocalResult { const zcu = f.object.dg.zcu; const target = &f.object.dg.mod.resolved_target.result; + const ctype_pool = &f.object.dg.ctype_pool; const writer = f.object.writer(); if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) { @@ -4825,49 +4752,54 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !Loca // Ensure padding bits have the expected value. if (dest_ty.isAbiInt(zcu)) { - const dest_cty = try f.typeToCType(dest_ty, .complete); + const dest_ctype = try f.ctypeFromType(dest_ty, .complete); const dest_info = dest_ty.intInfo(zcu); var bits: u16 = dest_info.bits; - var wrap_cty: ?CType = null; + var wrap_ctype: ?CType = null; var need_bitcasts = false; try f.writeCValue(writer, local, .Other); - if (dest_cty.castTag(.array)) |pl| { - try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { - .little => pl.data.len - 1, - .big => 0, - }}); - const elem_cty = f.indexToCType(pl.data.elem_type); - wrap_cty = elem_cty.toSignedness(dest_info.signedness); - need_bitcasts = wrap_cty.?.tag() == .zig_i128; - bits -= 1; - bits %= @as(u16, @intCast(f.byteSize(elem_cty) * 8)); - bits += 1; + switch (dest_ctype.info(ctype_pool)) { + else => {}, + .array => |array_info| { + try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { + .little => array_info.len - 1, + .big => 0, + }}); + wrap_ctype = array_info.elem_ctype.toSignedness(dest_info.signedness); + need_bitcasts = wrap_ctype.?.index == .zig_i128; + bits -= 1; + bits %= @as(u16, @intCast(f.byteSize(array_info.elem_ctype) * 8)); + bits += 1; + }, } try writer.writeAll(" = "); if (need_bitcasts) { try writer.writeAll("zig_bitCast_"); - try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?.toUnsigned()); + try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_ctype.?.toUnsigned()); try writer.writeByte('('); } try writer.writeAll("zig_wrap_"); const info_ty = try zcu.intType(dest_info.signedness, bits); - if (wrap_cty) |cty| - try f.object.dg.renderCTypeForBuiltinFnName(writer, cty) + if (wrap_ctype) |ctype| + try f.object.dg.renderCTypeForBuiltinFnName(writer, ctype) else try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty); try writer.writeByte('('); if (need_bitcasts) { try writer.writeAll("zig_bitCast_"); - try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?); + try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_ctype.?); try writer.writeByte('('); } try f.writeCValue(writer, local, .Other); - if (dest_cty.castTag(.array)) |pl| { - try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { - .little => pl.data.len - 1, - .big => 0, - }}); + switch (dest_ctype.info(ctype_pool)) { + else => {}, + .array => |array_info| try writer.print("[{d}]", .{ + switch (target.cpu.arch.endian()) { + .little => array_info.len - 1, + .big => 0, + }, + }), } if (need_bitcasts) try writer.writeByte(')'); try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits); @@ -5131,10 +5063,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (is_reg) { const output_ty = if (output == .none) inst_ty else f.typeOf(output).childType(zcu); try writer.writeAll("register "); - const alignment: Alignment = .none; - const local_value = try f.allocLocalValue(output_ty, alignment); + const local_value = try f.allocLocal(inst, output_ty); try f.allocs.put(gpa, local_value.new_local, false); - try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); + try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, .none, .complete); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); @@ -5164,10 +5095,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (asmInputNeedsLocal(f, constraint, input_val)) { const input_ty = f.typeOf(input); if (is_reg) try writer.writeAll("register "); - const alignment: Alignment = .none; - const local_value = try f.allocLocalValue(input_ty, alignment); + const local_value = try f.allocLocal(inst, input_ty); try f.allocs.put(gpa, local_value.new_local, false); - try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); + try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, .none, .complete); if (is_reg) { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); @@ -5512,59 +5442,74 @@ fn fieldLocation( end: void, } { const ip = &zcu.intern_pool; - const container_ty = container_ptr_ty.childType(zcu); - return switch (container_ty.zigTypeTag(zcu)) { - .Struct => blk: { - if (zcu.typeToPackedStruct(container_ty)) |struct_type| { - if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0) - break :blk .{ .byte_offset = @divExact(zcu.structPackedFieldBitOffset(struct_type, field_index) + container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) } + const container_ty = Type.fromInterned(ip.indexToKey(container_ptr_ty.toIntern()).ptr_type.child); + switch (ip.indexToKey(container_ty.toIntern())) { + .struct_type => { + const loaded_struct = ip.loadStructType(container_ty.toIntern()); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + var field_it = loaded_struct.iterateRuntimeOrder(ip); + var before = true; + while (field_it.next()) |next_field_index| { + if (next_field_index == field_index) before = false; + if (before) continue; + const field_type = Type.fromInterned(loaded_struct.field_types.get(ip)[next_field_index]); + if (!field_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; + return .{ .field = if (loaded_struct.fieldName(ip, next_field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } + else + .{ .field = next_field_index } }; + } + return if (container_ty.hasRuntimeBitsIgnoreComptime(zcu)) .end else .begin; + }, + .@"packed" => return if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0) + .{ .byte_offset = @divExact(zcu.structPackedFieldBitOffset(loaded_struct, field_index) + + container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) } else - break :blk .begin; + .begin, } - - for (field_index..container_ty.structFieldCount(zcu)) |next_field_index_usize| { - const next_field_index: u32 = @intCast(next_field_index_usize); - if (container_ty.structFieldIsComptime(next_field_index, zcu)) continue; - const field_ty = container_ty.structFieldType(next_field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - - break :blk .{ .field = if (container_ty.isSimpleTuple(zcu)) - .{ .field = next_field_index } + }, + .anon_struct_type => |anon_struct_info| { + for (field_index..anon_struct_info.types.len) |next_field_index| { + if (anon_struct_info.values.get(ip)[next_field_index] != .none) continue; + const field_type = Type.fromInterned(anon_struct_info.types.get(ip)[next_field_index]); + if (!field_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; + return .{ .field = if (anon_struct_info.fieldName(ip, next_field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } else - .{ .identifier = ip.stringToSlice(container_ty.legacyStructFieldName(next_field_index, zcu)) } }; + .{ .field = next_field_index } }; } - break :blk if (container_ty.hasRuntimeBitsIgnoreComptime(zcu)) .end else .begin; + return if (container_ty.hasRuntimeBitsIgnoreComptime(zcu)) .end else .begin; }, - .Union => { - const union_obj = zcu.typeToUnion(container_ty).?; - return switch (union_obj.getLayout(ip)) { + .union_type => { + const loaded_union = ip.loadUnionType(container_ty.toIntern()); + switch (loaded_union.getLayout(ip)) { .auto, .@"extern" => { - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); + const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) - return if (container_ty.unionTagTypeSafety(zcu) != null and - !container_ty.unionHasAllZeroBitFieldTypes(zcu)) + return if (loaded_union.hasTag(ip) and !container_ty.unionHasAllZeroBitFieldTypes(zcu)) .{ .field = .{ .identifier = "payload" } } else .begin; - const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; - return .{ .field = if (container_ty.unionTagTypeSafety(zcu)) |_| + const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index]; + return .{ .field = if (loaded_union.hasTag(ip)) .{ .payload_identifier = ip.stringToSlice(field_name) } else .{ .identifier = ip.stringToSlice(field_name) } }; }, - .@"packed" => .begin, - }; + .@"packed" => return .begin, + } }, - .Pointer => switch (container_ty.ptrSize(zcu)) { + .ptr_type => |ptr_info| switch (ptr_info.flags.size) { + .One, .Many, .C => unreachable, .Slice => switch (field_index) { - 0 => .{ .field = .{ .identifier = "ptr" } }, - 1 => .{ .field = .{ .identifier = "len" } }, + 0 => return .{ .field = .{ .identifier = "ptr" } }, + 1 => return .{ .field = .{ .identifier = "len" } }, else => unreachable, }, - .One, .Many, .C => unreachable, }, else => unreachable, - }; + } } fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -5653,7 +5598,7 @@ fn fieldPtr( const field_ptr_ty = f.typeOfIndex(inst); // Ensure complete type definition is visible before accessing fields. - _ = try f.typeToIndex(container_ty, .complete); + _ = try f.ctypeFromType(container_ty, .complete); const writer = f.object.writer(); const local = try f.allocLocal(inst, field_ptr_ty); @@ -5708,109 +5653,109 @@ 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.typeToIndex(struct_ty, .complete); + _ = try f.ctypeFromType(struct_ty, .complete); + + const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) { + .struct_type => field_name: { + const loaded_struct = ip.loadStructType(struct_ty.toIntern()); + switch (loaded_struct.layout) { + .auto, .@"extern" => break :field_name if (loaded_struct.fieldName(ip, extra.field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } + else + .{ .field = extra.field_index }, + .@"packed" => { + const int_info = struct_ty.intInfo(zcu); - const field_name: CValue = switch (zcu.intern_pool.indexToKey(struct_ty.toIntern())) { - .struct_type => switch (struct_ty.containerLayout(zcu)) { - .auto, .@"extern" => if (struct_ty.isSimpleTuple(zcu)) - .{ .field = extra.field_index } - else - .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, zcu)) }, - .@"packed" => { - const struct_type = zcu.typeToStruct(struct_ty).?; - const int_info = struct_ty.intInfo(zcu); + const bit_offset_ty = try zcu.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1)); - const bit_offset_ty = try zcu.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1)); + const bit_offset = zcu.structPackedFieldBitOffset(loaded_struct, extra.field_index); - const bit_offset = zcu.structPackedFieldBitOffset(struct_type, extra.field_index); + const field_int_signedness = if (inst_ty.isAbiInt(zcu)) + inst_ty.intInfo(zcu).signedness + else + .unsigned; + const field_int_ty = try zcu.intType(field_int_signedness, @as(u16, @intCast(inst_ty.bitSize(zcu)))); - const field_int_signedness = if (inst_ty.isAbiInt(zcu)) - inst_ty.intInfo(zcu).signedness - else - .unsigned; - const field_int_ty = try zcu.intType(field_int_signedness, @as(u16, @intCast(inst_ty.bitSize(zcu)))); - - const temp_local = try f.allocLocal(inst, field_int_ty); - try f.writeCValue(writer, temp_local, .Other); - try writer.writeAll(" = zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); - try writer.writeAll("(("); - try f.renderType(writer, field_int_ty); - try writer.writeByte(')'); - const cant_cast = int_info.bits > 64; - if (cant_cast) { - if (field_int_ty.bitSize(zcu) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); - try writer.writeAll("zig_lo_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); - try writer.writeByte('('); - } - if (bit_offset > 0) { - try writer.writeAll("zig_shr_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); - try writer.writeByte('('); - } - try f.writeCValue(writer, struct_byval, .Other); - if (bit_offset > 0) { - try writer.writeAll(", "); - try f.object.dg.renderValue(writer, try zcu.intValue(bit_offset_ty, bit_offset), .FunctionArgument); + const temp_local = try f.allocLocal(inst, field_int_ty); + try f.writeCValue(writer, temp_local, .Other); + try writer.writeAll(" = zig_wrap_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); + try writer.writeAll("(("); + try f.renderType(writer, field_int_ty); try writer.writeByte(')'); - } - if (cant_cast) try writer.writeByte(')'); - try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits); - try writer.writeAll(");\n"); - if (inst_ty.eql(field_int_ty, f.object.dg.zcu)) return temp_local; + const cant_cast = int_info.bits > 64; + if (cant_cast) { + if (field_int_ty.bitSize(zcu) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); + try writer.writeAll("zig_lo_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); + try writer.writeByte('('); + } + if (bit_offset > 0) { + try writer.writeAll("zig_shr_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); + try writer.writeByte('('); + } + try f.writeCValue(writer, struct_byval, .Other); + if (bit_offset > 0) try writer.print(", {})", .{ + try f.fmtIntLiteral(try zcu.intValue(bit_offset_ty, bit_offset)), + }); + if (cant_cast) try writer.writeByte(')'); + try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits); + try writer.writeAll(");\n"); + if (inst_ty.eql(field_int_ty, f.object.dg.zcu)) return temp_local; - const local = try f.allocLocal(inst, inst_ty); - try writer.writeAll("memcpy("); - try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); - try writer.writeAll(", sizeof("); - try f.renderType(writer, inst_ty); - try writer.writeAll("));\n"); - try freeLocal(f, inst, temp_local.new_local, null); - return local; - }, + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("));\n"); + try freeLocal(f, inst, temp_local.new_local, null); + return local; + }, + } }, - - .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0) - .{ .field = extra.field_index } + .anon_struct_type => |anon_struct_info| if (anon_struct_info.fieldName(ip, extra.field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } else - .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, zcu)) }, - + .{ .field = extra.field_index }, .union_type => field_name: { - const union_obj = ip.loadUnionType(struct_ty.toIntern()); - if (union_obj.flagsPtr(ip).layout == .@"packed") { - const operand_lval = if (struct_byval == .constant) blk: { - const operand_local = try f.allocLocal(inst, struct_ty); - try f.writeCValue(writer, operand_local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, struct_byval, .Initializer); - try writer.writeAll(";\n"); - break :blk operand_local; - } else struct_byval; - - const local = try f.allocLocal(inst, inst_ty); - try writer.writeAll("memcpy(&"); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(", &"); - try f.writeCValue(writer, operand_lval, .Other); - try writer.writeAll(", sizeof("); - try f.renderType(writer, inst_ty); - try writer.writeAll("));\n"); - - if (struct_byval == .constant) { - try freeLocal(f, inst, operand_lval.new_local, null); - } + const loaded_union = ip.loadUnionType(struct_ty.toIntern()); + switch (loaded_union.getLayout(ip)) { + .auto, .@"extern" => { + const name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index]; + break :field_name if (loaded_union.hasTag(ip)) + .{ .payload_identifier = ip.stringToSlice(name) } + else + .{ .identifier = ip.stringToSlice(name) }; + }, + .@"packed" => { + const operand_lval = if (struct_byval == .constant) blk: { + const operand_local = try f.allocLocal(inst, struct_ty); + try f.writeCValue(writer, operand_local, .Other); + try writer.writeAll(" = "); + try f.writeCValue(writer, struct_byval, .Initializer); + try writer.writeAll(";\n"); + break :blk operand_local; + } else struct_byval; + + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("memcpy(&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", &"); + try f.writeCValue(writer, operand_lval, .Other); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("));\n"); + + if (struct_byval == .constant) { + try freeLocal(f, inst, operand_lval.new_local, null); + } - return local; - } else { - const name = union_obj.loadTagType(ip).names.get(ip)[extra.field_index]; - break :field_name if (union_obj.hasTag(ip)) .{ - .payload_identifier = ip.stringToSlice(name), - } else .{ - .identifier = ip.stringToSlice(name), - }; + return local; + }, } }, else => unreachable, @@ -6089,6 +6034,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try f.resolveInst(ty_op.operand); @@ -6107,18 +6053,18 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (operand == .undef) { try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Initializer); } else { - const ptr_cty = try f.typeToIndex(ptr_ty, .complete); - const ptr_child_cty = f.indexToCType(ptr_cty).cast(CType.Payload.Child).?.data; + const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete); + const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype; const elem_ty = array_ty.childType(zcu); - const elem_cty = try f.typeToIndex(elem_ty, .complete); - if (ptr_child_cty != elem_cty) { + const elem_ctype = try f.ctypeFromType(elem_ty, .complete); + if (!ptr_child_ctype.eql(elem_ctype)) { try writer.writeByte('('); - try f.renderCType(writer, ptr_cty); + try f.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } - const operand_cty = try f.typeToCType(operand_ty, .complete); - const operand_child_cty = operand_cty.cast(CType.Payload.Child).?.data; - if (f.indexToCType(operand_child_cty).tag() == .array) { + const operand_ctype = try f.ctypeFromType(operand_ty, .complete); + const operand_child_ctype = operand_ctype.info(ctype_pool).pointer.elem_ctype; + if (operand_child_ctype.info(ctype_pool) == .array) { try writer.writeByte('&'); try f.writeCValueDeref(writer, operand); try writer.print("[{}]", .{try f.fmtIntLiteral(try zcu.intValue(Type.usize, 0))}); @@ -6229,8 +6175,8 @@ fn airUnBuiltinCall( const operand_ty = f.typeOf(ty_op.operand); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_cty.tag() == .array; + const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); @@ -6267,8 +6213,8 @@ fn airBinBuiltinCall( const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const operand_ty = f.typeOf(bin_op.lhs); - const operand_cty = try f.typeToCType(operand_ty, .complete); - const is_big = operand_cty.tag() == .array; + const operand_ctype = try f.ctypeFromType(operand_ty, .complete); + const is_big = operand_ctype.info(&f.object.dg.ctype_pool) == .array; const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -6278,8 +6224,8 @@ fn airBinBuiltinCall( const inst_scalar_ty = inst_ty.scalarType(zcu); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_cty.tag() == .array; + const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); @@ -6328,8 +6274,8 @@ fn airCmpBuiltinCall( const operand_ty = f.typeOf(data.lhs); const scalar_ty = operand_ty.scalarType(zcu); - const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); - const ref_ret = inst_scalar_cty.tag() == .array; + const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); + const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); @@ -7112,9 +7058,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - switch (inst_ty.zigTypeTag(zcu)) { - .Array, .Vector => { - const a = try Assignment.init(f, inst_ty.childType(zcu)); + switch (ip.indexToKey(inst_ty.toIntern())) { + inline .array_type, .vector_type => |info, tag| { + const a = try Assignment.init(f, Type.fromInterned(info.child)); for (resolved_elements, 0..) |element, i| { try a.restart(f, writer); try f.writeCValue(writer, local, .Other); @@ -7123,94 +7069,112 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, element, .Other); try a.end(f, writer); } - if (inst_ty.sentinel(zcu)) |sentinel| { + if (tag == .array_type and info.sentinel != .none) { try a.restart(f, writer); try f.writeCValue(writer, local, .Other); - try writer.print("[{d}]", .{resolved_elements.len}); + try writer.print("[{d}]", .{info.len}); try a.assign(f, writer); - try f.object.dg.renderValue(writer, sentinel, .Other); + try f.object.dg.renderValue(writer, Value.fromInterned(info.sentinel), .Other); try a.end(f, writer); } }, - .Struct => switch (inst_ty.containerLayout(zcu)) { - .auto, .@"extern" => for (resolved_elements, 0..) |element, field_index| { - if (inst_ty.structFieldIsComptime(field_index, zcu)) continue; - const field_ty = inst_ty.structFieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - - const a = try Assignment.start(f, writer, field_ty); - try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(zcu)) - .{ .field = field_index } - else - .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(@intCast(field_index), zcu)) }); - try a.assign(f, writer); - try f.writeCValue(writer, element, .Other); - try a.end(f, writer); - }, - .@"packed" => { - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - const int_info = inst_ty.intInfo(zcu); + .struct_type => { + const loaded_struct = ip.loadStructType(inst_ty.toIntern()); + switch (loaded_struct.layout) { + .auto, .@"extern" => { + var field_it = loaded_struct.iterateRuntimeOrder(ip); + while (field_it.next()) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - const bit_offset_ty = try zcu.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1)); + const a = try Assignment.start(f, writer, field_ty); + try f.writeCValueMember(writer, local, if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } + else + .{ .field = field_index }); + try a.assign(f, writer); + try f.writeCValue(writer, resolved_elements[field_index], .Other); + try a.end(f, writer); + } + }, + .@"packed" => { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + const int_info = inst_ty.intInfo(zcu); - var bit_offset: u64 = 0; + const bit_offset_ty = try zcu.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1)); - var empty = true; - for (0..elements.len) |field_index| { - if (inst_ty.structFieldIsComptime(field_index, zcu)) continue; - const field_ty = inst_ty.structFieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + var bit_offset: u64 = 0; - if (!empty) { - try writer.writeAll("zig_or_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); - try writer.writeByte('('); + var empty = true; + for (0..elements.len) |field_index| { + if (inst_ty.structFieldIsComptime(field_index, zcu)) continue; + const field_ty = inst_ty.structFieldType(field_index, zcu); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + + if (!empty) { + try writer.writeAll("zig_or_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try writer.writeByte('('); + } + empty = false; } - empty = false; - } - empty = true; - for (resolved_elements, 0..) |element, field_index| { - if (inst_ty.structFieldIsComptime(field_index, zcu)) continue; - const field_ty = inst_ty.structFieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - - if (!empty) try writer.writeAll(", "); - // TODO: Skip this entire shift if val is 0? - try writer.writeAll("zig_shlw_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); - try writer.writeByte('('); + empty = true; + for (resolved_elements, 0..) |element, field_index| { + if (inst_ty.structFieldIsComptime(field_index, zcu)) continue; + const field_ty = inst_ty.structFieldType(field_index, zcu); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (inst_ty.isAbiInt(zcu) and (field_ty.isAbiInt(zcu) or field_ty.isPtrAtRuntime(zcu))) { - try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument); - } else { + if (!empty) try writer.writeAll(", "); + // TODO: Skip this entire shift if val is 0? + try writer.writeAll("zig_shlw_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); - try f.renderType(writer, inst_ty); - try writer.writeByte(')'); - if (field_ty.isPtrAtRuntime(zcu)) { + + if (inst_ty.isAbiInt(zcu) and (field_ty.isAbiInt(zcu) or field_ty.isPtrAtRuntime(zcu))) { + try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument); + } else { try writer.writeByte('('); - try f.renderType(writer, switch (int_info.signedness) { - .unsigned => Type.usize, - .signed => Type.isize, - }); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); + if (field_ty.isPtrAtRuntime(zcu)) { + try writer.writeByte('('); + try f.renderType(writer, switch (int_info.signedness) { + .unsigned => Type.usize, + .signed => Type.isize, + }); + try writer.writeByte(')'); + } + try f.writeCValue(writer, element, .Other); } - try f.writeCValue(writer, element, .Other); - } - - try writer.print(", {}", .{ - try f.fmtIntLiteral(try zcu.intValue(bit_offset_ty, bit_offset)), - }); - try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); - try writer.writeByte(')'); - if (!empty) try writer.writeByte(')'); - bit_offset += field_ty.bitSize(zcu); - empty = false; - } + try writer.print(", {}", .{ + try f.fmtIntLiteral(try zcu.intValue(bit_offset_ty, bit_offset)), + }); + try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); + try writer.writeByte(')'); + if (!empty) try writer.writeByte(')'); - try writer.writeAll(";\n"); - }, + bit_offset += field_ty.bitSize(zcu); + empty = false; + } + try writer.writeAll(";\n"); + }, + } + }, + .anon_struct_type => |anon_struct_info| for (0..anon_struct_info.types.len) |field_index| { + if (anon_struct_info.values.get(ip)[field_index] != .none) continue; + const field_ty = Type.fromInterned(anon_struct_info.types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + + const a = try Assignment.start(f, writer, field_ty); + try f.writeCValueMember(writer, local, if (anon_struct_info.fieldName(ip, field_index).unwrap()) |field_name| + .{ .identifier = ip.stringToSlice(field_name) } + else + .{ .field = field_index }); + try a.assign(f, writer); + try f.writeCValue(writer, resolved_elements[field_index], .Other); + try a.end(f, writer); }, else => unreachable, } @@ -7225,15 +7189,15 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = f.typeOfIndex(inst); - const union_obj = zcu.typeToUnion(union_ty).?; - const field_name = union_obj.loadTagType(ip).names.get(ip)[extra.field_index]; + const loaded_union = ip.loadUnionType(union_ty.toIntern()); + const field_name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index]; const payload_ty = f.typeOf(extra.init); const payload = try f.resolveInst(extra.init); try reap(f, inst, &.{extra.init}); const writer = f.object.writer(); const local = try f.allocLocal(inst, union_ty); - if (union_obj.getLayout(ip) == .@"packed") { + if (loaded_union.getLayout(ip) == .@"packed") { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, payload, .Initializer); @@ -7465,16 +7429,16 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const decl_index = f.object.dg.pass.decl; const decl = zcu.declPtr(decl_index); - const fn_cty = try f.typeToCType(decl.typeOf(zcu), .complete); - const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; + const function_ctype = try f.ctypeFromType(decl.typeOf(zcu), .complete); + const params_len = function_ctype.info(&f.object.dg.ctype_pool).function.param_ctypes.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) { + if (params_len > 0) { try writer.writeAll(", "); - try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + try f.writeCValue(writer, .{ .arg = params_len - 1 }, .FunctionArgument); } try writer.writeAll(");\n"); return local; @@ -7823,7 +7787,7 @@ const FormatIntLiteralContext = struct { dg: *DeclGen, int_info: InternPool.Key.IntType, kind: CType.Kind, - cty: CType, + ctype: CType, val: Value, }; fn formatIntLiteral( @@ -7834,6 +7798,7 @@ fn formatIntLiteral( ) @TypeOf(writer).Error!void { const zcu = data.dg.zcu; const target = &data.dg.mod.resolved_target.result; + const ctype_pool = &data.dg.ctype_pool; const ExpectedContents = struct { const base = 10; @@ -7867,7 +7832,7 @@ fn formatIntLiteral( } else data.val.toBigInt(&int_buf, zcu); assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits)); - const c_bits: usize = @intCast(data.cty.byteSize(data.dg.ctypes.set, data.dg.mod) * 8); + const c_bits: usize = @intCast(data.ctype.byteSize(ctype_pool, data.dg.mod) * 8); var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); @@ -7879,45 +7844,45 @@ fn formatIntLiteral( defer allocator.free(wrap.limbs); const c_limb_info: struct { - cty: CType, + ctype: CType, count: usize, endian: std.builtin.Endian, homogeneous: bool, - } = switch (data.cty.tag()) { - else => .{ - .cty = CType.initTag(.void), - .count = 1, - .endian = .little, - .homogeneous = true, - }, - .zig_u128, .zig_i128 => .{ - .cty = CType.initTag(.uint64_t), - .count = 2, - .endian = .big, - .homogeneous = false, - }, - .array => info: { - const array_data = data.cty.castTag(.array).?.data; - break :info .{ - .cty = data.dg.indexToCType(array_data.elem_type), - .count = @as(usize, @intCast(array_data.len)), - .endian = target.cpu.arch.endian(), + } = switch (data.ctype.info(ctype_pool)) { + .basic => |basic_info| switch (basic_info) { + else => .{ + .ctype = .{ .index = .void }, + .count = 1, + .endian = .little, .homogeneous = true, - }; + }, + .zig_u128, .zig_i128 => .{ + .ctype = .{ .index = .uint64_t }, + .count = 2, + .endian = .big, + .homogeneous = false, + }, + }, + .array => |array_info| .{ + .ctype = array_info.elem_ctype, + .count = @intCast(array_info.len), + .endian = target.cpu.arch.endian(), + .homogeneous = true, }, + else => unreachable, }; if (c_limb_info.count == 1) { if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) return writer.print("{s}_{s}", .{ - data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ + data.ctype.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, }), if (int.positive) "MAX" else "MIN", }); if (!int.positive) try writer.writeByte('-'); - try data.cty.renderLiteralPrefix(writer, data.kind); + try data.ctype.renderLiteralPrefix(writer, data.kind, ctype_pool); const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { 0 => .{ .base = 10 }, @@ -7948,7 +7913,7 @@ fn formatIntLiteral( defer allocator.free(string); try writer.writeAll(string); } else { - try data.cty.renderLiteralPrefix(writer, data.kind); + try data.ctype.renderLiteralPrefix(writer, data.kind, ctype_pool); wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits); @memset(wrap.limbs[wrap.len..], 0); wrap.len = wrap.limbs.len; @@ -7958,7 +7923,7 @@ fn formatIntLiteral( .signedness = undefined, .bits = @as(u16, @intCast(@divExact(c_bits, c_limb_info.count))), }; - var c_limb_cty: CType = undefined; + var c_limb_ctype: CType = undefined; var limb_offset: usize = 0; const most_significant_limb_i = wrap.len - limbs_per_c_limb; @@ -7979,7 +7944,7 @@ fn formatIntLiteral( { // most significant limb is actually signed c_limb_int_info.signedness = .signed; - c_limb_cty = c_limb_info.cty.toSigned(); + c_limb_ctype = c_limb_info.ctype.toSigned(); c_limb_mut.positive = wrap.positive; c_limb_mut.truncate( @@ -7989,7 +7954,7 @@ fn formatIntLiteral( ); } else { c_limb_int_info.signedness = .unsigned; - c_limb_cty = c_limb_info.cty; + c_limb_ctype = c_limb_info.ctype; } if (limb_offset > 0) try writer.writeAll(", "); @@ -7997,12 +7962,12 @@ fn formatIntLiteral( .dg = data.dg, .int_info = c_limb_int_info, .kind = data.kind, - .cty = c_limb_cty, + .ctype = c_limb_ctype, .val = try zcu.intValue_big(Type.comptime_int, c_limb_mut.toConst()), }, fmt, options, writer); } } - try data.cty.renderLiteralSuffix(writer); + try data.ctype.renderLiteralSuffix(writer, ctype_pool); } const Materialize = struct { @@ -8045,10 +8010,10 @@ const Materialize = struct { }; const Assignment = struct { - cty: CType.Index, + ctype: CType, pub fn init(f: *Function, ty: Type) !Assignment { - return .{ .cty = try f.typeToIndex(ty, .complete) }; + return .{ .ctype = try f.ctypeFromType(ty, .complete) }; } pub fn start(f: *Function, writer: anytype, ty: Type) !Assignment { @@ -8076,7 +8041,7 @@ const Assignment = struct { .assign => {}, .memcpy => { try writer.writeAll(", sizeof("); - try f.renderCType(writer, self.cty); + try f.renderCType(writer, self.ctype); try writer.writeAll("))"); }, } @@ -8084,7 +8049,7 @@ const Assignment = struct { } fn strategy(self: Assignment, f: *Function) enum { assign, memcpy } { - return switch (f.indexToCType(self.cty).tag()) { + return switch (self.ctype.info(&f.object.dg.ctype_pool)) { else => .assign, .array, .vector => .memcpy, }; @@ -8129,28 +8094,6 @@ const Vectorize = struct { } }; -fn lowerFnRetTy(ret_ty: Type, zcu: *Zcu) !Type { - if (ret_ty.toIntern() == .noreturn_type) return Type.noreturn; - - if (lowersToArray(ret_ty, zcu)) { - const gpa = zcu.gpa; - const ip = &zcu.intern_pool; - const names = [1]InternPool.NullTerminatedString{ - try ip.getOrPutString(gpa, "array"), - }; - const types = [1]InternPool.Index{ret_ty.toIntern()}; - const values = [1]InternPool.Index{.none}; - const interned = try ip.getAnonStructType(gpa, .{ - .names = &names, - .types = &types, - .values = &values, - }); - return Type.fromInterned(interned); - } - - return if (ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) ret_ty else Type.void; -} - fn lowersToArray(ty: Type, zcu: *Zcu) bool { return switch (ty.zigTypeTag(zcu)) { .Array, .Vector => return true, |
