authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-03 16:09:14-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-03 16:09:14-08:00
logf6644255f595fb0bbfb2fa4276804ef7031330b1
tree2b43d96fec97caec177fb0851b63d5fa2729efa3
parent5cc131030c01e178453b12f235823124aa6a2d12
parent0151f3b76ad9dca6c73d44876c263d5e27d92ab3
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7598 from FireFox317/more-llvm-stage2

stage2: More improvements to self-hosted LLVM backend

12 files changed, 497 insertions(+), 162 deletions(-)

build.zig+111-89
...@@ -91,6 +91,7 @@ pub fn build(b: *Builder) !void {...@@ -91,6 +91,7 @@ pub fn build(b: *Builder) !void {
91 exe.addBuildOption(bool, "have_llvm", enable_llvm);91 exe.addBuildOption(bool, "have_llvm", enable_llvm);
92 if (enable_llvm) {92 if (enable_llvm) {
93 const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);93 const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
94
94 if (is_stage1) {95 if (is_stage1) {
95 exe.addIncludeDir("src");96 exe.addIncludeDir("src");
96 exe.addIncludeDir("deps/SoftFloat-3e/source/include");97 exe.addIncludeDir("deps/SoftFloat-3e/source/include");
...@@ -109,28 +110,8 @@ pub fn build(b: *Builder) !void {...@@ -109,28 +110,8 @@ pub fn build(b: *Builder) !void {
109 softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" });110 softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" });
110 exe.linkLibrary(softfloat);111 exe.linkLibrary(softfloat);
111112
112 const exe_cflags = [_][]const u8{
113 "-std=c++14",
114 "-D__STDC_CONSTANT_MACROS",
115 "-D__STDC_FORMAT_MACROS",
116 "-D__STDC_LIMIT_MACROS",
117 "-D_GNU_SOURCE",
118 "-fvisibility-inlines-hidden",
119 "-fno-exceptions",
120 "-fno-rtti",
121 "-Werror=type-limits",
122 "-Wno-missing-braces",
123 "-Wno-comment",
124 };
125 exe.addCSourceFiles(&stage1_sources, &exe_cflags);113 exe.addCSourceFiles(&stage1_sources, &exe_cflags);
126 exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" });114 exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" });
127 if (cmake_cfg == null) {
128 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
129 // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
130 // unavailable when LLVM is compiled in Release mode.
131 const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
132 exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags);
133 }
134 }115 }
135 if (cmake_cfg) |cfg| {116 if (cmake_cfg) |cfg| {
136 // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.117 // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
...@@ -139,79 +120,14 @@ pub fn build(b: *Builder) !void {...@@ -139,79 +120,14 @@ pub fn build(b: *Builder) !void {
139 if (cfg.cmake_prefix_path.len > 0) {120 if (cfg.cmake_prefix_path.len > 0) {
140 b.addSearchPrefix(cfg.cmake_prefix_path);121 b.addSearchPrefix(cfg.cmake_prefix_path);
141 }122 }
142 exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
143 cfg.cmake_binary_dir,
144 "zigcpp",
145 b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }),
146 }) catch unreachable);
147 assert(cfg.lld_include_dir.len != 0);
148 exe.addIncludeDir(cfg.lld_include_dir);
149 addCMakeLibraryList(exe, cfg.clang_libraries);
150 addCMakeLibraryList(exe, cfg.lld_libraries);
151 addCMakeLibraryList(exe, cfg.llvm_libraries);
152
153 const need_cpp_includes = tracy != null;
154
155 // System -lc++ must be used because in this code path we are attempting to link
156 // against system-provided LLVM, Clang, LLD.
157 if (exe.target.getOsTag() == .linux) {
158 // First we try to static link against gcc libstdc++. If that doesn't work,
159 // we fall back to -lc++ and cross our fingers.
160 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
161 error.RequiredLibraryNotFound => {
162 exe.linkSystemLibrary("c++");
163 },
164 else => |e| return e,
165 };
166
167 exe.linkSystemLibrary("pthread");
168 } else if (exe.target.isFreeBSD()) {
169 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
170 exe.linkSystemLibrary("pthread");
171 } else if (exe.target.getOsTag() == .openbsd) {
172 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
173 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
174 } else if (exe.target.isDarwin()) {
175 if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) {
176 // Compiler is GCC.
177 try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes);
178 exe.linkSystemLibrary("pthread");
179 // TODO LLD cannot perform this link.
180 // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead.
181 // See https://github.com/ziglang/zig/issues/1535
182 } else |err| switch (err) {
183 error.RequiredLibraryNotFound => {
184 // System compiler, not gcc.
185 exe.linkSystemLibrary("c++");
186 },
187 else => |e| return e,
188 }
189 }
190123
191 if (cfg.dia_guids_lib.len != 0) {124 try addCmakeCfgOptionsToExe(b, cfg, tracy, exe);
192 exe.addObjectFile(cfg.dia_guids_lib);125 try addCmakeCfgOptionsToExe(b, cfg, tracy, test_stage2);
193 }
194 } else {126 } else {
195 // Here we are -Denable-llvm but no cmake integration.127 // Here we are -Denable-llvm but no cmake integration.
196 for (clang_libs) |lib_name| {
197 exe.linkSystemLibrary(lib_name);
198 }
199
200 for (lld_libs) |lib_name| {
201 exe.linkSystemLibrary(lib_name);
202 }
203
204 for (llvm_libs) |lib_name| {
205 exe.linkSystemLibrary(lib_name);
206 }
207
208 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
209 exe.linkSystemLibrary("c++");
210128
211 if (target.getOs().tag == .windows) {129 try addStaticLlvmOptionsToExe(exe);
212 exe.linkSystemLibrary("version");130 try addStaticLlvmOptionsToExe(test_stage2);
213 exe.linkSystemLibrary("uuid");
214 }
215 }131 }
216 }132 }
217 if (link_libc) {133 if (link_libc) {
...@@ -357,6 +273,112 @@ pub fn build(b: *Builder) !void {...@@ -357,6 +273,112 @@ pub fn build(b: *Builder) !void {
357 test_step.dependOn(docs_step);273 test_step.dependOn(docs_step);
358}274}
359275
276const exe_cflags = [_][]const u8{
277 "-std=c++14",
278 "-D__STDC_CONSTANT_MACROS",
279 "-D__STDC_FORMAT_MACROS",
280 "-D__STDC_LIMIT_MACROS",
281 "-D_GNU_SOURCE",
282 "-fvisibility-inlines-hidden",
283 "-fno-exceptions",
284 "-fno-rtti",
285 "-Werror=type-limits",
286 "-Wno-missing-braces",
287 "-Wno-comment",
288};
289
290fn addCmakeCfgOptionsToExe(
291 b: *Builder,
292 cfg: CMakeConfig,
293 tracy: ?[]const u8,
294 exe: *std.build.LibExeObjStep,
295) !void {
296 exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
297 cfg.cmake_binary_dir,
298 "zigcpp",
299 b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }),
300 }) catch unreachable);
301 assert(cfg.lld_include_dir.len != 0);
302 exe.addIncludeDir(cfg.lld_include_dir);
303 addCMakeLibraryList(exe, cfg.clang_libraries);
304 addCMakeLibraryList(exe, cfg.lld_libraries);
305 addCMakeLibraryList(exe, cfg.llvm_libraries);
306
307 const need_cpp_includes = tracy != null;
308
309 // System -lc++ must be used because in this code path we are attempting to link
310 // against system-provided LLVM, Clang, LLD.
311 if (exe.target.getOsTag() == .linux) {
312 // First we try to static link against gcc libstdc++. If that doesn't work,
313 // we fall back to -lc++ and cross our fingers.
314 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
315 error.RequiredLibraryNotFound => {
316 exe.linkSystemLibrary("c++");
317 },
318 else => |e| return e,
319 };
320
321 exe.linkSystemLibrary("pthread");
322 } else if (exe.target.isFreeBSD()) {
323 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
324 exe.linkSystemLibrary("pthread");
325 } else if (exe.target.getOsTag() == .openbsd) {
326 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
327 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
328 } else if (exe.target.isDarwin()) {
329 if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) {
330 // Compiler is GCC.
331 try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes);
332 exe.linkSystemLibrary("pthread");
333 // TODO LLD cannot perform this link.
334 // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead.
335 // See https://github.com/ziglang/zig/issues/1535
336 } else |err| switch (err) {
337 error.RequiredLibraryNotFound => {
338 // System compiler, not gcc.
339 exe.linkSystemLibrary("c++");
340 },
341 else => |e| return e,
342 }
343 }
344
345 if (cfg.dia_guids_lib.len != 0) {
346 exe.addObjectFile(cfg.dia_guids_lib);
347 }
348}
349
350fn addStaticLlvmOptionsToExe(
351 exe: *std.build.LibExeObjStep,
352) !void {
353 // Adds the Zig C++ sources which both stage1 and stage2 need.
354 //
355 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
356 // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
357 // unavailable when LLVM is compiled in Release mode.
358 const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
359 exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags);
360
361 for (clang_libs) |lib_name| {
362 exe.linkSystemLibrary(lib_name);
363 }
364
365 for (lld_libs) |lib_name| {
366 exe.linkSystemLibrary(lib_name);
367 }
368
369 for (llvm_libs) |lib_name| {
370 exe.linkSystemLibrary(lib_name);
371 }
372
373 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
374 exe.linkSystemLibrary("c++");
375
376 if (exe.target.getOs().tag == .windows) {
377 exe.linkSystemLibrary("version");
378 exe.linkSystemLibrary("uuid");
379 }
380}
381
360fn addCxxKnownPath(382fn addCxxKnownPath(
361 b: *Builder,383 b: *Builder,
362 ctx: CMakeConfig,384 ctx: CMakeConfig,
src/Cache.zig+2-1
...@@ -16,7 +16,8 @@ const Allocator = std.mem.Allocator;...@@ -16,7 +16,8 @@ const Allocator = std.mem.Allocator;
16/// This protection is conditionally compiled depending on `want_debug_deadlock`.16/// This protection is conditionally compiled depending on `want_debug_deadlock`.
17var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{};17var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{};
18var all_cache_digest_lock: std.Mutex = .{};18var all_cache_digest_lock: std.Mutex = .{};
19const want_debug_deadlock = std.debug.runtime_safety;19// TODO: Figure out how to make sure that `all_cache_digest_set` does not leak memory!
20pub const want_debug_deadlock = false;
20const DebugBinDigest = if (want_debug_deadlock) BinDigest else void;21const DebugBinDigest = if (want_debug_deadlock) BinDigest else void;
21const null_debug_bin_digest = if (want_debug_deadlock) ([1]u8{0} ** bin_digest_len) else {};22const null_debug_bin_digest = if (want_debug_deadlock) ([1]u8{0} ** bin_digest_len) else {};
2223
src/Compilation.zig+5
...@@ -1173,6 +1173,7 @@ pub fn destroy(self: *Compilation) void {...@@ -1173,6 +1173,7 @@ pub fn destroy(self: *Compilation) void {
11731173
1174 const gpa = self.gpa;1174 const gpa = self.gpa;
1175 self.work_queue.deinit();1175 self.work_queue.deinit();
1176 self.c_object_work_queue.deinit();
11761177
1177 {1178 {
1178 var it = self.crt_files.iterator();1179 var it = self.crt_files.iterator();
...@@ -1202,6 +1203,10 @@ pub fn destroy(self: *Compilation) void {...@@ -1202,6 +1203,10 @@ pub fn destroy(self: *Compilation) void {
1202 crt_file.deinit(gpa);1203 crt_file.deinit(gpa);
1203 }1204 }
12041205
1206 if (self.glibc_so_files) |*glibc_file| {
1207 glibc_file.deinit(gpa);
1208 }
1209
1205 for (self.c_object_table.items()) |entry| {1210 for (self.c_object_table.items()) |entry| {
1206 entry.key.destroy(gpa);1211 entry.key.destroy(gpa);
1207 }1212 }
src/DepTokenizer.zig+1-1
...@@ -927,7 +927,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {...@@ -927,7 +927,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
927 try out.writeAll("\n");927 try out.writeAll("\n");
928 try printSection(out, "<<<< input", input);928 try printSection(out, "<<<< input", input);
929 try printSection(out, "==== expect", expect);929 try printSection(out, "==== expect", expect);
930 try printSection(out, ">>>> got", got);930 try printSection(out, ">>>> got", buffer.items);
931 try printRuler(out);931 try printRuler(out);
932932
933 testing.expect(false);933 testing.expect(false);
src/link/Coff.zig+5-2
...@@ -811,8 +811,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -811,8 +811,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
811 // If there is no Zig code to compile, then we should skip flushing the output file because it811 // If there is no Zig code to compile, then we should skip flushing the output file because it
812 // will not be part of the linker line anyway.812 // will not be part of the linker line anyway.
813 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {813 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
814 const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;814 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
815 if (use_stage1) {815 if (self.base.options.use_llvm) {
816 // Stage2 has to call flushModule since that outputs the LLVM object file.
817 if (!build_options.is_stage1) try self.flushModule(comp);
818
816 const obj_basename = try std.zig.binNameAlloc(arena, .{819 const obj_basename = try std.zig.binNameAlloc(arena, .{
817 .root_name = self.base.options.root_name,820 .root_name = self.base.options.root_name,
818 .target = self.base.options.target,821 .target = self.base.options.target,
src/link/Elf.zig+5-2
...@@ -1251,8 +1251,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1251,8 +1251,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1251 // If there is no Zig code to compile, then we should skip flushing the output file because it1251 // If there is no Zig code to compile, then we should skip flushing the output file because it
1252 // will not be part of the linker line anyway.1252 // will not be part of the linker line anyway.
1253 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {1253 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
1254 const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;1254 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
1255 if (use_stage1) {1255 if (self.base.options.use_llvm) {
1256 // Stage2 has to call flushModule since that outputs the LLVM object file.
1257 if (!build_options.is_stage1) try self.flushModule(comp);
1258
1256 const obj_basename = try std.zig.binNameAlloc(arena, .{1259 const obj_basename = try std.zig.binNameAlloc(arena, .{
1257 .root_name = self.base.options.root_name,1260 .root_name = self.base.options.root_name,
1258 .target = self.base.options.target,1261 .target = self.base.options.target,
src/llvm_backend.zig+228-65
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
3const Compilation = @import("Compilation.zig");4const Compilation = @import("Compilation.zig");
4const llvm = @import("llvm_bindings.zig");5const llvm = @import("llvm_bindings.zig");
...@@ -141,17 +142,36 @@ pub const LLVMIRModule = struct {...@@ -141,17 +142,36 @@ pub const LLVMIRModule = struct {
141 target_machine: *const llvm.TargetMachineRef,142 target_machine: *const llvm.TargetMachineRef,
142 builder: *const llvm.BuilderRef,143 builder: *const llvm.BuilderRef,
143144
144 output_path: []const u8,145 object_path: []const u8,
145146
146 gpa: *Allocator,147 gpa: *Allocator,
147 err_msg: ?*Compilation.ErrorMsg = null,148 err_msg: ?*Compilation.ErrorMsg = null,
148149
150 /// This stores the LLVM values used in a function, such that they can be
151 /// referred to in other instructions. This table is cleared before every function is generated.
152 func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.ValueRef) = .{},
153
154 /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction.
155 args: []*const llvm.ValueRef = &[_]*const llvm.ValueRef{},
156 arg_index: usize = 0,
157
149 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {158 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule {
150 const self = try allocator.create(LLVMIRModule);159 const self = try allocator.create(LLVMIRModule);
151 errdefer allocator.destroy(self);160 errdefer allocator.destroy(self);
152161
153 const gpa = options.module.?.gpa;162 const gpa = options.module.?.gpa;
154163
164 const obj_basename = try std.zig.binNameAlloc(gpa, .{
165 .root_name = options.root_name,
166 .target = options.target,
167 .output_mode = .Obj,
168 });
169 defer gpa.free(obj_basename);
170
171 const o_directory = options.module.?.zig_cache_artifact_directory;
172 const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename});
173 errdefer gpa.free(object_path);
174
155 initializeLLVMTargets();175 initializeLLVMTargets();
156176
157 const root_nameZ = try gpa.dupeZ(u8, options.root_name);177 const root_nameZ = try gpa.dupeZ(u8, options.root_name);
...@@ -203,7 +223,7 @@ pub const LLVMIRModule = struct {...@@ -203,7 +223,7 @@ pub const LLVMIRModule = struct {
203 .llvm_module = llvm_module,223 .llvm_module = llvm_module,
204 .target_machine = target_machine,224 .target_machine = target_machine,
205 .builder = builder,225 .builder = builder,
206 .output_path = sub_path,226 .object_path = object_path,
207 .gpa = gpa,227 .gpa = gpa,
208 };228 };
209 return self;229 return self;
...@@ -213,6 +233,10 @@ pub const LLVMIRModule = struct {...@@ -213,6 +233,10 @@ pub const LLVMIRModule = struct {
213 self.builder.disposeBuilder();233 self.builder.disposeBuilder();
214 self.target_machine.disposeTargetMachine();234 self.target_machine.disposeTargetMachine();
215 self.llvm_module.disposeModule();235 self.llvm_module.disposeModule();
236
237 self.func_inst_table.deinit(self.gpa);
238 self.gpa.free(self.object_path);
239
216 allocator.destroy(self);240 allocator.destroy(self);
217 }241 }
218242
...@@ -245,15 +269,13 @@ pub const LLVMIRModule = struct {...@@ -245,15 +269,13 @@ pub const LLVMIRModule = struct {
245 }269 }
246 }270 }
247271
248 const output_pathZ = try self.gpa.dupeZ(u8, self.output_path);272 const object_pathZ = try self.gpa.dupeZ(u8, self.object_path);
249 defer self.gpa.free(output_pathZ);273 defer self.gpa.free(object_pathZ);
250274
251 var error_message: [*:0]const u8 = undefined;275 var error_message: [*:0]const u8 = undefined;
252 // TODO: where to put the output object, zig-cache something?
253 // TODO: caching?
254 if (self.target_machine.emitToFile(276 if (self.target_machine.emitToFile(
255 self.llvm_module,277 self.llvm_module,
256 output_pathZ.ptr,278 object_pathZ.ptr,
257 .ObjectFile,279 .ObjectFile,
258 &error_message,280 &error_message,
259 )) {281 )) {
...@@ -271,6 +293,7 @@ pub const LLVMIRModule = struct {...@@ -271,6 +293,7 @@ pub const LLVMIRModule = struct {
271 error.CodegenFail => {293 error.CodegenFail => {
272 decl.analysis = .codegen_failure;294 decl.analysis = .codegen_failure;
273 try module.failed_decls.put(module.gpa, decl, self.err_msg.?);295 try module.failed_decls.put(module.gpa, decl, self.err_msg.?);
296 self.err_msg = null;
274 return;297 return;
275 },298 },
276 else => |e| return e,299 else => |e| return e,
...@@ -278,47 +301,71 @@ pub const LLVMIRModule = struct {...@@ -278,47 +301,71 @@ pub const LLVMIRModule = struct {
278 }301 }
279302
280 fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {303 fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
281 switch (typed_value.ty.zigTypeTag()) {304 if (typed_value.val.castTag(.function)) |func_inst| {
282 .Fn => {305 const func = func_inst.data;
283 const func = typed_value.val.castTag(.function).?.data;
284306
285 const llvm_func = try self.resolveLLVMFunction(func);307 const llvm_func = try self.resolveLLVMFunction(func, src);
286308
287 // We remove all the basic blocks of a function to support incremental309 // This gets the LLVM values from the function and stores them in `self.args`.
288 // compilation!310 const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen();
289 // TODO: remove all basic blocks if functions can have more than one311 var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len);
290 if (llvm_func.getFirstBasicBlock()) |bb| {312 defer self.gpa.free(args);
291 bb.deleteBasicBlock();
292 }
293313
294 const entry_block = llvm_func.appendBasicBlock("Entry");314 for (args) |*arg, i| {
295 self.builder.positionBuilderAtEnd(entry_block);315 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
296316 }
297 const instructions = func.body.instructions;317 self.args = args;
298 for (instructions) |inst| {318 self.arg_index = 0;
299 switch (inst.tag) {319
300 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),320 // Make sure no other LLVM values from other functions can be referenced
301 .call => try self.genCall(inst.castTag(.call).?),321 self.func_inst_table.clearRetainingCapacity();
302 .unreach => self.genUnreach(inst.castTag(.unreach).?),322
303 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),323 // We remove all the basic blocks of a function to support incremental
304 .arg => self.genArg(inst.castTag(.arg).?),324 // compilation!
305 .dbg_stmt => {325 // TODO: remove all basic blocks if functions can have more than one
306 // TODO: implement debug info326 if (llvm_func.getFirstBasicBlock()) |bb| {
307 },327 bb.deleteBasicBlock();
308 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),328 }
309 }329
310 }330 const entry_block = llvm_func.appendBasicBlock("Entry");
311 },331 self.builder.positionBuilderAtEnd(entry_block);
312 else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}),332
333 const instructions = func.body.instructions;
334 for (instructions) |inst| {
335 const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) {
336 .add => try self.genAdd(inst.castTag(.add).?),
337 .alloc => try self.genAlloc(inst.castTag(.alloc).?),
338 .arg => try self.genArg(inst.castTag(.arg).?),
339 .bitcast => try self.genBitCast(inst.castTag(.bitcast).?),
340 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
341 .call => try self.genCall(inst.castTag(.call).?),
342 .intcast => try self.genIntCast(inst.castTag(.intcast).?),
343 .load => try self.genLoad(inst.castTag(.load).?),
344 .not => try self.genNot(inst.castTag(.not).?),
345 .ret => try self.genRet(inst.castTag(.ret).?),
346 .retvoid => self.genRetVoid(inst.castTag(.retvoid).?),
347 .store => try self.genStore(inst.castTag(.store).?),
348 .sub => try self.genSub(inst.castTag(.sub).?),
349 .unreach => self.genUnreach(inst.castTag(.unreach).?),
350 .dbg_stmt => blk: {
351 // TODO: implement debug info
352 break :blk null;
353 },
354 else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}),
355 };
356 if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val);
357 }
358 } else {
359 return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty});
313 }360 }
314 }361 }
315362
316 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void {363 fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
317 if (inst.func.value()) |func_value| {364 if (inst.func.value()) |func_value| {
318 if (func_value.castTag(.function)) |func_payload| {365 if (func_value.castTag(.function)) |func_payload| {
319 const func = func_payload.data;366 const func = func_payload.data;
320 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;367 const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
321 const llvm_fn = try self.resolveLLVMFunction(func);368 const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src);
322369
323 const num_args = inst.args.len;370 const num_args = inst.args.len;
324371
...@@ -338,54 +385,165 @@ pub const LLVMIRModule = struct {...@@ -338,54 +385,165 @@ pub const LLVMIRModule = struct {
338 "",385 "",
339 );386 );
340387
341 if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) {388 const return_type = zig_fn_type.fnReturnType();
389 if (return_type.tag() == .noreturn) {
342 _ = self.builder.buildUnreachable();390 _ = self.builder.buildUnreachable();
343 }391 }
392
393 // No need to store the LLVM value if the return type is void or noreturn
394 if (!return_type.hasCodeGenBits()) return null;
395
396 return call;
344 }397 }
345 }398 }
399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
346 }400 }
347401
348 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void {402 fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
349 _ = self.builder.buildRetVoid();403 _ = self.builder.buildRetVoid();
404 return null;
405 }
406
407 fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
408 _ = self.builder.buildRet(try self.resolveInst(inst.operand));
409 return null;
410 }
411
412 fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
413 return self.builder.buildNot(try self.resolveInst(inst.operand), "");
350 }414 }
351415
352 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void {416 fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
353 _ = self.builder.buildUnreachable();417 _ = self.builder.buildUnreachable();
418 return null;
354 }419 }
355420
356 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void {421 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
357 // TODO: implement this422 const lhs = try self.resolveInst(inst.lhs);
423 const rhs = try self.resolveInst(inst.rhs);
424
425 if (!inst.base.ty.isInt())
426 return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty});
427
428 return if (inst.base.ty.isSignedInt())
429 self.builder.buildNSWAdd(lhs, rhs, "")
430 else
431 self.builder.buildNUWAdd(lhs, rhs, "");
358 }432 }
359433
360 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void {434 fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
361 // TODO: Store this function somewhere such that we dont have to add it again435 const lhs = try self.resolveInst(inst.lhs);
362 const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false);436 const rhs = try self.resolveInst(inst.rhs);
363 const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type);437
364 // TODO: add assertion: LLVMGetIntrinsicID438 if (!inst.base.ty.isInt())
365 _ = self.builder.buildCall(func, null, 0, "");439 return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty});
440
441 return if (inst.base.ty.isSignedInt())
442 self.builder.buildNSWSub(lhs, rhs, "")
443 else
444 self.builder.buildNUWSub(lhs, rhs, "");
445 }
446
447 fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
448 const val = try self.resolveInst(inst.operand);
449
450 const signed = inst.base.ty.isSignedInt();
451 // TODO: Should we use intcast here or just a simple bitcast?
452 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
453 return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, "");
454 }
455
456 fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
457 const val = try self.resolveInst(inst.operand);
458 const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src);
459
460 return self.builder.buildBitCast(val, dest_type, "");
461 }
462
463 fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef {
464 const arg_val = self.args[self.arg_index];
465 self.arg_index += 1;
466
467 const ptr_val = self.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), "");
468 _ = self.builder.buildStore(arg_val, ptr_val);
469 return self.builder.buildLoad(ptr_val, "");
470 }
471
472 fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
473 // buildAlloca expects the pointee type, not the pointer type, so assert that
474 // a Payload.PointerSimple is passed to the alloc instruction.
475 const pointee_type = inst.base.ty.castPointer().?.data;
476
477 // TODO: figure out a way to get the name of the var decl.
478 // TODO: set alignment and volatile
479 return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), "");
480 }
481
482 fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
483 const val = try self.resolveInst(inst.rhs);
484 const ptr = try self.resolveInst(inst.lhs);
485 _ = self.builder.buildStore(val, ptr);
486 return null;
487 }
488
489 fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
490 const ptr_val = try self.resolveInst(inst.operand);
491 return self.builder.buildLoad(ptr_val, "");
492 }
493
494 fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef {
495 const llvn_fn = self.getIntrinsic("llvm.debugtrap");
496 _ = self.builder.buildCall(llvn_fn, null, 0, "");
497 return null;
498 }
499
500 fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.ValueRef {
501 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
502 assert(id != 0);
503 // TODO: add support for overload intrinsics by passing the prefix of the intrinsic
504 // to `lookupIntrinsicID` and then passing the correct types to
505 // `getIntrinsicDeclaration`
506 return self.llvm_module.getIntrinsicDeclaration(id, null, 0);
366 }507 }
367508
368 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {509 fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
369 if (inst.castTag(.constant)) |const_inst| {510 if (inst.value()) |val| {
370 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });511 return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
371 }512 }
372 return self.fail(inst.src, "TODO implement resolveInst", .{});513 if (self.func_inst_table.get(inst)) |value| return value;
514
515 return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{});
373 }516 }
374517
375 fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef {518 fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef {
376 const llvm_type = self.getLLVMType(typed_value.ty);519 const llvm_type = try self.getLLVMType(tv.ty, src);
377520
378 if (typed_value.val.isUndef())521 if (tv.val.isUndef())
379 return llvm_type.getUndef();522 return llvm_type.getUndef();
380523
381 switch (typed_value.ty.zigTypeTag()) {524 switch (tv.ty.zigTypeTag()) {
382 .Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),525 .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(),
383 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),526 .Int => {
527 var bigint_space: Value.BigIntSpace = undefined;
528 const bigint = tv.val.toBigInt(&bigint_space);
529
530 if (bigint.eqZero()) return llvm_type.constNull();
531
532 if (bigint.limbs.len != 1) {
533 return self.fail(src, "TODO implement bigger bigint", .{});
534 }
535 const llvm_int = llvm_type.constInt(bigint.limbs[0], false);
536 if (!bigint.positive) {
537 return llvm.constNeg(llvm_int);
538 }
539 return llvm_int;
540 },
541 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
384 }542 }
385 }543 }
386544
387 /// If the llvm function does not exist, create it545 /// If the llvm function does not exist, create it
388 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef {546 fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
389 // TODO: do we want to store this in our own datastructure?547 // TODO: do we want to store this in our own datastructure?
390 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;548 if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
391549
...@@ -402,25 +560,25 @@ pub const LLVMIRModule = struct {...@@ -402,25 +560,25 @@ pub const LLVMIRModule = struct {
402 defer self.gpa.free(llvm_param);560 defer self.gpa.free(llvm_param);
403561
404 for (fn_param_types) |fn_param, i| {562 for (fn_param_types) |fn_param, i| {
405 llvm_param[i] = self.getLLVMType(fn_param);563 llvm_param[i] = try self.getLLVMType(fn_param, src);
406 }564 }
407565
408 const fn_type = llvm.TypeRef.functionType(566 const fn_type = llvm.TypeRef.functionType(
409 self.getLLVMType(return_type),567 try self.getLLVMType(return_type, src),
410 if (fn_param_len == 0) null else llvm_param.ptr,568 if (fn_param_len == 0) null else llvm_param.ptr,
411 @intCast(c_uint, fn_param_len),569 @intCast(c_uint, fn_param_len),
412 false,570 false,
413 );571 );
414 const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);572 const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type);
415573
416 if (return_type.zigTypeTag() == .NoReturn) {574 if (return_type.tag() == .noreturn) {
417 llvm_fn.addFnAttr("noreturn");575 llvm_fn.addFnAttr("noreturn");
418 }576 }
419577
420 return llvm_fn;578 return llvm_fn;
421 }579 }
422580
423 fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef {581 fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
424 switch (t.zigTypeTag()) {582 switch (t.zigTypeTag()) {
425 .Void => return llvm.voidType(),583 .Void => return llvm.voidType(),
426 .NoReturn => return llvm.voidType(),584 .NoReturn => return llvm.voidType(),
...@@ -429,13 +587,18 @@ pub const LLVMIRModule = struct {...@@ -429,13 +587,18 @@ pub const LLVMIRModule = struct {
429 return llvm.intType(info.bits);587 return llvm.intType(info.bits);
430 },588 },
431 .Bool => return llvm.intType(1),589 .Bool => return llvm.intType(1),
432 else => unreachable,590 .Pointer => {
591 const pointer = t.castPointer().?;
592 const elem_type = try self.getLLVMType(pointer.data, src);
593 return elem_type.pointerType(0);
594 },
595 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
433 }596 }
434 }597 }
435598
436 pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {599 pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
437 @setCold(true);600 @setCold(true);
438 std.debug.assert(self.err_msg == null);601 assert(self.err_msg == null);
439 self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);602 self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
440 return error.CodegenFail;603 return error.CodegenFail;
441 }604 }
src/llvm_bindings.zig+48
...@@ -43,8 +43,14 @@ pub const TypeRef = opaque {...@@ -43,8 +43,14 @@ pub const TypeRef = opaque {
43 pub const constAllOnes = LLVMConstAllOnes;43 pub const constAllOnes = LLVMConstAllOnes;
44 extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;44 extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;
4545
46 pub const constInt = LLVMConstInt;
47 extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef;
48
46 pub const getUndef = LLVMGetUndef;49 pub const getUndef = LLVMGetUndef;
47 extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;50 extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef;
51
52 pub const pointerType = LLVMPointerType;
53 extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef;
48};54};
4955
50pub const ModuleRef = opaque {56pub const ModuleRef = opaque {
...@@ -63,10 +69,16 @@ pub const ModuleRef = opaque {...@@ -63,10 +69,16 @@ pub const ModuleRef = opaque {
63 pub const getNamedFunction = LLVMGetNamedFunction;69 pub const getNamedFunction = LLVMGetNamedFunction;
64 extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;70 extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;
6571
72 pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration;
73 extern fn LLVMGetIntrinsicDeclaration(Mod: *const ModuleRef, ID: c_uint, ParamTypes: ?[*]*const TypeRef, ParamCount: usize) *const ValueRef;
74
66 pub const printToString = LLVMPrintModuleToString;75 pub const printToString = LLVMPrintModuleToString;
67 extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;76 extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;
68};77};
6978
79pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
80extern fn LLVMLookupIntrinsicID(Name: [*]const u8, NameLen: usize) c_uint;
81
70pub const disposeMessage = LLVMDisposeMessage;82pub const disposeMessage = LLVMDisposeMessage;
71extern fn LLVMDisposeMessage(Message: [*:0]const u8) void;83extern fn LLVMDisposeMessage(Message: [*:0]const u8) void;
7284
...@@ -76,9 +88,15 @@ pub const VerifierFailureAction = extern enum {...@@ -76,9 +88,15 @@ pub const VerifierFailureAction = extern enum {
76 ReturnStatus,88 ReturnStatus,
77};89};
7890
91pub const constNeg = LLVMConstNeg;
92extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
93
79pub const voidType = LLVMVoidType;94pub const voidType = LLVMVoidType;
80extern fn LLVMVoidType() *const TypeRef;95extern fn LLVMVoidType() *const TypeRef;
8196
97pub const getParam = LLVMGetParam;
98extern fn LLVMGetParam(Fn: *const ValueRef, Index: c_uint) *const ValueRef;
99
82pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;100pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
83extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;101extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
84102
...@@ -117,11 +135,41 @@ pub const BuilderRef = opaque {...@@ -117,11 +135,41 @@ pub const BuilderRef = opaque {
117 pub const buildRetVoid = LLVMBuildRetVoid;135 pub const buildRetVoid = LLVMBuildRetVoid;
118 extern fn LLVMBuildRetVoid(*const BuilderRef) *const ValueRef;136 extern fn LLVMBuildRetVoid(*const BuilderRef) *const ValueRef;
119137
138 pub const buildRet = LLVMBuildRet;
139 extern fn LLVMBuildRet(*const BuilderRef, V: *const ValueRef) *const ValueRef;
140
120 pub const buildUnreachable = LLVMBuildUnreachable;141 pub const buildUnreachable = LLVMBuildUnreachable;
121 extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef;142 extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef;
122143
123 pub const buildAlloca = LLVMBuildAlloca;144 pub const buildAlloca = LLVMBuildAlloca;
124 extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;145 extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
146
147 pub const buildStore = LLVMBuildStore;
148 extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef;
149
150 pub const buildLoad = LLVMBuildLoad;
151 extern fn LLVMBuildLoad(*const BuilderRef, PointerVal: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
152
153 pub const buildNot = LLVMBuildNot;
154 extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
155
156 pub const buildNSWAdd = LLVMBuildNSWAdd;
157 extern fn LLVMBuildNSWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
158
159 pub const buildNUWAdd = LLVMBuildNUWAdd;
160 extern fn LLVMBuildNUWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
161
162 pub const buildNSWSub = LLVMBuildNSWSub;
163 extern fn LLVMBuildNSWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
164
165 pub const buildNUWSub = LLVMBuildNUWSub;
166 extern fn LLVMBuildNUWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
167
168 pub const buildIntCast2 = LLVMBuildIntCast2;
169 extern fn LLVMBuildIntCast2(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, IsSigned: LLVMBool, Name: [*:0]const u8) *const ValueRef;
170
171 pub const buildBitCast = LLVMBuildBitCast;
172 extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
125};173};
126174
127pub const BasicBlockRef = opaque {175pub const BasicBlockRef = opaque {
src/test.zig+42-1
...@@ -135,6 +135,7 @@ pub const TestContext = struct {...@@ -135,6 +135,7 @@ pub const TestContext = struct {
135 extension: Extension,135 extension: Extension,
136 object_format: ?std.builtin.ObjectFormat = null,136 object_format: ?std.builtin.ObjectFormat = null,
137 emit_h: bool = false,137 emit_h: bool = false,
138 llvm_backend: bool = false,
138139
139 files: std.ArrayList(File),140 files: std.ArrayList(File),
140141
...@@ -266,6 +267,21 @@ pub const TestContext = struct {...@@ -266,6 +267,21 @@ pub const TestContext = struct {
266 return &ctx.cases.items[ctx.cases.items.len - 1];267 return &ctx.cases.items[ctx.cases.items.len - 1];
267 }268 }
268269
270 /// Adds a test case that uses the LLVM backend to emit an executable.
271 /// Currently this implies linking libc, because only then we can generate a testable executable.
272 pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
273 ctx.cases.append(Case{
274 .name = name,
275 .target = target,
276 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
277 .output_mode = .Exe,
278 .extension = .Zig,
279 .files = std.ArrayList(File).init(ctx.cases.allocator),
280 .llvm_backend = true,
281 }) catch unreachable;
282 return &ctx.cases.items[ctx.cases.items.len - 1];
283 }
284
269 pub fn addObj(285 pub fn addObj(
270 ctx: *TestContext,286 ctx: *TestContext,
271 name: []const u8,287 name: []const u8,
...@@ -518,10 +534,30 @@ pub const TestContext = struct {...@@ -518,10 +534,30 @@ pub const TestContext = struct {
518 try thread_pool.init(std.testing.allocator);534 try thread_pool.init(std.testing.allocator);
519 defer thread_pool.deinit();535 defer thread_pool.deinit();
520536
537 // Use the same global cache dir for all the tests, such that we for example don't have to
538 // rebuild musl libc for every case (when LLVM backend is enabled).
539 var global_tmp = std.testing.tmpDir(.{});
540 defer global_tmp.cleanup();
541
542 var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{});
543 defer cache_dir.close();
544 const tmp_dir_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path });
545 defer std.testing.allocator.free(tmp_dir_path);
546
547 const global_cache_directory: Compilation.Directory = .{
548 .handle = cache_dir,
549 .path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ tmp_dir_path, "zig-cache" }),
550 };
551 defer std.testing.allocator.free(global_cache_directory.path.?);
552
521 for (self.cases.items) |case| {553 for (self.cases.items) |case| {
522 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)554 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)
523 continue;555 continue;
524556
557 // Skip tests that require LLVM backend when it is not available
558 if (!build_options.have_llvm and case.llvm_backend)
559 continue;
560
525 var prg_node = root_node.start(case.name, case.updates.items.len);561 var prg_node = root_node.start(case.name, case.updates.items.len);
526 prg_node.activate();562 prg_node.activate();
527 defer prg_node.end();563 defer prg_node.end();
...@@ -537,6 +573,7 @@ pub const TestContext = struct {...@@ -537,6 +573,7 @@ pub const TestContext = struct {
537 case,573 case,
538 zig_lib_directory,574 zig_lib_directory,
539 &thread_pool,575 &thread_pool,
576 global_cache_directory,
540 );577 );
541 }578 }
542 }579 }
...@@ -548,6 +585,7 @@ pub const TestContext = struct {...@@ -548,6 +585,7 @@ pub const TestContext = struct {
548 case: Case,585 case: Case,
549 zig_lib_directory: Compilation.Directory,586 zig_lib_directory: Compilation.Directory,
550 thread_pool: *ThreadPool,587 thread_pool: *ThreadPool,
588 global_cache_directory: Compilation.Directory,
551 ) !void {589 ) !void {
552 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);590 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
553 const target = target_info.target;591 const target = target_info.target;
...@@ -601,7 +639,7 @@ pub const TestContext = struct {...@@ -601,7 +639,7 @@ pub const TestContext = struct {
601 null;639 null;
602 const comp = try Compilation.create(allocator, .{640 const comp = try Compilation.create(allocator, .{
603 .local_cache_directory = zig_cache_directory,641 .local_cache_directory = zig_cache_directory,
604 .global_cache_directory = zig_cache_directory,642 .global_cache_directory = global_cache_directory,
605 .zig_lib_directory = zig_lib_directory,643 .zig_lib_directory = zig_lib_directory,
606 .thread_pool = thread_pool,644 .thread_pool = thread_pool,
607 .root_name = "test_case",645 .root_name = "test_case",
...@@ -619,6 +657,9 @@ pub const TestContext = struct {...@@ -619,6 +657,9 @@ pub const TestContext = struct {
619 .object_format = case.object_format,657 .object_format = case.object_format,
620 .is_native_os = case.target.isNativeOs(),658 .is_native_os = case.target.isNativeOs(),
621 .is_native_abi = case.target.isNativeAbi(),659 .is_native_abi = case.target.isNativeAbi(),
660 .link_libc = case.llvm_backend,
661 .use_llvm = case.llvm_backend,
662 .self_exe_path = std.testing.zig_exe_path,
622 });663 });
623 defer comp.destroy();664 defer comp.destroy();
624665
src/zig_clang.h+19-1
...@@ -8,14 +8,32 @@...@@ -8,14 +8,32 @@
8#ifndef ZIG_ZIG_CLANG_H8#ifndef ZIG_ZIG_CLANG_H
9#define ZIG_ZIG_CLANG_H9#define ZIG_ZIG_CLANG_H
1010
11#include "stage1/stage2.h"
12#include <inttypes.h>11#include <inttypes.h>
13#include <stdbool.h>12#include <stdbool.h>
13#include <stddef.h>
14
15#ifdef __cplusplus
16#define ZIG_EXTERN_C extern "C"
17#else
18#define ZIG_EXTERN_C
19#endif
1420
15// ATTENTION: If you modify this file, be sure to update the corresponding21// ATTENTION: If you modify this file, be sure to update the corresponding
16// extern function declarations in the self-hosted compiler file22// extern function declarations in the self-hosted compiler file
17// src/clang.zig.23// src/clang.zig.
1824
25// ABI warning
26struct Stage2ErrorMsg {
27 const char *filename_ptr; // can be null
28 size_t filename_len;
29 const char *msg_ptr;
30 size_t msg_len;
31 const char *source; // valid until the ASTUnit is freed. can be null
32 unsigned line; // 0 based
33 unsigned column; // 0 based
34 unsigned offset; // byte offset into source
35};
36
19struct ZigClangSourceLocation {37struct ZigClangSourceLocation {
20 unsigned ID;38 unsigned ID;
21};39};
test/stage2/llvm_backend.zig created+30
...@@ -0,0 +1,30 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3const build_options = @import("build_options");
4
5// These tests should work with all platforms, but we're using linux_x64 for
6// now for consistency. Will be expanded eventually.
7const linux_x64 = std.zig.CrossTarget{
8 .cpu_arch = .x86_64,
9 .os_tag = .linux,
10};
11
12pub fn addCases(ctx: *TestContext) !void {
13 {
14 var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64);
15
16 case.addCompareOutput(
17 \\fn add(a: i32, b: i32) i32 {
18 \\ return a + b;
19 \\}
20 \\
21 \\export fn main() c_int {
22 \\ var a: i32 = -5;
23 \\ const x = add(a, 7);
24 \\ var y = add(2, 0);
25 \\ y -= x;
26 \\ return y;
27 \\}
28 , "");
29 }
30}
test/stage2/test.zig+1
...@@ -31,6 +31,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -31,6 +31,7 @@ pub fn addCases(ctx: *TestContext) !void {
31 try @import("spu-ii.zig").addCases(ctx);31 try @import("spu-ii.zig").addCases(ctx);
32 try @import("arm.zig").addCases(ctx);32 try @import("arm.zig").addCases(ctx);
33 try @import("aarch64.zig").addCases(ctx);33 try @import("aarch64.zig").addCases(ctx);
34 try @import("llvm_backend.zig").addCases(ctx);
3435
35 {36 {
36 var case = ctx.exe("hello world with updates", linux_x64);37 var case = ctx.exe("hello world with updates", linux_x64);