authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-17 13:18:13-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-17 13:18:13-04:00
logecf8da00c53b20085cc32e84030caf32e8b3e16b
tree1bb7f4589450a689c4e1c5ee927e39dc2353884c
parent1a7cf4cbce1e157067f27c289c8365c8612de395

self-hosted: linking


4 files changed, 348 insertions(+), 16 deletions(-)

src-self-hosted/codegen.zig+8
...@@ -90,6 +90,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -90,6 +90,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
90 llvm.DIBuilderFinalize(dibuilder);90 llvm.DIBuilderFinalize(dibuilder);
9191
92 if (comp.verbose_llvm_ir) {92 if (comp.verbose_llvm_ir) {
93 std.debug.warn("raw module:\n");
93 llvm.DumpModule(ofile.module);94 llvm.DumpModule(ofile.module);
94 }95 }
9596
...@@ -122,6 +123,13 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -122,6 +123,13 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
122 }123 }
123 //validate_inline_fns(g); TODO124 //validate_inline_fns(g); TODO
124 fn_val.containing_object = output_path;125 fn_val.containing_object = output_path;
126 if (comp.verbose_llvm_ir) {
127 std.debug.warn("optimized module:\n");
128 llvm.DumpModule(ofile.module);
129 }
130 if (comp.verbose_link) {
131 std.debug.warn("created {}\n", output_path.toSliceConst());
132 }
125}133}
126134
127pub const ObjectFile = struct {135pub const ObjectFile = struct {
src-self-hosted/compilation.zig+23-16
...@@ -27,6 +27,7 @@ const Type = Value.Type;...@@ -27,6 +27,7 @@ const Type = Value.Type;
27const Span = errmsg.Span;27const Span = errmsg.Span;
28const codegen = @import("codegen.zig");28const codegen = @import("codegen.zig");
29const Package = @import("package.zig").Package;29const Package = @import("package.zig").Package;
30const link = @import("link.zig").link;
3031
31/// Data that is local to the event loop.32/// Data that is local to the event loop.
32pub const EventLoopLocal = struct {33pub const EventLoopLocal = struct {
...@@ -238,6 +239,7 @@ pub const Compilation = struct {...@@ -238,6 +239,7 @@ pub const Compilation = struct {
238 LinkQuotaExceeded,239 LinkQuotaExceeded,
239 EnvironmentVariableNotFound,240 EnvironmentVariableNotFound,
240 AppDataDirUnavailable,241 AppDataDirUnavailable,
242 LinkFailed,
241 };243 };
242244
243 pub const Event = union(enum) {245 pub const Event = union(enum) {
...@@ -563,8 +565,7 @@ pub const Compilation = struct {...@@ -563,8 +565,7 @@ pub const Compilation = struct {
563 async fn buildAsync(self: *Compilation) void {565 async fn buildAsync(self: *Compilation) void {
564 while (true) {566 while (true) {
565 // TODO directly awaiting async should guarantee memory allocation elision567 // TODO directly awaiting async should guarantee memory allocation elision
566 // TODO also async before suspending should guarantee memory allocation elision568 const build_result = await (async self.compileAndLink() catch unreachable);
567 const build_result = await (async self.addRootSrc() catch unreachable);
568569
569 // this makes a handy error return trace and stack trace in debug mode570 // this makes a handy error return trace and stack trace in debug mode
570 if (std.debug.runtime_safety) {571 if (std.debug.runtime_safety) {
...@@ -595,7 +596,7 @@ pub const Compilation = struct {...@@ -595,7 +596,7 @@ pub const Compilation = struct {
595 }596 }
596 }597 }
597598
598 async fn addRootSrc(self: *Compilation) !void {599 async fn compileAndLink(self: *Compilation) !void {
599 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");600 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
600 // TODO async/await os.path.real601 // TODO async/await os.path.real
601 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {602 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
...@@ -669,6 +670,17 @@ pub const Compilation = struct {...@@ -669,6 +670,17 @@ pub const Compilation = struct {
669 }670 }
670 try await (async decl_group.wait() catch unreachable);671 try await (async decl_group.wait() catch unreachable);
671 try await (async self.prelink_group.wait() catch unreachable);672 try await (async self.prelink_group.wait() catch unreachable);
673
674 const any_prelink_errors = blk: {
675 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
676 defer compile_errors.release();
677
678 break :blk compile_errors.value.len != 0;
679 };
680
681 if (!any_prelink_errors) {
682 try link(self);
683 }
672 }684 }
673685
674 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {686 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
...@@ -722,11 +734,6 @@ pub const Compilation = struct {...@@ -722,11 +734,6 @@ pub const Compilation = struct {
722 }734 }
723 }735 }
724736
725 pub fn link(self: *Compilation, out_file: ?[]const u8) !void {
726 warn("TODO link");
727 return error.Todo;
728 }
729
730 pub fn haveLibC(self: *Compilation) bool {737 pub fn haveLibC(self: *Compilation) bool {
731 return self.libc_link_lib != null;738 return self.libc_link_lib != null;
732 }739 }
...@@ -882,9 +889,8 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {...@@ -882,9 +889,8 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
882 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);889 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
883 errdefer symbol_name.deinit();890 errdefer symbol_name.deinit();
884891
892 // The Decl.Fn owns the initial 1 reference count
885 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);893 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
886 defer fn_val.base.deref(comp);
887
888 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };894 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
889895
890 const unanalyzed_code = (await (async ir.gen(896 const unanalyzed_code = (await (async ir.gen(
...@@ -948,22 +954,23 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {...@@ -948,22 +954,23 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
948 return getAppDataDir(allocator, "zig");954 return getAppDataDir(allocator, "zig");
949}955}
950956
951
952const GetAppDataDirError = error{957const GetAppDataDirError = error{
953 OutOfMemory,958 OutOfMemory,
954 AppDataDirUnavailable,959 AppDataDirUnavailable,
955};960};
956961
957
958/// Caller owns returned memory.962/// Caller owns returned memory.
959/// TODO move to zig std lib963/// TODO move to zig std lib
960fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {964fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
961 switch (builtin.os) {965 switch (builtin.os) {
962 builtin.Os.windows => {966 builtin.Os.windows => {
963 var dir_path_ptr: [*]u16 = undefined;967 var dir_path_ptr: [*]u16 = undefined;
964 switch (os.windows.SHGetKnownFolderPath(&os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE,968 switch (os.windows.SHGetKnownFolderPath(
965 null, &dir_path_ptr,))969 &os.windows.FOLDERID_LocalAppData,
966 {970 os.windows.KF_FLAG_CREATE,
971 null,
972 &dir_path_ptr,
973 )) {
967 os.windows.S_OK => {974 os.windows.S_OK => {
968 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));975 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
969 const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr));976 const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr));
...@@ -974,7 +981,7 @@ fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirEr...@@ -974,7 +981,7 @@ fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirEr
974 else => return error.AppDataDirUnavailable,981 else => return error.AppDataDirUnavailable,
975 }982 }
976 },983 },
977 // TODO for macos it should be "~/Library/Application Support/<APPNAME>" 984 // TODO for macos it should be "~/Library/Application Support/<APPNAME>"
978 else => {985 else => {
979 const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) {986 const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) {
980 error.OutOfMemory => return error.OutOfMemory,987 error.OutOfMemory => return error.OutOfMemory,
src-self-hosted/link.zig created+314
...@@ -0,0 +1,314 @@
1const std = @import("std");
2const c = @import("c.zig");
3const builtin = @import("builtin");
4const ObjectFormat = builtin.ObjectFormat;
5const Compilation = @import("compilation.zig").Compilation;
6
7const Context = struct {
8 comp: *Compilation,
9 arena: std.heap.ArenaAllocator,
10 args: std.ArrayList([*]const u8),
11 link_in_crt: bool,
12
13 link_err: error{OutOfMemory}!void,
14 link_msg: std.Buffer,
15};
16
17pub fn link(comp: *Compilation) !void {
18 var ctx = Context{
19 .comp = comp,
20 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
21 .args = undefined,
22 .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
23 .link_err = {},
24 .link_msg = undefined,
25 };
26 defer ctx.arena.deinit();
27 ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
28 ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
29
30 // even though we're calling LLD as a library it thinks the first
31 // argument is its own exe name
32 try ctx.args.append(c"lld");
33
34 try constructLinkerArgs(&ctx);
35
36 if (comp.verbose_link) {
37 for (ctx.args.toSliceConst()) |arg, i| {
38 const space = if (i == 0) "" else " ";
39 std.debug.warn("{}{s}", space, arg);
40 }
41 std.debug.warn("\n");
42 }
43
44 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
45 const args_slice = ctx.args.toSlice();
46 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
47 if (!ctx.link_msg.isNull()) {
48 // TODO capture these messages and pass them through the system, reporting them through the
49 // event system instead of printing them directly here.
50 // perhaps try to parse and understand them.
51 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
52 }
53 return error.LinkFailed;
54 }
55}
56
57extern fn ZigLLDLink(
58 oformat: c.ZigLLVM_ObjectFormatType,
59 args: [*]const [*]const u8,
60 arg_count: usize,
61 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
62 context: *c_void,
63) bool;
64
65extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void {
66 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
67 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
68}
69
70fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
71 if (ctx.link_msg.isNull()) {
72 try ctx.link_msg.resize(0);
73 }
74 try ctx.link_msg.append(msg);
75}
76
77fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
78 return switch (ofmt) {
79 ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat,
80 ObjectFormat.coff => c.ZigLLVM_COFF,
81 ObjectFormat.elf => c.ZigLLVM_ELF,
82 ObjectFormat.macho => c.ZigLLVM_MachO,
83 ObjectFormat.wasm => c.ZigLLVM_Wasm,
84 };
85}
86
87fn constructLinkerArgs(ctx: *Context) !void {
88 switch (ctx.comp.target.getObjectFormat()) {
89 ObjectFormat.unknown => unreachable,
90 ObjectFormat.coff => return constructLinkerArgsCoff(ctx),
91 ObjectFormat.elf => return constructLinkerArgsElf(ctx),
92 ObjectFormat.macho => return constructLinkerArgsMachO(ctx),
93 ObjectFormat.wasm => return constructLinkerArgsWasm(ctx),
94 }
95}
96
97fn constructLinkerArgsElf(ctx: *Context) !void {
98 //if (g->libc_link_lib != nullptr) {
99 // find_libc_lib_path(g);
100 //}
101
102 //if (g->linker_script) {
103 // lj->args.append("-T");
104 // lj->args.append(g->linker_script);
105 //}
106
107 //if (g->no_rosegment_workaround) {
108 // lj->args.append("--no-rosegment");
109 //}
110 //lj->args.append("--gc-sections");
111
112 //lj->args.append("-m");
113 //lj->args.append(getLDMOption(&g->zig_target));
114
115 //bool is_lib = g->out_type == OutTypeLib;
116 //bool shared = !g->is_static && is_lib;
117 //Buf *soname = nullptr;
118 //if (g->is_static) {
119 // if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
120 // g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
121 // {
122 // lj->args.append("-Bstatic");
123 // } else {
124 // lj->args.append("-static");
125 // }
126 //} else if (shared) {
127 // lj->args.append("-shared");
128
129 // if (buf_len(&lj->out_file) == 0) {
130 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
131 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
132 // }
133 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
134 //}
135
136 //lj->args.append("-o");
137 //lj->args.append(buf_ptr(&lj->out_file));
138
139 //if (lj->link_in_crt) {
140 // const char *crt1o;
141 // const char *crtbegino;
142 // if (g->is_static) {
143 // crt1o = "crt1.o";
144 // crtbegino = "crtbeginT.o";
145 // } else {
146 // crt1o = "Scrt1.o";
147 // crtbegino = "crtbegin.o";
148 // }
149 // lj->args.append(get_libc_file(g, crt1o));
150 // lj->args.append(get_libc_file(g, "crti.o"));
151 // lj->args.append(get_libc_static_file(g, crtbegino));
152 //}
153
154 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
155 // Buf *rpath = g->rpath_list.at(i);
156 // add_rpath(lj, rpath);
157 //}
158 //if (g->each_lib_rpath) {
159 // for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
160 // const char *lib_dir = g->lib_dirs.at(i);
161 // for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
162 // LinkLib *link_lib = g->link_libs_list.at(i);
163 // if (buf_eql_str(link_lib->name, "c")) {
164 // continue;
165 // }
166 // bool does_exist;
167 // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
168 // if (os_file_exists(test_path, &does_exist) != ErrorNone) {
169 // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
170 // }
171 // if (does_exist) {
172 // add_rpath(lj, buf_create_from_str(lib_dir));
173 // break;
174 // }
175 // }
176 // }
177 //}
178
179 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
180 // const char *lib_dir = g->lib_dirs.at(i);
181 // lj->args.append("-L");
182 // lj->args.append(lib_dir);
183 //}
184
185 //if (g->libc_link_lib != nullptr) {
186 // lj->args.append("-L");
187 // lj->args.append(buf_ptr(g->libc_lib_dir));
188
189 // lj->args.append("-L");
190 // lj->args.append(buf_ptr(g->libc_static_lib_dir));
191 //}
192
193 //if (!g->is_static) {
194 // if (g->dynamic_linker != nullptr) {
195 // assert(buf_len(g->dynamic_linker) != 0);
196 // lj->args.append("-dynamic-linker");
197 // lj->args.append(buf_ptr(g->dynamic_linker));
198 // } else {
199 // Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
200 // lj->args.append("-dynamic-linker");
201 // lj->args.append(buf_ptr(resolved_dynamic_linker));
202 // }
203 //}
204
205 //if (shared) {
206 // lj->args.append("-soname");
207 // lj->args.append(buf_ptr(soname));
208 //}
209
210 // .o files
211 for (ctx.comp.link_objects) |link_object| {
212 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
213 try ctx.args.append(link_obj_with_null.ptr);
214 }
215 try addFnObjects(ctx);
216
217 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
218 // if (g->libc_link_lib == nullptr) {
219 // Buf *builtin_o_path = build_o(g, "builtin");
220 // lj->args.append(buf_ptr(builtin_o_path));
221 // }
222
223 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
224 // Buf *compiler_rt_o_path = build_compiler_rt(g);
225 // lj->args.append(buf_ptr(compiler_rt_o_path));
226 //}
227
228 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
229 // LinkLib *link_lib = g->link_libs_list.at(i);
230 // if (buf_eql_str(link_lib->name, "c")) {
231 // continue;
232 // }
233 // Buf *arg;
234 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
235 // buf_ends_with_str(link_lib->name, ".so"))
236 // {
237 // arg = link_lib->name;
238 // } else {
239 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
240 // }
241 // lj->args.append(buf_ptr(arg));
242 //}
243
244 //// libc dep
245 //if (g->libc_link_lib != nullptr) {
246 // if (g->is_static) {
247 // lj->args.append("--start-group");
248 // lj->args.append("-lgcc");
249 // lj->args.append("-lgcc_eh");
250 // lj->args.append("-lc");
251 // lj->args.append("-lm");
252 // lj->args.append("--end-group");
253 // } else {
254 // lj->args.append("-lgcc");
255 // lj->args.append("--as-needed");
256 // lj->args.append("-lgcc_s");
257 // lj->args.append("--no-as-needed");
258 // lj->args.append("-lc");
259 // lj->args.append("-lm");
260 // lj->args.append("-lgcc");
261 // lj->args.append("--as-needed");
262 // lj->args.append("-lgcc_s");
263 // lj->args.append("--no-as-needed");
264 // }
265 //}
266
267 //// crt end
268 //if (lj->link_in_crt) {
269 // lj->args.append(get_libc_static_file(g, "crtend.o"));
270 // lj->args.append(get_libc_file(g, "crtn.o"));
271 //}
272
273 //if (!g->is_native_target) {
274 // lj->args.append("--allow-shlib-undefined");
275 //}
276
277 //if (g->zig_target.os == OsZen) {
278 // lj->args.append("-e");
279 // lj->args.append("_start");
280
281 // lj->args.append("--image-base=0x10000000");
282 //}
283}
284
285fn constructLinkerArgsCoff(ctx: *Context) void {
286 @panic("TODO");
287}
288
289fn constructLinkerArgsMachO(ctx: *Context) void {
290 @panic("TODO");
291}
292
293fn constructLinkerArgsWasm(ctx: *Context) void {
294 @panic("TODO");
295}
296
297fn addFnObjects(ctx: *Context) !void {
298 // at this point it's guaranteed nobody else has this lock, so we circumvent it
299 // and avoid having to be a coroutine
300 const fn_link_set = &ctx.comp.fn_link_set.private_data;
301
302 var it = fn_link_set.first;
303 while (it) |node| {
304 const fn_val = node.data orelse {
305 // handle the tombstone. See Value.Fn.destroy.
306 it = node.next;
307 fn_link_set.remove(node);
308 ctx.comp.gpa().destroy(node);
309 continue;
310 };
311 try ctx.args.append(fn_val.containing_object.ptr());
312 it = node.next;
313 }
314}
src/zig_llvm.h+3
...@@ -22,6 +22,9 @@...@@ -22,6 +22,9 @@
22#define ZIG_EXTERN_C22#define ZIG_EXTERN_C
23#endif23#endif
2424
25// ATTENTION: If you modify this file, be sure to update the corresponding
26// extern function declarations in the self-hosted compiler.
27
25struct ZigLLVMDIType;28struct ZigLLVMDIType;
26struct ZigLLVMDIBuilder;29struct ZigLLVMDIBuilder;
27struct ZigLLVMDICompileUnit;30struct ZigLLVMDICompileUnit;