Skip to content

Commit 1310129

Browse files
authored
Merge pull request #15454 from jacobly0/cbe-extern
cbe: implement `@extern`
2 parents 295b8ca + a1fcb51 commit 1310129

File tree

7 files changed

+84
-70
lines changed

7 files changed

+84
-70
lines changed

lib/zig.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,14 @@ typedef char bool;
188188
#define zig_export(sig, symbol, name) __asm(name " = " symbol)
189189
#endif
190190

191+
#if zig_has_attribute(weak) || defined(zig_gnuc)
192+
#define zig_weak_linkage __attribute__((weak))
193+
#elif _MSC_VER
194+
#define zig_weak_linkage __declspec(selectany)
195+
#else
196+
#define zig_weak_linkage zig_weak_linkage_unavailable
197+
#endif
198+
191199
#if zig_has_builtin(trap)
192200
#define zig_trap() __builtin_trap()
193201
#elif _MSC_VER && (_M_IX86 || _M_X64)

src/Compilation.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5265,7 +5265,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
52655265

52665266
if (comp.bin_file.options.is_test) {
52675267
try buffer.appendSlice(
5268-
\\pub var test_functions: []std.builtin.TestFn = undefined; // overwritten later
5268+
\\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later
52695269
\\
52705270
);
52715271
if (comp.test_evented_io) {

src/Module.zig

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6439,19 +6439,25 @@ pub fn populateTestFunctions(
64396439
errdefer new_decl_arena.deinit();
64406440
const arena = new_decl_arena.allocator();
64416441

6442-
// This copy accesses the old Decl Type/Value so it must be done before `clearValues`.
6443-
const new_ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena));
6444-
const new_val = try Value.Tag.slice.create(arena, .{
6445-
.ptr = try Value.Tag.decl_ref.create(arena, array_decl_index),
6446-
.len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()),
6447-
});
6442+
{
6443+
// This copy accesses the old Decl Type/Value so it must be done before `clearValues`.
6444+
const new_ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena));
6445+
const new_var = try gpa.create(Var);
6446+
errdefer gpa.destroy(new_var);
6447+
new_var.* = decl.val.castTag(.variable).?.data.*;
6448+
new_var.init = try Value.Tag.slice.create(arena, .{
6449+
.ptr = try Value.Tag.decl_ref.create(arena, array_decl_index),
6450+
.len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()),
6451+
});
6452+
const new_val = try Value.Tag.variable.create(arena, new_var);
64486453

6449-
// Since we are replacing the Decl's value we must perform cleanup on the
6450-
// previous value.
6451-
decl.clearValues(mod);
6452-
decl.ty = new_ty;
6453-
decl.val = new_val;
6454-
decl.has_tv = true;
6454+
// Since we are replacing the Decl's value we must perform cleanup on the
6455+
// previous value.
6456+
decl.clearValues(mod);
6457+
decl.ty = new_ty;
6458+
decl.val = new_val;
6459+
decl.has_tv = true;
6460+
}
64556461

64566462
try decl.finalizeNewArena(&new_decl_arena);
64576463
}

src/codegen/c.zig

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ fn isReservedIdent(ident: []const u8) bool {
220220
'A'...'Z', '_' => return true,
221221
else => return false,
222222
}
223-
} else if (std.mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
224-
std.mem.startsWith(u8, ident, "DUMMYUNIONNAME"))
223+
} else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
224+
mem.startsWith(u8, ident, "DUMMYUNIONNAME"))
225225
{ // windows.h
226226
return true;
227227
} else return reserved_idents.has(ident);
@@ -279,8 +279,6 @@ pub const Function = struct {
279279
/// by type alignment.
280280
/// The value is whether the alloc needs to be emitted in the header.
281281
allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{},
282-
/// Needed for memory used by the keys of free_locals_map entries.
283-
arena: std.heap.ArenaAllocator,
284282

285283
fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue {
286284
if (Air.refToIndex(ref)) |inst| {
@@ -481,7 +479,6 @@ pub const Function = struct {
481479
f.object.code.deinit();
482480
f.object.dg.ctypes.deinit(gpa);
483481
f.object.dg.fwd_decl.deinit();
484-
f.arena.deinit();
485482
}
486483
};
487484

@@ -501,7 +498,7 @@ pub const Object = struct {
501498

502499
/// This data is available both when outputting .c code and when outputting an .h file.
503500
pub const DeclGen = struct {
504-
gpa: std.mem.Allocator,
501+
gpa: mem.Allocator,
505502
module: *Module,
506503
decl: ?*Decl,
507504
decl_index: Decl.OptionalIndex,
@@ -539,6 +536,9 @@ pub const DeclGen = struct {
539536
if (func.data.owner_decl != decl_index)
540537
return dg.renderDeclValue(writer, ty, val, func.data.owner_decl, location);
541538

539+
if (decl.val.castTag(.variable)) |var_payload|
540+
try dg.renderFwdDecl(decl_index, var_payload.data);
541+
542542
if (ty.isSlice()) {
543543
if (location == .StaticInitializer) {
544544
try writer.writeByte('{');
@@ -1819,8 +1819,23 @@ pub const DeclGen = struct {
18191819
try dg.writeCValue(writer, member);
18201820
}
18211821

1822-
const IdentHasher = std.crypto.auth.siphash.SipHash128(1, 3);
1823-
const ident_hasher_init: IdentHasher = IdentHasher.init(&[_]u8{0} ** IdentHasher.key_length);
1822+
fn renderFwdDecl(dg: *DeclGen, decl_index: Decl.Index, variable: *Module.Var) !void {
1823+
const decl = dg.module.declPtr(decl_index);
1824+
const fwd_decl_writer = dg.fwd_decl.writer();
1825+
const is_global = dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val }) or variable.is_extern;
1826+
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
1827+
if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal ");
1828+
if (variable.is_weak_linkage) try fwd_decl_writer.writeAll("zig_weak_linkage ");
1829+
try dg.renderTypeAndName(
1830+
fwd_decl_writer,
1831+
decl.ty,
1832+
.{ .decl = decl_index },
1833+
CQualifiers.init(.{ .@"const" = !variable.is_mutable }),
1834+
decl.@"align",
1835+
.complete,
1836+
);
1837+
try fwd_decl_writer.writeAll(";\n");
1838+
}
18241839

18251840
fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index, export_index: u32) !void {
18261841
const decl = dg.module.declPtr(decl_index);
@@ -1829,7 +1844,7 @@ pub const DeclGen = struct {
18291844
if (dg.module.decl_exports.get(decl_index)) |exports| {
18301845
try writer.writeAll(exports.items[export_index].options.name);
18311846
} else if (decl.isExtern()) {
1832-
try writer.writeAll(mem.sliceTo(decl.name, 0));
1847+
try writer.writeAll(mem.span(decl.name));
18331848
} else {
18341849
// MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
18351850
// expand to 3x the length of its input, but let's cut it off at a much shorter limit.
@@ -2396,9 +2411,9 @@ pub fn genErrDecls(o: *Object) !void {
23962411
const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len);
23972412
defer o.dg.gpa.free(name_buf);
23982413

2399-
std.mem.copy(u8, name_buf, name_prefix);
2414+
mem.copy(u8, name_buf, name_prefix);
24002415
for (o.dg.module.error_name_list.items) |name| {
2401-
std.mem.copy(u8, name_buf[name_prefix.len..], name);
2416+
mem.copy(u8, name_buf[name_prefix.len..], name);
24022417
const identifier = name_buf[0 .. name_prefix.len + name.len];
24032418

24042419
var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len };
@@ -2644,21 +2659,16 @@ pub fn genDecl(o: *Object) !void {
26442659
try genExports(o);
26452660
} else if (tv.val.castTag(.variable)) |var_payload| {
26462661
const variable: *Module.Var = var_payload.data;
2647-
2648-
const is_global = o.dg.declIsGlobal(tv) or variable.is_extern;
2649-
const fwd_decl_writer = o.dg.fwd_decl.writer();
2650-
2651-
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2652-
if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal ");
2653-
try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .{}, decl.@"align", .complete);
2654-
try fwd_decl_writer.writeAll(";\n");
2662+
try o.dg.renderFwdDecl(decl_c_value.decl, variable);
26552663
try genExports(o);
26562664

26572665
if (variable.is_extern) return;
26582666

2667+
const is_global = o.dg.declIsGlobal(tv) or variable.is_extern;
26592668
const w = o.writer();
26602669
if (!is_global) try w.writeAll("static ");
26612670
if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
2671+
if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
26622672
if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
26632673
try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.@"align", .complete);
26642674
if (decl.@"linksection" != null) try w.writeAll(", read, write)");
@@ -3396,7 +3406,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
33963406
var deref = is_ptr;
33973407
const is_array = lowersToArray(ret_ty, target);
33983408
const ret_val = if (is_array) ret_val: {
3399-
const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator()));
3409+
const array_local = try f.allocLocal(inst, lowered_ret_ty);
34003410
try writer.writeAll("memcpy(");
34013411
try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
34023412
try writer.writeAll(", ");
@@ -4113,7 +4123,7 @@ fn airCall(
41134123
var lowered_arg_buf: LowerFnRetTyBuffer = undefined;
41144124
const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target);
41154125

4116-
const array_local = try f.allocLocal(inst, try lowered_arg_ty.copy(f.arena.allocator()));
4126+
const array_local = try f.allocLocal(inst, lowered_arg_ty);
41174127
try writer.writeAll("memcpy(");
41184128
try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
41194129
try writer.writeAll(", ");
@@ -4156,7 +4166,7 @@ fn airCall(
41564166
try writer.writeByte(')');
41574167
break :result .none;
41584168
} else {
4159-
const local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator()));
4169+
const local = try f.allocLocal(inst, lowered_ret_ty);
41604170
try f.writeCValue(writer, local, .Other);
41614171
try writer.writeAll(" = ");
41624172
break :result local;
@@ -4732,9 +4742,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
47324742
const locals_begin = @intCast(LocalIndex, f.locals.items.len);
47334743
const constraints_extra_begin = extra_i;
47344744
for (outputs) |output| {
4735-
const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4736-
const constraint = std.mem.sliceTo(extra_bytes, 0);
4737-
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4745+
const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]);
4746+
const constraint = mem.sliceTo(extra_bytes, 0);
4747+
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
47384748
// This equation accounts for the fact that even if we have exactly 4 bytes
47394749
// for the string, we still use the next u32 for the null terminator.
47404750
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
@@ -4764,14 +4774,14 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
47644774
}
47654775
}
47664776
for (inputs) |input| {
4767-
const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4768-
const constraint = std.mem.sliceTo(extra_bytes, 0);
4769-
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4777+
const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]);
4778+
const constraint = mem.sliceTo(extra_bytes, 0);
4779+
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
47704780
// This equation accounts for the fact that even if we have exactly 4 bytes
47714781
// for the string, we still use the next u32 for the null terminator.
47724782
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
47734783

4774-
if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or
4784+
if (constraint.len < 1 or mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or
47754785
(constraint[0] == '{' and constraint[constraint.len - 1] != '}'))
47764786
{
47774787
return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
@@ -4797,7 +4807,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
47974807
}
47984808
}
47994809
for (0..clobbers_len) |_| {
4800-
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4810+
const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
48014811
// This equation accounts for the fact that even if we have exactly 4 bytes
48024812
// for the string, we still use the next u32 for the null terminator.
48034813
extra_i += clobber.len / 4 + 1;
@@ -4862,16 +4872,16 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
48624872
var locals_index = locals_begin;
48634873
try writer.writeByte(':');
48644874
for (outputs, 0..) |output, index| {
4865-
const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4866-
const constraint = std.mem.sliceTo(extra_bytes, 0);
4867-
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4875+
const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]);
4876+
const constraint = mem.sliceTo(extra_bytes, 0);
4877+
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
48684878
// This equation accounts for the fact that even if we have exactly 4 bytes
48694879
// for the string, we still use the next u32 for the null terminator.
48704880
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
48714881

48724882
if (index > 0) try writer.writeByte(',');
48734883
try writer.writeByte(' ');
4874-
if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4884+
if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
48754885
const is_reg = constraint[1] == '{';
48764886
try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
48774887
if (is_reg) {
@@ -4886,16 +4896,16 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
48864896
}
48874897
try writer.writeByte(':');
48884898
for (inputs, 0..) |input, index| {
4889-
const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4890-
const constraint = std.mem.sliceTo(extra_bytes, 0);
4891-
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4899+
const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]);
4900+
const constraint = mem.sliceTo(extra_bytes, 0);
4901+
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
48924902
// This equation accounts for the fact that even if we have exactly 4 bytes
48934903
// for the string, we still use the next u32 for the null terminator.
48944904
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
48954905

48964906
if (index > 0) try writer.writeByte(',');
48974907
try writer.writeByte(' ');
4898-
if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4908+
if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
48994909

49004910
const is_reg = constraint[0] == '{';
49014911
const input_val = try f.resolveInst(input);
@@ -4909,7 +4919,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
49094919
}
49104920
try writer.writeByte(':');
49114921
for (0..clobbers_len) |clobber_i| {
4912-
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4922+
const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
49134923
// This equation accounts for the fact that even if we have exactly 4 bytes
49144924
// for the string, we still use the next u32 for the null terminator.
49154925
extra_i += clobber.len / 4 + 1;
@@ -4924,9 +4934,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
49244934
extra_i = constraints_extra_begin;
49254935
locals_index = locals_begin;
49264936
for (outputs) |output| {
4927-
const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4928-
const constraint = std.mem.sliceTo(extra_bytes, 0);
4929-
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4937+
const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]);
4938+
const constraint = mem.sliceTo(extra_bytes, 0);
4939+
const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
49304940
// This equation accounts for the fact that even if we have exactly 4 bytes
49314941
// for the string, we still use the next u32 for the null terminator.
49324942
extra_i += (constraint.len + name.len + (2 + 3)) / 4;
@@ -5363,7 +5373,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
53635373
};
53645374
const field_int_ty = Type.initPayload(&field_int_pl.base);
53655375

5366-
const temp_local = try f.allocLocal(inst, try field_int_ty.copy(f.arena.allocator()));
5376+
const temp_local = try f.allocLocal(inst, field_int_ty);
53675377
try f.writeCValue(writer, temp_local, .Other);
53685378
try writer.writeAll(" = zig_wrap_");
53695379
try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty);
@@ -7277,7 +7287,7 @@ fn formatIntLiteral(
72777287
var int_buf: Value.BigIntSpace = undefined;
72787288
const int = if (data.val.isUndefDeep()) blk: {
72797289
undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits));
7280-
std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
7290+
mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
72817291

72827292
var undef_int = BigInt.Mutable{
72837293
.limbs = undef_limbs,
@@ -7372,7 +7382,7 @@ fn formatIntLiteral(
73727382
} else {
73737383
try data.cty.renderLiteralPrefix(writer, data.kind);
73747384
wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits);
7375-
std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
7385+
mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
73767386
wrap.len = wrap.limbs.len;
73777387
const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
73787388

src/codegen/c/type.zig

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,17 +1720,7 @@ pub const CType = extern union {
17201720
} else self.init(.anon_struct);
17211721
},
17221722

1723-
.Opaque => switch (ty.tag()) {
1724-
.anyopaque => self.init(.void),
1725-
.@"opaque" => {
1726-
self.storage = .{ .fwd = .{
1727-
.base = .{ .tag = .fwd_struct },
1728-
.data = ty.getOwnerDecl(),
1729-
} };
1730-
self.value = .{ .cty = initPayload(&self.storage.fwd) };
1731-
},
1732-
else => unreachable,
1733-
},
1723+
.Opaque => self.init(.void),
17341724

17351725
.Fn => {
17361726
const info = ty.fnInfo();

src/link/C.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
126126
.indent_writer = undefined, // set later so we can get a pointer to object.code
127127
},
128128
.lazy_fns = lazy_fns.*,
129-
.arena = std.heap.ArenaAllocator.init(gpa),
130129
};
131130

132131
function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };

test/behavior/basic.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ test "extern variable with non-pointer opaque type" {
774774
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
775775
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
776776
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
777+
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
777778

778779
@export(var_to_export, .{ .name = "opaque_extern_var" });
779780
try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);

0 commit comments

Comments
 (0)