diff --git a/lib/zig.h b/lib/zig.h index 5a57bbda04..f827af1d5c 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -65,8 +65,17 @@ typedef char bool; #elif zig_has_attribute(aligned) #define zig_align(alignment) __attribute__((aligned(alignment))) #elif _MSC_VER +#define zig_align zig_align_unavailable #else -#error the C compiler being used does not support aligning variables +#define zig_align zig_align_unavailable +#endif + +#if zig_has_attribute(aligned) +#define zig_align_fn(alignment) __attribute__((aligned(alignment))) +#elif _MSC_VER +#define zig_align_fn zig_align_fn_unavailable +#else +#define zig_align_fn zig_align_fn_unavailable #endif #if zig_has_builtin(unreachable) @@ -81,6 +90,12 @@ typedef char bool; #define zig_extern extern #endif +#if zig_has_attribute(alias) +#define zig_export(sig, symbol, name) zig_extern sig __attribute__((alias(symbol))) +#else +#define zig_export(sig, symbol, name) __asm(name " = " symbol) +#endif + #if zig_has_builtin(debugtrap) #define zig_breakpoint() __builtin_debugtrap() #elif zig_has_builtin(trap) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 6d5bb6b78f..7fd76a6962 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -569,7 +569,7 @@ pub const DeclGen = struct { try writer.writeByte(')'); } try writer.writeByte('&'); - try dg.renderDeclName(writer, decl_index); + try dg.renderDeclName(writer, decl_index, 0); if (need_typecast) try writer.writeByte(')'); } @@ -1026,11 +1026,11 @@ pub const DeclGen = struct { }, .function => { const func = val.castTag(.function).?.data; - try dg.renderDeclName(writer, func.owner_decl); + try dg.renderDeclName(writer, func.owner_decl, 0); }, .extern_fn => { const extern_fn = val.castTag(.extern_fn).?.data; - try dg.renderDeclName(writer, extern_fn.owner_decl); + try dg.renderDeclName(writer, extern_fn.owner_decl, 0); }, .int_u64, .one => { try writer.writeAll("(("); @@ -1356,7 +1356,7 @@ pub const DeclGen = struct { } } - fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind) !void { + fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void { const fn_info = dg.decl.ty.fnInfo(); if (fn_info.cc == .Naked) try w.writeAll("zig_naked "); if (dg.decl.val.castTag(.function)) |func_payload| @@ -1368,7 +1368,7 @@ pub const DeclGen = struct { try dg.renderType(w, ret_ty, kind); try w.writeByte(' '); - try dg.renderDeclName(w, dg.decl_index); + try dg.renderDeclName(w, dg.decl_index, export_index); try w.writeByte('('); var index: usize = 0; @@ -1387,6 +1387,7 @@ pub const DeclGen = struct { try dg.renderType(w, Type.void, kind); } try w.writeByte(')'); + if (fn_info.alignment > 0) try w.print(" zig_align_fn({})", .{fn_info.alignment}); } fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { @@ -1487,29 +1488,42 @@ pub const DeclGen = struct { // The forward declaration for T is stored with a key of *const T. const child_ty = t.childType(); - 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()); - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); + const bw = buffer.writer(); const tag = switch (child_ty.zigTypeTag()) { - .Struct => "struct ", - .Union => if (child_ty.unionTagTypeSafety()) |_| "struct " else "union ", + .Struct, .ErrorUnion, .Optional => "struct", + .Union => if (child_ty.unionTagTypeSafety()) |_| "struct" else "union", else => unreachable, }; - const name_begin = buffer.items.len + "typedef ".len + tag.len; - try buffer.writer().print("typedef {s}zig_S_{}__{d} ", .{ - tag, - fmtIdent(fqn_buf.items), - @enumToInt(owner_decl_index), - }); - const name_end = buffer.items.len - " ".len; - try buffer.ensureUnusedCapacity((name_end - name_begin) + ";\n".len); + 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"); @@ -1569,7 +1583,11 @@ pub const DeclGen = struct { return name; } - fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + fn renderTupleTypedef( + dg: *DeclGen, + t: Type, + kind: TypedefKind, + ) error{ OutOfMemory, AnalysisFail }![]const u8 { var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); @@ -1581,7 +1599,7 @@ pub const DeclGen = struct { 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 dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, kind); try buffer.appendSlice(";\n"); field_id += 1; @@ -1669,6 +1687,11 @@ pub const DeclGen = struct { 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(); @@ -1681,7 +1704,9 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); const payload_align = payload_ty.abiAlignment(target); const error_align = error_ty.abiAlignment(target); - try bw.writeAll("typedef struct {\n "); + 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 "); @@ -1691,15 +1716,10 @@ pub const DeclGen = struct { try bw.writeAll(";\n "); try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); } - try bw.writeAll(";\n} "); - const name_begin = buffer.items.len; - try bw.print("zig_E_{}", .{typeToCIdentifier(payload_ty, dg.module)}); - const name_end = buffer.items.len; - try bw.writeAll(";\n"); + try bw.writeAll(";\n};\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( @@ -1743,24 +1763,29 @@ pub const DeclGen = struct { return name; } - fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + 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(); - try bw.writeAll("typedef struct {\n "); - try dg.renderTypeAndName(bw, child_type, .{ .identifier = "payload" }, .Mut, 0, .Complete); + 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} "); - const name_begin = buffer.items.len; - try bw.print("zig_Q_{}", .{typeToCIdentifier(child_type, dg.module)}); - const name_end = buffer.items.len; - try bw.writeAll(";\n"); + try bw.writeAll(";\n};\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( @@ -1899,18 +1924,34 @@ pub const DeclGen = struct { }, .Optional => { var opt_buf: Type.Payload.ElemType = undefined; - const child_type = t.optionalChild(&opt_buf); + const child_ty = t.optionalChild(&opt_buf); - if (!child_type.hasRuntimeBitsIgnoreComptime()) + if (!child_ty.hasRuntimeBitsIgnoreComptime()) return dg.renderType(w, Type.bool, kind); if (t.optionalReprIsPayload()) - return dg.renderType(w, child_type, kind); + return dg.renderType(w, child_ty, kind); - const name = dg.getTypedefName(t) orelse - try dg.renderOptionalTypedef(t, child_type); + switch (kind) { + .Complete => { + const name = dg.getTypedefName(t) orelse + try dg.renderOptionalTypedef(t); - return w.writeAll(name); + 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(); @@ -1923,10 +1964,26 @@ pub const DeclGen = struct { }; const error_union_ty = Type.initPayload(&error_union_pl.base); - const name = dg.getTypedefName(error_union_ty) orelse - try dg.renderErrorUnionTypedef(error_union_ty); + switch (kind) { + .Complete => { + const name = dg.getTypedefName(error_union_ty) orelse + try dg.renderErrorUnionTypedef(error_union_ty); - return w.writeAll(name); + 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| { @@ -1964,28 +2021,31 @@ pub const DeclGen = struct { const tuple_ty = Type.initPayload(&tuple_pl.base); const name = dg.getTypedefName(tuple_ty) orelse - try dg.renderTupleTypedef(tuple_ty); + try dg.renderTupleTypedef(tuple_ty, kind); try w.writeAll(name); - } else if (kind == .Complete) { - const name = dg.getTypedefName(t) orelse switch (tag) { - .Struct => try dg.renderStructTypedef(t), - .Union => try dg.renderUnionTypedef(t), - else => unreachable, - }; + } 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); - } else { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = t, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); + 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); + const name = dg.getTypedefName(ptr_ty) orelse + try dg.renderFwdTypedef(ptr_ty); - try w.writeAll(name); + try w.writeAll(name); + }, }, .Enum => { // For enums, we simply use the integer tag type. @@ -2213,10 +2273,10 @@ pub const DeclGen = struct { .constant => unreachable, .arg => |i| return w.print("a{d}", .{i}), .field => |i| return w.print("f{d}", .{i}), - .decl => |decl| return dg.renderDeclName(w, decl), + .decl => |decl| return dg.renderDeclName(w, decl, 0), .decl_ref => |decl| { try w.writeByte('&'); - return dg.renderDeclName(w, decl); + return dg.renderDeclName(w, decl, 0); }, .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), @@ -2234,10 +2294,10 @@ pub const DeclGen = struct { .field => |i| return w.print("f{d}", .{i}), .decl => |decl| { try w.writeAll("(*"); - try dg.renderDeclName(w, decl); + try dg.renderDeclName(w, decl, 0); return w.writeByte(')'); }, - .decl_ref => |decl| return dg.renderDeclName(w, decl), + .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), .undef => unreachable, .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), .bytes => |bytes| { @@ -2269,12 +2329,12 @@ pub const DeclGen = struct { try dg.writeCValue(writer, member); } - fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index) !void { + fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index, export_index: u32) !void { const decl = dg.module.declPtr(decl_index); dg.module.markDeclAlive(decl); if (dg.module.decl_exports.get(decl_index)) |exports| { - return writer.writeAll(exports.items[0].options.name); + return writer.writeAll(exports.items[export_index].options.name); } else if (decl.isExtern()) { return writer.writeAll(mem.sliceTo(decl.name, 0)); } else if (dg.module.test_functions.get(decl_index)) |_| { @@ -2422,24 +2482,44 @@ pub fn genErrDecls(o: *Object) !void { try writer.writeAll("};\n"); } +fn genExports(o: *Object) !void { + const tracy = trace(@src()); + 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..]) |@"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), + }); + }; +} + pub fn genFunc(f: *Function) !void { const tracy = trace(@src()); defer tracy.end(); const o = &f.object; + const tv: TypedValue = .{ + .ty = o.dg.decl.ty, + .val = o.dg.decl.val, + }; o.code_header = std.ArrayList(u8).init(f.object.dg.gpa); defer o.code_header.deinit(); - const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl); + const is_global = o.dg.declIsGlobal(tv); const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); + try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); try fwd_decl_writer.writeAll(";\n"); + try genExports(o); try o.indent_writer.insertNewline(); if (!is_global) try o.writer().writeAll("static "); - try o.dg.renderFunctionSignature(o.writer(), .Complete); + try o.dg.renderFunctionSignature(o.writer(), .Complete, 0); try o.writer().writeByte(' '); // In case we need to use the header, populate it with a copy of the function @@ -2474,23 +2554,22 @@ pub fn genDecl(o: *Object) !void { if (tv.val.tag() == .extern_fn) { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll("zig_extern "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); + try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); try fwd_decl_writer.writeAll(";\n"); + try genExports(o); } else if (tv.val.castTag(.variable)) |var_payload| { const variable: *Module.Var = var_payload.data; + 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 = if (is_global) .{ - .bytes = mem.span(o.dg.decl.name), - } else .{ - .decl = o.dg.decl_index, - }; + 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 fwd_decl_writer.writeAll(";\n"); + try genExports(o); if (variable.is_extern) return; @@ -2536,7 +2615,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const is_global = dg.declIsGlobal(tv); if (is_global) { try writer.writeAll("zig_extern "); - try dg.renderFunctionSignature(writer, .Complete); + try dg.renderFunctionSignature(writer, .Complete, 0); try dg.fwd_decl.appendSlice(";\n"); } }, @@ -3676,7 +3755,7 @@ fn airCall( }; }; name = f.object.dg.module.declPtr(fn_decl).name; - try f.object.dg.renderDeclName(writer, fn_decl); + try f.object.dg.renderDeclName(writer, fn_decl, 0); break :callee; } // Fall back to function pointer call. @@ -4195,7 +4274,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } } { - const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; + const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; var stack = std.heap.stackFallback(256, f.object.dg.gpa); const allocator = stack.get(); @@ -4204,29 +4283,43 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { var src_i: usize = 0; var dst_i: usize = 0; - while (src_i < asm_source.len) : (src_i += 1) { - fixed_asm_source[dst_i] = asm_source[src_i]; - dst_i += 1; - if (asm_source[src_i] != '%' or src_i + 1 >= asm_source.len) continue; + while (true) { + const literal = mem.sliceTo(asm_source[src_i..], '%'); + src_i += literal.len; + + mem.copy(u8, fixed_asm_source[dst_i..], literal); + dst_i += literal.len; + + if (src_i >= asm_source.len) break; + src_i += 1; + if (src_i >= asm_source.len) + return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); + + fixed_asm_source[dst_i] = '%'; + dst_i += 1; + if (asm_source[src_i] != '[') { - // This handles %% + // This also handles %% fixed_asm_source[dst_i] = asm_source[src_i]; + src_i += 1; dst_i += 1; continue; } - const len = std.mem.indexOfScalar(u8, asm_source[src_i + 1 ..], ']') orelse - return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); - if (std.mem.indexOfScalar(u8, asm_source[src_i + 1 ..][0..len], ':')) |colon| { - const modifier = asm_source[src_i + 1 + colon + 1 .. src_i + 1 + len]; - std.mem.copy(u8, fixed_asm_source[dst_i..], modifier); - dst_i += modifier.len; - const name = asm_source[src_i .. src_i + 1 + colon]; - std.mem.copy(u8, fixed_asm_source[dst_i..], name); + const desc = mem.sliceTo(asm_source[src_i..], ']'); + if (mem.indexOfScalar(u8, desc, ':')) |colon| { + const name = desc[0..colon]; + const modifier = desc[colon + 1 ..]; + + mem.copy(u8, fixed_asm_source[dst_i..], modifier); + dst_i += modifier.len; + mem.copy(u8, fixed_asm_source[dst_i..], name); dst_i += name.len; - src_i += len; + src_i += desc.len; + if (src_i >= asm_source.len) + return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); } } @@ -4535,9 +4628,18 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc else => .none, }; - const field_name: CValue = switch (struct_ty.tag()) { + const FieldLoc = union(enum) { + begin: void, + field: CValue, + end: void, + }; + const field_loc = switch (struct_ty.tag()) { .@"struct" => switch (struct_ty.containerLayout()) { - .Auto, .Extern => CValue{ .identifier = struct_ty.structFieldName(index) }, + .Auto, .Extern => for (struct_ty.structFields().values()[index..]) |field, offset| { + if (field.ty.hasRuntimeBitsIgnoreComptime()) break FieldLoc{ .field = .{ + .identifier = struct_ty.structFieldName(index + offset), + } }; + } else @as(FieldLoc, .end), .Packed => if (field_ptr_info.data.host_size == 0) { const target = f.object.dg.module.getTarget(); @@ -4562,40 +4664,43 @@ 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(CValue, CValue.none), // this @as is needed because of a stage1 bug + } else @as(FieldLoc, .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 .{ + } else if (field_ty.hasRuntimeBitsIgnoreComptime()) FieldLoc{ .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; - for (tuple.values[0..index]) |value| - id += @boolToInt(value.tag() == .unreachable_value); - break :field_name .{ .field = id }; + break :field_name for (tuple.values) |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 => unreachable, }; - if (field_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.writeByte('&'); - if (extra_name != .none) { + 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); - if (field_name != .none) { - try writer.writeByte('.'); - try f.writeCValue(writer, field_name, .Other); - } - } else if (field_name != .none) - try f.writeCValueDerefMember(writer, struct_ptr, field_name) - else - try f.writeCValueDeref(writer, struct_ptr); - } else try f.writeCValue(writer, struct_ptr, .Other); + try writer.writeByte('.'); + try f.writeCValue(writer, field, .Other); + } else try f.writeCValueDerefMember(writer, struct_ptr, field), + } try writer.writeAll(";\n"); return local; } diff --git a/src/link/C.zig b/src/link/C.zig index 4af69e6fcf..a2d7ee143c 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -290,9 +290,17 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) f.remaining_decls.putAssumeCapacityNoClobber(decl_index, {}); } - while (f.remaining_decls.popOrNull()) |kv| { - const decl_index = kv.key; - try self.flushDecl(&f, decl_index); + { + var export_names = std.StringHashMapUnmanaged(void){}; + defer export_names.deinit(gpa); + try export_names.ensureTotalCapacity(gpa, @intCast(u32, module.decl_exports.entries.len)); + for (module.decl_exports.values()) |exports| for (exports.items) |@"export"| + try export_names.put(gpa, @"export".options.name, {}); + + while (f.remaining_decls.popOrNull()) |kv| { + const decl_index = kv.key; + try self.flushDecl(&f, decl_index, export_names); + } } f.all_buffers.items[typedef_index] = .{ @@ -415,7 +423,12 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { } /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. -fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError!void { +fn flushDecl( + self: *C, + f: *Flush, + decl_index: Module.Decl.Index, + export_names: std.StringHashMapUnmanaged(void), +) FlushDeclError!void { const module = self.base.options.module.?; const decl = module.declPtr(decl_index); // Before flushing any particular Decl we must ensure its @@ -423,7 +436,7 @@ fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError! // file comes out correctly. for (decl.dependencies.keys()) |dep| { if (f.remaining_decls.swapRemove(dep)) { - try flushDecl(self, f, dep); + try flushDecl(self, f, dep, export_names); } } @@ -432,7 +445,8 @@ fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError! try self.flushTypedefs(f, decl_block.typedefs); try f.all_buffers.ensureUnusedCapacity(gpa, 2); - f.appendBufAssumeCapacity(decl_block.fwd_decl.items); + if (!(decl.isExtern() and export_names.contains(mem.span(decl.name)))) + f.appendBufAssumeCapacity(decl_block.fwd_decl.items); } pub fn flushEmitH(module: *Module) !void { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 912e7cfdb6..884b6d23c9 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -520,7 +520,6 @@ test "align(@alignOf(T)) T does not force resolution of T" { test "align(N) on functions" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 9f6dac81ca..80df087920 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -788,7 +788,6 @@ test "extern variable with non-pointer opaque type" { return error.SkipZigTest; } if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12680.zig b/test/behavior/bugs/12680.zig index c7bd8f63aa..8ff03272a1 100644 --- a/test/behavior/bugs/12680.zig +++ b/test/behavior/bugs/12680.zig @@ -8,7 +8,6 @@ extern fn test_func() callconv(.C) usize; test "export a function twice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bugs/3112.zig b/test/behavior/bugs/3112.zig index 96c9249d67..91432325fd 100644 --- a/test/behavior/bugs/3112.zig +++ b/test/behavior/bugs/3112.zig @@ -14,7 +14,6 @@ fn prev(p: ?State) void { test "zig test crash" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var global: State = undefined; global.enter = prev; diff --git a/test/behavior/bugs/529.zig b/test/behavior/bugs/529.zig index b111cea564..161cfa1111 100644 --- a/test/behavior/bugs/529.zig +++ b/test/behavior/bugs/529.zig @@ -11,7 +11,6 @@ comptime { const builtin = @import("builtin"); test "issue 529 fixed" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 9058f4e136..014d7e117d 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -184,7 +184,6 @@ test "nested orelse" { } test "self-referential struct through a slice of optional" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 39e1bd10bb..78eb820daa 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1390,7 +1390,6 @@ test "address of zero-bit field is equal to address of only field" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO { const A = struct { b: void = {}, u: u8 };