diff options
| author | Jacob Young <jacobly0@users.noreply.github.com> | 2023-02-17 05:00:17 -0500 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2023-02-20 23:48:36 -0500 |
| commit | 7768d2024bfbb4aad143fb8a4143e324445bfd93 (patch) | |
| tree | 4e664cbecf7a49e2feabce10e7e92c5166ea91f9 /src/codegen/c.zig | |
| parent | d8fada6b6325e07015fddd68bb4c6369a66f23f3 (diff) | |
| download | zig-7768d2024bfbb4aad143fb8a4143e324445bfd93.tar.gz zig-7768d2024bfbb4aad143fb8a4143e324445bfd93.zip | |
CBE: use CType for type rendering
Diffstat (limited to 'src/codegen/c.zig')
| -rw-r--r-- | src/codegen/c.zig | 574 |
1 files changed, 287 insertions, 287 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 872d5fd344..aa540d6984 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1954,291 +1954,311 @@ pub const DeclGen = struct { return name; } + fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { + return dg.ctypes.indexToCType(idx); + } fn typeToCType(dg: *DeclGen, ty: Type) !CType { return dg.ctypes.typeToCType(dg.gpa, ty, dg.module); } - - /// Renders a type as a single identifier, generating intermediate typedefs - /// if necessary. - /// - /// This is guaranteed to be valid in both typedefs and declarations/definitions. - /// - /// There are three type formats in total that we support rendering: - /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | - /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | - /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | - /// - fn renderType( + fn typeToIndex(dg: *DeclGen, ty: Type) !CType.Index { + return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module); + } + + const CTypeFix = enum { prefix, suffix }; + const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); + const CTypeRenderTrailing = enum { + no_space, + maybe_space, + + pub fn format( + self: @This(), + comptime fmt: []const u8, + _: std.fmt.FormatOptions, + w: anytype, + ) @TypeOf(w).Error!void { + if (fmt.len != 0) + @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ + @typeName(@This()) ++ "'"); + comptime assert(fmt.len == 0); + switch (self) { + .no_space => {}, + .maybe_space => try w.writeByte(' '), + } + } + }; + fn renderTypePrefix( dg: *DeclGen, w: anytype, - t: Type, - kind: TypedefKind, - ) error{ OutOfMemory, AnalysisFail }!void { - _ = try dg.typeToCType(t); - - const target = dg.module.getTarget(); - - switch (t.zigTypeTag()) { - .Void => try w.writeAll("void"), - .Bool => try w.writeAll("bool"), - .NoReturn, .Float => { - try w.writeAll("zig_"); - try t.print(w, dg.module); - }, - .Int => { - if (t.isNamedInt()) { - try w.writeAll("zig_"); - try t.print(w, dg.module); - } else { - return renderTypeUnnamed(dg, w, t, kind); - } - }, - .ErrorSet => { - return renderTypeUnnamed(dg, w, t, kind); + idx: CType.Index, + parent_fix: CTypeFix, + qualifiers: CQualifiers, + ) @TypeOf(w).Error!CTypeRenderTrailing { + var trailing = CTypeRenderTrailing.maybe_space; + + const cty = dg.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, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => |tag| try w.writeAll(@tagName(tag)), + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => |tag| { + const child_idx = cty.cast(CType.Payload.Child).?.data; + try w.print("{}*", .{try dg.renderTypePrefix(w, child_idx, .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, + }, + }))}); + trailing = .no_space; }, - .Pointer => { - const ptr_info = t.ptrInfo().data; - if (ptr_info.size == .Slice) { - var slice_pl = Type.Payload.ElemType{ - .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, - .data = ptr_info.pointee_type, - }; - const slice_ty = Type.initPayload(&slice_pl.base); - - const name = dg.getTypedefName(slice_ty) orelse - try dg.renderSliceTypedef(slice_ty); - return w.writeAll(name); - } - - if (ptr_info.pointee_type.zigTypeTag() == .Fn) { - const name = dg.getTypedefName(ptr_info.pointee_type) orelse - try dg.renderPtrToFnTypedef(ptr_info.pointee_type); - - return w.writeAll(name); + .array, + .vector, + => { + const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; + const child_trailing = try dg.renderTypePrefix(w, child_idx, .suffix, qualifiers); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; + }, + .suffix => return child_trailing, } - - if (ptr_info.host_size != 0) { - var host_pl = Type.Payload.Bits{ - .base = .{ .tag = .int_unsigned }, - .data = ptr_info.host_size * 8, - }; - const host_ty = Type.initPayload(&host_pl.base); - - try dg.renderType(w, host_ty, .Forward); - } else if (t.isCPtr() and ptr_info.pointee_type.eql(Type.u8, dg.module) and - (dg.decl.val.tag() == .extern_fn or - std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) - { - // This is a hack, since the c compiler expects a lot of external - // library functions to have char pointers in their signatures, but - // u8 and i8 produce unsigned char and signed char respectively, - // which in C are (not very usefully) different than char. - try w.writeAll("char"); - } else try dg.renderType(w, switch (ptr_info.pointee_type.tag()) { - .anyopaque => Type.void, - else => ptr_info.pointee_type, - }, .Forward); - if (t.isConstPtr()) try w.writeAll(" const"); - if (t.isVolatilePtr()) try w.writeAll(" volatile"); - return w.writeAll(" *"); - }, - .Array, .Vector => { - var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ - .len = t.arrayLenIncludingSentinel(), - .elem_type = t.childType(), - } }; - const array_ty = Type.initPayload(&array_pl.base); - - const name = dg.getTypedefName(array_ty) orelse - try dg.renderArrayTypedef(array_ty); - - return w.writeAll(name); }, - .Optional => { - var opt_buf: Type.Payload.ElemType = undefined; - const child_ty = t.optionalChild(&opt_buf); - - if (!child_ty.hasRuntimeBitsIgnoreComptime()) - return dg.renderType(w, Type.bool, kind); - - if (t.optionalReprIsPayload()) - return dg.renderType(w, child_ty, kind); - switch (kind) { - .Complete => { - const name = dg.getTypedefName(t) orelse - try dg.renderOptionalTypedef(t); - - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = t, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); + .fwd_struct, + .fwd_union, + .anon_struct, + .packed_anon_struct, + => |tag| try w.print("{s} {}__{d}", .{ + switch (tag) { + .fwd_struct, + .anon_struct, + .packed_anon_struct, + => "struct", + .fwd_union => "union", + else => unreachable, + }, + fmtIdent(switch (tag) { + .fwd_struct, + .fwd_union, + => mem.span(dg.module.declPtr(cty.cast(CType.Payload.FwdDecl).?.data).name), + .anon_struct, + .packed_anon_struct, + => "anon", + else => unreachable, + }), + idx, + }), - try w.writeAll(name); + .@"struct", + .packed_struct, + .@"union", + .packed_union, + => return dg.renderTypePrefix( + w, + cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, + parent_fix, + qualifiers, + ), + + .function, + .varargs_function, + => { + const child_trailing = try dg.renderTypePrefix( + w, + cty.cast(CType.Payload.Function).?.data.return_type, + .suffix, + CQualifiers.initEmpty(), + ); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; }, + .suffix => return child_trailing, } }, - .ErrorUnion => { - const payload_ty = t.errorUnionPayload(); + } - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) - return dg.renderType(w, Type.anyerror, kind); + var qualifier_it = qualifiers.iterator(); + while (qualifier_it.next()) |qualifier| { + try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); + trailing = .maybe_space; + } - var error_union_pl = Type.Payload.ErrorUnion{ - .data = .{ .error_set = Type.anyerror, .payload = payload_ty }, - }; - const error_union_ty = Type.initPayload(&error_union_pl.base); + return trailing; + } + fn renderTypeSuffix( + dg: *DeclGen, + w: anytype, + idx: CType.Index, + parent_fix: CTypeFix, + ) @TypeOf(w).Error!void { + const cty = dg.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, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => {}, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Child).?.data, .prefix), + + .array, + .vector, + => { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } - switch (kind) { - .Complete => { - const name = dg.getTypedefName(error_union_ty) orelse - try dg.renderErrorUnionTypedef(error_union_ty); + try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); + try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix); + }, - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = error_union_ty, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); + .fwd_struct, + .fwd_union, + .anon_struct, + .packed_anon_struct, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => {}, + + .function, + .varargs_function, + => |tag| { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); + const data = cty.cast(CType.Payload.Function).?.data; - try w.writeAll(name); - }, + try w.writeByte('('); + var need_comma = false; + for (data.param_types) |param_type| { + if (need_comma) try w.writeAll(", "); + need_comma = true; + _ = try dg.renderTypePrefix(w, param_type, .suffix, CQualifiers.initEmpty()); + try dg.renderTypeSuffix(w, param_type, .suffix); } - }, - .Struct, .Union => |tag| if (t.containerLayout() == .Packed) { - if (t.castTag(.@"struct")) |struct_obj| { - try dg.renderType(w, struct_obj.data.backing_int_ty, kind); - } else { - var buf: Type.Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = @intCast(u16, t.bitSize(target)), - }; - try dg.renderType(w, Type.initPayload(&buf.base), kind); + switch (tag) { + .function => {}, + .varargs_function => { + if (need_comma) try w.writeAll(", "); + need_comma = true; + try w.writeAll("..."); + }, + else => unreachable, } - } else if (t.isSimpleTupleOrAnonStruct()) { - const ExpectedContents = struct { types: [8]Type, values: [8]Value }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa); - const allocator = stack.get(); - - var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){}; - defer tuple_storage.deinit(allocator); - try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount()); - - const fields = t.tupleFields(); - for (fields.values, 0..) |value, index| - if (value.tag() == .unreachable_value) - tuple_storage.appendAssumeCapacity(.{ - .type = fields.types[index], - .value = value, - }); - - const tuple_slice = tuple_storage.slice(); - var tuple_pl = Type.Payload.Tuple{ .data = .{ - .types = tuple_slice.items(.type), - .values = tuple_slice.items(.value), - } }; - const tuple_ty = Type.initPayload(&tuple_pl.base); - - const name = dg.getTypedefName(tuple_ty) orelse - try dg.renderTupleTypedef(tuple_ty); - - try w.writeAll(name); - } else switch (kind) { - .Complete => { - const name = dg.getTypedefName(t) orelse switch (tag) { - .Struct => try dg.renderStructTypedef(t), - .Union => try dg.renderUnionTypedef(t), - else => unreachable, - }; - - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = t, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - try w.writeAll(name); - }, - }, - .Enum => { - // For enums, we simply use the integer tag type. - var int_tag_buf: Type.Payload.Bits = undefined; - const int_tag_ty = t.intTagType(&int_tag_buf); + if (!need_comma) try w.writeAll("void"); + try w.writeByte(')'); - try dg.renderType(w, int_tag_ty, kind); + try dg.renderTypeSuffix(w, data.return_type, .suffix); }, - .Opaque => switch (t.tag()) { - .@"opaque" => { - const name = dg.getTypedefName(t) orelse - try dg.renderOpaqueTypedef(t); - - try w.writeAll(name); - }, - else => unreachable, - }, - - .Frame, - .AnyFrame, - => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ - @tagName(tag), - }), - - .Fn => unreachable, // This is a function body, not a function pointer. - - .Null, - .Undefined, - .EnumLiteral, - .ComptimeFloat, - .ComptimeInt, - .Type, - => unreachable, // must be const or comptime } } - fn renderTypeUnnamed( + /// Renders a type as a single identifier, generating intermediate typedefs + /// if necessary. + /// + /// This is guaranteed to be valid in both typedefs and declarations/definitions. + /// + /// There are three type formats in total that we support rendering: + /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | + /// |---------------------|-----------------|---------------------| + /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | + /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | + /// + fn renderType( dg: *DeclGen, w: anytype, t: Type, - kind: TypedefKind, + _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - const target = dg.module.getTarget(); - const int_info = t.intInfo(target); - if (toCIntBits(int_info.bits)) |c_bits| - return w.print("zig_{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }) - else if (loweredArrayInfo(t, target)) |array_info| { - assert(array_info.sentinel == null); - var array_pl = Type.Payload.Array{ - .base = .{ .tag = .array }, - .data = .{ .len = array_info.len, .elem_type = array_info.elem_type }, - }; - const array_ty = Type.initPayload(&array_pl.base); - - return dg.renderType(w, array_ty, kind); - } else return dg.fail("C backend: Unable to lower unnamed integer type {}", .{ - t.fmt(dg.module), - }); + const idx = try dg.typeToIndex(t); + _ = try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.initEmpty()); + try dg.renderTypeSuffix(w, idx, .suffix); } const IntCastContext = union(enum) { @@ -2348,10 +2368,10 @@ pub const DeclGen = struct { /// |---------------------|-----------------|---------------------| /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { - return renderTypeAndName(dg, w, ty, .{ .bytes = "" }, .Mut, 0, .Complete); + try dg.renderType(w, ty, undefined); } /// Renders a type and name in field declaration/definition format. @@ -2361,7 +2381,7 @@ pub const DeclGen = struct { /// |---------------------|-----------------|---------------------| /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderTypeAndName( dg: *DeclGen, @@ -2370,46 +2390,26 @@ pub const DeclGen = struct { name: CValue, mutability: Mutability, alignment: u32, - kind: TypedefKind, + _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - var suffix = std.ArrayList(u8).init(dg.gpa); - defer suffix.deinit(); - const suffix_writer = suffix.writer(); - - // Any top-level array types are rendered here as a suffix, which - // avoids creating typedefs for every array type - const target = dg.module.getTarget(); - var render_ty = ty; - var depth: u32 = 0; - while (loweredArrayInfo(render_ty, target)) |array_info| { - const c_len = array_info.len + @boolToInt(array_info.sentinel != null); - var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; - const c_len_val = Value.initPayload(&c_len_pl.base); - - try suffix_writer.writeByte('['); - if (mutability == .ConstArgument and depth == 0) try suffix_writer.writeAll("zig_const_arr "); - try suffix.writer().print("{}]", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); - render_ty = array_info.elem_type; - depth += 1; - } - if (alignment != 0) { - const abi_alignment = ty.abiAlignment(target); + const abi_alignment = ty.abiAlignment(dg.module.getTarget()); if (alignment < abi_alignment) { try w.print("zig_under_align({}) ", .{alignment}); } else if (alignment > abi_alignment) { try w.print("zig_align({}) ", .{alignment}); } } - try dg.renderType(w, render_ty, kind); - const const_prefix = switch (mutability) { - .Const, .ConstArgument => "const ", - .Mut => "", - }; - try w.print(" {s}", .{const_prefix}); + const idx = try dg.typeToIndex(ty); + try w.print("{}", .{try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.init(.{ + .@"const" = switch (mutability) { + .Const, .ConstArgument => true, + .Mut => false, + }, + }))}); try dg.writeCValue(w, name); - try w.writeAll(suffix.items); + try dg.renderTypeSuffix(w, idx, .suffix); } fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { |
