authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-11 23:34:59-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-11 23:34:59-05:00
log23058d8b435266852d4ca69ee89f8c3d27f52e24
treee36b981713c6436bf3cc45ab64753cd8aa107290
parented4d94a5d54bc49b3661d602301a5ec926abef61

self-hosted: link with LLVM


6 files changed, 313 insertions(+), 94 deletions(-)

build.zig+179-2
......@@ -1,6 +1,13 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
2const Builder = std.build.Builder;
23const tests = @import("test/tests.zig");
3const os = @import("std").os;
4const os = std.os;
5const BufMap = std.BufMap;
6const warn = std.debug.warn;
7const mem = std.mem;
8const ArrayList = std.ArrayList;
9const Buffer = std.Buffer;
10const io = std.io;
411
512pub fn build(b: &Builder) {
613 const mode = b.standardReleaseOptions();
......@@ -28,6 +35,7 @@ pub fn build(b: &Builder) {
2835 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
2936 exe.setBuildMode(mode);
3037 exe.linkSystemLibrary("c");
38 dependOnLib(exe, findLLVM(b));
3139
3240 b.default_step.dependOn(&exe.step);
3341 b.default_step.dependOn(docs_step);
......@@ -64,3 +72,172 @@ pub fn build(b: &Builder) {
6472 test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));
6573 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
6674}
75
76fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) {
77 for (dep.libdirs.toSliceConst()) |lib_dir| {
78 lib_exe_obj.addLibPath(lib_dir);
79 }
80 for (dep.libs.toSliceConst()) |lib| {
81 lib_exe_obj.linkSystemLibrary(lib);
82 }
83 for (dep.includes.toSliceConst()) |include_path| {
84 lib_exe_obj.addIncludeDir(include_path);
85 }
86}
87
88const LibraryDep = struct {
89 libdirs: ArrayList([]const u8),
90 libs: ArrayList([]const u8),
91 includes: ArrayList([]const u8),
92};
93
94fn findLLVM(b: &Builder) -> LibraryDep {
95 const libs_output = {
96 const args1 = [][]const u8{"llvm-config-5.0", "--libs", "--system-libs"};
97 const args2 = [][]const u8{"llvm-config", "--libs", "--system-libs"};
98 const max_output_size = 10 * 1024;
99 const good_result = exec(b.allocator, args1, null, null, max_output_size) %% |err| {
100 if (err == error.FileNotFound) {
101 exec(b.allocator, args2, null, null, max_output_size) %% |err2| {
102 std.debug.panic("unable to spawn {}: {}\n", args2[0], err2);
103 }
104 } else {
105 std.debug.panic("unable to spawn {}: {}\n", args1[0], err);
106 }
107 };
108 switch (good_result.term) {
109 os.ChildProcess.Term.Exited => |code| {
110 if (code != 0) {
111 std.debug.panic("llvm-config exited with {}:\n{}\n", code, good_result.stderr);
112 }
113 },
114 else => {
115 std.debug.panic("llvm-config failed:\n{}\n", good_result.stderr);
116 },
117 }
118 good_result.stdout
119 };
120 const includes_output = {
121 const args1 = [][]const u8{"llvm-config-5.0", "--includedir"};
122 const args2 = [][]const u8{"llvm-config", "--includedir"};
123 const max_output_size = 10 * 1024;
124 const good_result = exec(b.allocator, args1, null, null, max_output_size) %% |err| {
125 if (err == error.FileNotFound) {
126 exec(b.allocator, args2, null, null, max_output_size) %% |err2| {
127 std.debug.panic("unable to spawn {}: {}\n", args2[0], err2);
128 }
129 } else {
130 std.debug.panic("unable to spawn {}: {}\n", args1[0], err);
131 }
132 };
133 switch (good_result.term) {
134 os.ChildProcess.Term.Exited => |code| {
135 if (code != 0) {
136 std.debug.panic("llvm-config --includedir exited with {}:\n{}\n", code, good_result.stderr);
137 }
138 },
139 else => {
140 std.debug.panic("llvm-config failed:\n{}\n", good_result.stderr);
141 },
142 }
143 good_result.stdout
144 };
145 const libdir_output = {
146 const args1 = [][]const u8{"llvm-config-5.0", "--libdir"};
147 const args2 = [][]const u8{"llvm-config", "--libdir"};
148 const max_output_size = 10 * 1024;
149 const good_result = exec(b.allocator, args1, null, null, max_output_size) %% |err| {
150 if (err == error.FileNotFound) {
151 exec(b.allocator, args2, null, null, max_output_size) %% |err2| {
152 std.debug.panic("unable to spawn {}: {}\n", args2[0], err2);
153 }
154 } else {
155 std.debug.panic("unable to spawn {}: {}\n", args1[0], err);
156 }
157 };
158 switch (good_result.term) {
159 os.ChildProcess.Term.Exited => |code| {
160 if (code != 0) {
161 std.debug.panic("llvm-config --libdir exited with {}:\n{}\n", code, good_result.stderr);
162 }
163 },
164 else => {
165 std.debug.panic("llvm-config failed:\n{}\n", good_result.stderr);
166 },
167 }
168 good_result.stdout
169 };
170
171 var result = LibraryDep {
172 .libs = ArrayList([]const u8).init(b.allocator),
173 .includes = ArrayList([]const u8).init(b.allocator),
174 .libdirs = ArrayList([]const u8).init(b.allocator),
175 };
176 {
177 var it = mem.split(libs_output, " \n");
178 while (it.next()) |lib_arg| {
179 if (mem.startsWith(u8, lib_arg, "-l")) {
180 %%result.libs.append(lib_arg[2..]);
181 }
182 }
183 }
184 {
185 var it = mem.split(includes_output, " \n");
186 while (it.next()) |include_arg| {
187 if (mem.startsWith(u8, include_arg, "-I")) {
188 %%result.includes.append(include_arg[2..]);
189 } else {
190 %%result.includes.append(include_arg);
191 }
192 }
193 }
194 {
195 var it = mem.split(libdir_output, " \n");
196 while (it.next()) |libdir| {
197 if (mem.startsWith(u8, libdir, "-L")) {
198 %%result.libdirs.append(libdir[2..]);
199 } else {
200 %%result.libdirs.append(libdir);
201 }
202 }
203 }
204 return result;
205}
206
207
208// TODO move to std lib
209const ExecResult = struct {
210 term: os.ChildProcess.Term,
211 stdout: []u8,
212 stderr: []u8,
213};
214
215fn exec(allocator: &std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?&const BufMap, max_output_size: usize) -> %ExecResult {
216 const child = %%os.ChildProcess.init(argv, allocator);
217 defer child.deinit();
218
219 child.stdin_behavior = os.ChildProcess.StdIo.Ignore;
220 child.stdout_behavior = os.ChildProcess.StdIo.Pipe;
221 child.stderr_behavior = os.ChildProcess.StdIo.Pipe;
222 child.cwd = cwd;
223 child.env_map = env_map;
224
225 %return child.spawn();
226
227 var stdout = Buffer.initNull(allocator);
228 var stderr = Buffer.initNull(allocator);
229 defer Buffer.deinit(&stdout);
230 defer Buffer.deinit(&stderr);
231
232 var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);
233 var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);
234
235 %return stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
236 %return stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
237
238 return ExecResult {
239 .term = %return child.wait(),
240 .stdout = stdout.toOwnedSlice(),
241 .stderr = stderr.toOwnedSlice(),
242 };
243}
src-self-hosted/c.zig created+7
......@@ -0,0 +1,7 @@
1pub use @cImport({
2 @cInclude("llvm-c/Core.h");
3 @cInclude("llvm-c/Analysis.h");
4 @cInclude("llvm-c/Target.h");
5 @cInclude("llvm-c/Initialization.h");
6 @cInclude("llvm-c/TargetMachine.h");
7});
src-self-hosted/main.zig+6-91
......@@ -1,6 +1,5 @@
11const std = @import("std");
22const mem = std.mem;
3const builtin = @import("builtin");
43const io = std.io;
54const os = std.os;
65const heap = std.heap;
......@@ -9,6 +8,7 @@ const Tokenizer = @import("tokenizer.zig").Tokenizer;
98const Token = @import("tokenizer.zig").Token;
109const Parser = @import("parser.zig").Parser;
1110const assert = std.debug.assert;
11const target = @import("target.zig");
1212
1313pub fn main() -> %void {
1414 main2() %% |err| {
......@@ -26,6 +26,8 @@ pub fn main2() -> %void {
2626 const args = %return os.argsAlloc(allocator);
2727 defer os.argsFree(allocator, args);
2828
29 target.initializeAll();
30
2931 const target_file = args[1];
3032
3133 const target_file_buf = %return io.readFileAlloc(target_file, allocator);
......@@ -66,94 +68,7 @@ pub fn main2() -> %void {
6668 %return parser.renderSource(out_stream, root_node);
6769}
6870
69
70var fixed_buffer_mem: [100 * 1024]u8 = undefined;
71
72fn testParse(source: []const u8, allocator: &mem.Allocator) -> %[]u8 {
73 var tokenizer = Tokenizer.init(source);
74 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
75 defer parser.deinit();
76
77 const root_node = %return parser.parse();
78 defer parser.freeAst(root_node);
79
80 var buffer = %return std.Buffer.initSize(allocator, 0);
81 var buffer_out_stream = io.BufferOutStream.init(&buffer);
82 %return parser.renderSource(&buffer_out_stream.stream, root_node);
83 return buffer.toOwnedSlice();
84}
85
86fn testCanonical(source: []const u8) {
87 const needed_alloc_count = {
88 // Try it once with unlimited memory, make sure it works
89 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
90 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
91 const result_source = testParse(source, &failing_allocator.allocator) %% @panic("test failed");
92 if (!mem.eql(u8, result_source, source)) {
93 warn("\n====== expected this output: =========\n");
94 warn("{}", source);
95 warn("\n======== instead found this: =========\n");
96 warn("{}", result_source);
97 warn("\n======================================\n");
98 @panic("test failed");
99 }
100 failing_allocator.allocator.free(result_source);
101 failing_allocator.index
102 };
103
104 var fail_index = needed_alloc_count;
105 while (fail_index != 0) {
106 fail_index -= 1;
107 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
108 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
109 if (testParse(source, &failing_allocator.allocator)) |_| {
110 @panic("non-deterministic memory usage");
111 } else |err| {
112 assert(err == error.OutOfMemory);
113 }
114 }
115}
116
117test "zig fmt" {
118 if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
119 // TODO get this test passing
120 // https://github.com/zig-lang/zig/issues/537
121 return;
122 }
123
124 testCanonical(
125 \\extern fn puts(s: &const u8) -> c_int;
126 \\
127 );
128
129 testCanonical(
130 \\const a = b;
131 \\pub const a = b;
132 \\var a = b;
133 \\pub var a = b;
134 \\const a: i32 = b;
135 \\pub const a: i32 = b;
136 \\var a: i32 = b;
137 \\pub var a: i32 = b;
138 \\
139 );
140
141 testCanonical(
142 \\extern var foo: c_int;
143 \\
144 );
145
146 testCanonical(
147 \\fn main(argc: c_int, argv: &&u8) -> c_int {
148 \\ const a = b;
149 \\}
150 \\
151 );
152
153 testCanonical(
154 \\fn foo(argc: c_int, argv: &&u8) -> c_int {
155 \\ return 0;
156 \\}
157 \\
158 );
71test "import other tests" {
72 _ = @import("parser.zig");
73 _ = @import("tokenizer.zig");
15974}
src-self-hosted/parser.zig+95
......@@ -5,6 +5,8 @@ const mem = std.mem;
55const ast = @import("ast.zig");
66const Tokenizer = @import("tokenizer.zig").Tokenizer;
77const Token = @import("tokenizer.zig").Token;
8const builtin = @import("builtin");
9const io = std.io;
810
911// TODO when we make parse errors into error types instead of printing directly,
1012// get rid of this
......@@ -1095,3 +1097,96 @@ pub const Parser = struct {
10951097
10961098};
10971099
1100var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1101
1102fn testParse(source: []const u8, allocator: &mem.Allocator) -> %[]u8 {
1103 var tokenizer = Tokenizer.init(source);
1104 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1105 defer parser.deinit();
1106
1107 const root_node = %return parser.parse();
1108 defer parser.freeAst(root_node);
1109
1110 var buffer = %return std.Buffer.initSize(allocator, 0);
1111 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1112 %return parser.renderSource(&buffer_out_stream.stream, root_node);
1113 return buffer.toOwnedSlice();
1114}
1115
1116// TODO test for memory leaks
1117// TODO test for valid frees
1118fn testCanonical(source: []const u8) {
1119 const needed_alloc_count = {
1120 // Try it once with unlimited memory, make sure it works
1121 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1122 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1123 const result_source = testParse(source, &failing_allocator.allocator) %% @panic("test failed");
1124 if (!mem.eql(u8, result_source, source)) {
1125 warn("\n====== expected this output: =========\n");
1126 warn("{}", source);
1127 warn("\n======== instead found this: =========\n");
1128 warn("{}", result_source);
1129 warn("\n======================================\n");
1130 @panic("test failed");
1131 }
1132 failing_allocator.allocator.free(result_source);
1133 failing_allocator.index
1134 };
1135
1136 // TODO make this pass
1137 //var fail_index = needed_alloc_count;
1138 //while (fail_index != 0) {
1139 // fail_index -= 1;
1140 // var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1141 // var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1142 // if (testParse(source, &failing_allocator.allocator)) |_| {
1143 // @panic("non-deterministic memory usage");
1144 // } else |err| {
1145 // assert(err == error.OutOfMemory);
1146 // }
1147 //}
1148}
1149
1150test "zig fmt" {
1151 if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
1152 // TODO get this test passing
1153 // https://github.com/zig-lang/zig/issues/537
1154 return;
1155 }
1156
1157 testCanonical(
1158 \\extern fn puts(s: &const u8) -> c_int;
1159 \\
1160 );
1161
1162 testCanonical(
1163 \\const a = b;
1164 \\pub const a = b;
1165 \\var a = b;
1166 \\pub var a = b;
1167 \\const a: i32 = b;
1168 \\pub const a: i32 = b;
1169 \\var a: i32 = b;
1170 \\pub var a: i32 = b;
1171 \\
1172 );
1173
1174 testCanonical(
1175 \\extern var foo: c_int;
1176 \\
1177 );
1178
1179 testCanonical(
1180 \\fn main(argc: c_int, argv: &&u8) -> c_int {
1181 \\ const a = b;
1182 \\}
1183 \\
1184 );
1185
1186 testCanonical(
1187 \\fn foo(argc: c_int, argv: &&u8) -> c_int {
1188 \\ return 0;
1189 \\}
1190 \\
1191 );
1192}
src-self-hosted/target.zig created+9
......@@ -0,0 +1,9 @@
1const c = @import("c.zig");
2
3pub fn initializeAll() {
4 c.LLVMInitializeAllTargets();
5 c.LLVMInitializeAllTargetInfos();
6 c.LLVMInitializeAllTargetMCs();
7 c.LLVMInitializeAllAsmPrinters();
8 c.LLVMInitializeAllAsmParsers();
9}
std/build.zig+17-1
......@@ -755,6 +755,7 @@ pub const LibExeObjStep = struct {
755755 is_zig: bool,
756756 cflags: ArrayList([]const u8),
757757 include_dirs: ArrayList([]const u8),
758 lib_paths: ArrayList([]const u8),
758759 disable_libc: bool,
759760 frameworks: BufSet,
760761
......@@ -865,6 +866,7 @@ pub const LibExeObjStep = struct {
865866 .cflags = ArrayList([]const u8).init(builder.allocator),
866867 .source_files = undefined,
867868 .include_dirs = ArrayList([]const u8).init(builder.allocator),
869 .lib_paths = ArrayList([]const u8).init(builder.allocator),
868870 .object_src = undefined,
869871 .disable_libc = true,
870872 };
......@@ -888,6 +890,7 @@ pub const LibExeObjStep = struct {
888890 .frameworks = BufSet.init(builder.allocator),
889891 .full_path_libs = ArrayList([]const u8).init(builder.allocator),
890892 .include_dirs = ArrayList([]const u8).init(builder.allocator),
893 .lib_paths = ArrayList([]const u8).init(builder.allocator),
891894 .output_path = null,
892895 .out_filename = undefined,
893896 .major_only_filename = undefined,
......@@ -1069,11 +1072,14 @@ pub const LibExeObjStep = struct {
10691072 %%self.include_dirs.append(self.builder.cache_root);
10701073 }
10711074
1072 // TODO put include_dirs in zig command line
10731075 pub fn addIncludeDir(self: &LibExeObjStep, path: []const u8) {
10741076 %%self.include_dirs.append(path);
10751077 }
10761078
1079 pub fn addLibPath(self: &LibExeObjStep, path: []const u8) {
1080 %%self.lib_paths.append(path);
1081 }
1082
10771083 pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) {
10781084 assert(self.is_zig);
10791085
......@@ -1222,6 +1228,11 @@ pub const LibExeObjStep = struct {
12221228 %%zig_args.append("--pkg-end");
12231229 }
12241230
1231 for (self.include_dirs.toSliceConst()) |include_path| {
1232 %%zig_args.append("-isystem");
1233 %%zig_args.append(self.builder.pathFromRoot(include_path));
1234 }
1235
12251236 for (builder.include_paths.toSliceConst()) |include_path| {
12261237 %%zig_args.append("-isystem");
12271238 %%zig_args.append(builder.pathFromRoot(include_path));
......@@ -1232,6 +1243,11 @@ pub const LibExeObjStep = struct {
12321243 %%zig_args.append(rpath);
12331244 }
12341245
1246 for (self.lib_paths.toSliceConst()) |lib_path| {
1247 %%zig_args.append("--library-path");
1248 %%zig_args.append(lib_path);
1249 }
1250
12351251 for (builder.lib_paths.toSliceConst()) |lib_path| {
12361252 %%zig_args.append("--library-path");
12371253 %%zig_args.append(lib_path);