aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-02-22 11:06:13 -0500
committerGitHub <noreply@github.com>2023-02-22 11:06:13 -0500
commitc9e02d3e69f909a6eb215286c6109f2b3f1e68a2 (patch)
tree2cd77390db945adb9b54ca9cdb5356bc66bb381d /src/codegen/c.zig
parent436e99d13ba188412b8a431b69cc9ff29c6bec4a (diff)
parent248fb40dcc5eb50cf19e711197c5d1b210abf1b3 (diff)
downloadzig-c9e02d3e69f909a6eb215286c6109f2b3f1e68a2.tar.gz
zig-c9e02d3e69f909a6eb215286c6109f2b3f1e68a2.zip
Merge pull request #14691 from jacobly0/ctype
Diffstat (limited to 'src/codegen/c.zig')
-rw-r--r--src/codegen/c.zig1993
1 files changed, 890 insertions, 1103 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 399549da3a..bfc6e6451b 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -23,12 +23,15 @@ const libcFloatSuffix = target_util.libcFloatSuffix;
const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev;
const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev;
-const Mutability = enum { Const, ConstArgument, Mut };
+const Mutability = enum { @"const", mut };
const BigIntLimb = std.math.big.Limb;
const BigInt = std.math.big.int;
+pub const CType = @import("c/type.zig").CType;
+
pub const CValue = union(enum) {
none: void,
+ new_local: LocalIndex,
local: LocalIndex,
/// Address of a local.
local_ref: LocalIndex,
@@ -36,6 +39,8 @@ pub const CValue = union(enum) {
constant: Air.Inst.Ref,
/// Index into the parameters
arg: usize,
+ /// The payload field of a parameter
+ arg_array: usize,
/// Index into a tuple's fields
field: usize,
/// By-value
@@ -61,12 +66,17 @@ const TypedefKind = enum {
};
pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);
-pub const TypedefMap = std.ArrayHashMap(
- Type,
- struct { name: []const u8, rendered: []u8 },
- Type.HashContext32,
- true,
-);
+
+pub const LazyFnKey = union(enum) {
+ tag_name: Decl.Index,
+};
+pub const LazyFnValue = struct {
+ fn_name: []const u8,
+ data: union {
+ tag_name: Type,
+ },
+};
+pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue);
const LoopDepth = u16;
const Local = struct {
@@ -81,11 +91,6 @@ const LocalsList = std.ArrayListUnmanaged(LocalIndex);
const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true);
const LocalsStack = std.ArrayListUnmanaged(LocalsMap);
-const FormatTypeAsCIdentContext = struct {
- ty: Type,
- mod: *Module,
-};
-
const ValueRenderLocation = enum {
FunctionArgument,
Initializer,
@@ -106,26 +111,6 @@ const BuiltinInfo = enum {
Bits,
};
-fn formatTypeAsCIdentifier(
- data: FormatTypeAsCIdentContext,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
-) !void {
- var stack = std.heap.stackFallback(128, data.mod.gpa);
- const allocator = stack.get();
- const str = std.fmt.allocPrint(allocator, "{}", .{data.ty.fmt(data.mod)}) catch "";
- defer allocator.free(str);
- return formatIdent(str, fmt, options, writer);
-}
-
-pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) {
- return .{ .data = .{
- .ty = ty,
- .mod = mod,
- } };
-}
-
const reserved_idents = std.ComptimeStringMap(void, .{
// C language
.{ "alignas", {
@@ -281,6 +266,7 @@ pub const Function = struct {
next_arg_index: usize = 0,
next_block_index: usize = 0,
object: Object,
+ lazy_fns: LazyFnMap,
func: *Module.Fn,
/// All the locals, to be emitted at the top of the function.
locals: std.ArrayListUnmanaged(Local) = .{},
@@ -315,9 +301,9 @@ pub const Function = struct {
const alignment = 0;
const decl_c_value = try f.allocLocalValue(ty, alignment);
const gpa = f.object.dg.gpa;
- try f.allocs.put(gpa, decl_c_value.local, true);
+ try f.allocs.put(gpa, decl_c_value.new_local, true);
try writer.writeAll("static ");
- try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, alignment, .Complete);
+ try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .@"const", alignment, .Complete);
try writer.writeAll(" = ");
try f.object.dg.renderValue(writer, ty, val, .StaticInitializer);
try writer.writeAll(";\n ");
@@ -347,12 +333,12 @@ pub const Function = struct {
.alignment = alignment,
.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1),
});
- return CValue{ .local = @intCast(LocalIndex, f.locals.items.len - 1) };
+ return CValue{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) };
}
fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue {
- const result = try f.allocAlignedLocal(ty, .Mut, 0);
- log.debug("%{d}: allocating t{d}", .{ inst, result.local });
+ const result = try f.allocAlignedLocal(ty, .mut, 0);
+ log.debug("%{d}: allocating t{d}", .{ inst, result.new_local });
return result;
}
@@ -366,7 +352,7 @@ pub const Function = struct {
if (local.alignment >= alignment) {
local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1);
_ = locals_list.swapRemove(i);
- return CValue{ .local = local_index };
+ return CValue{ .new_local = local_index };
}
}
}
@@ -446,7 +432,31 @@ pub const Function = struct {
return f.object.dg.fmtIntLiteral(ty, val);
}
- pub fn deinit(f: *Function, gpa: mem.Allocator) void {
+ fn getTagNameFn(f: *Function, enum_ty: Type) ![]const u8 {
+ const gpa = f.object.dg.gpa;
+ const owner_decl = enum_ty.getOwnerDecl();
+
+ const gop = try f.lazy_fns.getOrPut(gpa, .{ .tag_name = owner_decl });
+ 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();
+
+ gop.value_ptr.* = .{
+ .fn_name = try std.fmt.allocPrint(arena, "zig_tagName_{}__{d}", .{
+ fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)),
+ @enumToInt(owner_decl),
+ }),
+ .data = .{ .tag_name = try enum_ty.copy(arena) },
+ };
+ }
+ return gop.value_ptr.fn_name;
+ }
+
+ pub fn deinit(f: *Function) void {
+ const gpa = f.object.dg.gpa;
f.allocs.deinit(gpa);
f.locals.deinit(gpa);
for (f.free_locals_stack.items) |*free_locals| {
@@ -455,11 +465,9 @@ pub const Function = struct {
f.free_locals_stack.deinit(gpa);
f.blocks.deinit(gpa);
f.value_map.deinit();
+ f.lazy_fns.deinit(gpa);
f.object.code.deinit();
- for (f.object.dg.typedefs.values()) |typedef| {
- gpa.free(typedef.rendered);
- }
- f.object.dg.typedefs.deinit();
+ f.object.dg.ctypes.deinit(gpa);
f.object.dg.fwd_decl.deinit();
f.arena.deinit();
}
@@ -483,30 +491,20 @@ pub const Object = struct {
pub const DeclGen = struct {
gpa: std.mem.Allocator,
module: *Module,
- decl: *Decl,
- decl_index: Decl.Index,
+ decl: ?*Decl,
+ decl_index: Decl.OptionalIndex,
fwd_decl: std.ArrayList(u8),
error_msg: ?*Module.ErrorMsg,
- /// The key of this map is Type which has references to typedefs_arena.
- typedefs: TypedefMap,
- typedefs_arena: std.mem.Allocator,
+ ctypes: CType.Store,
fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
@setCold(true);
const src = LazySrcLoc.nodeOffset(0);
- const src_loc = src.toSrcLoc(dg.decl);
+ const src_loc = src.toSrcLoc(dg.decl.?);
dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args);
return error.AnalysisFail;
}
- fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 {
- if (dg.typedefs.get(t)) |typedef| {
- return typedef.name;
- } else {
- return null;
- }
- }
-
fn renderDeclValue(
dg: *DeclGen,
writer: anytype,
@@ -747,7 +745,7 @@ pub const DeclGen = struct {
try writer.writeAll("zig_cast_");
try dg.renderTypeForBuiltinFnName(writer, ty);
- try writer.writeAll(" zig_as_");
+ try writer.writeAll(" zig_make_");
try dg.renderTypeForBuiltinFnName(writer, ty);
try writer.writeByte('(');
switch (bits) {
@@ -821,7 +819,7 @@ pub const DeclGen = struct {
empty = false;
}
- if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)});
+
return writer.writeByte('}');
},
.Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}),
@@ -957,7 +955,7 @@ pub const DeclGen = struct {
try writer.writeByte(' ');
var empty = true;
if (std.math.isFinite(f128_val)) {
- try writer.writeAll("zig_as_");
+ try writer.writeAll("zig_make_");
try dg.renderTypeForBuiltinFnName(writer, ty);
try writer.writeByte('(');
switch (bits) {
@@ -992,7 +990,7 @@ pub const DeclGen = struct {
// return dg.fail("Only quiet nans are supported in global variable initializers", .{});
}
- try writer.writeAll("zig_as_special_");
+ try writer.writeAll("zig_make_special_");
if (location == .StaticInitializer) try writer.writeAll("constant_");
try dg.renderTypeForBuiltinFnName(writer, ty);
try writer.writeByte('(');
@@ -1292,7 +1290,6 @@ pub const DeclGen = struct {
empty = false;
}
- if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)});
try writer.writeByte('}');
},
.Packed => {
@@ -1309,7 +1306,7 @@ pub const DeclGen = struct {
const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
var eff_num_fields: usize = 0;
- for (field_vals, 0..) |_, index| {
+ for (0..field_vals.len) |index| {
const field_ty = ty.structFieldType(index);
if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -1413,6 +1410,7 @@ pub const DeclGen = struct {
return;
}
+ var has_payload_init = false;
try writer.writeByte('{');
if (ty.unionTagTypeSafety()) |tag_ty| {
const layout = ty.unionGetLayout(target);
@@ -1421,7 +1419,10 @@ pub const DeclGen = struct {
try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type);
try writer.writeAll(", ");
}
- try writer.writeAll(".payload = {");
+ if (!ty.unionHasAllZeroBitFieldTypes()) {
+ try writer.writeAll(".payload = {");
+ has_payload_init = true;
+ }
}
var it = ty.unionFields().iterator();
@@ -1433,8 +1434,8 @@ pub const DeclGen = struct {
try writer.print(".{ } = ", .{fmtIdent(field.key_ptr.*)});
try dg.renderValue(writer, field.value_ptr.ty, Value.undef, initializer_type);
break;
- } else try writer.writeAll(".empty_union = 0");
- if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
+ }
+ if (has_payload_init) try writer.writeByte('}');
try writer.writeByte('}');
},
@@ -1457,496 +1458,62 @@ pub const DeclGen = struct {
}
fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void {
- const fn_info = dg.decl.ty.fnInfo();
+ const store = &dg.ctypes.set;
+ const module = dg.module;
+
+ const fn_ty = dg.decl.?.ty;
+ const fn_cty_idx = try dg.typeToIndex(fn_ty, switch (kind) {
+ .Forward => .forward,
+ .Complete => .complete,
+ });
+
+ const fn_info = fn_ty.fnInfo();
if (fn_info.cc == .Naked) {
switch (kind) {
.Forward => try w.writeAll("zig_naked_decl "),
.Complete => try w.writeAll("zig_naked "),
}
}
- if (dg.decl.val.castTag(.function)) |func_payload|
+ if (dg.decl.?.val.castTag(.function)) |func_payload|
if (func_payload.data.is_cold) try w.writeAll("zig_cold ");
-
- const target = dg.module.getTarget();
- var ret_buf: LowerFnRetTyBuffer = undefined;
- const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target);
-
- try dg.renderType(w, ret_ty, kind);
- try w.writeByte(' ');
+ if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn ");
+
+ const trailing = try renderTypePrefix(
+ dg.decl_index,
+ store.*,
+ module,
+ w,
+ fn_cty_idx,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ try w.print("{}", .{trailing});
if (toCallingConvention(fn_info.cc)) |call_conv| {
try w.print("zig_callconv({s}) ", .{call_conv});
}
- if (fn_info.alignment > 0 and kind == .Complete) try w.print(" zig_align_fn({})", .{fn_info.alignment});
-
- try dg.renderDeclName(w, dg.decl_index, export_index);
- try w.writeByte('(');
-
- var index: usize = 0;
- for (fn_info.param_types) |param_type| {
- if (!param_type.hasRuntimeBitsIgnoreComptime()) continue;
- if (index > 0) try w.writeAll(", ");
- const name = CValue{ .arg = index };
- try dg.renderTypeAndName(w, param_type, name, .ConstArgument, 0, kind);
- index += 1;
+ if (fn_info.alignment > 0 and kind == .Complete) {
+ try w.print(" zig_align_fn({})", .{fn_info.alignment});
}
- if (fn_info.is_var_args) {
- if (index > 0) try w.writeAll(", ");
- try w.writeAll("...");
- } else if (index == 0) {
- try dg.renderType(w, Type.void, kind);
- }
- try w.writeByte(')');
- if (fn_info.alignment > 0 and kind == .Forward) try w.print(" zig_align_fn({})", .{fn_info.alignment});
- }
-
- fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
+ try dg.renderDeclName(w, dg.decl_index.unwrap().?, export_index);
- const fn_info = t.fnInfo();
+ try renderTypeSuffix(dg.decl_index, store.*, module, w, fn_cty_idx, .suffix);
- const target = dg.module.getTarget();
- var ret_buf: LowerFnRetTyBuffer = undefined;
- const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target);
-
- try bw.writeAll("typedef ");
- try dg.renderType(bw, ret_ty, .Forward);
- try bw.writeAll(" (*");
- const name_begin = buffer.items.len;
- try bw.print("zig_F_{}", .{typeToCIdentifier(t, dg.module)});
- const name_end = buffer.items.len;
- try bw.writeAll(")(");
-
- const param_len = fn_info.param_types.len;
-
- var params_written: usize = 0;
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- const param_ty = fn_info.param_types[index];
- if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
- if (params_written > 0) {
- try bw.writeAll(", ");
- }
- try dg.renderTypeAndName(bw, param_ty, .{ .bytes = "" }, .Mut, 0, .Forward);
- params_written += 1;
+ if (fn_info.alignment > 0 and kind == .Forward) {
+ try w.print(" zig_align_fn({})", .{fn_info.alignment});
}
-
- if (fn_info.is_var_args) {
- if (params_written != 0) try bw.writeAll(", ");
- try bw.writeAll("...");
- } else if (params_written == 0) {
- try dg.renderType(bw, Type.void, .Forward);
- }
- try bw.writeAll(");\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
}
- fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- std.debug.assert(t.sentinel() == null); // expected canonical type
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- var ptr_ty_buf: Type.SlicePtrFieldTypeBuffer = undefined;
- const ptr_ty = t.slicePtrFieldType(&ptr_ty_buf);
- const ptr_name = CValue{ .identifier = "ptr" };
- const len_ty = Type.usize;
- const len_name = CValue{ .identifier = "len" };
-
- try bw.writeAll("typedef struct {\n ");
- try dg.renderTypeAndName(bw, ptr_ty, ptr_name, .Mut, 0, .Complete);
- try bw.writeAll(";\n ");
- try dg.renderTypeAndName(bw, len_ty, len_name, .Mut, 0, .Complete);
-
- try bw.writeAll(";\n} ");
- const name_begin = buffer.items.len;
- try bw.print("zig_{c}_{}", .{
- @as(u8, if (t.isConstPtr()) 'L' else 'M'),
- typeToCIdentifier(t.childType(), dg.module),
- });
- const name_end = buffer.items.len;
- try bw.writeAll(";\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
+ fn indexToCType(dg: *DeclGen, idx: CType.Index) CType {
+ return dg.ctypes.indexToCType(idx);
}
-
- fn renderFwdTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- // The forward declaration for T is stored with a key of *const T.
- const child_ty = t.childType();
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- const tag = switch (child_ty.zigTypeTag()) {
- .Struct, .ErrorUnion, .Optional => "struct",
- .Union => if (child_ty.unionTagTypeSafety()) |_| "struct" else "union",
- else => unreachable,
- };
- try bw.writeAll("typedef ");
- try bw.writeAll(tag);
- const name_begin = buffer.items.len + " ".len;
- try bw.writeAll(" zig_");
- switch (child_ty.zigTypeTag()) {
- .Struct, .Union => {
- var fqn_buf = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer fqn_buf.deinit();
-
- const owner_decl_index = child_ty.getOwnerDecl();
- const owner_decl = dg.module.declPtr(owner_decl_index);
- try owner_decl.renderFullyQualifiedName(dg.module, fqn_buf.writer());
-
- try bw.print("S_{}__{d}", .{ fmtIdent(fqn_buf.items), @enumToInt(owner_decl_index) });
- },
- .ErrorUnion => {
- try bw.print("E_{}", .{typeToCIdentifier(child_ty.errorUnionPayload(), dg.module)});
- },
- .Optional => {
- var opt_buf: Type.Payload.ElemType = undefined;
- try bw.print("Q_{}", .{typeToCIdentifier(child_ty.optionalChild(&opt_buf), dg.module)});
- },
- else => unreachable,
- }
- const name_end = buffer.items.len;
- try buffer.ensureUnusedCapacity(" ".len + (name_end - name_begin) + ";\n".len);
- buffer.appendAssumeCapacity(' ');
- buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]);
- buffer.appendSliceAssumeCapacity(";\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
+ fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index {
+ return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind);
}
-
- fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- 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);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
-
- try buffer.appendSlice("struct ");
-
- var needs_pack_attr = false;
- {
- var it = t.structFields().iterator();
- while (it.next()) |field| {
- const field_ty = field.value_ptr.ty;
- if (!field_ty.hasRuntimeBits()) continue;
- const alignment = field.value_ptr.abi_align;
- if (alignment != 0 and alignment < field_ty.abiAlignment(dg.module.getTarget())) {
- needs_pack_attr = true;
- try buffer.appendSlice("zig_packed(");
- break;
- }
- }
- }
-
- try buffer.appendSlice(name);
- try buffer.appendSlice(" {\n");
- {
- var it = t.structFields().iterator();
- var empty = true;
- while (it.next()) |field| {
- const field_ty = field.value_ptr.ty;
- if (!field_ty.hasRuntimeBits()) continue;
-
- const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout());
- const field_name = CValue{ .identifier = field.key_ptr.* };
- try buffer.append(' ');
- try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete);
- try buffer.appendSlice(";\n");
-
- empty = false;
- }
- if (empty) try buffer.appendSlice(" char empty_struct;\n");
- }
- if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
-
- try buffer.appendSlice("typedef struct {\n");
- {
- const fields = t.tupleFields();
- var field_id: usize = 0;
- for (fields.types, 0..) |field_ty, i| {
- if (!field_ty.hasRuntimeBits() or fields.values[i].tag() != .unreachable_value) continue;
-
- try buffer.append(' ');
- try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, .Complete);
- try buffer.appendSlice(";\n");
-
- field_id += 1;
- }
- if (field_id == 0) try buffer.appendSlice(" char empty_tuple;\n");
- }
- const name_begin = buffer.items.len + "} ".len;
- try buffer.writer().print("}} zig_T_{}_{d};\n", .{ typeToCIdentifier(t, dg.module), @truncate(u16, t.hash(dg.module)) });
- const name_end = buffer.items.len - ";\n".len;
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- 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);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
-
- try buffer.appendSlice(if (t.unionTagTypeSafety()) |_| "struct " else "union ");
- try buffer.appendSlice(name);
- try buffer.appendSlice(" {\n");
-
- const indent = if (t.unionTagTypeSafety()) |tag_ty| indent: {
- const target = dg.module.getTarget();
- const layout = t.unionGetLayout(target);
- if (layout.tag_size != 0) {
- try buffer.append(' ');
- try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0, .Complete);
- try buffer.appendSlice(";\n");
- }
- try buffer.appendSlice(" union {\n");
- break :indent " ";
- } else " ";
-
- {
- var it = t.unionFields().iterator();
- var empty = true;
- while (it.next()) |field| {
- const field_ty = field.value_ptr.ty;
- if (!field_ty.hasRuntimeBits()) continue;
-
- const alignment = field.value_ptr.abi_align;
- const field_name = CValue{ .identifier = field.key_ptr.* };
- try buffer.appendSlice(indent);
- try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete);
- try buffer.appendSlice(";\n");
-
- empty = false;
- }
- if (empty) {
- try buffer.appendSlice(indent);
- try buffer.appendSlice("char empty_union;\n");
- }
- }
-
- if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n");
- try buffer.appendSlice("};\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- assert(t.errorUnionSet().tag() == .anyerror);
-
- 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);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- const payload_ty = t.errorUnionPayload();
- const payload_name = CValue{ .identifier = "payload" };
- const error_ty = t.errorUnionSet();
- const error_name = CValue{ .identifier = "error" };
-
- const target = dg.module.getTarget();
- const payload_align = payload_ty.abiAlignment(target);
- const error_align = error_ty.abiAlignment(target);
- try bw.writeAll("struct ");
- try bw.writeAll(name);
- try bw.writeAll(" {\n ");
- if (error_align > payload_align) {
- try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete);
- try bw.writeAll(";\n ");
- try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete);
- } else {
- try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete);
- try bw.writeAll(";\n ");
- try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete);
- }
- try bw.writeAll(";\n};\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderArrayTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- const info = t.arrayInfo();
- std.debug.assert(info.sentinel == null); // expected canonical type
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- try bw.writeAll("typedef ");
- try dg.renderType(bw, info.elem_type, .Complete);
-
- const name_begin = buffer.items.len + " ".len;
- try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len });
- const name_end = buffer.items.len;
-
- const c_len = if (info.len > 0) info.len else 1;
- 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 bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)});
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderOptionalTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- 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);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- var opt_buf: Type.Payload.ElemType = undefined;
- const child_ty = t.optionalChild(&opt_buf);
-
- try bw.writeAll("struct ");
- try bw.writeAll(name);
- try bw.writeAll(" {\n");
- try dg.renderTypeAndName(bw, child_ty, .{ .identifier = "payload" }, .Mut, 0, .Complete);
- try bw.writeAll(";\n ");
- try dg.renderTypeAndName(bw, Type.bool, .{ .identifier = "is_null" }, .Mut, 0, .Complete);
- try bw.writeAll(";\n};\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn renderOpaqueTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- const opaque_ty = t.cast(Type.Payload.Opaque).?.data;
- const unqualified_name = dg.module.declPtr(opaque_ty.owner_decl).name;
- const fqn = try opaque_ty.getFullyQualifiedName(dg.module);
- defer dg.typedefs.allocator.free(fqn);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
-
- try buffer.writer().print("typedef struct { } ", .{fmtIdent(std.mem.span(unqualified_name))});
-
- const name_begin = buffer.items.len;
- try buffer.writer().print("zig_O_{}", .{fmtIdent(fqn)});
- const name_end = buffer.items.len;
- try buffer.appendSlice(";\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
+ fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType {
+ return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind);
}
/// Renders a type as a single identifier, generating intermediate typedefs
@@ -1959,275 +1526,27 @@ 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 renderType(
dg: *DeclGen,
w: anytype,
t: Type,
- kind: TypedefKind,
- ) error{ OutOfMemory, AnalysisFail }!void {
- 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);
- },
- .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);
- }
-
- 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);
-
- try w.writeAll(name);
- },
- }
- },
- .ErrorUnion => {
- const payload_ty = t.errorUnionPayload();
-
- if (!payload_ty.hasRuntimeBitsIgnoreComptime())
- return dg.renderType(w, Type.anyerror, kind);
-
- 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);
-
- switch (kind) {
- .Complete => {
- const name = dg.getTypedefName(error_union_ty) orelse
- try dg.renderErrorUnionTypedef(error_union_ty);
-
- 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);
-
- const name = dg.getTypedefName(ptr_ty) orelse
- try dg.renderFwdTypedef(ptr_ty);
-
- try w.writeAll(name);
- },
- }
- },
- .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);
- }
- } 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);
-
- try dg.renderType(w, int_tag_ty, kind);
- },
- .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(
- 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 store = &dg.ctypes.set;
+ const module = dg.module;
+ const idx = try dg.typeToIndex(t, .complete);
+ _ = try renderTypePrefix(
+ dg.decl_index,
+ store.*,
+ module,
+ w,
+ idx,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix);
}
const IntCastContext = union(enum) {
@@ -2254,16 +1573,16 @@ pub const DeclGen = struct {
/// Renders a cast to an int type, from either an int or a pointer.
///
/// Some platforms don't have 128 bit integers, so we need to use
- /// the zig_as_ and zig_lo_ macros in those cases.
+ /// the zig_make_ and zig_lo_ macros in those cases.
///
/// | Dest type bits | Src type | Result
/// |------------------|------------------|---------------------------|
/// | < 64 bit integer | pointer | (zig_<dest_ty>)(zig_<u|i>size)src
/// | < 64 bit integer | < 64 bit integer | (zig_<dest_ty>)src
/// | < 64 bit integer | > 64 bit integer | zig_lo(src)
- /// | > 64 bit integer | pointer | zig_as_<dest_ty>(0, (zig_<u|i>size)src)
- /// | > 64 bit integer | < 64 bit integer | zig_as_<dest_ty>(0, src)
- /// | > 64 bit integer | > 64 bit integer | zig_as_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src))
+ /// | > 64 bit integer | pointer | zig_make_<dest_ty>(0, (zig_<u|i>size)src)
+ /// | > 64 bit integer | < 64 bit integer | zig_make_<dest_ty>(0, src)
+ /// | > 64 bit integer | > 64 bit integer | zig_make_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src))
fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void {
const target = dg.module.getTarget();
const dest_bits = dest_ty.bitSize(target);
@@ -2301,7 +1620,7 @@ pub const DeclGen = struct {
try context.writeValue(dg, w, src_ty, .FunctionArgument);
try w.writeByte(')');
} else if (dest_bits > 64 and src_bits <= 64) {
- try w.writeAll("zig_as_");
+ try w.writeAll("zig_make_");
try dg.renderTypeForBuiltinFnName(w, dest_ty);
try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral?
if (src_is_ptr) {
@@ -2313,7 +1632,7 @@ pub const DeclGen = struct {
try w.writeByte(')');
} else {
assert(!src_is_ptr);
- try w.writeAll("zig_as_");
+ try w.writeAll("zig_make_");
try dg.renderTypeForBuiltinFnName(w, dest_ty);
try w.writeAll("(zig_hi_");
try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
@@ -2337,10 +1656,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.
@@ -2350,7 +1669,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,
@@ -2359,65 +1678,45 @@ 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);
- 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 store = &dg.ctypes.set;
+ const module = dg.module;
- const const_prefix = switch (mutability) {
- .Const, .ConstArgument => "const ",
- .Mut => "",
+ if (alignment != 0) switch (std.math.order(alignment, ty.abiAlignment(dg.module.getTarget()))) {
+ .lt => try w.print("zig_under_align({}) ", .{alignment}),
+ .eq => {},
+ .gt => try w.print("zig_align({}) ", .{alignment}),
};
- try w.print(" {s}", .{const_prefix});
+
+ const idx = try dg.typeToIndex(ty, .complete);
+ const trailing = try renderTypePrefix(
+ dg.decl_index,
+ store.*,
+ module,
+ w,
+ idx,
+ .suffix,
+ CQualifiers.init(.{ .@"const" = mutability == .@"const" }),
+ );
+ try w.print("{}", .{trailing});
try dg.writeCValue(w, name);
- try w.writeAll(suffix.items);
+ try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix);
}
- fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
+ fn renderTagNameFn(dg: *DeclGen, w: anytype, fn_name: []const u8, enum_ty: Type) !void {
const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
- try buffer.appendSlice("static ");
- try dg.renderType(bw, name_slice_ty, .Complete);
- const name_begin = buffer.items.len + " ".len;
- try bw.print(" zig_tagName_{}_{d}(", .{ typeToCIdentifier(enum_ty, dg.module), @enumToInt(enum_ty.getOwnerDecl()) });
- const name_end = buffer.items.len - "(".len;
- try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0, .Complete);
- try buffer.appendSlice(") {\n switch (tag) {\n");
+ try w.writeAll("static ");
+ try dg.renderType(w, name_slice_ty, .Complete);
+ try w.writeByte(' ');
+ try w.writeAll(fn_name);
+ try w.writeByte('(');
+ try dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, .@"const", 0, .Complete);
+ try w.writeAll(") {\n switch (tag) {\n");
for (enum_ty.enumFields().keys(), 0..) |name, index| {
- const name_z = try dg.typedefs.allocator.dupeZ(u8, name);
- defer dg.typedefs.allocator.free(name_z);
+ const name_z = try dg.gpa.dupeZ(u8, name);
+ defer dg.gpa.free(name_z);
const name_bytes = name_z[0 .. name_z.len + 1];
var tag_pl: Value.Payload.U32 = .{
@@ -2438,40 +1737,23 @@ pub const DeclGen = struct {
var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
const len_val = Value.initPayload(&len_pl.base);
- try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)});
- try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0, .Complete);
- try buffer.appendSlice(" = ");
- try dg.renderValue(bw, name_ty, name_val, .Initializer);
- try buffer.appendSlice(";\n return (");
- try dg.renderTypecast(bw, name_slice_ty);
- try bw.print("){{{}, {}}};\n", .{
+ try w.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)});
+ try dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, .@"const", 0, .Complete);
+ try w.writeAll(" = ");
+ try dg.renderValue(w, name_ty, name_val, .Initializer);
+ try w.writeAll(";\n return (");
+ try dg.renderTypecast(w, name_slice_ty);
+ try w.print("){{{}, {}}};\n", .{
fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val),
});
- try buffer.appendSlice(" }\n");
+ try w.writeAll(" }\n");
}
- try buffer.appendSlice(" }\n while (");
- try dg.renderValue(bw, Type.bool, Value.true, .Other);
- try buffer.appendSlice(") ");
- _ = try airBreakpoint(bw);
- try buffer.appendSlice("}\n");
-
- const rendered = try buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_begin..name_end];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- dg.typedefs.putAssumeCapacityNoClobber(
- try enum_ty.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
-
- return name;
- }
-
- fn getTagNameFn(dg: *DeclGen, enum_ty: Type) ![]const u8 {
- return dg.getTypedefName(enum_ty) orelse
- try dg.renderTagNameFn(enum_ty);
+ try w.writeAll(" }\n while (");
+ try dg.renderValue(w, Type.bool, Value.true, .Other);
+ try w.writeAll(") ");
+ _ = try airBreakpoint(w);
+ try w.writeAll("}\n");
}
fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
@@ -2492,10 +1774,11 @@ pub const DeclGen = struct {
fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void {
switch (c_value) {
.none => unreachable,
- .local => |i| return w.print("t{d}", .{i}),
+ .local, .new_local => |i| return w.print("t{d}", .{i}),
.local_ref => |i| return w.print("&t{d}", .{i}),
.constant => unreachable,
.arg => |i| return w.print("a{d}", .{i}),
+ .arg_array => |i| return dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }),
.field => |i| return w.print("f{d}", .{i}),
.decl => |decl| return dg.renderDeclName(w, decl, 0),
.decl_ref => |decl| {
@@ -2511,10 +1794,15 @@ pub const DeclGen = struct {
fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void {
switch (c_value) {
.none => unreachable,
- .local => |i| return w.print("(*t{d})", .{i}),
+ .local, .new_local => |i| return w.print("(*t{d})", .{i}),
.local_ref => |i| return w.print("t{d}", .{i}),
.constant => unreachable,
.arg => |i| return w.print("(*a{d})", .{i}),
+ .arg_array => |i| {
+ try w.writeAll("(*");
+ try dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" });
+ return w.writeByte(')');
+ },
.field => |i| return w.print("f{d}", .{i}),
.decl => |decl| {
try w.writeAll("(*");
@@ -2541,7 +1829,7 @@ pub const DeclGen = struct {
fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void {
switch (c_value) {
.none, .constant, .field, .undef => unreachable,
- .local, .arg, .decl, .identifier, .bytes => {
+ .new_local, .local, .arg, .arg_array, .decl, .identifier, .bytes => {
try dg.writeCValue(writer, c_value);
try writer.writeAll("->");
},
@@ -2668,10 +1956,493 @@ pub const DeclGen = struct {
}
};
-pub fn genGlobalAsm(mod: *Module, code: *std.ArrayList(u8)) !void {
+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 renderTypeName(
+ mod: *Module,
+ w: anytype,
+ idx: CType.Index,
+ cty: CType,
+ 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,
+ }),
+
+ .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(mem.span(mod.declPtr(owner_decl).name)),
+ @enumToInt(owner_decl),
+ });
+ },
+ }
+}
+fn renderTypePrefix(
+ decl: Decl.OptionalIndex,
+ store: CType.Store.Set,
+ mod: *Module,
+ w: anytype,
+ idx: CType.Index,
+ parent_fix: CTypeFix,
+ qualifiers: CQualifiers,
+) @TypeOf(w).Error!CTypeRenderTrailing {
+ var trailing = CTypeRenderTrailing.maybe_space;
+
+ 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(
+ decl,
+ store,
+ mod,
+ 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,
+ } }),
+ );
+ try w.print("{}*", .{child_trailing});
+ trailing = .no_space;
+ },
+
+ .array,
+ .vector,
+ => {
+ const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type;
+ const child_trailing = try renderTypePrefix(
+ decl,
+ store,
+ mod,
+ w,
+ child_idx,
+ .suffix,
+ qualifiers,
+ );
+ switch (parent_fix) {
+ .prefix => {
+ try w.print("{}(", .{child_trailing});
+ return .no_space;
+ },
+ .suffix => return child_trailing,
+ }
+ },
+
+ .fwd_anon_struct,
+ .fwd_anon_union,
+ => if (decl.unwrap()) |decl_index|
+ try w.print("anon__{d}_{d}", .{ @enumToInt(decl_index), idx })
+ else
+ try renderTypeName(mod, w, idx, cty, ""),
+
+ .fwd_struct,
+ .fwd_union,
+ => try renderTypeName(mod, 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(mod, w, store, cty, 1);
+ if (cty.isPacked()) try w.writeByte(')');
+ },
+
+ .anon_struct,
+ .anon_union,
+ .@"struct",
+ .@"union",
+ .packed_struct,
+ .packed_union,
+ => return renderTypePrefix(
+ decl,
+ store,
+ mod,
+ w,
+ cty.cast(CType.Payload.Aggregate).?.data.fwd_decl,
+ parent_fix,
+ qualifiers,
+ ),
+
+ .function,
+ .varargs_function,
+ => {
+ const child_trailing = try renderTypePrefix(
+ decl,
+ store,
+ mod,
+ w,
+ cty.cast(CType.Payload.Function).?.data.return_type,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ switch (parent_fix) {
+ .prefix => {
+ try w.print("{}(", .{child_trailing});
+ return .no_space;
+ },
+ .suffix => return child_trailing,
+ }
+ },
+ }
+
+ var qualifier_it = qualifiers.iterator();
+ while (qualifier_it.next()) |qualifier| {
+ try w.print("{}{s}", .{ trailing, @tagName(qualifier) });
+ trailing = .maybe_space;
+ }
+
+ return trailing;
+}
+fn renderTypeSuffix(
+ decl: Decl.OptionalIndex,
+ store: CType.Store.Set,
+ mod: *Module,
+ w: anytype,
+ idx: CType.Index,
+ parent_fix: CTypeFix,
+) @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(decl, store, mod, w, cty.cast(CType.Payload.Child).?.data, .prefix),
+
+ .array,
+ .vector,
+ => {
+ switch (parent_fix) {
+ .prefix => try w.writeByte(')'),
+ .suffix => {},
+ }
+
+ try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len});
+ try renderTypeSuffix(
+ decl,
+ store,
+ mod,
+ w,
+ cty.cast(CType.Payload.Sequence).?.data.elem_type,
+ .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| {
+ 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| {
+ if (need_comma) try w.writeAll(", ");
+ need_comma = true;
+ const trailing = try renderTypePrefix(
+ decl,
+ store,
+ mod,
+ w,
+ param_type,
+ .suffix,
+ CQualifiers.init(.{ .@"const" = true }),
+ );
+ try w.print("{}a{d}", .{ trailing, param_i });
+ try renderTypeSuffix(decl, store, mod, w, param_type, .suffix);
+ }
+ switch (tag) {
+ .function => {},
+ .varargs_function => {
+ if (need_comma) try w.writeAll(", ");
+ need_comma = true;
+ try w.writeAll("...");
+ },
+ else => unreachable,
+ }
+ if (!need_comma) try w.writeAll("void");
+ try w.writeByte(')');
+
+ try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix);
+ },
+ }
+}
+fn renderAggregateFields(
+ mod: *Module,
+ writer: anytype,
+ store: CType.Store.Set,
+ cty: CType,
+ indent: usize,
+) !void {
+ try writer.writeAll("{\n");
+ const fields = cty.fields();
+ for (fields) |field| {
+ try writer.writeByteNTimes(' ', indent + 1);
+ switch (std.math.order(field.alignas.@"align", field.alignas.abi)) {
+ .lt => try writer.print("zig_under_align({}) ", .{field.alignas.getAlign()}),
+ .eq => {},
+ .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}),
+ }
+ const trailing = try renderTypePrefix(
+ .none,
+ store,
+ mod,
+ writer,
+ field.type,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) });
+ try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix);
+ try writer.writeAll(";\n");
+ }
+ try writer.writeByteNTimes(' ', indent);
+ try writer.writeByte('}');
+}
+
+pub fn genTypeDecl(
+ mod: *Module,
+ writer: anytype,
+ global_store: CType.Store.Set,
+ global_idx: CType.Index,
+ decl: Decl.OptionalIndex,
+ decl_store: CType.Store.Set,
+ decl_idx: CType.Index,
+ found_existing: bool,
+) !void {
+ const global_cty = global_store.indexToCType(global_idx);
+ switch (global_cty.tag()) {
+ .fwd_anon_struct => if (decl != .none) {
+ try writer.writeAll("typedef ");
+ _ = try renderTypePrefix(
+ .none,
+ global_store,
+ mod,
+ writer,
+ global_idx,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ try writer.writeByte(' ');
+ _ = try renderTypePrefix(
+ decl,
+ decl_store,
+ mod,
+ writer,
+ decl_idx,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ 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(
+ .none,
+ global_store,
+ mod,
+ writer,
+ global_idx,
+ .suffix,
+ CQualifiers.init(.{}),
+ );
+ try writer.writeAll("; // ");
+ try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer);
+ try writer.writeByte('\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(
+ mod,
+ writer,
+ fwd_idx,
+ global_store.indexToCType(fwd_idx),
+ if (global_cty.isPacked()) "zig_packed(" else "",
+ );
+ try writer.writeByte(' ');
+ try renderAggregateFields(mod, writer, global_store, global_cty, 0);
+ if (global_cty.isPacked()) try writer.writeByte(')');
+ try writer.writeAll(";\n");
+ },
+
+ else => unreachable,
+ }
+ },
+
+ else => {},
+ }
+}
+
+pub fn genGlobalAsm(mod: *Module, writer: anytype) !void {
var it = mod.global_assembly.valueIterator();
while (it.next()) |asm_source| {
- try code.writer().print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)});
+ try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)});
}
}
@@ -2709,7 +2480,7 @@ pub fn genErrDecls(o: *Object) !void {
const name_val = Value.initPayload(&name_pl.base);
try writer.writeAll("static ");
- try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0, .Complete);
+ try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .@"const", 0, .Complete);
try writer.writeAll(" = ");
try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer);
try writer.writeAll(";\n");
@@ -2722,7 +2493,7 @@ pub fn genErrDecls(o: *Object) !void {
const name_array_ty = Type.initPayload(&name_array_ty_pl.base);
try writer.writeAll("static ");
- try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0, .Complete);
+ try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .@"const", 0, .Complete);
try writer.writeAll(" = {");
for (o.dg.module.error_name_list.items, 0..) |name, value| {
if (value != 0) try writer.writeByte(',');
@@ -2742,14 +2513,27 @@ fn genExports(o: *Object) !void {
defer tracy.end();
const fwd_decl_writer = o.dg.fwd_decl.writer();
- if (o.dg.module.decl_exports.get(o.dg.decl_index)) |exports| for (exports.items[1..], 0..) |@"export", i| {
- try fwd_decl_writer.writeAll("zig_export(");
- try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, 1 + i));
- try fwd_decl_writer.print(", {s}, {s});\n", .{
- fmtStringLiteral(exports.items[0].options.name),
- fmtStringLiteral(@"export".options.name),
- });
- };
+ if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| {
+ for (exports.items[1..], 1..) |@"export", i| {
+ try fwd_decl_writer.writeAll("zig_export(");
+ try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, i));
+ try fwd_decl_writer.print(", {s}, {s});\n", .{
+ fmtStringLiteral(exports.items[0].options.name),
+ fmtStringLiteral(@"export".options.name),
+ });
+ }
+ }
+}
+
+pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
+ const writer = o.writer();
+ switch (lazy_fn.key_ptr.*) {
+ .tag_name => _ = try o.dg.renderTagNameFn(
+ writer,
+ lazy_fn.value_ptr.fn_name,
+ lazy_fn.value_ptr.data.tag_name,
+ ),
+ }
}
pub fn genFunc(f: *Function) !void {
@@ -2759,8 +2543,8 @@ pub fn genFunc(f: *Function) !void {
const o = &f.object;
const gpa = o.dg.gpa;
const tv: TypedValue = .{
- .ty = o.dg.decl.ty,
- .val = o.dg.decl.val,
+ .ty = o.dg.decl.?.ty,
+ .val = o.dg.decl.?.val,
};
o.code_header = std.ArrayList(u8).init(gpa);
@@ -2799,9 +2583,8 @@ pub fn genFunc(f: *Function) !void {
// missing. These are added now to complete the map. Then we can sort by
// alignment, descending.
const free_locals = f.getFreeLocals();
- const values = f.allocs.values();
- for (f.allocs.keys(), 0..) |local_index, i| {
- if (values[i]) continue; // static
+ for (f.allocs.keys(), f.allocs.values()) |local_index, value| {
+ if (value) continue; // static
const local = f.locals.items[local_index];
log.debug("inserting local {d} into free_locals", .{local_index});
const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx());
@@ -2830,7 +2613,7 @@ pub fn genFunc(f: *Function) !void {
w,
local.ty,
.{ .local = local_index },
- .Mut,
+ .mut,
local.alignment,
.Complete,
);
@@ -2850,10 +2633,10 @@ pub fn genDecl(o: *Object) !void {
const tracy = trace(@src());
defer tracy.end();
- const tv: TypedValue = .{
- .ty = o.dg.decl.ty,
- .val = o.dg.decl.val,
- };
+ const decl = o.dg.decl.?;
+ const decl_c_value: CValue = .{ .decl = o.dg.decl_index.unwrap().? };
+ const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val };
+
if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return;
if (tv.val.tag() == .extern_fn) {
const fwd_decl_writer = o.dg.fwd_decl.writer();
@@ -2867,11 +2650,9 @@ pub fn genDecl(o: *Object) !void {
const is_global = o.dg.declIsGlobal(tv) or variable.is_extern;
const fwd_decl_writer = o.dg.fwd_decl.writer();
- const decl_c_value = CValue{ .decl = o.dg.decl_index };
-
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal ");
- try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
+ try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .mut, decl.@"align", .Complete);
try fwd_decl_writer.writeAll(";\n");
try genExports(o);
@@ -2880,27 +2661,26 @@ pub fn genDecl(o: *Object) !void {
const w = o.writer();
if (!is_global) try w.writeAll("static ");
if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
- if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
- try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
- if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)");
+ if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
+ try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .mut, decl.@"align", .Complete);
+ if (decl.@"linksection" != null) try w.writeAll(", read, write)");
try w.writeAll(" = ");
try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer);
try w.writeByte(';');
try o.indent_writer.insertNewline();
} else {
- const is_global = o.dg.module.decl_exports.contains(o.dg.decl_index);
+ const is_global = o.dg.module.decl_exports.contains(decl_c_value.decl);
const fwd_decl_writer = o.dg.fwd_decl.writer();
- const decl_c_value: CValue = .{ .decl = o.dg.decl_index };
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
- try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
+ try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete);
try fwd_decl_writer.writeAll(";\n");
const w = o.writer();
if (!is_global) try w.writeAll("static ");
- if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
- try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
- if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)");
+ if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
+ try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete);
+ if (decl.@"linksection" != null) try w.writeAll(", read)");
try w.writeAll(" = ");
try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer);
try w.writeAll(";\n");
@@ -2912,8 +2692,8 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
defer tracy.end();
const tv: TypedValue = .{
- .ty = dg.decl.ty,
- .val = dg.decl.val,
+ .ty = dg.decl.?.ty,
+ .val = dg.decl.?.val,
};
const writer = dg.fwd_decl.writer();
@@ -2951,7 +2731,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
// zig fmt: off
.constant => unreachable, // excluded from function bodies
.const_ty => unreachable, // excluded from function bodies
- .arg => airArg(f),
+ .arg => try airArg(f, inst),
.breakpoint => try airBreakpoint(f.object.writer()),
.ret_addr => try airRetAddr(f, inst),
@@ -3200,13 +2980,14 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.c_va_start => return f.fail("TODO implement c_va_start", .{}),
// zig fmt: on
};
- if (result_value == .local) {
- log.debug("map %{d} to t{d}", .{ inst, result_value.local });
- }
- switch (result_value) {
- .none => {},
- else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value),
+ if (result_value == .new_local) {
+ log.debug("map %{d} to t{d}", .{ inst, result_value.new_local });
}
+ try f.value_map.putNoClobber(Air.indexToRef(inst), switch (result_value) {
+ .none => continue,
+ .new_local => |i| .{ .local = i },
+ else => result_value,
+ });
}
}
@@ -3283,6 +3064,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
return CValue.none;
}
+ const inst_ty = f.air.typeOfIndex(inst);
const ptr_ty = f.air.typeOf(bin_op.lhs);
const child_ty = ptr_ty.childType();
@@ -3297,7 +3079,9 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer();
const local = try f.allocLocal(inst, f.air.typeOfIndex(inst));
try f.writeCValue(writer, local, .Other);
- try writer.writeAll(" = &(");
+ try writer.writeAll(" = (");
+ try f.renderTypecast(writer, inst_ty);
+ try writer.writeAll(")&(");
if (ptr_ty.ptrSize() == .One) {
// It's a pointer to an array, so we need to de-reference.
try f.writeCValueDeref(writer, ptr);
@@ -3428,13 +3212,13 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
return CValue{ .undef = inst_ty };
}
- const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
+ const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut;
const target = f.object.dg.module.getTarget();
const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target));
- log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local });
+ log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
const gpa = f.object.dg.module.gpa;
- try f.allocs.put(gpa, local.local, false);
- return CValue{ .local_ref = local.local };
+ try f.allocs.put(gpa, local.new_local, false);
+ return CValue{ .local_ref = local.new_local };
}
fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
@@ -3445,19 +3229,25 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
return CValue{ .undef = inst_ty };
}
- const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
+ const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut;
const target = f.object.dg.module.getTarget();
const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target));
- log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local });
+ log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
const gpa = f.object.dg.module.gpa;
- try f.allocs.put(gpa, local.local, false);
- return CValue{ .local_ref = local.local };
+ try f.allocs.put(gpa, local.new_local, false);
+ return CValue{ .local_ref = local.new_local };
}
-fn airArg(f: *Function) CValue {
+fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
+ const inst_ty = f.air.typeOfIndex(inst);
+ const inst_cty = try f.object.dg.typeToIndex(inst_ty, .parameter);
+
const i = f.next_arg_index;
f.next_arg_index += 1;
- return .{ .arg = i };
+ return if (inst_cty != try f.object.dg.typeToIndex(inst_ty, .complete))
+ .{ .arg_array = i }
+ else
+ .{ .arg = i };
}
fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
@@ -3567,7 +3357,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
const ret_val = if (is_array) ret_val: {
const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator()));
try writer.writeAll("memcpy(");
- try f.writeCValueMember(writer, array_local, .{ .field = 0 });
+ try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
try writer.writeAll(", ");
if (deref)
try f.writeCValueDeref(writer, operand)
@@ -3587,14 +3377,13 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
try f.writeCValue(writer, ret_val, .Other);
try writer.writeAll(";\n");
if (is_array) {
- try freeLocal(f, inst, ret_val.local, 0);
+ try freeLocal(f, inst, ret_val.new_local, 0);
}
} else {
try reap(f, inst, &.{un_op});
- if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) {
+ if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() != .Naked)
// Not even allowed to return void in a naked function.
try writer.writeAll("return;\n");
- }
}
return CValue.none;
}
@@ -3796,7 +3585,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
try f.renderTypecast(writer, src_ty);
try writer.writeAll("))");
if (src_val == .constant) {
- try freeLocal(f, inst, array_src.local, 0);
+ try freeLocal(f, inst, array_src.new_local, 0);
}
} else if (ptr_info.host_size != 0) {
const host_bits = ptr_info.host_size * 8;
@@ -3847,7 +3636,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64;
if (cant_cast) {
if (src_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
- try writer.writeAll("zig_as_");
+ try writer.writeAll("zig_make_");
try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
try writer.writeAll("(0, ");
} else {
@@ -4118,32 +3907,31 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
const inst_ty = f.air.typeOfIndex(inst);
- const elem_ty = switch (inst_ty.ptrSize()) {
- .One => blk: {
- const array_ty = inst_ty.childType();
- break :blk array_ty.childType();
- },
- else => inst_ty.childType(),
- };
+ const elem_ty = inst_ty.elemType2();
- // We must convert to and from integer types to prevent UB if the operation
- // results in a NULL pointer, or if LHS is NULL. The operation is only UB
- // if the result is NULL and then dereferenced.
const local = try f.allocLocal(inst, inst_ty);
const writer = f.object.writer();
try f.writeCValue(writer, local, .Other);
- try writer.writeAll(" = (");
- try f.renderTypecast(writer, inst_ty);
- try writer.writeAll(")(((uintptr_t)");
- try f.writeCValue(writer, lhs, .Other);
- try writer.writeAll(") ");
- try writer.writeByte(operator);
- try writer.writeAll(" (");
- try f.writeCValue(writer, rhs, .Other);
- try writer.writeAll("*sizeof(");
- try f.renderTypecast(writer, elem_ty);
- try writer.writeAll(")));\n");
+ try writer.writeAll(" = ");
+
+ if (elem_ty.hasRuntimeBitsIgnoreComptime()) {
+ // We must convert to and from integer types to prevent UB if the operation
+ // results in a NULL pointer, or if LHS is NULL. The operation is only UB
+ // if the result is NULL and then dereferenced.
+ try writer.writeByte('(');
+ try f.renderTypecast(writer, inst_ty);
+ try writer.writeAll(")(((uintptr_t)");
+ try f.writeCValue(writer, lhs, .Other);
+ try writer.writeAll(") ");
+ try writer.writeByte(operator);
+ try writer.writeAll(" (");
+ try f.writeCValue(writer, rhs, .Other);
+ try writer.writeAll("*sizeof(");
+ try f.renderTypecast(writer, elem_ty);
+ try writer.writeAll(")))");
+ } else try f.writeCValue(writer, lhs, .Initializer);
+ try writer.writeAll(";\n");
return local;
}
@@ -4222,8 +4010,12 @@ fn airCall(
modifier: std.builtin.CallModifier,
) !CValue {
// Not even allowed to call panic in a naked function.
- if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
+ if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none;
+
const gpa = f.object.dg.gpa;
+ const module = f.object.dg.module;
+ const target = module.getTarget();
+ const writer = f.object.writer();
switch (modifier) {
.auto => {},
@@ -4238,8 +4030,28 @@ fn airCall(
const resolved_args = try gpa.alloc(CValue, args.len);
defer gpa.free(resolved_args);
- for (args, 0..) |arg, i| {
- resolved_args[i] = try f.resolveInst(arg);
+ for (resolved_args, args) |*resolved_arg, arg| {
+ const arg_ty = f.air.typeOf(arg);
+ const arg_cty = try f.object.dg.typeToIndex(arg_ty, .parameter);
+ if (f.object.dg.indexToCType(arg_cty).tag() == .void) {
+ resolved_arg.* = .none;
+ continue;
+ }
+ resolved_arg.* = try f.resolveInst(arg);
+ if (arg_cty != try f.object.dg.typeToIndex(arg_ty, .complete)) {
+ var lowered_arg_buf: LowerFnRetTyBuffer = undefined;
+ const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target);
+
+ const array_local = try f.allocLocal(inst, try lowered_arg_ty.copy(f.arena.allocator()));
+ 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.renderTypecast(writer, lowered_arg_ty);
+ try writer.writeAll("));\n");
+ resolved_arg.* = array_local;
+ }
}
const callee = try f.resolveInst(pl_op.operand);
@@ -4256,9 +4068,7 @@ fn airCall(
.Pointer => callee_ty.childType(),
else => unreachable,
};
- const writer = f.object.writer();
- const target = f.object.dg.module.getTarget();
const ret_ty = fn_ty.fnReturnType();
var lowered_ret_buf: LowerFnRetTyBuffer = undefined;
const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target);
@@ -4293,7 +4103,7 @@ fn airCall(
else => break :known,
};
};
- name = f.object.dg.module.declPtr(fn_decl).name;
+ name = module.declPtr(fn_decl).name;
try f.object.dg.renderDeclName(writer, fn_decl, 0);
break :callee;
}
@@ -4303,22 +4113,11 @@ fn airCall(
try writer.writeByte('(');
var args_written: usize = 0;
- for (args, 0..) |arg, arg_i| {
- const ty = f.air.typeOf(arg);
- if (!ty.hasRuntimeBitsIgnoreComptime()) continue;
- if (args_written != 0) {
- try writer.writeAll(", ");
- }
- if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and
- ty.isCPtr() and ty.childType().tag() == .u8)
- {
- // Corresponds with hack in renderType .Pointer case.
- try writer.writeAll("(char");
- if (ty.isConstPtr()) try writer.writeAll(" const");
- if (ty.isVolatilePtr()) try writer.writeAll(" volatile");
- try writer.writeAll(" *)");
- }
- try f.writeCValue(writer, resolved_args[arg_i], .FunctionArgument);
+ for (resolved_args) |resolved_arg| {
+ if (resolved_arg == .none) continue;
+ if (args_written != 0) try writer.writeAll(", ");
+ try f.writeCValue(writer, resolved_arg, .FunctionArgument);
+ if (resolved_arg == .new_local) try freeLocal(f, inst, resolved_arg.new_local, 0);
args_written += 1;
}
try writer.writeAll(");\n");
@@ -4331,11 +4130,11 @@ fn airCall(
try writer.writeAll("memcpy(");
try f.writeCValue(writer, array_local, .FunctionArgument);
try writer.writeAll(", ");
- try f.writeCValueMember(writer, result_local, .{ .field = 0 });
+ try f.writeCValueMember(writer, result_local, .{ .identifier = "array" });
try writer.writeAll(", sizeof(");
try f.renderTypecast(writer, ret_ty);
try writer.writeAll("));\n");
- try freeLocal(f, inst, result_local.local, 0);
+ try freeLocal(f, inst, result_local.new_local, 0);
break :r array_local;
};
@@ -4599,7 +4398,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
}
if (operand == .constant) {
- try freeLocal(f, inst, operand_lval.local, 0);
+ try freeLocal(f, inst, operand_lval.new_local, 0);
}
return local;
@@ -4645,7 +4444,7 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
fn airUnreach(f: *Function) !CValue {
// Not even allowed to call unreachable in a naked function.
- if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
+ if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none;
try f.object.writer().writeAll("zig_unreachable();\n");
return CValue.none;
@@ -4922,7 +4721,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
writer,
output_ty,
local_value,
- .Mut,
+ .mut,
alignment,
.Complete,
);
@@ -4961,7 +4760,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
writer,
input_ty,
local_value,
- .Const,
+ .@"const",
alignment,
.Complete,
);
@@ -5119,7 +4918,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
const is_reg = constraint[1] == '{';
if (is_reg) {
try f.writeCValueDeref(writer, if (output == .none)
- CValue{ .local_ref = local.local }
+ CValue{ .local_ref = local.new_local }
else
try f.resolveInst(output));
try writer.writeAll(" = ");
@@ -5425,18 +5224,20 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
else => .none,
};
- const FieldLoc = union(enum) {
+ const field_loc: union(enum) {
begin: void,
field: CValue,
end: void,
- };
- const field_loc = switch (struct_ty.tag()) {
- .@"struct" => switch (struct_ty.containerLayout()) {
- .Auto, .Extern => for (struct_ty.structFields().values()[index..], 0..) |field, offset| {
- if (field.ty.hasRuntimeBitsIgnoreComptime()) break FieldLoc{ .field = .{
- .identifier = struct_ty.structFieldName(index + offset),
- } };
- } else @as(FieldLoc, .end),
+ } = switch (struct_ty.tag()) {
+ .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) {
+ .Auto, .Extern => for (index..struct_ty.structFieldCount()) |field_i| {
+ if (!struct_ty.structFieldIsComptime(field_i) and
+ struct_ty.structFieldType(field_i).hasRuntimeBitsIgnoreComptime())
+ break .{ .field = if (struct_ty.isSimpleTuple())
+ .{ .field = field_i }
+ else
+ .{ .identifier = struct_ty.structFieldName(field_i) } };
+ } else .end,
.Packed => if (field_ptr_info.data.host_size == 0) {
const target = f.object.dg.module.getTarget();
@@ -5461,45 +5262,33 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
try f.writeCValue(writer, struct_ptr, .Other);
try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)});
return local;
- } else @as(FieldLoc, .begin),
+ } else .begin,
},
.@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) {
try f.writeCValue(writer, struct_ptr, .Other);
try writer.writeAll(";\n");
return local;
- } else if (field_ty.hasRuntimeBitsIgnoreComptime()) FieldLoc{ .field = .{
+ } else if (field_ty.hasRuntimeBitsIgnoreComptime()) .{ .field = .{
.identifier = struct_ty.unionFields().keys()[index],
- } } else @as(FieldLoc, .end),
- .tuple, .anon_struct => field_name: {
- const tuple = struct_ty.tupleFields();
- if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
-
- var id: usize = 0;
- break :field_name for (tuple.values, 0..) |value, i| {
- if (value.tag() != .unreachable_value) continue;
- if (!tuple.types[i].hasRuntimeBitsIgnoreComptime()) continue;
- if (i >= index) break FieldLoc{ .field = .{ .field = id } };
- id += 1;
- } else @as(FieldLoc, .end);
- },
+ } } else .end,
else => unreachable,
};
- try writer.writeByte('&');
- switch (field_loc) {
- .begin, .end => {
- try writer.writeByte('(');
- try f.writeCValue(writer, struct_ptr, .Other);
- try writer.print(")[{}]", .{
- @boolToInt(field_loc == .end and struct_ty.hasRuntimeBitsIgnoreComptime()),
- });
- },
- .field => |field| if (extra_name != .none) {
- try f.writeCValueDerefMember(writer, struct_ptr, extra_name);
- try writer.writeByte('.');
- try f.writeCValue(writer, field, .Other);
- } else try f.writeCValueDerefMember(writer, struct_ptr, field),
- }
+ if (struct_ty.hasRuntimeBitsIgnoreComptime()) {
+ try writer.writeByte('&');
+ switch (field_loc) {
+ .begin, .end => {
+ try writer.writeByte('(');
+ try f.writeCValue(writer, struct_ptr, .Other);
+ try writer.print(")[{}]", .{@boolToInt(field_loc == .end)});
+ },
+ .field => |field| if (extra_name != .none) {
+ try f.writeCValueDerefMember(writer, struct_ptr, extra_name);
+ try writer.writeByte('.');
+ try f.writeCValue(writer, field, .Other);
+ } else try f.writeCValueDerefMember(writer, struct_ptr, field),
+ }
+ } else try f.writeCValue(writer, struct_ptr, .Other);
try writer.writeAll(";\n");
return local;
}
@@ -5534,8 +5323,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
};
const field_name: CValue = switch (struct_ty.tag()) {
- .@"struct" => switch (struct_ty.containerLayout()) {
- .Auto, .Extern => .{ .identifier = struct_ty.structFieldName(extra.field_index) },
+ .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) {
+ .Auto, .Extern => if (struct_ty.isSimpleTuple())
+ .{ .field = extra.field_index }
+ else
+ .{ .identifier = struct_ty.structFieldName(extra.field_index) },
.Packed => {
const struct_obj = struct_ty.castTag(.@"struct").?.data;
const int_info = struct_ty.intInfo(target);
@@ -5593,13 +5385,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
const local = try f.allocLocal(inst, inst_ty);
try writer.writeAll("memcpy(");
- try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument);
+ try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument);
try writer.writeAll(", ");
- try f.writeCValue(writer, .{ .local_ref = temp_local.local }, .FunctionArgument);
+ try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument);
try writer.writeAll(", sizeof(");
try f.renderTypecast(writer, inst_ty);
try writer.writeAll("));\n");
- try freeLocal(f, inst, temp_local.local, 0);
+ try freeLocal(f, inst, temp_local.new_local, 0);
return local;
},
},
@@ -5623,22 +5415,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll("));\n");
if (struct_byval == .constant) {
- try freeLocal(f, inst, operand_lval.local, 0);
+ try freeLocal(f, inst, operand_lval.new_local, 0);
}
return local;
} else .{
.identifier = struct_ty.unionFields().keys()[extra.field_index],
},
- .tuple, .anon_struct => blk: {
- const tuple = struct_ty.tupleFields();
- if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
-
- var id: usize = 0;
- for (tuple.values[0..extra.field_index]) |value|
- id += @boolToInt(value.tag() == .unreachable_value);
- break :blk .{ .field = id };
- },
else => unreachable,
};
@@ -5965,26 +5748,28 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
const inst_ty = f.air.typeOfIndex(inst);
const writer = f.object.writer();
const local = try f.allocLocal(inst, inst_ty);
- try f.writeCValue(writer, local, .Other);
- const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
+ const array_ty = f.air.typeOf(ty_op.operand).childType();
- try writer.writeAll(".ptr = ");
+ try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });
+ try writer.writeAll(" = ");
+ // Unfortunately, C does not support any equivalent to
+ // &(*(void *)p)[0], although LLVM does via GetElementPtr
if (operand == .undef) {
- // Unfortunately, C does not support any equivalent to
- // &(*(void *)p)[0], although LLVM does via GetElementPtr
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer);
- } else {
+ } else if (array_ty.hasRuntimeBitsIgnoreComptime()) {
try writer.writeAll("&(");
try f.writeCValueDeref(writer, operand);
try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)});
- }
+ } else try f.writeCValue(writer, operand, .Initializer);
+ try writer.writeAll("; ");
+ const array_len = array_ty.arrayLen();
var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len };
const len_val = Value.initPayload(&len_pl.base);
- try writer.writeAll("; ");
- try f.writeCValue(writer, local, .Other);
- try writer.print(".len = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)});
+ try f.writeCValueMember(writer, local, .{ .identifier = "len" });
+ try writer.print(" = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)});
+
return local;
}
@@ -6223,7 +6008,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
}
if (f.liveness.isUnused(inst)) {
- try freeLocal(f, inst, local.local, 0);
+ try freeLocal(f, inst, local.new_local, 0);
return CValue.none;
}
@@ -6266,7 +6051,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(");\n");
if (f.liveness.isUnused(inst)) {
- try freeLocal(f, inst, local.local, 0);
+ try freeLocal(f, inst, local.new_local, 0);
return CValue.none;
}
@@ -6363,7 +6148,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(";\n");
try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
- try freeLocal(f, inst, index.local, 0);
+ try freeLocal(f, inst, index.new_local, 0);
return CValue.none;
}
@@ -6465,7 +6250,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer();
const local = try f.allocLocal(inst, inst_ty);
try f.writeCValue(writer, local, .Other);
- try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)});
+ try writer.print(" = {s}(", .{try f.getTagNameFn(enum_ty)});
try f.writeCValue(writer, operand, .Other);
try writer.writeAll(");\n");
@@ -6680,7 +6465,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(";\n");
- try freeLocal(f, inst, it.local, 0);
+ try freeLocal(f, inst, it.new_local, 0);
return accum;
}
@@ -6693,8 +6478,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
const gpa = f.object.dg.gpa;
const resolved_elements = try gpa.alloc(CValue, elements.len);
defer gpa.free(resolved_elements);
- for (elements, 0..) |element, i| {
- resolved_elements[i] = try f.resolveInst(element);
+ for (resolved_elements, elements) |*resolved_element, element| {
+ resolved_element.* = try f.resolveInst(element);
}
{
var bt = iterateBigTomb(f, inst);
@@ -6733,46 +6518,47 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(")");
try writer.writeByte('{');
var empty = true;
- for (elements, 0..) |element, index| {
- if (inst_ty.structFieldValueComptime(index)) |_| continue;
+ for (elements, resolved_elements, 0..) |element, resolved_element, field_i| {
+ if (inst_ty.structFieldValueComptime(field_i)) |_| continue;
if (!empty) try writer.writeAll(", ");
- if (!inst_ty.isTupleOrAnonStruct()) {
- try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))});
- }
+
+ const field_name: CValue = if (inst_ty.isSimpleTuple())
+ .{ .field = field_i }
+ else
+ .{ .identifier = inst_ty.structFieldName(field_i) };
+ try writer.writeByte('.');
+ try f.object.dg.writeCValue(writer, field_name);
+ try writer.writeAll(" = ");
const element_ty = f.air.typeOf(element);
try f.writeCValue(writer, switch (element_ty.zigTypeTag()) {
.Array => CValue{ .undef = element_ty },
- else => resolved_elements[index],
+ else => resolved_element,
}, .Initializer);
empty = false;
}
- if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
try writer.writeAll("};\n");
- var field_id: usize = 0;
- for (elements, 0..) |element, index| {
- if (inst_ty.structFieldValueComptime(index)) |_| continue;
+ for (elements, resolved_elements, 0..) |element, resolved_element, field_i| {
+ if (inst_ty.structFieldValueComptime(field_i)) |_| continue;
const element_ty = f.air.typeOf(element);
if (element_ty.zigTypeTag() != .Array) continue;
- const field_name = if (inst_ty.isTupleOrAnonStruct())
- CValue{ .field = field_id }
+ const field_name: CValue = if (inst_ty.isSimpleTuple())
+ .{ .field = field_i }
else
- CValue{ .identifier = inst_ty.structFieldName(index) };
+ .{ .identifier = inst_ty.structFieldName(field_i) };
try writer.writeAll(";\n");
try writer.writeAll("memcpy(");
try f.writeCValueMember(writer, local, field_name);
try writer.writeAll(", ");
- try f.writeCValue(writer, resolved_elements[index], .FunctionArgument);
+ try f.writeCValue(writer, resolved_element, .FunctionArgument);
try writer.writeAll(", sizeof(");
try f.renderTypecast(writer, element_ty);
try writer.writeAll("));\n");
-
- field_id += 1;
}
},
.Packed => {
@@ -6790,7 +6576,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base);
var empty = true;
- for (elements, 0..) |_, index| {
+ for (0..elements.len) |index| {
const field_ty = inst_ty.structFieldType(index);
if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -6839,13 +6625,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
empty = false;
}
- if (empty) {
- try writer.writeByte('(');
- try f.renderTypecast(writer, inst_ty);
- try writer.writeByte(')');
- try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer);
- }
-
try writer.writeAll(";\n");
},
},
@@ -7350,7 +7129,7 @@ fn formatIntLiteral(
use_twos_comp = true;
} else {
// TODO: Use fmtIntLiteral for 0?
- try writer.print("zig_sub_{c}{d}(zig_as_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits });
+ try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits });
}
} else {
try writer.writeByte('-');
@@ -7360,11 +7139,16 @@ fn formatIntLiteral(
switch (data.ty.tag()) {
.c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {},
else => {
- if (int_info.bits > 64 and data.location != null and data.location.? == .StaticInitializer) {
+ if (int_info.bits <= 64) {
+ try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) {
+ .signed => "",
+ .unsigned => "U",
+ }, c_bits });
+ } else if (data.location != null and data.location.? == .StaticInitializer) {
// MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers
- try writer.print("zig_as_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
+ try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
} else {
- try writer.print("zig_as_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
+ try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
}
},
}
@@ -7473,17 +7257,20 @@ fn isByRef(ty: Type) bool {
}
const LowerFnRetTyBuffer = struct {
+ names: [1][]const u8,
types: [1]Type,
values: [1]Value,
- payload: Type.Payload.Tuple,
+ payload: Type.Payload.AnonStruct,
};
fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type {
if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn);
if (lowersToArray(ret_ty, target)) {
+ buffer.names = [1][]const u8{"array"};
buffer.types = [1]Type{ret_ty};
buffer.values = [1]Value{Value.initTag(.unreachable_value)};
buffer.payload = .{ .data = .{
+ .names = &buffer.names,
.types = &buffer.types,
.values = &buffer.values,
} };
@@ -7539,7 +7326,7 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {
if (f.air.instructions.items(.tag)[ref_inst] == .constant) return;
const c_value = (f.value_map.fetchRemove(ref) orelse return).value;
const local_index = switch (c_value) {
- .local => |l| l,
+ .local, .new_local => |l| l,
else => return,
};
try freeLocal(f, inst, local_index, ref_inst);
@@ -7614,8 +7401,8 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void {
}
fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void {
- for (f.locals.items[pre_locals_len..], 0..) |*local, local_offset| {
- const local_index = pre_locals_len + @intCast(LocalIndex, local_offset);
+ for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| {
+ const local_index = @intCast(LocalIndex, local_i);
if (f.allocs.contains(local_index)) continue; // allocs are not freeable
// free more deeply nested locals from other branches at current depth