@@ -46,10 +46,9 @@ pub fn main() void {
|
||||
|
||||
var leaks: usize = 0;
|
||||
for (test_fn_list) |test_fn, i| {
|
||||
const gpa_works = builtin.zig_backend == .stage1 or builtin.os.tag != .macos;
|
||||
if (gpa_works) std.testing.allocator_instance = .{};
|
||||
std.testing.allocator_instance = .{};
|
||||
defer {
|
||||
if (gpa_works and std.testing.allocator_instance.deinit()) {
|
||||
if (std.testing.allocator_instance.deinit()) {
|
||||
leaks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,6 +1529,7 @@ pub const Var = struct {
|
||||
is_extern: bool,
|
||||
is_mutable: bool,
|
||||
is_threadlocal: bool,
|
||||
is_weak_linkage: bool,
|
||||
|
||||
pub fn deinit(variable: *Var, gpa: Allocator) void {
|
||||
if (variable.lib_name) |lib_name| {
|
||||
|
||||
157
src/Sema.zig
157
src/Sema.zig
@@ -12371,7 +12371,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const type_res = try sema.resolveType(block, src, extra.lhs);
|
||||
try sema.checkPtrType(block, type_src, type_res);
|
||||
_ = try sema.resolveTypeLayout(block, src, type_res.childType());
|
||||
try sema.resolveTypeLayout(block, src, type_res.elemType2());
|
||||
const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
|
||||
|
||||
if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
|
||||
@@ -13019,20 +13019,24 @@ fn resolveExportOptions(
|
||||
) CompileError!std.builtin.ExportOptions {
|
||||
const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");
|
||||
const air_ref = sema.resolveInst(zir_ref);
|
||||
const coerced = try sema.coerce(block, export_options_ty, air_ref, src);
|
||||
const val = try sema.resolveConstValue(block, src, coerced);
|
||||
const fields = val.castTag(.@"struct").?.data;
|
||||
const struct_obj = export_options_ty.castTag(.@"struct").?.data;
|
||||
const name_index = struct_obj.fields.getIndex("name").?;
|
||||
const linkage_index = struct_obj.fields.getIndex("linkage").?;
|
||||
const section_index = struct_obj.fields.getIndex("section").?;
|
||||
if (!fields[section_index].isNull()) {
|
||||
const options = try sema.coerce(block, export_options_ty, air_ref, src);
|
||||
|
||||
const name = try sema.fieldVal(block, src, options, "name", src);
|
||||
const name_val = try sema.resolveConstValue(block, src, name);
|
||||
|
||||
const linkage = try sema.fieldVal(block, src, options, "linkage", src);
|
||||
const linkage_val = try sema.resolveConstValue(block, src, linkage);
|
||||
|
||||
const section = try sema.fieldVal(block, src, options, "section", src);
|
||||
const section_val = try sema.resolveConstValue(block, src, section);
|
||||
|
||||
if (!section_val.isNull()) {
|
||||
return sema.fail(block, src, "TODO: implement exporting with linksection", .{});
|
||||
}
|
||||
const name_ty = Type.initTag(.const_slice_u8);
|
||||
return std.builtin.ExportOptions{
|
||||
.name = try fields[name_index].toAllocatedBytes(name_ty, sema.arena),
|
||||
.linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage),
|
||||
.name = try name_val.toAllocatedBytes(name_ty, sema.arena),
|
||||
.linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
|
||||
.section = null, // TODO
|
||||
};
|
||||
}
|
||||
@@ -13384,17 +13388,19 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
const args = sema.resolveInst(extra.data.args);
|
||||
|
||||
const modifier: std.builtin.CallOptions.Modifier = modifier: {
|
||||
const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
|
||||
const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);
|
||||
const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
|
||||
const fields = options_val.castTag(.@"struct").?.data;
|
||||
const struct_obj = export_options_ty.castTag(.@"struct").?.data;
|
||||
const modifier_index = struct_obj.fields.getIndex("modifier").?;
|
||||
const stack_index = struct_obj.fields.getIndex("stack").?;
|
||||
if (!fields[stack_index].isNull()) {
|
||||
const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
|
||||
const coerced_options = try sema.coerce(block, call_options_ty, options, options_src);
|
||||
|
||||
const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src);
|
||||
const modifier_val = try sema.resolveConstValue(block, options_src, modifier);
|
||||
|
||||
const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src);
|
||||
const stack_val = try sema.resolveConstValue(block, options_src, stack);
|
||||
|
||||
if (!stack_val.isNull()) {
|
||||
return sema.fail(block, options_src, "TODO: implement @call with stack", .{});
|
||||
}
|
||||
break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier);
|
||||
break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier);
|
||||
};
|
||||
|
||||
const args_ty = sema.typeOf(args);
|
||||
@@ -13743,6 +13749,7 @@ fn zirVarExtended(
|
||||
.is_extern = small.is_extern,
|
||||
.is_mutable = true, // TODO get rid of this unused field
|
||||
.is_threadlocal = small.is_threadlocal,
|
||||
.is_weak_linkage = false,
|
||||
.lib_name = null,
|
||||
};
|
||||
|
||||
@@ -13937,7 +13944,103 @@ fn zirBuiltinExtern(
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
|
||||
const src: LazySrcLoc = .{ .node_offset = extra.node };
|
||||
return sema.fail(block, src, "TODO: implement Sema.zirBuiltinExtern", .{});
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
|
||||
const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
|
||||
|
||||
var ty = try sema.resolveType(block, ty_src, extra.lhs);
|
||||
const options_inst = sema.resolveInst(extra.rhs);
|
||||
|
||||
const options = options: {
|
||||
const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions");
|
||||
const options = try sema.coerce(block, extern_options_ty, options_inst, options_src);
|
||||
|
||||
const name = try sema.fieldVal(block, options_src, options, "name", options_src);
|
||||
const name_val = try sema.resolveConstValue(block, options_src, name);
|
||||
|
||||
const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src);
|
||||
const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst);
|
||||
|
||||
const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src);
|
||||
const linkage_val = try sema.resolveConstValue(block, options_src, linkage);
|
||||
|
||||
const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src);
|
||||
const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local);
|
||||
|
||||
var library_name: ?[]const u8 = null;
|
||||
if (!library_name_val.isNull()) {
|
||||
const payload = library_name_val.castTag(.opt_payload).?.data;
|
||||
library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena);
|
||||
}
|
||||
|
||||
break :options std.builtin.ExternOptions{
|
||||
.name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena),
|
||||
.library_name = library_name,
|
||||
.linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
|
||||
.is_thread_local = is_thread_local_val.toBool(),
|
||||
};
|
||||
};
|
||||
|
||||
if (!ty.isPtrAtRuntime()) {
|
||||
return sema.fail(block, options_src, "expected (optional) pointer", .{});
|
||||
}
|
||||
|
||||
if (options.name.len == 0) {
|
||||
return sema.fail(block, options_src, "extern symbol name cannot be empty", .{});
|
||||
}
|
||||
|
||||
if (options.linkage != .Weak and options.linkage != .Strong) {
|
||||
return sema.fail(block, options_src, "extern symbol must use strong or weak linkage", .{});
|
||||
}
|
||||
|
||||
if (options.linkage == .Weak and !ty.ptrAllowsZero()) {
|
||||
ty = try Type.optional(sema.arena, ty);
|
||||
}
|
||||
|
||||
// TODO check duplicate extern
|
||||
|
||||
const new_decl = try sema.mod.allocateNewDecl(try sema.gpa.dupeZ(u8, options.name), sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
|
||||
errdefer new_decl.destroy(sema.mod);
|
||||
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_decl_arena.deinit();
|
||||
const new_decl_arena_allocator = new_decl_arena.allocator();
|
||||
|
||||
const new_var = try new_decl_arena_allocator.create(Module.Var);
|
||||
errdefer new_decl_arena_allocator.destroy(new_var);
|
||||
|
||||
new_var.* = .{
|
||||
.owner_decl = sema.owner_decl,
|
||||
.init = Value.initTag(.unreachable_value),
|
||||
.is_extern = true,
|
||||
.is_mutable = false, // TODO get rid of this unused field
|
||||
.is_threadlocal = options.is_thread_local,
|
||||
.is_weak_linkage = options.linkage == .Weak,
|
||||
.lib_name = null,
|
||||
};
|
||||
|
||||
if (options.library_name) |library_name| {
|
||||
if (library_name.len == 0) {
|
||||
return sema.fail(block, options_src, "library name name cannot be empty", .{});
|
||||
}
|
||||
new_var.lib_name = try sema.handleExternLibName(block, options_src, library_name);
|
||||
}
|
||||
|
||||
new_decl.src_line = sema.owner_decl.src_line;
|
||||
new_decl.ty = try ty.copy(new_decl_arena_allocator);
|
||||
new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
|
||||
new_decl.align_val = Value.@"null";
|
||||
new_decl.linksection_val = Value.@"null";
|
||||
new_decl.has_tv = true;
|
||||
new_decl.analysis = .complete;
|
||||
new_decl.generation = sema.mod.generation;
|
||||
|
||||
const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
|
||||
arena_state.* = new_decl_arena.state;
|
||||
new_decl.value_arena = arena_state;
|
||||
|
||||
const ref = try sema.analyzeDeclRef(new_decl);
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBitCast(ty, ref);
|
||||
}
|
||||
|
||||
fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
|
||||
@@ -15409,6 +15512,14 @@ fn coerce(
|
||||
return sema.addConstant(dest_ty, Value.@"null");
|
||||
}
|
||||
|
||||
// cast from ?*T and ?[*]T to ?*anyopaque
|
||||
// but don't do it if the source type is a double pointer
|
||||
if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and
|
||||
inst_ty.isPtrLikeOptional() and inst_ty.elemType2().zigTypeTag() != .Pointer)
|
||||
{
|
||||
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
|
||||
}
|
||||
|
||||
// T to ?T
|
||||
const child_type = try dest_ty.optionalChildAlloc(sema.arena);
|
||||
const intermediate = try sema.coerce(block, child_type, inst, inst_src);
|
||||
@@ -16688,6 +16799,7 @@ fn coerceCompatiblePtrs(
|
||||
inst: Air.Inst.Ref,
|
||||
inst_src: LazySrcLoc,
|
||||
) !Air.Inst.Ref {
|
||||
// TODO check const/volatile/alignment
|
||||
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
|
||||
// The comptime Value representation is compatible with both types.
|
||||
return sema.addConstant(dest_ty, val);
|
||||
@@ -18403,6 +18515,7 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE
|
||||
if (inferred_error_set.is_resolved) {
|
||||
return;
|
||||
}
|
||||
inferred_error_set.is_resolved = true;
|
||||
|
||||
var it = inferred_error_set.inferred_error_sets.keyIterator();
|
||||
while (it.next()) |other_error_set_ptr| {
|
||||
@@ -18423,8 +18536,6 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE
|
||||
if (other_error_set_ptr.*.is_anyerror)
|
||||
inferred_error_set.is_anyerror = true;
|
||||
}
|
||||
|
||||
inferred_error_set.is_resolved = true;
|
||||
}
|
||||
|
||||
fn resolveInferredErrorSetTy(sema: *Sema, ty: Type) CompileError!void {
|
||||
|
||||
@@ -548,6 +548,10 @@ pub const Object = struct {
|
||||
llvm_global.setValueName(decl.name);
|
||||
llvm_global.setUnnamedAddr(.False);
|
||||
llvm_global.setLinkage(.External);
|
||||
if (decl.val.castTag(.variable)) |variable| {
|
||||
if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
|
||||
if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
|
||||
}
|
||||
} else if (exports.len != 0) {
|
||||
const exp_name = exports[0].options.name;
|
||||
llvm_global.setValueName2(exp_name.ptr, exp_name.len);
|
||||
@@ -558,6 +562,9 @@ pub const Object = struct {
|
||||
.Weak => llvm_global.setLinkage(.WeakODR),
|
||||
.LinkOnce => llvm_global.setLinkage(.LinkOnceODR),
|
||||
}
|
||||
if (decl.val.castTag(.variable)) |variable| {
|
||||
if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
|
||||
}
|
||||
// If a Decl is exported more than one time (which is rare),
|
||||
// we add aliases for all but the first export.
|
||||
// TODO LLVM C API does not support deleting aliases. We need to
|
||||
@@ -788,8 +795,15 @@ pub const DeclGen = struct {
|
||||
const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
|
||||
gop.value_ptr.* = llvm_global;
|
||||
|
||||
const is_extern = decl.val.tag() == .unreachable_value;
|
||||
if (!is_extern) {
|
||||
if (decl.isExtern()) {
|
||||
llvm_global.setValueName(decl.name);
|
||||
llvm_global.setUnnamedAddr(.False);
|
||||
llvm_global.setLinkage(.External);
|
||||
if (decl.val.castTag(.variable)) |variable| {
|
||||
if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
|
||||
if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
|
||||
}
|
||||
} else {
|
||||
llvm_global.setLinkage(.Internal);
|
||||
llvm_global.setUnnamedAddr(.True);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,9 @@ pub const Value = opaque {
|
||||
pub const setUnnamedAddr = LLVMSetUnnamedAddr;
|
||||
extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void;
|
||||
|
||||
pub const setThreadLocalMode = LLVMSetThreadLocalMode;
|
||||
extern fn LLVMSetThreadLocalMode(Global: *const Value, Mode: ThreadLocalMode) void;
|
||||
|
||||
pub const deleteGlobal = LLVMDeleteGlobal;
|
||||
extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;
|
||||
|
||||
@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {
|
||||
LinkerPrivateWeak,
|
||||
};
|
||||
|
||||
pub const ThreadLocalMode = enum(c_uint) {
|
||||
NotThreadLocal,
|
||||
GeneralDynamicTLSModel,
|
||||
LocalDynamicTLSModel,
|
||||
InitialExecTLSModel,
|
||||
LocalExecTLSModel,
|
||||
};
|
||||
|
||||
pub const AtomicOrdering = enum(c_uint) {
|
||||
NotAtomic = 0,
|
||||
Unordered = 1,
|
||||
|
||||
17
src/type.zig
17
src/type.zig
@@ -2168,7 +2168,14 @@ pub const Type = extern union {
|
||||
.mut_slice,
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
=> return self.cast(Payload.ElemType).?.data.abiAlignment(target),
|
||||
=> {
|
||||
const child_type = self.cast(Payload.ElemType).?.data;
|
||||
if (child_type.zigTypeTag() == .Opaque) {
|
||||
return 1;
|
||||
} else {
|
||||
return child_type.abiAlignment(target);
|
||||
}
|
||||
},
|
||||
|
||||
.manyptr_u8,
|
||||
.manyptr_const_u8,
|
||||
@@ -2181,10 +2188,13 @@ pub const Type = extern union {
|
||||
const ptr_info = self.castTag(.pointer).?.data;
|
||||
if (ptr_info.@"align" != 0) {
|
||||
return ptr_info.@"align";
|
||||
} else if (ptr_info.pointee_type.zigTypeTag() == .Opaque) {
|
||||
return 1;
|
||||
} else {
|
||||
return ptr_info.pointee_type.abiAlignment(target);
|
||||
}
|
||||
},
|
||||
.optional => return self.castTag(.optional).?.data.ptrAlignment(target),
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
@@ -3235,8 +3245,9 @@ pub const Type = extern union {
|
||||
return child_ty;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO handle optionals
|
||||
.optional => ty.castTag(.optional).?.data.childType(),
|
||||
.optional_single_mut_pointer => ty.castPointer().?.data,
|
||||
.optional_single_const_pointer => ty.castPointer().?.data,
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user