diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 75fb373d9b..42180c6529 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -822,7 +822,7 @@ pub const Object = struct { type_map: TypeMap, di_type_map: DITypeMap, /// The LLVM global table which holds the names corresponding to Zig errors. - /// Note that the values are not added until flushModule, when all errors in + /// Note that the values are not added until `emit`, when all errors in /// the compilation are known. error_name_table: Builder.Variable.Index, /// This map is usually very close to empty. It tracks only the cases when a @@ -850,7 +850,7 @@ pub const Object = struct { pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); - /// This is an ArrayHashMap as opposed to a HashMap because in `flushModule` we + /// This is an ArrayHashMap as opposed to a HashMap because in `emit` we /// want to iterate over it while adding entries to it. pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr); @@ -1026,17 +1026,6 @@ pub const Object = struct { self.* = undefined; } - fn locPath( - arena: Allocator, - opt_loc: ?Compilation.EmitLoc, - cache_directory: Compilation.Directory, - ) !?[*:0]u8 { - const loc = opt_loc orelse return null; - const directory = loc.directory orelse cache_directory; - const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename}); - return slice.ptr; - } - fn genErrorNameTable(o: *Object) Allocator.Error!void { // If o.error_name_table is null, then it was not referenced by any instructions. if (o.error_name_table == .none) return; @@ -1175,12 +1164,22 @@ pub const Object = struct { } } - pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { - var sub_prog_node = prog_node.start("LLVM Emit Object", 0); - sub_prog_node.activate(); - sub_prog_node.context.refresh(); - defer sub_prog_node.end(); + pub const EmitOptions = struct { + pre_ir_path: ?[]const u8, + pre_bc_path: ?[]const u8, + bin_path: ?[*:0]const u8, + emit_asm: ?[*:0]const u8, + post_ir_path: ?[*:0]const u8, + post_bc_path: ?[*:0]const u8, + is_debug: bool, + is_small: bool, + time_report: bool, + sanitize_thread: bool, + lto: bool, + }; + + pub fn emit(self: *Object, options: EmitOptions) !void { try self.resolveExportExternCollisions(); try self.genErrorNameTable(); try self.genCmpLtErrorsLenFunction(); @@ -1206,7 +1205,7 @@ pub const Object = struct { dib.finalize(); } - if (comp.verbose_llvm_ir) |path| { + if (options.pre_ir_path) |path| { if (std.mem.eql(u8, path, "-")) { self.builder.dump(); } else { @@ -1214,91 +1213,72 @@ pub const Object = struct { } } - if (comp.verbose_llvm_bc) |path| _ = try self.builder.writeBitcodeToFile(path); - - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const mod = comp.module.?; - const cache_dir = mod.zig_cache_artifact_directory; + if (options.pre_bc_path) |path| _ = try self.builder.writeBitcodeToFile(path); if (std.debug.runtime_safety and !try self.builder.verify()) { - if (try locPath(arena, comp.emit_llvm_ir, cache_dir)) |emit_llvm_ir_path| - _ = self.builder.printToFileZ(emit_llvm_ir_path); @panic("LLVM module verification failed"); } - var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| - try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?)) - else - null; - - const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); - var emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); - const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir); - - const emit_asm_msg = emit_asm_path orelse "(none)"; - const emit_bin_msg = emit_bin_path orelse "(none)"; - const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)"; - const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)"; + const emit_asm_msg = options.asm_path orelse "(none)"; + const emit_bin_msg = options.bin_path orelse "(none)"; + const post_llvm_ir_msg = options.post_ir_path orelse "(none)"; + const post_llvm_bc_msg = options.post_bc_path orelse "(none)"; log.debug("emit LLVM object asm={s} bin={s} ir={s} bc={s}", .{ - emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg, + emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, }); - if (emit_asm_path == null and emit_bin_path == null and - emit_llvm_ir_path == null and emit_llvm_bc_path == null) return; + if (options.asm_path == null and options.bin_path == null and + options.post_ir_path == null and options.post_bc_path == null) return; - if (!self.builder.useLibLlvm()) { - log.err("emitting without libllvm not implemented", .{}); - return error.FailedToEmit; - } + if (!self.builder.useLibLlvm()) unreachable; // caught in Compilation.Config.resolve // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. // So we call the entire pipeline multiple times if this is requested. var error_message: [*:0]const u8 = undefined; - if (emit_asm_path != null and emit_bin_path != null) { + var emit_bin_path = options.bin_path; + var post_ir_path = options.post_ir_path; + if (options.asm_path != null and options.bin_path != null) { if (self.target_machine.emitToFile( self.builder.llvm.module.?, &error_message, - comp.bin_file.options.optimize_mode == .Debug, - comp.bin_file.options.optimize_mode == .ReleaseSmall, - comp.time_report, - comp.bin_file.options.tsan, - comp.bin_file.options.lto, + options.is_debug, + options.is_small, + options.time_report, + options.sanitize_thread, + options.lto, null, emit_bin_path, - emit_llvm_ir_path, + post_ir_path, null, )) { defer llvm.disposeMessage(error_message); log.err("LLVM failed to emit bin={s} ir={s}: {s}", .{ - emit_bin_msg, emit_llvm_ir_msg, error_message, + emit_bin_msg, post_llvm_ir_msg, error_message, }); return error.FailedToEmit; } emit_bin_path = null; - emit_llvm_ir_path = null; + post_ir_path = null; } if (self.target_machine.emitToFile( self.builder.llvm.module.?, &error_message, - comp.bin_file.options.optimize_mode == .Debug, - comp.bin_file.options.optimize_mode == .ReleaseSmall, - comp.time_report, - comp.bin_file.options.tsan, - comp.bin_file.options.lto, - emit_asm_path, + options.is_debug, + options.is_small, + options.time_report, + options.sanitize_thread, + options.lto, + options.asm_path, emit_bin_path, - emit_llvm_ir_path, - emit_llvm_bc_path, + post_ir_path, + options.post_bc_path, )) { defer llvm.disposeMessage(error_message); log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ - emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg, + emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, }); return error.FailedToEmit; @@ -1307,17 +1287,19 @@ pub const Object = struct { pub fn updateFunc( o: *Object, - mod: *Module, + zcu: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness, ) !void { - const func = mod.funcInfo(func_index); + const func = zcu.funcInfo(func_index); const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); - const fn_info = mod.typeToFunc(decl.ty).?; - const target = mod.getTarget(); - const ip = &mod.intern_pool; + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + const fn_info = zcu.typeToFunc(decl.ty).?; + const target = zcu.getTarget(); + const ip = &zcu.intern_pool; var dg: DeclGen = .{ .object = o, @@ -1352,7 +1334,7 @@ pub const Object = struct { } // TODO: disable this if safety is off for the function scope - const ssp_buf_size = mod.comp.bin_file.options.stack_protector; + const ssp_buf_size = owner_mod.stack_protector; if (ssp_buf_size != 0) { try attributes.addFnAttr(.sspstrong, &o.builder); try attributes.addFnAttr(.{ .string = .{ @@ -1362,7 +1344,7 @@ pub const Object = struct { } // TODO: disable this if safety is off for the function scope - if (mod.comp.bin_file.options.stack_check) { + if (owner_mod.stack_check) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("probe-stack"), .value = try o.builder.string("__zig_probe_stack"), @@ -1385,20 +1367,22 @@ pub const Object = struct { var llvm_arg_i: u32 = 0; // This gets the LLVM values from the function and stores them in `dg.args`. - const sret = firstParamSRet(fn_info, mod); + const sret = firstParamSRet(fn_info, zcu); const ret_ptr: Builder.Value = if (sret) param: { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; break :param param; } else .none; - if (ccAbiPromoteInt(fn_info.cc, mod, Type.fromInterned(fn_info.return_type))) |s| switch (s) { + if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { .signed => try attributes.addRetAttr(.signext, &o.builder), .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), }; - const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.config.any_error_tracing; + const comp = zcu.comp; + + const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(zcu) and + comp.config.any_error_tracing; const err_ret_trace: Builder.Value = if (err_return_tracing) param: { const param = wip.arg(llvm_arg_i); @@ -1426,8 +1410,8 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); const param = wip.arg(llvm_arg_i); - if (isByRef(param_ty, mod)) { - const alignment = param_ty.abiAlignment(mod).toLlvm(); + if (isByRef(param_ty, zcu)) { + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const param_llvm_ty = param.typeOfWip(&wip); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); @@ -1443,12 +1427,12 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); const param = wip.arg(llvm_arg_i); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); try o.addByRefParamAttrs(&attributes, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty); llvm_arg_i += 1; - if (isByRef(param_ty, mod)) { + if (isByRef(param_ty, zcu)) { args.appendAssumeCapacity(param); } else { args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); @@ -1458,12 +1442,12 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); const param = wip.arg(llvm_arg_i); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); try attributes.addParamAttr(llvm_arg_i, .noundef, &o.builder); llvm_arg_i += 1; - if (isByRef(param_ty, mod)) { + if (isByRef(param_ty, zcu)) { args.appendAssumeCapacity(param); } else { args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); @@ -1476,11 +1460,11 @@ pub const Object = struct { llvm_arg_i += 1; const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1488,14 +1472,14 @@ pub const Object = struct { .slice => { assert(!it.byval_attr); const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); - const ptr_info = param_ty.ptrInfo(mod); + const ptr_info = param_ty.ptrInfo(zcu); if (math.cast(u5, it.zig_index - 1)) |i| { if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); } } - if (param_ty.zigTypeTag(mod) != .Optional) { + if (param_ty.zigTypeTag(zcu) != .Optional) { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); } if (ptr_info.flags.is_const) { @@ -1504,7 +1488,7 @@ pub const Object = struct { const elem_align = (if (ptr_info.flags.alignment != .none) @as(InternPool.Alignment, ptr_info.flags.alignment) else - Type.fromInterned(ptr_info.child).abiAlignment(mod).max(.@"1")).toLlvm(); + Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); const ptr_param = wip.arg(llvm_arg_i); llvm_arg_i += 1; @@ -1521,7 +1505,7 @@ pub const Object = struct { const field_types = it.types_buffer[0..it.types_len]; const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); - const param_alignment = param_ty.abiAlignment(mod).toLlvm(); + const param_alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, param_alignment, target); const llvm_ty = try o.builder.structType(.normal, field_types); for (0..field_types.len) |field_i| { @@ -1533,7 +1517,7 @@ pub const Object = struct { _ = try wip.store(.normal, param, field_ptr, alignment); } - const is_by_ref = isByRef(param_ty, mod); + const is_by_ref = isByRef(param_ty, zcu); args.appendAssumeCapacity(if (is_by_ref) arg_ptr else @@ -1551,11 +1535,11 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1566,11 +1550,11 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1584,14 +1568,12 @@ pub const Object = struct { var di_file: ?if (build_options.have_llvm) *llvm.DIFile else noreturn = null; var di_scope: ?if (build_options.have_llvm) *llvm.DIScope else noreturn = null; - const namespace = mod.namespacePtr(decl.src_namespace); - if (o.di_builder) |dib| { di_file = try o.getDIFile(gpa, namespace.file_scope); const line_number = decl.src_line + 1; - const is_internal_linkage = decl.val.getExternFunc(mod) == null and - !mod.decl_exports.contains(decl_index); + const is_internal_linkage = decl.val.getExternFunc(zcu) == null and + !zcu.decl_exports.contains(decl_index); const noret_bit: c_uint = if (fn_info.return_type == .noreturn_type) llvm.DIFlags.NoReturn else @@ -1608,7 +1590,7 @@ pub const Object = struct { true, // is definition line_number + func.lbrace_line, // scope line llvm.DIFlags.StaticMember | noret_bit, - mod.comp.bin_file.options.optimize_mode != .Debug, + owner_mod.optimize_mode != .Debug, null, // decl_subprogram ); try o.di_map.put(gpa, decl, subprogram.toNode()); @@ -1618,8 +1600,6 @@ pub const Object = struct { di_scope = subprogram.toScope(); } - const single_threaded = namespace.file_scope.mod.single_threaded; - var fg: FuncGen = .{ .gpa = gpa, .air = air, @@ -1631,7 +1611,7 @@ pub const Object = struct { .arg_index = 0, .func_inst_table = .{}, .blocks = .{}, - .sync_scope = if (single_threaded) .singlethread else .system, + .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, .di_scope = di_scope, .di_file = di_file, .base_line = dg.decl.src_line, @@ -1645,7 +1625,7 @@ pub const Object = struct { fg.genBody(air.getMainBody()) catch |err| switch (err) { error.CodegenFail => { decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, dg.err_msg.?); + try zcu.failed_decls.put(zcu.gpa, decl_index, dg.err_msg.?); dg.err_msg = null; return; }, @@ -1654,7 +1634,7 @@ pub const Object = struct { try fg.wip.finish(); - try o.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); + try o.updateExports(zcu, .{ .decl_index = decl_index }, zcu.getDeclExports(decl_index)); } pub fn updateDecl(self: *Object, module: *Module, decl_index: InternPool.DeclIndex) !void { @@ -2933,22 +2913,24 @@ pub const Object = struct { o: *Object, decl_index: InternPool.DeclIndex, ) Allocator.Error!Builder.Function.Index { - const mod = o.module; - const ip = &mod.intern_pool; + const zcu = o.module; + const ip = &zcu.intern_pool; const gpa = o.gpa; - const decl = mod.declPtr(decl_index); + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; const zig_fn_type = decl.ty; const gop = try o.decl_map.getOrPut(gpa, decl_index); if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function; assert(decl.has_tv); - const fn_info = mod.typeToFunc(zig_fn_type).?; - const target = mod.getTarget(); - const sret = firstParamSRet(fn_info, mod); + const fn_info = zcu.typeToFunc(zig_fn_type).?; + const target = owner_mod.resolved_target.result; + const sret = firstParamSRet(fn_info, zcu); const function_index = try o.builder.addFunction( try o.lowerType(zig_fn_type), - try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod))), + try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(zcu))), toLlvmAddressSpace(decl.@"addrspace", target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -2956,7 +2938,7 @@ pub const Object = struct { var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - const is_extern = decl.isExtern(mod); + const is_extern = decl.isExtern(zcu); if (!is_extern) { function_index.setLinkage(.internal, &o.builder); function_index.setUnnamedAddr(.unnamed_addr, &o.builder); @@ -2966,7 +2948,7 @@ pub const Object = struct { .kind = try o.builder.string("wasm-import-name"), .value = try o.builder.string(ip.stringToSlice(decl.name)), } }, &o.builder); - if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(mod).?.lib_name)) |lib_name| { + if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(zcu).?.lib_name)) |lib_name| { if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("wasm-import-module"), .value = try o.builder.string(lib_name), @@ -2987,8 +2969,8 @@ pub const Object = struct { llvm_arg_i += 1; } - const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.config.any_error_tracing; + const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(zcu) and + zcu.comp.config.any_error_tracing; if (err_return_tracing) { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); @@ -3009,7 +2991,7 @@ pub const Object = struct { function_index.setAlignment(fn_info.alignment.toLlvm(), &o.builder); // Function attributes that are independent of analysis results of the function body. - try o.addCommonFnAttributes(&attributes); + try o.addCommonFnAttributes(&attributes, owner_mod); if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); @@ -3022,14 +3004,14 @@ pub const Object = struct { .byval => { const param_index = it.zig_index - 1; const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); - if (!isByRef(param_ty, mod)) { + if (!isByRef(param_ty, zcu)) { try o.addByValParamAttrs(&attributes, param_ty, param_index, fn_info, it.llvm_index - 1); } }, .byref => { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod); + const alignment = param_ty.abiAlignment(zcu); try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); }, .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), @@ -3055,13 +3037,14 @@ pub const Object = struct { fn addCommonFnAttributes( o: *Object, attributes: *Builder.FunctionAttributes.Wip, + owner_mod: *Package.Module, ) Allocator.Error!void { const comp = o.module.comp; - if (!comp.bin_file.options.red_zone) { + if (!owner_mod.red_zone) { try attributes.addFnAttr(.noredzone, &o.builder); } - if (comp.bin_file.options.omit_frame_pointer) { + if (owner_mod.omit_frame_pointer) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("frame-pointer"), .value = try o.builder.string("none"), @@ -3073,7 +3056,7 @@ pub const Object = struct { } }, &o.builder); } try attributes.addFnAttr(.nounwind, &o.builder); - if (comp.unwind_tables) { + if (owner_mod.unwind_tables) { try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); } if (comp.skip_linker_dependencies or comp.no_builtin) { @@ -3084,26 +3067,27 @@ pub const Object = struct { // overflow instead of performing memcpy. try attributes.addFnAttr(.nobuiltin, &o.builder); } - if (comp.bin_file.options.optimize_mode == .ReleaseSmall) { + if (owner_mod.optimize_mode == .ReleaseSmall) { try attributes.addFnAttr(.minsize, &o.builder); try attributes.addFnAttr(.optsize, &o.builder); } - if (comp.bin_file.options.tsan) { + if (owner_mod.sanitize_thread) { try attributes.addFnAttr(.sanitize_thread, &o.builder); } - if (comp.getTarget().cpu.model.llvm_name) |s| { + const target = owner_mod.resolved_target.result; + if (target.cpu.model.llvm_name) |s| { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("target-cpu"), .value = try o.builder.string(s), } }, &o.builder); } - if (comp.bin_file.options.llvm_cpu_features) |s| { + if (owner_mod.resolved_target.llvm_cpu_features) |s| { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("target-features"), .value = try o.builder.string(std.mem.span(s)), } }, &o.builder); } - if (comp.getTarget().cpu.arch.isBpf()) { + if (target.cpu.arch.isBpf()) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("no-builtins"), .value = .empty, @@ -4646,6 +4630,100 @@ pub const Object = struct { .field_index = @intCast(field_index), }); } + + fn getCmpLtErrorsLenFunction(o: *Object) !Builder.Function.Index { + const name = try o.builder.string(lt_errors_fn_name); + if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; + + const zcu = o.module; + const target = zcu.root_mod.resolved_target.result; + const function_index = try o.builder.addFunction( + try o.builder.fnType(.i1, &.{try o.errorIntType()}, .normal), + name, + toLlvmAddressSpace(.generic, target), + ); + + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); + + function_index.setLinkage(.internal, &o.builder); + function_index.setCallConv(.fastcc, &o.builder); + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + return function_index; + } + + fn getEnumTagNameFunction(o: *Object, enum_ty: Type) !Builder.Function.Index { + const zcu = o.module; + const ip = &zcu.intern_pool; + const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type; + + // TODO: detect when the type changes and re-emit this function. + const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); + if (gop.found_existing) return gop.value_ptr.ptrConst(&o.builder).kind.function; + errdefer assert(o.decl_map.remove(enum_type.decl)); + + const usize_ty = try o.lowerType(Type.usize); + const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); + const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const target = zcu.root_mod.resolved_target.result; + const function_index = try o.builder.addFunction( + try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(ip)}), + toLlvmAddressSpace(.generic, target), + ); + + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); + + function_index.setLinkage(.internal, &o.builder); + function_index.setCallConv(.fastcc, &o.builder); + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + gop.value_ptr.* = function_index.ptrConst(&o.builder).global; + + var wip = try Builder.WipFunction.init(&o.builder, function_index); + defer wip.deinit(); + wip.cursor = .{ .block = try wip.block(0, "Entry") }; + + const bad_value_block = try wip.block(1, "BadValue"); + const tag_int_value = wip.arg(0); + var wip_switch = + try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); + defer wip_switch.finish(&wip); + + for (0..enum_type.names.len) |field_index| { + const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index])); + const name_init = try o.builder.stringNullConst(name); + const name_variable_index = + try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); + try name_variable_index.setInitializer(name_init, &o.builder); + name_variable_index.setLinkage(.private, &o.builder); + name_variable_index.setMutability(.constant, &o.builder); + name_variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); + name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); + + const name_val = try o.builder.structValue(ret_ty, &.{ + name_variable_index.toConst(&o.builder), + try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), + }); + + const return_block = try wip.block(1, "Name"); + const this_tag_int_value = try o.lowerValue( + (try zcu.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), + ); + try wip_switch.addCase(this_tag_int_value, return_block, &wip); + + wip.cursor = .{ .block = return_block }; + _ = try wip.ret(name_val); + } + + wip.cursor = .{ .block = bad_value_block }; + _ = try wip.@"unreachable"(); + + try wip.finish(); + return function_index; + } }; pub const DeclGen = struct { @@ -4654,6 +4732,13 @@ pub const DeclGen = struct { decl_index: InternPool.DeclIndex, err_msg: ?*Module.ErrorMsg, + fn ownerModule(dg: DeclGen) *Package.Module { + const o = dg.object; + const zcu = o.module; + const namespace = zcu.namespacePtr(dg.decl.src_namespace); + return namespace.file_scope.mod; + } + fn todo(dg: *DeclGen, comptime format: []const u8, args: anytype) Error { @setCold(true); assert(dg.err_msg == null); @@ -5614,7 +5699,7 @@ pub const FuncGen = struct { const o = self.dg.object; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try self.resolveInst(un_op); - const llvm_fn = try self.getCmpLtErrorsLenFunction(); + const llvm_fn = try o.getCmpLtErrorsLenFunction(); return self.wip.call( .normal, .fastcc, @@ -6547,11 +6632,13 @@ pub const FuncGen = struct { const dib = o.di_builder orelse return .none; const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = o.module; - const func = mod.funcInfo(ty_fn.func); + const zcu = o.module; + const func = zcu.funcInfo(ty_fn.func); const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); - const di_file = try o.getDIFile(self.gpa, mod.namespacePtr(decl.src_namespace).file_scope); + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + const di_file = try o.getDIFile(self.gpa, zcu.namespacePtr(decl.src_namespace).file_scope); self.di_file = di_file; const line_number = decl.src_line + 1; const cur_debug_location = self.wip.llvm.builder.getCurrentDebugLocation2(); @@ -6562,18 +6649,18 @@ pub const FuncGen = struct { .base_line = self.base_line, }); - const fqn = try decl.getFullyQualifiedName(mod); + const fqn = try decl.getFullyQualifiedName(zcu); - const is_internal_linkage = !mod.decl_exports.contains(decl_index); - const fn_ty = try mod.funcType(.{ + const is_internal_linkage = !zcu.decl_exports.contains(decl_index); + const fn_ty = try zcu.funcType(.{ .param_types = &.{}, .return_type = .void_type, }); const fn_di_ty = try o.lowerDebugType(fn_ty, .full); const subprogram = dib.createFunction( di_file.toScope(), - mod.intern_pool.stringToSlice(decl.name), - mod.intern_pool.stringToSlice(fqn), + zcu.intern_pool.stringToSlice(decl.name), + zcu.intern_pool.stringToSlice(fqn), di_file, line_number, fn_di_ty, @@ -6581,7 +6668,7 @@ pub const FuncGen = struct { true, // is definition line_number + func.lbrace_line, // scope line llvm.DIFlags.StaticMember, - mod.comp.bin_file.options.optimize_mode != .Debug, + owner_mod.optimize_mode != .Debug, null, // decl_subprogram ); @@ -6676,11 +6763,12 @@ pub const FuncGen = struct { null; const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); const insert_block = self.wip.cursor.block.toLlvm(&self.wip); - const mod = o.module; - if (isByRef(operand_ty, mod)) { + const zcu = o.module; + const owner_mod = self.dg.ownerModule(); + if (isByRef(operand_ty, zcu)) { _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { - const alignment = operand_ty.abiAlignment(mod).toLlvm(); + } else if (owner_mod.optimize_mode == .Debug) { + const alignment = operand_ty.abiAlignment(zcu).toLlvm(); const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, operand, alloca, alignment); _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); @@ -8729,9 +8817,10 @@ pub const FuncGen = struct { const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?, null); const insert_block = self.wip.cursor.block.toLlvm(&self.wip); + const owner_mod = self.dg.ownerModule(); if (isByRef(inst_ty, mod)) { _ = dib.insertDeclareAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { + } else if (owner_mod.optimize_mode == .Debug) { const alignment = inst_ty.abiAlignment(mod).toLlvm(); const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, arg_val, alloca, alignment); @@ -8821,7 +8910,8 @@ pub const FuncGen = struct { len, if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal, ); - if (safety and mod.comp.bin_file.options.valgrind) { + const owner_mod = self.dg.ownerModule(); + if (safety and owner_mod.valgrind) { try self.valgrindMarkUndef(dest_ptr, len); } return .none; @@ -9137,7 +9227,8 @@ pub const FuncGen = struct { } else { _ = try self.wip.callMemSet(dest_ptr, dest_ptr_align, fill_byte, len, access_kind); } - if (safety and mod.comp.bin_file.options.valgrind) { + const owner_mod = self.dg.ownerModule(); + if (safety and owner_mod.valgrind) { try self.valgrindMarkUndef(dest_ptr, len); } return .none; @@ -9488,24 +9579,25 @@ pub const FuncGen = struct { fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { const o = self.dg.object; - const mod = o.module; - const enum_type = mod.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; + const zcu = o.module; + const enum_type = zcu.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; // TODO: detect when the type changes and re-emit this function. const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_type.decl)); - const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); + const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(.i1, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.fmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&mod.intern_pool)}), - toLlvmAddressSpace(.generic, mod.getTarget()), + try o.builder.fmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&zcu.intern_pool)}), + toLlvmAddressSpace(.generic, target), ); var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); function_index.setLinkage(.internal, &o.builder); function_index.setCallConv(.fastcc, &o.builder); @@ -9524,7 +9616,7 @@ pub const FuncGen = struct { for (0..enum_type.names.len) |field_index| { const this_tag_int_value = try o.lowerValue( - (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), + (try zcu.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), ); try wip_switch.addCase(this_tag_int_value, named_block, &wip); } @@ -9544,7 +9636,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const enum_ty = self.typeOf(un_op); - const llvm_fn = try self.getEnumTagNameFunction(enum_ty); + const llvm_fn = try o.getEnumTagNameFunction(enum_ty); return self.wip.call( .normal, .fastcc, @@ -9556,100 +9648,6 @@ pub const FuncGen = struct { ); } - fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { - const o = self.dg.object; - const mod = o.module; - const ip = &mod.intern_pool; - const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type; - - // TODO: detect when the type changes and re-emit this function. - const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); - if (gop.found_existing) return gop.value_ptr.ptrConst(&o.builder).kind.function; - errdefer assert(o.decl_map.remove(enum_type.decl)); - - const usize_ty = try o.lowerType(Type.usize); - const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); - const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); - const function_index = try o.builder.addFunction( - try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(ip)}), - toLlvmAddressSpace(.generic, mod.getTarget()), - ); - - var attributes: Builder.FunctionAttributes.Wip = .{}; - defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); - - function_index.setLinkage(.internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); - gop.value_ptr.* = function_index.ptrConst(&o.builder).global; - - var wip = try Builder.WipFunction.init(&o.builder, function_index); - defer wip.deinit(); - wip.cursor = .{ .block = try wip.block(0, "Entry") }; - - const bad_value_block = try wip.block(1, "BadValue"); - const tag_int_value = wip.arg(0); - var wip_switch = - try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); - defer wip_switch.finish(&wip); - - for (0..enum_type.names.len) |field_index| { - const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index])); - const name_init = try o.builder.stringNullConst(name); - const name_variable_index = - try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); - try name_variable_index.setInitializer(name_init, &o.builder); - name_variable_index.setLinkage(.private, &o.builder); - name_variable_index.setMutability(.constant, &o.builder); - name_variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); - name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); - - const name_val = try o.builder.structValue(ret_ty, &.{ - name_variable_index.toConst(&o.builder), - try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), - }); - - const return_block = try wip.block(1, "Name"); - const this_tag_int_value = try o.lowerValue( - (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), - ); - try wip_switch.addCase(this_tag_int_value, return_block, &wip); - - wip.cursor = .{ .block = return_block }; - _ = try wip.ret(name_val); - } - - wip.cursor = .{ .block = bad_value_block }; - _ = try wip.@"unreachable"(); - - try wip.finish(); - return function_index; - } - - fn getCmpLtErrorsLenFunction(self: *FuncGen) !Builder.Function.Index { - const o = self.dg.object; - - const name = try o.builder.string(lt_errors_fn_name); - if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; - - const function_index = try o.builder.addFunction( - try o.builder.fnType(.i1, &.{try o.errorIntType()}, .normal), - name, - toLlvmAddressSpace(.generic, o.module.getTarget()), - ); - - var attributes: Builder.FunctionAttributes.Wip = .{}; - defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); - - function_index.setLinkage(.internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); - return function_index; - } - fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; diff --git a/src/link.zig b/src/link.zig index ce12e922d7..77169afa29 100644 --- a/src/link.zig +++ b/src/link.zig @@ -18,6 +18,7 @@ const Module = @import("Module.zig"); const InternPool = @import("InternPool.zig"); const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); +const LlvmObject = @import("codegen/llvm.zig").Object; /// When adding a new field, remember to update `hashAddSystemLibs`. /// These are *always* dynamically linked. Static libraries will be @@ -1046,6 +1047,51 @@ pub const File = struct { return output_mode == .Lib and !self.isStatic(); } + pub fn resolveEmitLoc( + base: File, + arena: Allocator, + opt_loc: ?Compilation.EmitLoc, + ) Allocator.Error!?[*:0]u8 { + const loc = opt_loc orelse return null; + const slice = if (loc.directory) |directory| + try directory.joinZ(arena, &.{loc.basename}) + else + try base.emit.basenamePath(arena, loc.basename); + return slice.ptr; + } + + pub fn emitLlvmObject( + base: File, + arena: Allocator, + llvm_object: *LlvmObject, + prog_node: *std.Progress.Node, + ) !void { + const comp = base.comp; + + var sub_prog_node = prog_node.start("LLVM Emit Object", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + + try llvm_object.emit(comp, .{ + .pre_ir_path = comp.verbose_llvm_ir, + .pre_bc_path = comp.verbose_llvm_bc, + .bin_path = try base.resolveEmitLoc(arena, .{ + .directory = null, + .basename = base.intermediary_basename.?, + }), + .asm_path = try base.resolveEmitLoc(arena, comp.emit_asm), + .post_llvm_ir_path = try base.resolveEmitLoc(arena, comp.emit_llvm_ir), + .post_llvm_bc_path = try base.resolveEmitLoc(arena, comp.emit_llvm_bc), + + .is_debug = comp.root_mod.optimize_mode == .Debug, + .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, + .time_report = comp.time_report, + .sanitize_thread = comp.config.any_sanitize_thread, + .lto = comp.config.lto, + }); + } + pub const C = @import("link/C.zig"); pub const Coff = @import("link/Coff.zig"); pub const Plan9 = @import("link/Plan9.zig"); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 762591442b..52ca7e554c 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -3,7 +3,7 @@ //! LLD for traditional linking (linking relocatable object files). //! LLD is also the default linker for LLVM. -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to intermediary_basename. llvm_object: ?*LlvmObject = null, base: link.File, @@ -1711,17 +1711,22 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const tracy = trace(@src()); defer tracy.end(); + const gpa = comp.gpa; + if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + return; } var sub_prog_node = prog_node.start("COFF Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - const gpa = self.base.comp.gpa; - - const module = self.base.comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but @@ -1822,7 +1827,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try self.writeDataDirectoriesHeaders(); try self.writeSectionHeaders(); - if (self.entry_addr == null and self.base.comp.config.output_mode == .Exe) { + if (self.entry_addr == null and comp.config.output_mode == .Exe) { log.debug("flushing. no_entry_point_found = true\n", .{}); self.base.error_flags.no_entry_point_found = true; } else { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3e237fe494..fe89a6321f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -27,7 +27,7 @@ version_script: ?[]const u8, ptr_width: PtrWidth, -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to intermediary_basename. llvm_object: ?*LlvmObject = null, /// A list of all input files. @@ -1031,24 +1031,23 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_object) |llvm_object| { - try llvm_object.flushModule(comp, prog_node); - - const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; - if (use_lld) return; - } - - const gpa = self.base.comp.gpa; - var sub_prog_node = prog_node.start("ELF Flush", 0); - sub_prog_node.activate(); - defer sub_prog_node.end(); - + const gpa = comp.gpa; var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const target = self.base.comp.root_mod.resolved_target.result; - const link_mode = self.base.comp.config.link_mode; + if (self.llvm_object) |llvm_object| { + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + const use_lld = build_options.have_llvm and comp.config.use_lld; + if (use_lld) return; + } + + var sub_prog_node = prog_node.start("ELF Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + + const target = comp.root_mod.resolved_target.result; + const link_mode = comp.config.link_mode; const directory = self.base.emit.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: { @@ -1060,7 +1059,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else null; // --verbose-link - if (self.base.comp.verbose_link) try self.dumpArgv(comp); + if (comp.verbose_link) try self.dumpArgv(comp); const csu = try CsuObjects.init(arena, comp); const compiler_rt_path: ?[]const u8 = blk: { @@ -1082,8 +1081,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (csu.crti) |v| try positionals.append(.{ .path = v }); if (csu.crtbegin) |v| try positionals.append(.{ .path = v }); - try positionals.ensureUnusedCapacity(self.base.comp.objects.len); - positionals.appendSliceAssumeCapacity(self.base.comp.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1106,13 +1105,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node var test_path = std.ArrayList(u8).init(gpa); defer test_path.deinit(); for (self.lib_dirs) |lib_dir_path| { - for (self.base.comp.system_libs.keys()) |link_lib| { + for (comp.system_libs.keys()) |link_lib| { if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic))) continue; _ = try rpath_table.put(lib_dir_path, {}); } } - for (self.base.comp.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -1122,7 +1121,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } // TSAN - if (self.base.comp.config.any_sanitize_thread) { + if (comp.config.any_sanitize_thread) { try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path }); } @@ -1146,27 +1145,27 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node var system_libs = std.ArrayList(SystemLib).init(arena); - try system_libs.ensureUnusedCapacity(self.base.comp.system_libs.values().len); - for (self.base.comp.system_libs.values()) |lib_info| { + try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); + for (comp.system_libs.values()) |lib_info| { system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? }); } // libc++ dep - if (self.base.comp.config.link_libcpp) { + if (comp.config.link_libcpp) { try system_libs.ensureUnusedCapacity(2); system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); } // libunwind dep - if (self.base.comp.config.link_libunwind) { + if (comp.config.link_libunwind) { try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path }); } // libc dep self.base.error_flags.missing_libc = false; - if (self.base.comp.config.link_libc) { - if (self.base.comp.libc_installation) |lc| { + if (comp.config.link_libc) { + if (comp.libc_installation) |lc| { const flags = target_util.libcFullLinkFlags(target); try system_libs.ensureUnusedCapacity(flags.len); @@ -1305,7 +1304,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Look for entry address in objects if not set by the incremental compiler. if (self.entry_index == null) { const entry: ?[]const u8 = entry: { - if (self.base.comp.config.entry) |entry| break :entry entry; + if (comp.config.entry) |entry| break :entry entry; if (!self.base.isDynLib()) break :entry "_start"; break :entry null; }; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 3dd35dfe63..7c7d3fd17c 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -43,7 +43,7 @@ pub fn outputShndx(symbol: Symbol) ?u16 { } pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { - if (elf_file.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; + if (elf_file.base.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; return !(symbol.flags.import or symbol.flags.@"export"); } @@ -186,7 +186,7 @@ const GetOrCreateZigGotEntryResult = struct { }; pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { - assert(!elf_file.isRelocatable()); + assert(!elf_file.base.isRelocatable()); assert(symbol.flags.needs_zig_got); if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); @@ -237,7 +237,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (elf_file.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; + if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 75bc53bb48..b6be4013ce 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -926,7 +926,7 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); assert(sym.flags.has_zig_got); const extra = sym.extra(elf_file).?; @@ -944,7 +944,7 @@ fn updateDeclCode( sym.flags.needs_zig_got = true; esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } @@ -1262,7 +1262,7 @@ fn updateLazySymbol( local_sym.flags.needs_zig_got = true; local_esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 104c83b357..9af563b047 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1,6 +1,6 @@ base: File, -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to intermediary_basename. llvm_object: ?*LlvmObject = null, /// Debug symbols bundle (or dSym). @@ -352,22 +352,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); - } - - const gpa = self.base.comp.gpa; + const gpa = comp.gpa; var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); + if (self.llvm_object) |llvm_object| { + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + return; + } + var sub_prog_node = prog_node.start("MachO Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - const output_mode = self.base.comp.config.output_mode; - const module = self.base.comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; - const target = self.base.comp.root_mod.resolved_target.result; + const output_mode = comp.config.output_mode; + const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const target = comp.root_mod.resolved_target.result; if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but @@ -619,7 +620,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No .stacksize = self.base.stack_size, }); }, - .Lib => if (self.base.comp.config.link_mode == .Dynamic) { + .Lib => if (comp.config.link_mode == .Dynamic) { try load_commands.writeDylibIdLC(self, lc_writer); }, else => {}, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 8f63e7c6fd..6488231afa 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -3700,8 +3700,15 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const tracy = trace(@src()); defer tracy.end(); + const gpa = comp.gpa; + // Used for all temporary memory allocated during flushin + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + if (wasm.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); + try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); + return; } var sub_prog_node = prog_node.start("Wasm Flush", 0); @@ -3711,13 +3718,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // ensure the error names table is populated when an error name is referenced try wasm.populateErrorNameTable(); - // Used for all temporary memory allocated during flushin - const gpa = wasm.base.comp.gpa; - var arena_instance = std.heap.ArenaAllocator.init(gpa); - defer arena_instance.deinit(); - const arena = arena_instance.allocator(); - - const objects = wasm.base.comp.objects; + const objects = comp.objects; // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList([]const u8).init(arena); @@ -3755,7 +3756,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.markReferences(); try wasm.setupErrorsLen(); try wasm.setupImports(); - if (wasm.base.comp.module) |mod| { + if (comp.module) |mod| { var decl_it = wasm.decls.iterator(); while (decl_it.next()) |entry| { const decl = mod.declPtr(entry.key_ptr.*); @@ -3810,7 +3811,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } if (wasm.dwarf) |*dwarf| { - try dwarf.flushModule(wasm.base.comp.module.?); + try dwarf.flushModule(comp.module.?); } }