authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-11-02 03:10:29-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-11-02 10:21:05-04:00
log37c104ade05487f24f4ed1fd7e3251a6fc2c804d
tree3820048242a03025528fbc2654bb028fda2712cf
parent5f31070b8bb204ce7d42881903805c7a4b2263d0

cbe: ignore comptime fields when generating tuple typedefs

This vastly reduces the amount of deduplication state we need to deal with.

2 files changed, 103 insertions(+), 73 deletions(-)

src/codegen/c.zig+103-72
......@@ -32,6 +32,8 @@ pub const CValue = union(enum) {
3232 constant: Air.Inst.Ref,
3333 /// Index into the parameters
3434 arg: usize,
35 /// Index into a tuple's fields
36 field: usize,
3537 /// By-value
3638 decl: Decl.Index,
3739 decl_ref: Decl.Index,
......@@ -1382,26 +1384,17 @@ pub const DeclGen = struct {
13821384 try buffer.appendSlice("typedef struct {\n");
13831385 {
13841386 const fields = t.tupleFields();
1385 var empty = true;
1387 var field_id: usize = 0;
13861388 for (fields.types) |field_ty, i| {
1387 if (!field_ty.hasRuntimeBits()) continue;
1388 const val = fields.values[i];
1389 if (val.tag() != .unreachable_value) continue;
1390
1391 var field_name_buf: []const u8 = &.{};
1392 defer dg.typedefs.allocator.free(field_name_buf);
1393 const field_name = if (t.isTuple()) field_name: {
1394 field_name_buf = try std.fmt.allocPrint(dg.typedefs.allocator, "field_{d}", .{i});
1395 break :field_name field_name_buf;
1396 } else t.structFieldName(i);
1389 if (!field_ty.hasRuntimeBits() or fields.values[i].tag() != .unreachable_value) continue;
13971390
13981391 try buffer.append(' ');
1399 try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0, .Complete);
1392 try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, .Complete);
14001393 try buffer.appendSlice(";\n");
14011394
1402 empty = false;
1395 field_id += 1;
14031396 }
1404 if (empty) try buffer.appendSlice(" char empty_tuple;\n");
1397 if (field_id == 0) try buffer.appendSlice(" char empty_tuple;\n");
14051398 }
14061399 const name_begin = buffer.items.len + "} ".len;
14071400 try buffer.writer().print("}} zig_T_{};\n", .{typeToCIdentifier(t, dg.module)});
......@@ -1755,12 +1748,38 @@ pub const DeclGen = struct {
17551748 },
17561749 .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed)
17571750 try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind)
1758 else if (kind == .Complete or t.isTupleOrAnonStruct()) {
1751 else if (t.isTupleOrAnonStruct()) {
1752 const ExpectedContents = struct { types: [8]Type, values: [8]Value };
1753 var stack align(@alignOf(ExpectedContents)) =
1754 std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);
1755 const allocator = stack.get();
1756
1757 var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){};
1758 defer tuple_storage.deinit(allocator);
1759 try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount());
1760
1761 const fields = t.tupleFields();
1762 for (fields.values) |value, index|
1763 if (value.tag() == .unreachable_value)
1764 tuple_storage.appendAssumeCapacity(.{
1765 .type = fields.types[index],
1766 .value = value,
1767 });
1768
1769 const tuple_slice = tuple_storage.slice();
1770 var tuple_pl = Type.Payload.Tuple{ .data = .{
1771 .types = tuple_slice.items(.type),
1772 .values = tuple_slice.items(.value),
1773 } };
1774 const tuple_ty = Type.initPayload(&tuple_pl.base);
1775
1776 const name = dg.getTypedefName(tuple_ty) orelse
1777 try dg.renderTupleTypedef(tuple_ty);
1778
1779 try w.writeAll(name);
1780 } else if (kind == .Complete) {
17591781 const name = dg.getTypedefName(t) orelse switch (tag) {
1760 .Struct => if (t.isTupleOrAnonStruct())
1761 try dg.renderTupleTypedef(t)
1762 else
1763 try dg.renderStructTypedef(t),
1782 .Struct => try dg.renderStructTypedef(t),
17641783 .Union => try dg.renderUnionTypedef(t),
17651784 else => unreachable,
17661785 };
......@@ -1980,6 +1999,7 @@ pub const DeclGen = struct {
19801999 .local_ref => |i| return w.print("&t{d}", .{i}),
19812000 .constant => unreachable,
19822001 .arg => |i| return w.print("a{d}", .{i}),
2002 .field => |i| return w.print("f{d}", .{i}),
19832003 .decl => |decl| return dg.renderDeclName(w, decl),
19842004 .decl_ref => |decl| {
19852005 try w.writeByte('&');
......@@ -1998,6 +2018,7 @@ pub const DeclGen = struct {
19982018 .local_ref => |i| return w.print("t{d}", .{i}),
19992019 .constant => unreachable,
20002020 .arg => |i| return w.print("(*a{d})", .{i}),
2021 .field => |i| return w.print("f{d}", .{i}),
20012022 .decl => |decl| {
20022023 try w.writeAll("(*");
20032024 try dg.renderDeclName(w, decl);
......@@ -2022,7 +2043,7 @@ pub const DeclGen = struct {
20222043
20232044 fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void {
20242045 switch (c_value) {
2025 .none, .constant, .undef => unreachable,
2046 .none, .constant, .field, .undef => unreachable,
20262047 .local, .arg, .decl, .identifier, .bytes => {
20272048 try dg.writeCValue(writer, c_value);
20282049 try writer.writeAll("->");
......@@ -2868,7 +2889,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
28682889 const array_local = try f.allocLocal(lowered_ret_ty, .Mut);
28692890 try writer.writeAll(";\n");
28702891 try writer.writeAll("memcpy(");
2871 try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
2892 try f.writeCValueMember(writer, array_local, .{ .field = 0 });
28722893 try writer.writeAll(", ");
28732894 if (deref)
28742895 try f.writeCValueDeref(writer, operand)
......@@ -3114,13 +3135,13 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
31143135 const local = try f.allocLocal(inst_ty, .Mut);
31153136 try w.writeAll(";\n");
31163137
3117 try f.writeCValue(w, local, .Other);
3118 try w.writeAll(".field_1 = zig_");
3138 try f.writeCValueMember(w, local, .{ .field = 1 });
3139 try w.writeAll(" = zig_");
31193140 try w.writeAll(operation);
31203141 try w.writeAll("o_");
31213142 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
31223143 try w.writeAll("(&");
3123 try f.writeCValueMember(w, local, .{ .identifier = "field_0" });
3144 try f.writeCValueMember(w, local, .{ .field = 0 });
31243145 try w.writeAll(", ");
31253146 try f.writeCValue(w, lhs, .FunctionArgument);
31263147 try w.writeAll(", ");
......@@ -3480,7 +3501,7 @@ fn airCall(
34803501 try writer.writeAll("memcpy(");
34813502 try f.writeCValue(writer, array_local, .FunctionArgument);
34823503 try writer.writeAll(", ");
3483 try f.writeCValueMember(writer, result_local, .{ .identifier = "array" });
3504 try f.writeCValueMember(writer, result_local, .{ .field = 0 });
34843505 try writer.writeAll(", sizeof(");
34853506 try f.renderTypecast(writer, ret_ty);
34863507 try writer.writeAll("));\n");
......@@ -4232,16 +4253,14 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
42324253 try f.renderTypecast(writer, field_ptr_ty);
42334254 try writer.writeByte(')');
42344255
4235 const extra_name: ?[]const u8 = switch (struct_ty.tag()) {
4236 .union_tagged, .union_safety_tagged => "payload",
4237 else => null,
4256 const extra_name: CValue = switch (struct_ty.tag()) {
4257 .union_tagged, .union_safety_tagged => .{ .identifier = "payload" },
4258 else => .none,
42384259 };
42394260
4240 var field_name_buf: []const u8 = &.{};
4241 defer f.object.dg.gpa.free(field_name_buf);
4242 const field_name: ?[]const u8 = switch (struct_ty.tag()) {
4261 const field_name: CValue = switch (struct_ty.tag()) {
42434262 .@"struct" => switch (struct_ty.containerLayout()) {
4244 .Auto, .Extern => struct_ty.structFieldName(index),
4263 .Auto, .Extern => CValue{ .identifier = struct_ty.structFieldName(index) },
42454264 .Packed => if (field_ptr_info.data.host_size == 0) {
42464265 const target = f.object.dg.module.getTarget();
42474266
......@@ -4262,29 +4281,35 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
42624281 try f.writeCValue(writer, struct_ptr, .Other);
42634282 try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)});
42644283 return local;
4265 } else null,
4284 } else @as(CValue, CValue.none), // this @as is needed because of a stage1 bug
42664285 },
4267 .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[index],
4268 .tuple, .anon_struct => |tag| field_name: {
4286 .@"union", .union_safety_tagged, .union_tagged => .{
4287 .identifier = struct_ty.unionFields().keys()[index],
4288 },
4289 .tuple, .anon_struct => field_name: {
42694290 const tuple = struct_ty.tupleFields();
42704291 if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
42714292
4272 if (tag == .anon_struct) break :field_name struct_ty.structFieldName(index);
4273
4274 field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
4275 break :field_name field_name_buf;
4293 var id: usize = 0;
4294 for (tuple.values[0..index]) |value|
4295 id += @boolToInt(value.tag() == .unreachable_value);
4296 break :field_name .{ .field = id };
42764297 },
42774298 else => unreachable,
42784299 };
42794300
42804301 if (field_ty.hasRuntimeBitsIgnoreComptime()) {
42814302 try writer.writeByte('&');
4282 if (extra_name orelse field_name) |name|
4283 try f.writeCValueDerefMember(writer, struct_ptr, .{ .identifier = name })
4303 if (extra_name != .none) {
4304 try f.writeCValueDerefMember(writer, struct_ptr, extra_name);
4305 if (field_name != .none) {
4306 try writer.writeByte('.');
4307 try f.writeCValue(writer, field_name, .Other);
4308 }
4309 } else if (field_name != .none)
4310 try f.writeCValueDerefMember(writer, struct_ptr, field_name)
42844311 else
42854312 try f.writeCValueDeref(writer, struct_ptr);
4286 if (extra_name) |_| if (field_name) |name|
4287 try writer.print(".{ }", .{fmtIdent(name)});
42884313 } else try f.writeCValue(writer, struct_ptr, .Other);
42894314 try writer.writeAll(";\n");
42904315 return local;
......@@ -4307,11 +4332,14 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
43074332 // Ensure complete type definition is visible before accessing fields.
43084333 try f.renderType(std.io.null_writer, struct_ty);
43094334
4310 var field_name_buf: []const u8 = "";
4311 defer f.object.dg.gpa.free(field_name_buf);
4312 const field_name = switch (struct_ty.tag()) {
4335 const extra_name: CValue = switch (struct_ty.tag()) {
4336 .union_tagged, .union_safety_tagged => .{ .identifier = "payload" },
4337 else => .none,
4338 };
4339
4340 const field_name: CValue = switch (struct_ty.tag()) {
43134341 .@"struct" => switch (struct_ty.containerLayout()) {
4314 .Auto, .Extern => struct_ty.structFieldName(extra.field_index),
4342 .Auto, .Extern => .{ .identifier = struct_ty.structFieldName(extra.field_index) },
43154343 .Packed => {
43164344 const struct_obj = struct_ty.castTag(.@"struct").?.data;
43174345 const int_info = struct_ty.intInfo(target);
......@@ -4369,19 +4397,20 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
43694397 return local;
43704398 },
43714399 },
4372 .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
4373 .tuple, .anon_struct => |tag| blk: {
4400 .@"union", .union_safety_tagged, .union_tagged => .{
4401 .identifier = struct_ty.unionFields().keys()[extra.field_index],
4402 },
4403 .tuple, .anon_struct => blk: {
43744404 const tuple = struct_ty.tupleFields();
43754405 if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
43764406
4377 if (tag == .anon_struct) break :blk struct_ty.structFieldName(extra.field_index);
4378
4379 field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{extra.field_index});
4380 break :blk field_name_buf;
4407 var id: usize = 0;
4408 for (tuple.values[0..extra.field_index]) |value|
4409 id += @boolToInt(value.tag() == .unreachable_value);
4410 break :blk .{ .field = id };
43814411 },
43824412 else => unreachable,
43834413 };
4384 const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
43854414
43864415 const is_array = lowersToArray(inst_ty, target);
43874416 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
......@@ -4390,15 +4419,18 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
43904419 try writer.writeAll("memcpy(");
43914420 try f.writeCValue(writer, local, .FunctionArgument);
43924421 try writer.writeAll(", ");
4393 try f.writeCValue(writer, struct_byval, .Other);
4394 try writer.print(".{s}{ }, sizeof(", .{ payload, fmtIdent(field_name) });
4422 } else try writer.writeAll(" = ");
4423 if (extra_name != .none) {
4424 try f.writeCValueMember(writer, struct_byval, extra_name);
4425 try writer.writeByte('.');
4426 try f.writeCValue(writer, field_name, .Other);
4427 } else try f.writeCValueMember(writer, struct_byval, field_name);
4428 if (is_array) {
4429 try writer.writeAll(", sizeof(");
43954430 try f.renderTypecast(writer, inst_ty);
4396 try writer.writeAll("));\n");
4397 } else {
4398 try writer.writeAll(" = ");
4399 try f.writeCValue(writer, struct_byval, .Other);
4400 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
4431 try writer.writeAll("))");
44014432 }
4433 try writer.writeAll(";\n");
44024434 return local;
44034435}
44044436
......@@ -5180,27 +5212,28 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
51805212 if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
51815213 try writer.writeAll("};\n");
51825214
5215 var field_id: usize = 0;
51835216 for (elements) |element, index| {
51845217 if (inst_ty.structFieldValueComptime(index)) |_| continue;
51855218
51865219 const element_ty = f.air.typeOf(element);
51875220 if (element_ty.zigTypeTag() != .Array) continue;
51885221
5189 var field_name_buf: []u8 = &.{};
5190 defer f.object.dg.gpa.free(field_name_buf);
5191 const field_name = if (inst_ty.isTuple()) field_name: {
5192 field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
5193 break :field_name field_name_buf;
5194 } else inst_ty.structFieldName(index);
5222 const field_name = if (inst_ty.isTupleOrAnonStruct())
5223 CValue{ .field = field_id }
5224 else
5225 CValue{ .identifier = inst_ty.structFieldName(index) };
51955226
51965227 try writer.writeAll(";\n");
51975228 try writer.writeAll("memcpy(");
5198 try f.writeCValue(writer, local, .Other);
5199 try writer.print(".{ }, ", .{fmtIdent(field_name)});
5229 try f.writeCValueMember(writer, local, field_name);
5230 try writer.writeAll(", ");
52005231 try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument);
52015232 try writer.writeAll(", sizeof(");
52025233 try f.renderTypecast(writer, element_ty);
52035234 try writer.writeAll("));\n");
5235
5236 field_id += 1;
52045237 }
52055238 },
52065239 .Packed => {
......@@ -5746,10 +5779,9 @@ fn isByRef(ty: Type) bool {
57465779}
57475780
57485781const LowerFnRetTyBuffer = struct {
5749 const names = [1][]const u8{"array"};
57505782 types: [1]Type,
57515783 values: [1]Value,
5752 payload: Type.Payload.AnonStruct,
5784 payload: Type.Payload.Tuple,
57535785};
57545786fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type {
57555787 if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn);
......@@ -5758,7 +5790,6 @@ fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) T
57585790 buffer.types = [1]Type{ret_ty};
57595791 buffer.values = [1]Value{Value.initTag(.unreachable_value)};
57605792 buffer.payload = .{ .data = .{
5761 .names = &LowerFnRetTyBuffer.names,
57625793 .types = &buffer.types,
57635794 .values = &buffer.values,
57645795 } };
test/behavior/type.zig-1
......@@ -260,7 +260,6 @@ test "Type.ErrorSet" {
260260
261261test "Type.Struct" {
262262 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
263 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
264263 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
265264 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
266265 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO