From 071417161d5d7ab91c32073693590ef161017dbe Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 19 Dec 2020 11:22:49 +0100 Subject: stage2: add initial impl of LLVM backend in self-hosted compiler --- src/codegen/llvm.zig | 125 --------------------------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 src/codegen/llvm.zig (limited to 'src/codegen/llvm.zig') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig deleted file mode 100644 index f87a7a940c..0000000000 --- a/src/codegen/llvm.zig +++ /dev/null @@ -1,125 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; - -pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 { - const llvm_arch = switch (target.cpu.arch) { - .arm => "arm", - .armeb => "armeb", - .aarch64 => "aarch64", - .aarch64_be => "aarch64_be", - .aarch64_32 => "aarch64_32", - .arc => "arc", - .avr => "avr", - .bpfel => "bpfel", - .bpfeb => "bpfeb", - .hexagon => "hexagon", - .mips => "mips", - .mipsel => "mipsel", - .mips64 => "mips64", - .mips64el => "mips64el", - .msp430 => "msp430", - .powerpc => "powerpc", - .powerpc64 => "powerpc64", - .powerpc64le => "powerpc64le", - .r600 => "r600", - .amdgcn => "amdgcn", - .riscv32 => "riscv32", - .riscv64 => "riscv64", - .sparc => "sparc", - .sparcv9 => "sparcv9", - .sparcel => "sparcel", - .s390x => "s390x", - .tce => "tce", - .tcele => "tcele", - .thumb => "thumb", - .thumbeb => "thumbeb", - .i386 => "i386", - .x86_64 => "x86_64", - .xcore => "xcore", - .nvptx => "nvptx", - .nvptx64 => "nvptx64", - .le32 => "le32", - .le64 => "le64", - .amdil => "amdil", - .amdil64 => "amdil64", - .hsail => "hsail", - .hsail64 => "hsail64", - .spir => "spir", - .spir64 => "spir64", - .kalimba => "kalimba", - .shave => "shave", - .lanai => "lanai", - .wasm32 => "wasm32", - .wasm64 => "wasm64", - .renderscript32 => "renderscript32", - .renderscript64 => "renderscript64", - .ve => "ve", - .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, - }; - // TODO Add a sub-arch for some architectures depending on CPU features. - - const llvm_os = switch (target.os.tag) { - .freestanding => "unknown", - .ananas => "ananas", - .cloudabi => "cloudabi", - .dragonfly => "dragonfly", - .freebsd => "freebsd", - .fuchsia => "fuchsia", - .ios => "ios", - .kfreebsd => "kfreebsd", - .linux => "linux", - .lv2 => "lv2", - .macos => "macosx", - .netbsd => "netbsd", - .openbsd => "openbsd", - .solaris => "solaris", - .windows => "windows", - .haiku => "haiku", - .minix => "minix", - .rtems => "rtems", - .nacl => "nacl", - .cnk => "cnk", - .aix => "aix", - .cuda => "cuda", - .nvcl => "nvcl", - .amdhsa => "amdhsa", - .ps4 => "ps4", - .elfiamcu => "elfiamcu", - .tvos => "tvos", - .watchos => "watchos", - .mesa3d => "mesa3d", - .contiki => "contiki", - .amdpal => "amdpal", - .hermit => "hermit", - .hurd => "hurd", - .wasi => "wasi", - .emscripten => "emscripten", - .uefi => "windows", - .other => "unknown", - }; - - const llvm_abi = switch (target.abi) { - .none => "unknown", - .gnu => "gnu", - .gnuabin32 => "gnuabin32", - .gnuabi64 => "gnuabi64", - .gnueabi => "gnueabi", - .gnueabihf => "gnueabihf", - .gnux32 => "gnux32", - .code16 => "code16", - .eabi => "eabi", - .eabihf => "eabihf", - .android => "android", - .musl => "musl", - .musleabi => "musleabi", - .musleabihf => "musleabihf", - .msvc => "msvc", - .itanium => "itanium", - .cygnus => "cygnus", - .coreclr => "coreclr", - .simulator => "simulator", - .macabi => "macabi", - }; - - return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); -} -- cgit v1.2.3 From b1cfa923bee5210fd78c7508d1af92dde3361c8c Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Wed, 6 Jan 2021 01:27:06 +0100 Subject: stage2: rename and move files related to LLVM backend --- CMakeLists.txt | 4 +- src/Compilation.zig | 2 +- src/codegen/llvm.zig | 722 ++++++++++++++++++++++++++++++++++++++++++ src/codegen/llvm/bindings.zig | 571 +++++++++++++++++++++++++++++++++ src/link.zig | 2 +- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/llvm_backend.zig | 722 ------------------------------------------ src/llvm_bindings.zig | 574 --------------------------------- src/main.zig | 6 +- src/mingw.zig | 2 +- src/target.zig | 2 +- test/stage2/llvm.zig | 43 +++ test/stage2/llvm_backend.zig | 43 --- test/stage2/test.zig | 2 +- 15 files changed, 1348 insertions(+), 1351 deletions(-) create mode 100644 src/codegen/llvm.zig create mode 100644 src/codegen/llvm/bindings.zig delete mode 100644 src/llvm_backend.zig delete mode 100644 src/llvm_bindings.zig create mode 100644 test/stage2/llvm.zig delete mode 100644 test/stage2/llvm_backend.zig (limited to 'src/codegen/llvm.zig') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ed25b93d2..eb4b465ef3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -541,6 +541,8 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig" "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig" "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" + "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" + "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig" "${CMAKE_SOURCE_DIR}/src/codegen/spu-mk2.zig" "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig" @@ -562,8 +564,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/cbe.h" "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" "${CMAKE_SOURCE_DIR}/src/liveness.zig" - "${CMAKE_SOURCE_DIR}/src/llvm_backend.zig" - "${CMAKE_SOURCE_DIR}/src/llvm_bindings.zig" "${CMAKE_SOURCE_DIR}/src/main.zig" "${CMAKE_SOURCE_DIR}/src/mingw.zig" "${CMAKE_SOURCE_DIR}/src/musl.zig" diff --git a/src/Compilation.zig b/src/Compilation.zig index 9912520437..77f9f79bae 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2116,7 +2116,7 @@ pub fn addCCArgs( try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); } - const llvm_triple = try @import("llvm_backend.zig").targetTriple(arena, target); + const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); switch (ext) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig new file mode 100644 index 0000000000..df97f7e0ef --- /dev/null +++ b/src/codegen/llvm.zig @@ -0,0 +1,722 @@ +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Compilation = @import("../Compilation.zig"); +const llvm = @import("llvm/bindings.zig"); +const link = @import("../link.zig"); +const log = std.log.scoped(.codegen); + +const Module = @import("../Module.zig"); +const TypedValue = @import("../TypedValue.zig"); +const ir = @import("../ir.zig"); +const Inst = ir.Inst; + +const Value = @import("../value.zig").Value; +const Type = @import("../type.zig").Type; + +pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { + const llvm_arch = switch (target.cpu.arch) { + .arm => "arm", + .armeb => "armeb", + .aarch64 => "aarch64", + .aarch64_be => "aarch64_be", + .aarch64_32 => "aarch64_32", + .arc => "arc", + .avr => "avr", + .bpfel => "bpfel", + .bpfeb => "bpfeb", + .hexagon => "hexagon", + .mips => "mips", + .mipsel => "mipsel", + .mips64 => "mips64", + .mips64el => "mips64el", + .msp430 => "msp430", + .powerpc => "powerpc", + .powerpc64 => "powerpc64", + .powerpc64le => "powerpc64le", + .r600 => "r600", + .amdgcn => "amdgcn", + .riscv32 => "riscv32", + .riscv64 => "riscv64", + .sparc => "sparc", + .sparcv9 => "sparcv9", + .sparcel => "sparcel", + .s390x => "s390x", + .tce => "tce", + .tcele => "tcele", + .thumb => "thumb", + .thumbeb => "thumbeb", + .i386 => "i386", + .x86_64 => "x86_64", + .xcore => "xcore", + .nvptx => "nvptx", + .nvptx64 => "nvptx64", + .le32 => "le32", + .le64 => "le64", + .amdil => "amdil", + .amdil64 => "amdil64", + .hsail => "hsail", + .hsail64 => "hsail64", + .spir => "spir", + .spir64 => "spir64", + .kalimba => "kalimba", + .shave => "shave", + .lanai => "lanai", + .wasm32 => "wasm32", + .wasm64 => "wasm64", + .renderscript32 => "renderscript32", + .renderscript64 => "renderscript64", + .ve => "ve", + .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, + }; + // TODO Add a sub-arch for some architectures depending on CPU features. + + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .ios => "ios", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .macos => "macosx", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .cnk => "cnk", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .elfiamcu => "elfiamcu", + .tvos => "tvos", + .watchos => "watchos", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + .other => "unknown", + }; + + const llvm_abi = switch (target.abi) { + .none => "unknown", + .gnu => "gnu", + .gnuabin32 => "gnuabin32", + .gnuabi64 => "gnuabi64", + .gnueabi => "gnueabi", + .gnueabihf => "gnueabihf", + .gnux32 => "gnux32", + .code16 => "code16", + .eabi => "eabi", + .eabihf => "eabihf", + .android => "android", + .musl => "musl", + .musleabi => "musleabi", + .musleabihf => "musleabihf", + .msvc => "msvc", + .itanium => "itanium", + .cygnus => "cygnus", + .coreclr => "coreclr", + .simulator => "simulator", + .macabi => "macabi", + }; + + return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); +} + +pub const LLVMIRModule = struct { + module: *Module, + llvm_module: *const llvm.Module, + context: *const llvm.Context, + target_machine: *const llvm.TargetMachine, + builder: *const llvm.Builder, + + object_path: []const u8, + + gpa: *Allocator, + err_msg: ?*Compilation.ErrorMsg = null, + + // TODO: The fields below should really move into a different struct, + // because they are only valid when generating a function + + /// This stores the LLVM values used in a function, such that they can be + /// referred to in other instructions. This table is cleared before every function is generated. + func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, + + /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. + args: []*const llvm.Value = &[_]*const llvm.Value{}, + arg_index: usize = 0, + + entry_block: *const llvm.BasicBlock = undefined, + /// This fields stores the last alloca instruction, such that we can append more alloca instructions + /// to the top of the function. + latest_alloca_inst: ?*const llvm.Value = null, + + pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { + const self = try allocator.create(LLVMIRModule); + errdefer allocator.destroy(self); + + const gpa = options.module.?.gpa; + + const obj_basename = try std.zig.binNameAlloc(gpa, .{ + .root_name = options.root_name, + .target = options.target, + .output_mode = .Obj, + }); + defer gpa.free(obj_basename); + + const o_directory = options.module.?.zig_cache_artifact_directory; + const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); + errdefer gpa.free(object_path); + + const context = llvm.Context.create(); + errdefer context.dispose(); + + initializeLLVMTargets(); + + const root_nameZ = try gpa.dupeZ(u8, options.root_name); + defer gpa.free(root_nameZ); + const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); + errdefer llvm_module.dispose(); + + const llvm_target_triple = try targetTriple(gpa, options.target); + defer gpa.free(llvm_target_triple); + + var error_message: [*:0]const u8 = undefined; + var target: *const llvm.Target = undefined; + if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) { + defer llvm.disposeMessage(error_message); + + const stderr = std.io.getStdErr().outStream(); + try stderr.print( + \\Zig is expecting LLVM to understand this target: '{s}' + \\However LLVM responded with: "{s}" + \\Zig is unable to continue. This is a bug in Zig: + \\https://github.com/ziglang/zig/issues/438 + \\ + , + .{ + llvm_target_triple, + error_message, + }, + ); + return error.InvalidLLVMTriple; + } + + const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive; + const target_machine = llvm.TargetMachine.create( + target, + llvm_target_triple.ptr, + "", + "", + opt_level, + .Static, + .Default, + ); + errdefer target_machine.dispose(); + + const builder = context.createBuilder(); + errdefer builder.dispose(); + + self.* = .{ + .module = options.module.?, + .llvm_module = llvm_module, + .context = context, + .target_machine = target_machine, + .builder = builder, + .object_path = object_path, + .gpa = gpa, + }; + return self; + } + + pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { + self.builder.dispose(); + self.target_machine.dispose(); + self.llvm_module.dispose(); + self.context.dispose(); + + self.func_inst_table.deinit(self.gpa); + self.gpa.free(self.object_path); + + allocator.destroy(self); + } + + fn initializeLLVMTargets() void { + llvm.initializeAllTargets(); + llvm.initializeAllTargetInfos(); + llvm.initializeAllTargetMCs(); + llvm.initializeAllAsmPrinters(); + llvm.initializeAllAsmParsers(); + } + + pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { + if (comp.verbose_llvm_ir) { + const dump = self.llvm_module.printToString(); + defer llvm.disposeMessage(dump); + + const stderr = std.io.getStdErr().outStream(); + try stderr.writeAll(std.mem.spanZ(dump)); + } + + { + var error_message: [*:0]const u8 = undefined; + // verifyModule always allocs the error_message even if there is no error + defer llvm.disposeMessage(error_message); + + if (self.llvm_module.verify(.ReturnStatus, &error_message)) { + const stderr = std.io.getStdErr().outStream(); + try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message}); + return error.BrokenLLVMModule; + } + } + + const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); + defer self.gpa.free(object_pathZ); + + var error_message: [*:0]const u8 = undefined; + if (self.target_machine.emitToFile( + self.llvm_module, + object_pathZ.ptr, + .ObjectFile, + &error_message, + )) { + defer llvm.disposeMessage(error_message); + + const stderr = std.io.getStdErr().outStream(); + try stderr.print("LLVM failed to emit file: {s}\n", .{error_message}); + return error.FailedToEmit; + } + } + + pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { + self.gen(module, decl) catch |err| switch (err) { + error.CodegenFail => { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, self.err_msg.?); + self.err_msg = null; + return; + }, + else => |e| return e, + }; + } + + fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { + const typed_value = decl.typed_value.most_recent.typed_value; + const src = decl.src(); + + log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); + + if (typed_value.val.castTag(.function)) |func_payload| { + const func = func_payload.data; + + const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src); + + // This gets the LLVM values from the function and stores them in `self.args`. + const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); + var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); + defer self.gpa.free(args); + + for (args) |*arg, i| { + arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); + } + self.args = args; + self.arg_index = 0; + + // Make sure no other LLVM values from other functions can be referenced + self.func_inst_table.clearRetainingCapacity(); + + // We remove all the basic blocks of a function to support incremental + // compilation! + // TODO: remove all basic blocks if functions can have more than one + if (llvm_func.getFirstBasicBlock()) |bb| { + bb.deleteBasicBlock(); + } + + self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); + self.builder.positionBuilderAtEnd(self.entry_block); + self.latest_alloca_inst = null; + + const instructions = func.body.instructions; + for (instructions) |inst| { + const opt_llvm_val: ?*const llvm.Value = switch (inst.tag) { + .add => try self.genAdd(inst.castTag(.add).?), + .alloc => try self.genAlloc(inst.castTag(.alloc).?), + .arg => try self.genArg(inst.castTag(.arg).?), + .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), + .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .call => try self.genCall(inst.castTag(.call).?), + .intcast => try self.genIntCast(inst.castTag(.intcast).?), + .load => try self.genLoad(inst.castTag(.load).?), + .not => try self.genNot(inst.castTag(.not).?), + .ret => try self.genRet(inst.castTag(.ret).?), + .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), + .store => try self.genStore(inst.castTag(.store).?), + .sub => try self.genSub(inst.castTag(.sub).?), + .unreach => self.genUnreach(inst.castTag(.unreach).?), + .dbg_stmt => blk: { + // TODO: implement debug info + break :blk null; + }, + else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), + }; + if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); + } + } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { + _ = try self.resolveLLVMFunction(extern_fn.data, src); + } else { + _ = try self.resolveGlobalDecl(decl, src); + } + } + + fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { + if (inst.func.value()) |func_value| { + const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| + extern_fn.data + else if (func_value.castTag(.function)) |func_payload| + func_payload.data.owner_decl + else + unreachable; + + const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; + const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src); + + const num_args = inst.args.len; + + const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args); + defer self.gpa.free(llvm_param_vals); + + for (inst.args) |arg, i| { + llvm_param_vals[i] = try self.resolveInst(arg); + } + + // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs + // Do we need that? + const call = self.builder.buildCall( + llvm_fn, + if (num_args == 0) null else llvm_param_vals.ptr, + @intCast(c_uint, num_args), + "", + ); + + const return_type = zig_fn_type.fnReturnType(); + if (return_type.tag() == .noreturn) { + _ = self.builder.buildUnreachable(); + } + + // No need to store the LLVM value if the return type is void or noreturn + if (!return_type.hasCodeGenBits()) return null; + + return call; + } else { + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); + } + } + + fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { + _ = self.builder.buildRetVoid(); + return null; + } + + fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { + _ = self.builder.buildRet(try self.resolveInst(inst.operand)); + return null; + } + + fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { + return self.builder.buildNot(try self.resolveInst(inst.operand), ""); + } + + fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { + _ = self.builder.buildUnreachable(); + return null; + } + + fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWAdd(lhs, rhs, "") + else + self.builder.buildNUWAdd(lhs, rhs, ""); + } + + fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWSub(lhs, rhs, "") + else + self.builder.buildNUWSub(lhs, rhs, ""); + } + + fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { + const val = try self.resolveInst(inst.operand); + + const signed = inst.base.ty.isSignedInt(); + // TODO: Should we use intcast here or just a simple bitcast? + // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes + return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, ""); + } + + fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { + const val = try self.resolveInst(inst.operand); + const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); + + return self.builder.buildBitCast(val, dest_type, ""); + } + + fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value { + const arg_val = self.args[self.arg_index]; + self.arg_index += 1; + + const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src)); + _ = self.builder.buildStore(arg_val, ptr_val); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { + // buildAlloca expects the pointee type, not the pointer type, so assert that + // a Payload.PointerSimple is passed to the alloc instruction. + const pointee_type = inst.base.ty.castPointer().?.data; + + // TODO: figure out a way to get the name of the var decl. + // TODO: set alignment and volatile + return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src)); + } + + /// Use this instead of builder.buildAlloca, because this function makes sure to + /// put the alloca instruction at the top of the function! + fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { + if (self.latest_alloca_inst) |latest_alloc| { + // builder.positionBuilder adds it before the instruction, + // but we want to put it after the last alloca instruction. + self.builder.positionBuilder(self.entry_block, latest_alloc.getNextInstruction().?); + } else { + // There might have been other instructions emitted before the + // first alloca has been generated. However the alloca should still + // be first in the function. + if (self.entry_block.getFirstInstruction()) |first_inst| { + self.builder.positionBuilder(self.entry_block, first_inst); + } + } + defer self.builder.positionBuilderAtEnd(self.entry_block); + + const val = self.builder.buildAlloca(t, ""); + self.latest_alloca_inst = val; + return val; + } + + fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { + const val = try self.resolveInst(inst.rhs); + const ptr = try self.resolveInst(inst.lhs); + _ = self.builder.buildStore(val, ptr); + return null; + } + + fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { + const ptr_val = try self.resolveInst(inst.operand); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { + const llvn_fn = self.getIntrinsic("llvm.debugtrap"); + _ = self.builder.buildCall(llvn_fn, null, 0, ""); + return null; + } + + fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value { + const id = llvm.lookupIntrinsicID(name.ptr, name.len); + assert(id != 0); + // TODO: add support for overload intrinsics by passing the prefix of the intrinsic + // to `lookupIntrinsicID` and then passing the correct types to + // `getIntrinsicDeclaration` + return self.llvm_module.getIntrinsicDeclaration(id, null, 0); + } + + fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value { + if (inst.value()) |val| { + return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); + } + if (self.func_inst_table.get(inst)) |value| return value; + + return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); + } + + fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { + const llvm_type = try self.getLLVMType(tv.ty, src); + + if (tv.val.isUndef()) + return llvm_type.getUndef(); + + switch (tv.ty.zigTypeTag()) { + .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), + .Int => { + var bigint_space: Value.BigIntSpace = undefined; + const bigint = tv.val.toBigInt(&bigint_space); + + if (bigint.eqZero()) return llvm_type.constNull(); + + if (bigint.limbs.len != 1) { + return self.fail(src, "TODO implement bigger bigint", .{}); + } + const llvm_int = llvm_type.constInt(bigint.limbs[0], false); + if (!bigint.positive) { + return llvm.constNeg(llvm_int); + } + return llvm_int; + }, + .Pointer => switch (tv.val.tag()) { + .decl_ref => { + const decl = tv.val.castTag(.decl_ref).?.data; + const val = try self.resolveGlobalDecl(decl, src); + + const usize_type = try self.getLLVMType(Type.initTag(.usize), src); + + // TODO: second index should be the index into the memory! + var indices: [2]*const llvm.Value = .{ + usize_type.constNull(), + usize_type.constNull(), + }; + + // TODO: consider using buildInBoundsGEP2 for opaque pointers + return self.builder.buildInBoundsGEP(val, &indices, 2, ""); + }, + else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), + }, + .Array => { + if (tv.val.castTag(.bytes)) |payload| { + const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { + if (sentinel.tag() == .zero) break :blk true; + return self.fail(src, "TODO handle other sentinel values", .{}); + } else false; + + return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel); + } else { + return self.fail(src, "TODO handle more array values", .{}); + } + }, + else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), + } + } + + fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Type { + switch (t.zigTypeTag()) { + .Void => return self.context.voidType(), + .NoReturn => return self.context.voidType(), + .Int => { + const info = t.intInfo(self.module.getTarget()); + return self.context.intType(info.bits); + }, + .Bool => return self.context.intType(1), + .Pointer => { + if (t.isSlice()) { + return self.fail(src, "TODO: LLVM backend: implement slices", .{}); + } else { + const elem_type = try self.getLLVMType(t.elemType(), src); + return elem_type.pointerType(0); + } + }, + .Array => { + const elem_type = try self.getLLVMType(t.elemType(), src); + return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); + }, + else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), + } + } + + fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Value { + // TODO: do we want to store this in our own datastructure? + if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val; + + const typed_value = decl.typed_value.most_recent.typed_value; + + // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. + const llvm_type = try self.getLLVMType(typed_value.ty, src); + const val = try self.genTypedValue(src, typed_value); + const global = self.llvm_module.addGlobal(llvm_type, decl.name); + llvm.setInitializer(global, val); + + // TODO ask the Decl if it is const + // https://github.com/ziglang/zig/issues/7582 + + return global; + } + + /// If the llvm function does not exist, create it + fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.Value { + // TODO: do we want to store this in our own datastructure? + if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn; + + const zig_fn_type = func.typed_value.most_recent.typed_value.ty; + const return_type = zig_fn_type.fnReturnType(); + + const fn_param_len = zig_fn_type.fnParamLen(); + + const fn_param_types = try self.gpa.alloc(Type, fn_param_len); + defer self.gpa.free(fn_param_types); + zig_fn_type.fnParamTypes(fn_param_types); + + const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); + defer self.gpa.free(llvm_param); + + for (fn_param_types) |fn_param, i| { + llvm_param[i] = try self.getLLVMType(fn_param, src); + } + + const fn_type = llvm.Type.functionType( + try self.getLLVMType(return_type, src), + if (fn_param_len == 0) null else llvm_param.ptr, + @intCast(c_uint, fn_param_len), + false, + ); + const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); + + if (return_type.tag() == .noreturn) { + self.addFnAttr(llvm_fn, "noreturn"); + } + + return llvm_fn; + } + + // Helper functions + fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { + const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); + assert(kind_id != 0); + const llvm_attr = self.context.createEnumAttribute(kind_id, 0); + val.addAttributeAtIndex(index, llvm_attr); + } + + fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void { + // TODO: improve this API, `addAttr(-1, attr_name)` + self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); + } + + pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { + @setCold(true); + assert(self.err_msg == null); + self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); + return error.CodegenFail; + } +}; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig new file mode 100644 index 0000000000..cd928877b1 --- /dev/null +++ b/src/codegen/llvm/bindings.zig @@ -0,0 +1,571 @@ +//! We do this instead of @cImport because the self-hosted compiler is easier +//! to bootstrap if it does not depend on translate-c. + +const LLVMBool = bool; +pub const AttributeIndex = c_uint; + +/// Make sure to use the *InContext functions instead of the global ones. +pub const Context = opaque { + pub const create = LLVMContextCreate; + extern fn LLVMContextCreate() *const Context; + + pub const dispose = LLVMContextDispose; + extern fn LLVMContextDispose(C: *const Context) void; + + pub const createEnumAttribute = LLVMCreateEnumAttribute; + extern fn LLVMCreateEnumAttribute(*const Context, KindID: c_uint, Val: u64) *const Attribute; + + pub const intType = LLVMIntTypeInContext; + extern fn LLVMIntTypeInContext(C: *const Context, NumBits: c_uint) *const Type; + + pub const voidType = LLVMVoidTypeInContext; + extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; + + pub const constString = LLVMConstStringInContext; + extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; + + pub const appendBasicBlock = LLVMAppendBasicBlockInContext; + extern fn LLVMAppendBasicBlockInContext(C: *const Context, Fn: *const Value, Name: [*:0]const u8) *const BasicBlock; + + pub const createBuilder = LLVMCreateBuilderInContext; + extern fn LLVMCreateBuilderInContext(C: *const Context) *const Builder; +}; + +pub const Value = opaque { + pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; + extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void; + + pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; + extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; + + pub const getNextInstruction = LLVMGetNextInstruction; + extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value; +}; + +pub const Type = opaque { + pub const functionType = LLVMFunctionType; + extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type; + + pub const constNull = LLVMConstNull; + extern fn LLVMConstNull(Ty: *const Type) *const Value; + + pub const constAllOnes = LLVMConstAllOnes; + extern fn LLVMConstAllOnes(Ty: *const Type) *const Value; + + pub const constInt = LLVMConstInt; + extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value; + + pub const constArray = LLVMConstArray; + extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value; + + pub const getUndef = LLVMGetUndef; + extern fn LLVMGetUndef(Ty: *const Type) *const Value; + + pub const pointerType = LLVMPointerType; + extern fn LLVMPointerType(ElementType: *const Type, AddressSpace: c_uint) *const Type; + + pub const arrayType = LLVMArrayType; + extern fn LLVMArrayType(ElementType: *const Type, ElementCount: c_uint) *const Type; +}; + +pub const Module = opaque { + pub const createWithName = LLVMModuleCreateWithNameInContext; + extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *const Context) *const Module; + + pub const dispose = LLVMDisposeModule; + extern fn LLVMDisposeModule(*const Module) void; + + pub const verify = LLVMVerifyModule; + extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool; + + pub const addFunction = LLVMAddFunction; + extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; + + pub const getNamedFunction = LLVMGetNamedFunction; + extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value; + + pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration; + extern fn LLVMGetIntrinsicDeclaration(Mod: *const Module, ID: c_uint, ParamTypes: ?[*]*const Type, ParamCount: usize) *const Value; + + pub const printToString = LLVMPrintModuleToString; + extern fn LLVMPrintModuleToString(*const Module) [*:0]const u8; + + pub const addGlobal = LLVMAddGlobal; + extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value; + + pub const getNamedGlobal = LLVMGetNamedGlobal; + extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value; +}; + +pub const lookupIntrinsicID = LLVMLookupIntrinsicID; +extern fn LLVMLookupIntrinsicID(Name: [*]const u8, NameLen: usize) c_uint; + +pub const disposeMessage = LLVMDisposeMessage; +extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; + +pub const VerifierFailureAction = extern enum { + AbortProcess, + PrintMessage, + ReturnStatus, +}; + +pub const constNeg = LLVMConstNeg; +extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value; + +pub const setInitializer = LLVMSetInitializer; +extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void; + +pub const getParam = LLVMGetParam; +extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value; + +pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; +extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; + +pub const Attribute = opaque {}; + +pub const Builder = opaque { + pub const dispose = LLVMDisposeBuilder; + extern fn LLVMDisposeBuilder(Builder: *const Builder) void; + + pub const positionBuilder = LLVMPositionBuilder; + extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void; + + pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd; + extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void; + + pub const getInsertBlock = LLVMGetInsertBlock; + extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; + + pub const buildCall = LLVMBuildCall; + extern fn LLVMBuildCall(*const Builder, Fn: *const Value, Args: ?[*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value; + + pub const buildCall2 = LLVMBuildCall2; + extern fn LLVMBuildCall2(*const Builder, *const Type, Fn: *const Value, Args: [*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value; + + pub const buildRetVoid = LLVMBuildRetVoid; + extern fn LLVMBuildRetVoid(*const Builder) *const Value; + + pub const buildRet = LLVMBuildRet; + extern fn LLVMBuildRet(*const Builder, V: *const Value) *const Value; + + pub const buildUnreachable = LLVMBuildUnreachable; + extern fn LLVMBuildUnreachable(*const Builder) *const Value; + + pub const buildAlloca = LLVMBuildAlloca; + extern fn LLVMBuildAlloca(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; + + pub const buildStore = LLVMBuildStore; + extern fn LLVMBuildStore(*const Builder, Val: *const Value, Ptr: *const Value) *const Value; + + pub const buildLoad = LLVMBuildLoad; + extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNot = LLVMBuildNot; + extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNSWAdd = LLVMBuildNSWAdd; + extern fn LLVMBuildNSWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNUWAdd = LLVMBuildNUWAdd; + extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNSWSub = LLVMBuildNSWSub; + extern fn LLVMBuildNSWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildNUWSub = LLVMBuildNUWSub; + extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildIntCast2 = LLVMBuildIntCast2; + extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value; + + pub const buildBitCast = LLVMBuildBitCast; + extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value; + + pub const buildInBoundsGEP = LLVMBuildInBoundsGEP; + extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value; +}; + +pub const BasicBlock = opaque { + pub const deleteBasicBlock = LLVMDeleteBasicBlock; + extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void; + + pub const getFirstInstruction = LLVMGetFirstInstruction; + extern fn LLVMGetFirstInstruction(BB: *const BasicBlock) ?*const Value; +}; + +pub const TargetMachine = opaque { + pub const create = LLVMCreateTargetMachine; + extern fn LLVMCreateTargetMachine( + T: *const Target, + Triple: [*:0]const u8, + CPU: [*:0]const u8, + Features: [*:0]const u8, + Level: CodeGenOptLevel, + Reloc: RelocMode, + CodeModel: CodeMode, + ) *const TargetMachine; + + pub const dispose = LLVMDisposeTargetMachine; + extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void; + + pub const emitToFile = LLVMTargetMachineEmitToFile; + extern fn LLVMTargetMachineEmitToFile(*const TargetMachine, M: *const Module, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool; +}; + +pub const CodeMode = extern enum { + Default, + JITDefault, + Tiny, + Small, + Kernel, + Medium, + Large, +}; + +pub const CodeGenOptLevel = extern enum { + None, + Less, + Default, + Aggressive, +}; + +pub const RelocMode = extern enum { + Default, + Static, + PIC, + DynamicNoPic, + ROPI, + RWPI, + ROPI_RWPI, +}; + +pub const CodeGenFileType = extern enum { + AssemblyFile, + ObjectFile, +}; + +pub const Target = opaque { + pub const getFromTriple = LLVMGetTargetFromTriple; + extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool; +}; + +extern fn LLVMInitializeAArch64TargetInfo() void; +extern fn LLVMInitializeAMDGPUTargetInfo() void; +extern fn LLVMInitializeARMTargetInfo() void; +extern fn LLVMInitializeAVRTargetInfo() void; +extern fn LLVMInitializeBPFTargetInfo() void; +extern fn LLVMInitializeHexagonTargetInfo() void; +extern fn LLVMInitializeLanaiTargetInfo() void; +extern fn LLVMInitializeMipsTargetInfo() void; +extern fn LLVMInitializeMSP430TargetInfo() void; +extern fn LLVMInitializeNVPTXTargetInfo() void; +extern fn LLVMInitializePowerPCTargetInfo() void; +extern fn LLVMInitializeRISCVTargetInfo() void; +extern fn LLVMInitializeSparcTargetInfo() void; +extern fn LLVMInitializeSystemZTargetInfo() void; +extern fn LLVMInitializeWebAssemblyTargetInfo() void; +extern fn LLVMInitializeX86TargetInfo() void; +extern fn LLVMInitializeXCoreTargetInfo() void; +extern fn LLVMInitializeAArch64Target() void; +extern fn LLVMInitializeAMDGPUTarget() void; +extern fn LLVMInitializeARMTarget() void; +extern fn LLVMInitializeAVRTarget() void; +extern fn LLVMInitializeBPFTarget() void; +extern fn LLVMInitializeHexagonTarget() void; +extern fn LLVMInitializeLanaiTarget() void; +extern fn LLVMInitializeMipsTarget() void; +extern fn LLVMInitializeMSP430Target() void; +extern fn LLVMInitializeNVPTXTarget() void; +extern fn LLVMInitializePowerPCTarget() void; +extern fn LLVMInitializeRISCVTarget() void; +extern fn LLVMInitializeSparcTarget() void; +extern fn LLVMInitializeSystemZTarget() void; +extern fn LLVMInitializeWebAssemblyTarget() void; +extern fn LLVMInitializeX86Target() void; +extern fn LLVMInitializeXCoreTarget() void; +extern fn LLVMInitializeAArch64TargetMC() void; +extern fn LLVMInitializeAMDGPUTargetMC() void; +extern fn LLVMInitializeARMTargetMC() void; +extern fn LLVMInitializeAVRTargetMC() void; +extern fn LLVMInitializeBPFTargetMC() void; +extern fn LLVMInitializeHexagonTargetMC() void; +extern fn LLVMInitializeLanaiTargetMC() void; +extern fn LLVMInitializeMipsTargetMC() void; +extern fn LLVMInitializeMSP430TargetMC() void; +extern fn LLVMInitializeNVPTXTargetMC() void; +extern fn LLVMInitializePowerPCTargetMC() void; +extern fn LLVMInitializeRISCVTargetMC() void; +extern fn LLVMInitializeSparcTargetMC() void; +extern fn LLVMInitializeSystemZTargetMC() void; +extern fn LLVMInitializeWebAssemblyTargetMC() void; +extern fn LLVMInitializeX86TargetMC() void; +extern fn LLVMInitializeXCoreTargetMC() void; +extern fn LLVMInitializeAArch64AsmPrinter() void; +extern fn LLVMInitializeAMDGPUAsmPrinter() void; +extern fn LLVMInitializeARMAsmPrinter() void; +extern fn LLVMInitializeAVRAsmPrinter() void; +extern fn LLVMInitializeBPFAsmPrinter() void; +extern fn LLVMInitializeHexagonAsmPrinter() void; +extern fn LLVMInitializeLanaiAsmPrinter() void; +extern fn LLVMInitializeMipsAsmPrinter() void; +extern fn LLVMInitializeMSP430AsmPrinter() void; +extern fn LLVMInitializeNVPTXAsmPrinter() void; +extern fn LLVMInitializePowerPCAsmPrinter() void; +extern fn LLVMInitializeRISCVAsmPrinter() void; +extern fn LLVMInitializeSparcAsmPrinter() void; +extern fn LLVMInitializeSystemZAsmPrinter() void; +extern fn LLVMInitializeWebAssemblyAsmPrinter() void; +extern fn LLVMInitializeX86AsmPrinter() void; +extern fn LLVMInitializeXCoreAsmPrinter() void; +extern fn LLVMInitializeAArch64AsmParser() void; +extern fn LLVMInitializeAMDGPUAsmParser() void; +extern fn LLVMInitializeARMAsmParser() void; +extern fn LLVMInitializeAVRAsmParser() void; +extern fn LLVMInitializeBPFAsmParser() void; +extern fn LLVMInitializeHexagonAsmParser() void; +extern fn LLVMInitializeLanaiAsmParser() void; +extern fn LLVMInitializeMipsAsmParser() void; +extern fn LLVMInitializeMSP430AsmParser() void; +extern fn LLVMInitializePowerPCAsmParser() void; +extern fn LLVMInitializeRISCVAsmParser() void; +extern fn LLVMInitializeSparcAsmParser() void; +extern fn LLVMInitializeSystemZAsmParser() void; +extern fn LLVMInitializeWebAssemblyAsmParser() void; +extern fn LLVMInitializeX86AsmParser() void; + +pub const initializeAllTargetInfos = LLVMInitializeAllTargetInfos; +fn LLVMInitializeAllTargetInfos() callconv(.C) void { + LLVMInitializeAArch64TargetInfo(); + LLVMInitializeAMDGPUTargetInfo(); + LLVMInitializeARMTargetInfo(); + LLVMInitializeAVRTargetInfo(); + LLVMInitializeBPFTargetInfo(); + LLVMInitializeHexagonTargetInfo(); + LLVMInitializeLanaiTargetInfo(); + LLVMInitializeMipsTargetInfo(); + LLVMInitializeMSP430TargetInfo(); + LLVMInitializeNVPTXTargetInfo(); + LLVMInitializePowerPCTargetInfo(); + LLVMInitializeRISCVTargetInfo(); + LLVMInitializeSparcTargetInfo(); + LLVMInitializeSystemZTargetInfo(); + LLVMInitializeWebAssemblyTargetInfo(); + LLVMInitializeX86TargetInfo(); + LLVMInitializeXCoreTargetInfo(); +} +pub const initializeAllTargets = LLVMInitializeAllTargets; +fn LLVMInitializeAllTargets() callconv(.C) void { + LLVMInitializeAArch64Target(); + LLVMInitializeAMDGPUTarget(); + LLVMInitializeARMTarget(); + LLVMInitializeAVRTarget(); + LLVMInitializeBPFTarget(); + LLVMInitializeHexagonTarget(); + LLVMInitializeLanaiTarget(); + LLVMInitializeMipsTarget(); + LLVMInitializeMSP430Target(); + LLVMInitializeNVPTXTarget(); + LLVMInitializePowerPCTarget(); + LLVMInitializeRISCVTarget(); + LLVMInitializeSparcTarget(); + LLVMInitializeSystemZTarget(); + LLVMInitializeWebAssemblyTarget(); + LLVMInitializeX86Target(); + LLVMInitializeXCoreTarget(); +} +pub const initializeAllTargetMCs = LLVMInitializeAllTargetMCs; +fn LLVMInitializeAllTargetMCs() callconv(.C) void { + LLVMInitializeAArch64TargetMC(); + LLVMInitializeAMDGPUTargetMC(); + LLVMInitializeARMTargetMC(); + LLVMInitializeAVRTargetMC(); + LLVMInitializeBPFTargetMC(); + LLVMInitializeHexagonTargetMC(); + LLVMInitializeLanaiTargetMC(); + LLVMInitializeMipsTargetMC(); + LLVMInitializeMSP430TargetMC(); + LLVMInitializeNVPTXTargetMC(); + LLVMInitializePowerPCTargetMC(); + LLVMInitializeRISCVTargetMC(); + LLVMInitializeSparcTargetMC(); + LLVMInitializeSystemZTargetMC(); + LLVMInitializeWebAssemblyTargetMC(); + LLVMInitializeX86TargetMC(); + LLVMInitializeXCoreTargetMC(); +} +pub const initializeAllAsmPrinters = LLVMInitializeAllAsmPrinters; +fn LLVMInitializeAllAsmPrinters() callconv(.C) void { + LLVMInitializeAArch64AsmPrinter(); + LLVMInitializeAMDGPUAsmPrinter(); + LLVMInitializeARMAsmPrinter(); + LLVMInitializeAVRAsmPrinter(); + LLVMInitializeBPFAsmPrinter(); + LLVMInitializeHexagonAsmPrinter(); + LLVMInitializeLanaiAsmPrinter(); + LLVMInitializeMipsAsmPrinter(); + LLVMInitializeMSP430AsmPrinter(); + LLVMInitializeNVPTXAsmPrinter(); + LLVMInitializePowerPCAsmPrinter(); + LLVMInitializeRISCVAsmPrinter(); + LLVMInitializeSparcAsmPrinter(); + LLVMInitializeSystemZAsmPrinter(); + LLVMInitializeWebAssemblyAsmPrinter(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeXCoreAsmPrinter(); +} +pub const initializeAllAsmParsers = LLVMInitializeAllAsmParsers; +fn LLVMInitializeAllAsmParsers() callconv(.C) void { + LLVMInitializeAArch64AsmParser(); + LLVMInitializeAMDGPUAsmParser(); + LLVMInitializeARMAsmParser(); + LLVMInitializeAVRAsmParser(); + LLVMInitializeBPFAsmParser(); + LLVMInitializeHexagonAsmParser(); + LLVMInitializeLanaiAsmParser(); + LLVMInitializeMipsAsmParser(); + LLVMInitializeMSP430AsmParser(); + LLVMInitializePowerPCAsmParser(); + LLVMInitializeRISCVAsmParser(); + LLVMInitializeSparcAsmParser(); + LLVMInitializeSystemZAsmParser(); + LLVMInitializeWebAssemblyAsmParser(); + LLVMInitializeX86AsmParser(); +} + +extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; +extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; +extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; +extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; + +pub const LinkCOFF = ZigLLDLinkCOFF; +pub const LinkELF = ZigLLDLinkELF; +pub const LinkMachO = ZigLLDLinkMachO; +pub const LinkWasm = ZigLLDLinkWasm; + +pub const ObjectFormatType = extern enum(c_int) { + Unknown, + COFF, + ELF, + MachO, + Wasm, + XCOFF, +}; + +pub const GetHostCPUName = LLVMGetHostCPUName; +extern fn LLVMGetHostCPUName() ?[*:0]u8; + +pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; +extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; + +pub const WriteArchive = ZigLLVMWriteArchive; +extern fn ZigLLVMWriteArchive( + archive_name: [*:0]const u8, + file_names_ptr: [*]const [*:0]const u8, + file_names_len: usize, + os_type: OSType, +) bool; + +pub const OSType = extern enum(c_int) { + UnknownOS = 0, + Ananas = 1, + CloudABI = 2, + Darwin = 3, + DragonFly = 4, + FreeBSD = 5, + Fuchsia = 6, + IOS = 7, + KFreeBSD = 8, + Linux = 9, + Lv2 = 10, + MacOSX = 11, + NetBSD = 12, + OpenBSD = 13, + Solaris = 14, + Win32 = 15, + Haiku = 16, + Minix = 17, + RTEMS = 18, + NaCl = 19, + CNK = 20, + AIX = 21, + CUDA = 22, + NVCL = 23, + AMDHSA = 24, + PS4 = 25, + ELFIAMCU = 26, + TvOS = 27, + WatchOS = 28, + Mesa3D = 29, + Contiki = 30, + AMDPAL = 31, + HermitCore = 32, + Hurd = 33, + WASI = 34, + Emscripten = 35, +}; + +pub const ArchType = extern enum(c_int) { + UnknownArch = 0, + arm = 1, + armeb = 2, + aarch64 = 3, + aarch64_be = 4, + aarch64_32 = 5, + arc = 6, + avr = 7, + bpfel = 8, + bpfeb = 9, + hexagon = 10, + mips = 11, + mipsel = 12, + mips64 = 13, + mips64el = 14, + msp430 = 15, + ppc = 16, + ppc64 = 17, + ppc64le = 18, + r600 = 19, + amdgcn = 20, + riscv32 = 21, + riscv64 = 22, + sparc = 23, + sparcv9 = 24, + sparcel = 25, + systemz = 26, + tce = 27, + tcele = 28, + thumb = 29, + thumbeb = 30, + x86 = 31, + x86_64 = 32, + xcore = 33, + nvptx = 34, + nvptx64 = 35, + le32 = 36, + le64 = 37, + amdil = 38, + amdil64 = 39, + hsail = 40, + hsail64 = 41, + spir = 42, + spir64 = 43, + kalimba = 44, + shave = 45, + lanai = 46, + wasm32 = 47, + wasm64 = 48, + renderscript32 = 49, + renderscript64 = 50, + ve = 51, +}; + +pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions; +extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void; + +pub const WriteImportLibrary = ZigLLVMWriteImportLibrary; +extern fn ZigLLVMWriteImportLibrary( + def_path: [*:0]const u8, + arch: ArchType, + output_lib_path: [*c]const u8, + kill_at: bool, +) bool; diff --git a/src/link.zig b/src/link.zig index 3dbfb3b922..7893e69cd5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -567,7 +567,7 @@ pub const File = struct { std.debug.print("\n", .{}); } - const llvm = @import("llvm_bindings.zig"); + const llvm = @import("codegen/llvm/bindings.zig"); const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag); const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type); if (bad) return error.UnableToWriteArchive; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 117bdef4a7..f7d646356c 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -16,7 +16,7 @@ const link = @import("../link.zig"); const build_options = @import("build_options"); const Cache = @import("../Cache.zig"); const mingw = @import("../mingw.zig"); -const llvm_backend = @import("../llvm_backend.zig"); +const llvm_backend = @import("../codegen/llvm.zig"); const allocation_padding = 4 / 3; const minimum_text_block_size = 64 * allocation_padding; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b6b8fd7750..f3073824a5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -24,7 +24,7 @@ const build_options = @import("build_options"); const target_util = @import("../target.zig"); const glibc = @import("../glibc.zig"); const Cache = @import("../Cache.zig"); -const llvm_backend = @import("../llvm_backend.zig"); +const llvm_backend = @import("../codegen/llvm.zig"); const default_entry_addr = 0x8000000; diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig deleted file mode 100644 index 2bac4523c9..0000000000 --- a/src/llvm_backend.zig +++ /dev/null @@ -1,722 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; -const Compilation = @import("Compilation.zig"); -const llvm = @import("llvm_bindings.zig"); -const link = @import("link.zig"); -const log = std.log.scoped(.codegen); - -const Module = @import("Module.zig"); -const TypedValue = @import("TypedValue.zig"); -const ir = @import("ir.zig"); -const Inst = ir.Inst; - -const Value = @import("value.zig").Value; -const Type = @import("type.zig").Type; - -pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { - const llvm_arch = switch (target.cpu.arch) { - .arm => "arm", - .armeb => "armeb", - .aarch64 => "aarch64", - .aarch64_be => "aarch64_be", - .aarch64_32 => "aarch64_32", - .arc => "arc", - .avr => "avr", - .bpfel => "bpfel", - .bpfeb => "bpfeb", - .hexagon => "hexagon", - .mips => "mips", - .mipsel => "mipsel", - .mips64 => "mips64", - .mips64el => "mips64el", - .msp430 => "msp430", - .powerpc => "powerpc", - .powerpc64 => "powerpc64", - .powerpc64le => "powerpc64le", - .r600 => "r600", - .amdgcn => "amdgcn", - .riscv32 => "riscv32", - .riscv64 => "riscv64", - .sparc => "sparc", - .sparcv9 => "sparcv9", - .sparcel => "sparcel", - .s390x => "s390x", - .tce => "tce", - .tcele => "tcele", - .thumb => "thumb", - .thumbeb => "thumbeb", - .i386 => "i386", - .x86_64 => "x86_64", - .xcore => "xcore", - .nvptx => "nvptx", - .nvptx64 => "nvptx64", - .le32 => "le32", - .le64 => "le64", - .amdil => "amdil", - .amdil64 => "amdil64", - .hsail => "hsail", - .hsail64 => "hsail64", - .spir => "spir", - .spir64 => "spir64", - .kalimba => "kalimba", - .shave => "shave", - .lanai => "lanai", - .wasm32 => "wasm32", - .wasm64 => "wasm64", - .renderscript32 => "renderscript32", - .renderscript64 => "renderscript64", - .ve => "ve", - .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, - }; - // TODO Add a sub-arch for some architectures depending on CPU features. - - const llvm_os = switch (target.os.tag) { - .freestanding => "unknown", - .ananas => "ananas", - .cloudabi => "cloudabi", - .dragonfly => "dragonfly", - .freebsd => "freebsd", - .fuchsia => "fuchsia", - .ios => "ios", - .kfreebsd => "kfreebsd", - .linux => "linux", - .lv2 => "lv2", - .macos => "macosx", - .netbsd => "netbsd", - .openbsd => "openbsd", - .solaris => "solaris", - .windows => "windows", - .haiku => "haiku", - .minix => "minix", - .rtems => "rtems", - .nacl => "nacl", - .cnk => "cnk", - .aix => "aix", - .cuda => "cuda", - .nvcl => "nvcl", - .amdhsa => "amdhsa", - .ps4 => "ps4", - .elfiamcu => "elfiamcu", - .tvos => "tvos", - .watchos => "watchos", - .mesa3d => "mesa3d", - .contiki => "contiki", - .amdpal => "amdpal", - .hermit => "hermit", - .hurd => "hurd", - .wasi => "wasi", - .emscripten => "emscripten", - .uefi => "windows", - .other => "unknown", - }; - - const llvm_abi = switch (target.abi) { - .none => "unknown", - .gnu => "gnu", - .gnuabin32 => "gnuabin32", - .gnuabi64 => "gnuabi64", - .gnueabi => "gnueabi", - .gnueabihf => "gnueabihf", - .gnux32 => "gnux32", - .code16 => "code16", - .eabi => "eabi", - .eabihf => "eabihf", - .android => "android", - .musl => "musl", - .musleabi => "musleabi", - .musleabihf => "musleabihf", - .msvc => "msvc", - .itanium => "itanium", - .cygnus => "cygnus", - .coreclr => "coreclr", - .simulator => "simulator", - .macabi => "macabi", - }; - - return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); -} - -pub const LLVMIRModule = struct { - module: *Module, - llvm_module: *const llvm.Module, - context: *const llvm.Context, - target_machine: *const llvm.TargetMachine, - builder: *const llvm.Builder, - - object_path: []const u8, - - gpa: *Allocator, - err_msg: ?*Compilation.ErrorMsg = null, - - // TODO: The fields below should really move into a different struct, - // because they are only valid when generating a function - - /// This stores the LLVM values used in a function, such that they can be - /// referred to in other instructions. This table is cleared before every function is generated. - func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, - - /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. - args: []*const llvm.Value = &[_]*const llvm.Value{}, - arg_index: usize = 0, - - entry_block: *const llvm.BasicBlock = undefined, - /// This fields stores the last alloca instruction, such that we can append more alloca instructions - /// to the top of the function. - latest_alloca_inst: ?*const llvm.Value = null, - - pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { - const self = try allocator.create(LLVMIRModule); - errdefer allocator.destroy(self); - - const gpa = options.module.?.gpa; - - const obj_basename = try std.zig.binNameAlloc(gpa, .{ - .root_name = options.root_name, - .target = options.target, - .output_mode = .Obj, - }); - defer gpa.free(obj_basename); - - const o_directory = options.module.?.zig_cache_artifact_directory; - const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); - errdefer gpa.free(object_path); - - const context = llvm.Context.create(); - errdefer context.dispose(); - - initializeLLVMTargets(); - - const root_nameZ = try gpa.dupeZ(u8, options.root_name); - defer gpa.free(root_nameZ); - const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); - errdefer llvm_module.dispose(); - - const llvm_target_triple = try targetTriple(gpa, options.target); - defer gpa.free(llvm_target_triple); - - var error_message: [*:0]const u8 = undefined; - var target: *const llvm.Target = undefined; - if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) { - defer llvm.disposeMessage(error_message); - - const stderr = std.io.getStdErr().outStream(); - try stderr.print( - \\Zig is expecting LLVM to understand this target: '{s}' - \\However LLVM responded with: "{s}" - \\Zig is unable to continue. This is a bug in Zig: - \\https://github.com/ziglang/zig/issues/438 - \\ - , - .{ - llvm_target_triple, - error_message, - }, - ); - return error.InvalidLLVMTriple; - } - - const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive; - const target_machine = llvm.TargetMachine.create( - target, - llvm_target_triple.ptr, - "", - "", - opt_level, - .Static, - .Default, - ); - errdefer target_machine.dispose(); - - const builder = context.createBuilder(); - errdefer builder.dispose(); - - self.* = .{ - .module = options.module.?, - .llvm_module = llvm_module, - .context = context, - .target_machine = target_machine, - .builder = builder, - .object_path = object_path, - .gpa = gpa, - }; - return self; - } - - pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void { - self.builder.dispose(); - self.target_machine.dispose(); - self.llvm_module.dispose(); - self.context.dispose(); - - self.func_inst_table.deinit(self.gpa); - self.gpa.free(self.object_path); - - allocator.destroy(self); - } - - fn initializeLLVMTargets() void { - llvm.initializeAllTargets(); - llvm.initializeAllTargetInfos(); - llvm.initializeAllTargetMCs(); - llvm.initializeAllAsmPrinters(); - llvm.initializeAllAsmParsers(); - } - - pub fn flushModule(self: *LLVMIRModule, comp: *Compilation) !void { - if (comp.verbose_llvm_ir) { - const dump = self.llvm_module.printToString(); - defer llvm.disposeMessage(dump); - - const stderr = std.io.getStdErr().outStream(); - try stderr.writeAll(std.mem.spanZ(dump)); - } - - { - var error_message: [*:0]const u8 = undefined; - // verifyModule always allocs the error_message even if there is no error - defer llvm.disposeMessage(error_message); - - if (self.llvm_module.verify(.ReturnStatus, &error_message)) { - const stderr = std.io.getStdErr().outStream(); - try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message}); - return error.BrokenLLVMModule; - } - } - - const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); - defer self.gpa.free(object_pathZ); - - var error_message: [*:0]const u8 = undefined; - if (self.target_machine.emitToFile( - self.llvm_module, - object_pathZ.ptr, - .ObjectFile, - &error_message, - )) { - defer llvm.disposeMessage(error_message); - - const stderr = std.io.getStdErr().outStream(); - try stderr.print("LLVM failed to emit file: {s}\n", .{error_message}); - return error.FailedToEmit; - } - } - - pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { - self.gen(module, decl) catch |err| switch (err) { - error.CodegenFail => { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, self.err_msg.?); - self.err_msg = null; - return; - }, - else => |e| return e, - }; - } - - fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { - const typed_value = decl.typed_value.most_recent.typed_value; - const src = decl.src(); - - log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); - - if (typed_value.val.castTag(.function)) |func_payload| { - const func = func_payload.data; - - const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src); - - // This gets the LLVM values from the function and stores them in `self.args`. - const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); - var args = try self.gpa.alloc(*const llvm.Value, fn_param_len); - defer self.gpa.free(args); - - for (args) |*arg, i| { - arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); - } - self.args = args; - self.arg_index = 0; - - // Make sure no other LLVM values from other functions can be referenced - self.func_inst_table.clearRetainingCapacity(); - - // We remove all the basic blocks of a function to support incremental - // compilation! - // TODO: remove all basic blocks if functions can have more than one - if (llvm_func.getFirstBasicBlock()) |bb| { - bb.deleteBasicBlock(); - } - - self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); - self.builder.positionBuilderAtEnd(self.entry_block); - self.latest_alloca_inst = null; - - const instructions = func.body.instructions; - for (instructions) |inst| { - const opt_llvm_val: ?*const llvm.Value = switch (inst.tag) { - .add => try self.genAdd(inst.castTag(.add).?), - .alloc => try self.genAlloc(inst.castTag(.alloc).?), - .arg => try self.genArg(inst.castTag(.arg).?), - .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), - .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), - .call => try self.genCall(inst.castTag(.call).?), - .intcast => try self.genIntCast(inst.castTag(.intcast).?), - .load => try self.genLoad(inst.castTag(.load).?), - .not => try self.genNot(inst.castTag(.not).?), - .ret => try self.genRet(inst.castTag(.ret).?), - .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), - .store => try self.genStore(inst.castTag(.store).?), - .sub => try self.genSub(inst.castTag(.sub).?), - .unreach => self.genUnreach(inst.castTag(.unreach).?), - .dbg_stmt => blk: { - // TODO: implement debug info - break :blk null; - }, - else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), - }; - if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); - } - } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { - _ = try self.resolveLLVMFunction(extern_fn.data, src); - } else { - _ = try self.resolveGlobalDecl(decl, src); - } - } - - fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { - if (inst.func.value()) |func_value| { - const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| - extern_fn.data - else if (func_value.castTag(.function)) |func_payload| - func_payload.data.owner_decl - else - unreachable; - - const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; - const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src); - - const num_args = inst.args.len; - - const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args); - defer self.gpa.free(llvm_param_vals); - - for (inst.args) |arg, i| { - llvm_param_vals[i] = try self.resolveInst(arg); - } - - // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs - // Do we need that? - const call = self.builder.buildCall( - llvm_fn, - if (num_args == 0) null else llvm_param_vals.ptr, - @intCast(c_uint, num_args), - "", - ); - - const return_type = zig_fn_type.fnReturnType(); - if (return_type.tag() == .noreturn) { - _ = self.builder.buildUnreachable(); - } - - // No need to store the LLVM value if the return type is void or noreturn - if (!return_type.hasCodeGenBits()) return null; - - return call; - } else { - return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); - } - } - - fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { - _ = self.builder.buildRetVoid(); - return null; - } - - fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { - _ = self.builder.buildRet(try self.resolveInst(inst.operand)); - return null; - } - - fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { - return self.builder.buildNot(try self.resolveInst(inst.operand), ""); - } - - fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value { - _ = self.builder.buildUnreachable(); - return null; - } - - fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { - const lhs = try self.resolveInst(inst.lhs); - const rhs = try self.resolveInst(inst.rhs); - - if (!inst.base.ty.isInt()) - return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty}); - - return if (inst.base.ty.isSignedInt()) - self.builder.buildNSWAdd(lhs, rhs, "") - else - self.builder.buildNUWAdd(lhs, rhs, ""); - } - - fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { - const lhs = try self.resolveInst(inst.lhs); - const rhs = try self.resolveInst(inst.rhs); - - if (!inst.base.ty.isInt()) - return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty}); - - return if (inst.base.ty.isSignedInt()) - self.builder.buildNSWSub(lhs, rhs, "") - else - self.builder.buildNUWSub(lhs, rhs, ""); - } - - fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { - const val = try self.resolveInst(inst.operand); - - const signed = inst.base.ty.isSignedInt(); - // TODO: Should we use intcast here or just a simple bitcast? - // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes - return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, ""); - } - - fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { - const val = try self.resolveInst(inst.operand); - const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); - - return self.builder.buildBitCast(val, dest_type, ""); - } - - fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.Value { - const arg_val = self.args[self.arg_index]; - self.arg_index += 1; - - const ptr_val = self.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src)); - _ = self.builder.buildStore(arg_val, ptr_val); - return self.builder.buildLoad(ptr_val, ""); - } - - fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { - // buildAlloca expects the pointee type, not the pointer type, so assert that - // a Payload.PointerSimple is passed to the alloc instruction. - const pointee_type = inst.base.ty.castPointer().?.data; - - // TODO: figure out a way to get the name of the var decl. - // TODO: set alignment and volatile - return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src)); - } - - /// Use this instead of builder.buildAlloca, because this function makes sure to - /// put the alloca instruction at the top of the function! - fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { - if (self.latest_alloca_inst) |latest_alloc| { - // builder.positionBuilder adds it before the instruction, - // but we want to put it after the last alloca instruction. - self.builder.positionBuilder(self.entry_block, latest_alloc.getNextInstruction().?); - } else { - // There might have been other instructions emitted before the - // first alloca has been generated. However the alloca should still - // be first in the function. - if (self.entry_block.getFirstInstruction()) |first_inst| { - self.builder.positionBuilder(self.entry_block, first_inst); - } - } - defer self.builder.positionBuilderAtEnd(self.entry_block); - - const val = self.builder.buildAlloca(t, ""); - self.latest_alloca_inst = val; - return val; - } - - fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { - const val = try self.resolveInst(inst.rhs); - const ptr = try self.resolveInst(inst.lhs); - _ = self.builder.buildStore(val, ptr); - return null; - } - - fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { - const ptr_val = try self.resolveInst(inst.operand); - return self.builder.buildLoad(ptr_val, ""); - } - - fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.Value { - const llvn_fn = self.getIntrinsic("llvm.debugtrap"); - _ = self.builder.buildCall(llvn_fn, null, 0, ""); - return null; - } - - fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.Value { - const id = llvm.lookupIntrinsicID(name.ptr, name.len); - assert(id != 0); - // TODO: add support for overload intrinsics by passing the prefix of the intrinsic - // to `lookupIntrinsicID` and then passing the correct types to - // `getIntrinsicDeclaration` - return self.llvm_module.getIntrinsicDeclaration(id, null, 0); - } - - fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value { - if (inst.value()) |val| { - return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); - } - if (self.func_inst_table.get(inst)) |value| return value; - - return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); - } - - fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value { - const llvm_type = try self.getLLVMType(tv.ty, src); - - if (tv.val.isUndef()) - return llvm_type.getUndef(); - - switch (tv.ty.zigTypeTag()) { - .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), - .Int => { - var bigint_space: Value.BigIntSpace = undefined; - const bigint = tv.val.toBigInt(&bigint_space); - - if (bigint.eqZero()) return llvm_type.constNull(); - - if (bigint.limbs.len != 1) { - return self.fail(src, "TODO implement bigger bigint", .{}); - } - const llvm_int = llvm_type.constInt(bigint.limbs[0], false); - if (!bigint.positive) { - return llvm.constNeg(llvm_int); - } - return llvm_int; - }, - .Pointer => switch (tv.val.tag()) { - .decl_ref => { - const decl = tv.val.castTag(.decl_ref).?.data; - const val = try self.resolveGlobalDecl(decl, src); - - const usize_type = try self.getLLVMType(Type.initTag(.usize), src); - - // TODO: second index should be the index into the memory! - var indices: [2]*const llvm.Value = .{ - usize_type.constNull(), - usize_type.constNull(), - }; - - // TODO: consider using buildInBoundsGEP2 for opaque pointers - return self.builder.buildInBoundsGEP(val, &indices, 2, ""); - }, - else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), - }, - .Array => { - if (tv.val.castTag(.bytes)) |payload| { - const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { - if (sentinel.tag() == .zero) break :blk true; - return self.fail(src, "TODO handle other sentinel values", .{}); - } else false; - - return self.context.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel); - } else { - return self.fail(src, "TODO handle more array values", .{}); - } - }, - else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), - } - } - - fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Type { - switch (t.zigTypeTag()) { - .Void => return self.context.voidType(), - .NoReturn => return self.context.voidType(), - .Int => { - const info = t.intInfo(self.module.getTarget()); - return self.context.intType(info.bits); - }, - .Bool => return self.context.intType(1), - .Pointer => { - if (t.isSlice()) { - return self.fail(src, "TODO: LLVM backend: implement slices", .{}); - } else { - const elem_type = try self.getLLVMType(t.elemType(), src); - return elem_type.pointerType(0); - } - }, - .Array => { - const elem_type = try self.getLLVMType(t.elemType(), src); - return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); - }, - else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), - } - } - - fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.Value { - // TODO: do we want to store this in our own datastructure? - if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val; - - const typed_value = decl.typed_value.most_recent.typed_value; - - // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. - const llvm_type = try self.getLLVMType(typed_value.ty, src); - const val = try self.genTypedValue(src, typed_value); - const global = self.llvm_module.addGlobal(llvm_type, decl.name); - llvm.setInitializer(global, val); - - // TODO ask the Decl if it is const - // https://github.com/ziglang/zig/issues/7582 - - return global; - } - - /// If the llvm function does not exist, create it - fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.Value { - // TODO: do we want to store this in our own datastructure? - if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn; - - const zig_fn_type = func.typed_value.most_recent.typed_value.ty; - const return_type = zig_fn_type.fnReturnType(); - - const fn_param_len = zig_fn_type.fnParamLen(); - - const fn_param_types = try self.gpa.alloc(Type, fn_param_len); - defer self.gpa.free(fn_param_types); - zig_fn_type.fnParamTypes(fn_param_types); - - const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); - defer self.gpa.free(llvm_param); - - for (fn_param_types) |fn_param, i| { - llvm_param[i] = try self.getLLVMType(fn_param, src); - } - - const fn_type = llvm.Type.functionType( - try self.getLLVMType(return_type, src), - if (fn_param_len == 0) null else llvm_param.ptr, - @intCast(c_uint, fn_param_len), - false, - ); - const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); - - if (return_type.tag() == .noreturn) { - self.addFnAttr(llvm_fn, "noreturn"); - } - - return llvm_fn; - } - - // Helper functions - fn addAttr(self: LLVMIRModule, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { - const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); - assert(kind_id != 0); - const llvm_attr = self.context.createEnumAttribute(kind_id, 0); - val.addAttributeAtIndex(index, llvm_attr); - } - - fn addFnAttr(self: *LLVMIRModule, val: *const llvm.Value, attr_name: []const u8) void { - // TODO: improve this API, `addAttr(-1, attr_name)` - self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); - } - - pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { - @setCold(true); - assert(self.err_msg == null); - self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); - return error.CodegenFail; - } -}; diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig deleted file mode 100644 index 1af64e85f6..0000000000 --- a/src/llvm_bindings.zig +++ /dev/null @@ -1,574 +0,0 @@ -//! We do this instead of @cImport because the self-hosted compiler is easier -//! to bootstrap if it does not depend on translate-c. - -const std = @import("std"); -const assert = std.debug.assert; - -const LLVMBool = bool; -pub const AttributeIndex = c_uint; - -/// Make sure to use the *InContext functions instead of the global ones. -pub const Context = opaque { - pub const create = LLVMContextCreate; - extern fn LLVMContextCreate() *const Context; - - pub const dispose = LLVMContextDispose; - extern fn LLVMContextDispose(C: *const Context) void; - - pub const createEnumAttribute = LLVMCreateEnumAttribute; - extern fn LLVMCreateEnumAttribute(*const Context, KindID: c_uint, Val: u64) *const Attribute; - - pub const intType = LLVMIntTypeInContext; - extern fn LLVMIntTypeInContext(C: *const Context, NumBits: c_uint) *const Type; - - pub const voidType = LLVMVoidTypeInContext; - extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; - - pub const constString = LLVMConstStringInContext; - extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; - - pub const appendBasicBlock = LLVMAppendBasicBlockInContext; - extern fn LLVMAppendBasicBlockInContext(C: *const Context, Fn: *const Value, Name: [*:0]const u8) *const BasicBlock; - - pub const createBuilder = LLVMCreateBuilderInContext; - extern fn LLVMCreateBuilderInContext(C: *const Context) *const Builder; -}; - -pub const Value = opaque { - pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; - extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void; - - pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; - extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; - - pub const getNextInstruction = LLVMGetNextInstruction; - extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value; -}; - -pub const Type = opaque { - pub const functionType = LLVMFunctionType; - extern fn LLVMFunctionType(ReturnType: *const Type, ParamTypes: ?[*]*const Type, ParamCount: c_uint, IsVarArg: LLVMBool) *const Type; - - pub const constNull = LLVMConstNull; - extern fn LLVMConstNull(Ty: *const Type) *const Value; - - pub const constAllOnes = LLVMConstAllOnes; - extern fn LLVMConstAllOnes(Ty: *const Type) *const Value; - - pub const constInt = LLVMConstInt; - extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: LLVMBool) *const Value; - - pub const constArray = LLVMConstArray; - extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: ?[*]*const Value, Length: c_uint) *const Value; - - pub const getUndef = LLVMGetUndef; - extern fn LLVMGetUndef(Ty: *const Type) *const Value; - - pub const pointerType = LLVMPointerType; - extern fn LLVMPointerType(ElementType: *const Type, AddressSpace: c_uint) *const Type; - - pub const arrayType = LLVMArrayType; - extern fn LLVMArrayType(ElementType: *const Type, ElementCount: c_uint) *const Type; -}; - -pub const Module = opaque { - pub const createWithName = LLVMModuleCreateWithNameInContext; - extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *const Context) *const Module; - - pub const dispose = LLVMDisposeModule; - extern fn LLVMDisposeModule(*const Module) void; - - pub const verify = LLVMVerifyModule; - extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool; - - pub const addFunction = LLVMAddFunction; - extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; - - pub const getNamedFunction = LLVMGetNamedFunction; - extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value; - - pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration; - extern fn LLVMGetIntrinsicDeclaration(Mod: *const Module, ID: c_uint, ParamTypes: ?[*]*const Type, ParamCount: usize) *const Value; - - pub const printToString = LLVMPrintModuleToString; - extern fn LLVMPrintModuleToString(*const Module) [*:0]const u8; - - pub const addGlobal = LLVMAddGlobal; - extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value; - - pub const getNamedGlobal = LLVMGetNamedGlobal; - extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value; -}; - -pub const lookupIntrinsicID = LLVMLookupIntrinsicID; -extern fn LLVMLookupIntrinsicID(Name: [*]const u8, NameLen: usize) c_uint; - -pub const disposeMessage = LLVMDisposeMessage; -extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; - -pub const VerifierFailureAction = extern enum { - AbortProcess, - PrintMessage, - ReturnStatus, -}; - -pub const constNeg = LLVMConstNeg; -extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value; - -pub const setInitializer = LLVMSetInitializer; -extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void; - -pub const getParam = LLVMGetParam; -extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value; - -pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; -extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; - -pub const Attribute = opaque {}; - -pub const Builder = opaque { - pub const dispose = LLVMDisposeBuilder; - extern fn LLVMDisposeBuilder(Builder: *const Builder) void; - - pub const positionBuilder = LLVMPositionBuilder; - extern fn LLVMPositionBuilder(Builder: *const Builder, Block: *const BasicBlock, Instr: *const Value) void; - - pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd; - extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void; - - pub const getInsertBlock = LLVMGetInsertBlock; - extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock; - - pub const buildCall = LLVMBuildCall; - extern fn LLVMBuildCall(*const Builder, Fn: *const Value, Args: ?[*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value; - - pub const buildCall2 = LLVMBuildCall2; - extern fn LLVMBuildCall2(*const Builder, *const Type, Fn: *const Value, Args: [*]*const Value, NumArgs: c_uint, Name: [*:0]const u8) *const Value; - - pub const buildRetVoid = LLVMBuildRetVoid; - extern fn LLVMBuildRetVoid(*const Builder) *const Value; - - pub const buildRet = LLVMBuildRet; - extern fn LLVMBuildRet(*const Builder, V: *const Value) *const Value; - - pub const buildUnreachable = LLVMBuildUnreachable; - extern fn LLVMBuildUnreachable(*const Builder) *const Value; - - pub const buildAlloca = LLVMBuildAlloca; - extern fn LLVMBuildAlloca(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; - - pub const buildStore = LLVMBuildStore; - extern fn LLVMBuildStore(*const Builder, Val: *const Value, Ptr: *const Value) *const Value; - - pub const buildLoad = LLVMBuildLoad; - extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildNot = LLVMBuildNot; - extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildNSWAdd = LLVMBuildNSWAdd; - extern fn LLVMBuildNSWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildNUWAdd = LLVMBuildNUWAdd; - extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildNSWSub = LLVMBuildNSWSub; - extern fn LLVMBuildNSWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildNUWSub = LLVMBuildNUWSub; - extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; - - pub const buildIntCast2 = LLVMBuildIntCast2; - extern fn LLVMBuildIntCast2(*const Builder, Val: *const Value, DestTy: *const Type, IsSigned: LLVMBool, Name: [*:0]const u8) *const Value; - - pub const buildBitCast = LLVMBuildBitCast; - extern fn LLVMBuildBitCast(*const Builder, Val: *const Value, DestTy: *const Type, Name: [*:0]const u8) *const Value; - - pub const buildInBoundsGEP = LLVMBuildInBoundsGEP; - extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value; -}; - -pub const BasicBlock = opaque { - pub const deleteBasicBlock = LLVMDeleteBasicBlock; - extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void; - - pub const getFirstInstruction = LLVMGetFirstInstruction; - extern fn LLVMGetFirstInstruction(BB: *const BasicBlock) ?*const Value; -}; - -pub const TargetMachine = opaque { - pub const create = LLVMCreateTargetMachine; - extern fn LLVMCreateTargetMachine( - T: *const Target, - Triple: [*:0]const u8, - CPU: [*:0]const u8, - Features: [*:0]const u8, - Level: CodeGenOptLevel, - Reloc: RelocMode, - CodeModel: CodeMode, - ) *const TargetMachine; - - pub const dispose = LLVMDisposeTargetMachine; - extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void; - - pub const emitToFile = LLVMTargetMachineEmitToFile; - extern fn LLVMTargetMachineEmitToFile(*const TargetMachine, M: *const Module, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool; -}; - -pub const CodeMode = extern enum { - Default, - JITDefault, - Tiny, - Small, - Kernel, - Medium, - Large, -}; - -pub const CodeGenOptLevel = extern enum { - None, - Less, - Default, - Aggressive, -}; - -pub const RelocMode = extern enum { - Default, - Static, - PIC, - DynamicNoPic, - ROPI, - RWPI, - ROPI_RWPI, -}; - -pub const CodeGenFileType = extern enum { - AssemblyFile, - ObjectFile, -}; - -pub const Target = opaque { - pub const getFromTriple = LLVMGetTargetFromTriple; - extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) LLVMBool; -}; - -extern fn LLVMInitializeAArch64TargetInfo() void; -extern fn LLVMInitializeAMDGPUTargetInfo() void; -extern fn LLVMInitializeARMTargetInfo() void; -extern fn LLVMInitializeAVRTargetInfo() void; -extern fn LLVMInitializeBPFTargetInfo() void; -extern fn LLVMInitializeHexagonTargetInfo() void; -extern fn LLVMInitializeLanaiTargetInfo() void; -extern fn LLVMInitializeMipsTargetInfo() void; -extern fn LLVMInitializeMSP430TargetInfo() void; -extern fn LLVMInitializeNVPTXTargetInfo() void; -extern fn LLVMInitializePowerPCTargetInfo() void; -extern fn LLVMInitializeRISCVTargetInfo() void; -extern fn LLVMInitializeSparcTargetInfo() void; -extern fn LLVMInitializeSystemZTargetInfo() void; -extern fn LLVMInitializeWebAssemblyTargetInfo() void; -extern fn LLVMInitializeX86TargetInfo() void; -extern fn LLVMInitializeXCoreTargetInfo() void; -extern fn LLVMInitializeAArch64Target() void; -extern fn LLVMInitializeAMDGPUTarget() void; -extern fn LLVMInitializeARMTarget() void; -extern fn LLVMInitializeAVRTarget() void; -extern fn LLVMInitializeBPFTarget() void; -extern fn LLVMInitializeHexagonTarget() void; -extern fn LLVMInitializeLanaiTarget() void; -extern fn LLVMInitializeMipsTarget() void; -extern fn LLVMInitializeMSP430Target() void; -extern fn LLVMInitializeNVPTXTarget() void; -extern fn LLVMInitializePowerPCTarget() void; -extern fn LLVMInitializeRISCVTarget() void; -extern fn LLVMInitializeSparcTarget() void; -extern fn LLVMInitializeSystemZTarget() void; -extern fn LLVMInitializeWebAssemblyTarget() void; -extern fn LLVMInitializeX86Target() void; -extern fn LLVMInitializeXCoreTarget() void; -extern fn LLVMInitializeAArch64TargetMC() void; -extern fn LLVMInitializeAMDGPUTargetMC() void; -extern fn LLVMInitializeARMTargetMC() void; -extern fn LLVMInitializeAVRTargetMC() void; -extern fn LLVMInitializeBPFTargetMC() void; -extern fn LLVMInitializeHexagonTargetMC() void; -extern fn LLVMInitializeLanaiTargetMC() void; -extern fn LLVMInitializeMipsTargetMC() void; -extern fn LLVMInitializeMSP430TargetMC() void; -extern fn LLVMInitializeNVPTXTargetMC() void; -extern fn LLVMInitializePowerPCTargetMC() void; -extern fn LLVMInitializeRISCVTargetMC() void; -extern fn LLVMInitializeSparcTargetMC() void; -extern fn LLVMInitializeSystemZTargetMC() void; -extern fn LLVMInitializeWebAssemblyTargetMC() void; -extern fn LLVMInitializeX86TargetMC() void; -extern fn LLVMInitializeXCoreTargetMC() void; -extern fn LLVMInitializeAArch64AsmPrinter() void; -extern fn LLVMInitializeAMDGPUAsmPrinter() void; -extern fn LLVMInitializeARMAsmPrinter() void; -extern fn LLVMInitializeAVRAsmPrinter() void; -extern fn LLVMInitializeBPFAsmPrinter() void; -extern fn LLVMInitializeHexagonAsmPrinter() void; -extern fn LLVMInitializeLanaiAsmPrinter() void; -extern fn LLVMInitializeMipsAsmPrinter() void; -extern fn LLVMInitializeMSP430AsmPrinter() void; -extern fn LLVMInitializeNVPTXAsmPrinter() void; -extern fn LLVMInitializePowerPCAsmPrinter() void; -extern fn LLVMInitializeRISCVAsmPrinter() void; -extern fn LLVMInitializeSparcAsmPrinter() void; -extern fn LLVMInitializeSystemZAsmPrinter() void; -extern fn LLVMInitializeWebAssemblyAsmPrinter() void; -extern fn LLVMInitializeX86AsmPrinter() void; -extern fn LLVMInitializeXCoreAsmPrinter() void; -extern fn LLVMInitializeAArch64AsmParser() void; -extern fn LLVMInitializeAMDGPUAsmParser() void; -extern fn LLVMInitializeARMAsmParser() void; -extern fn LLVMInitializeAVRAsmParser() void; -extern fn LLVMInitializeBPFAsmParser() void; -extern fn LLVMInitializeHexagonAsmParser() void; -extern fn LLVMInitializeLanaiAsmParser() void; -extern fn LLVMInitializeMipsAsmParser() void; -extern fn LLVMInitializeMSP430AsmParser() void; -extern fn LLVMInitializePowerPCAsmParser() void; -extern fn LLVMInitializeRISCVAsmParser() void; -extern fn LLVMInitializeSparcAsmParser() void; -extern fn LLVMInitializeSystemZAsmParser() void; -extern fn LLVMInitializeWebAssemblyAsmParser() void; -extern fn LLVMInitializeX86AsmParser() void; - -pub const initializeAllTargetInfos = LLVMInitializeAllTargetInfos; -fn LLVMInitializeAllTargetInfos() callconv(.C) void { - LLVMInitializeAArch64TargetInfo(); - LLVMInitializeAMDGPUTargetInfo(); - LLVMInitializeARMTargetInfo(); - LLVMInitializeAVRTargetInfo(); - LLVMInitializeBPFTargetInfo(); - LLVMInitializeHexagonTargetInfo(); - LLVMInitializeLanaiTargetInfo(); - LLVMInitializeMipsTargetInfo(); - LLVMInitializeMSP430TargetInfo(); - LLVMInitializeNVPTXTargetInfo(); - LLVMInitializePowerPCTargetInfo(); - LLVMInitializeRISCVTargetInfo(); - LLVMInitializeSparcTargetInfo(); - LLVMInitializeSystemZTargetInfo(); - LLVMInitializeWebAssemblyTargetInfo(); - LLVMInitializeX86TargetInfo(); - LLVMInitializeXCoreTargetInfo(); -} -pub const initializeAllTargets = LLVMInitializeAllTargets; -fn LLVMInitializeAllTargets() callconv(.C) void { - LLVMInitializeAArch64Target(); - LLVMInitializeAMDGPUTarget(); - LLVMInitializeARMTarget(); - LLVMInitializeAVRTarget(); - LLVMInitializeBPFTarget(); - LLVMInitializeHexagonTarget(); - LLVMInitializeLanaiTarget(); - LLVMInitializeMipsTarget(); - LLVMInitializeMSP430Target(); - LLVMInitializeNVPTXTarget(); - LLVMInitializePowerPCTarget(); - LLVMInitializeRISCVTarget(); - LLVMInitializeSparcTarget(); - LLVMInitializeSystemZTarget(); - LLVMInitializeWebAssemblyTarget(); - LLVMInitializeX86Target(); - LLVMInitializeXCoreTarget(); -} -pub const initializeAllTargetMCs = LLVMInitializeAllTargetMCs; -fn LLVMInitializeAllTargetMCs() callconv(.C) void { - LLVMInitializeAArch64TargetMC(); - LLVMInitializeAMDGPUTargetMC(); - LLVMInitializeARMTargetMC(); - LLVMInitializeAVRTargetMC(); - LLVMInitializeBPFTargetMC(); - LLVMInitializeHexagonTargetMC(); - LLVMInitializeLanaiTargetMC(); - LLVMInitializeMipsTargetMC(); - LLVMInitializeMSP430TargetMC(); - LLVMInitializeNVPTXTargetMC(); - LLVMInitializePowerPCTargetMC(); - LLVMInitializeRISCVTargetMC(); - LLVMInitializeSparcTargetMC(); - LLVMInitializeSystemZTargetMC(); - LLVMInitializeWebAssemblyTargetMC(); - LLVMInitializeX86TargetMC(); - LLVMInitializeXCoreTargetMC(); -} -pub const initializeAllAsmPrinters = LLVMInitializeAllAsmPrinters; -fn LLVMInitializeAllAsmPrinters() callconv(.C) void { - LLVMInitializeAArch64AsmPrinter(); - LLVMInitializeAMDGPUAsmPrinter(); - LLVMInitializeARMAsmPrinter(); - LLVMInitializeAVRAsmPrinter(); - LLVMInitializeBPFAsmPrinter(); - LLVMInitializeHexagonAsmPrinter(); - LLVMInitializeLanaiAsmPrinter(); - LLVMInitializeMipsAsmPrinter(); - LLVMInitializeMSP430AsmPrinter(); - LLVMInitializeNVPTXAsmPrinter(); - LLVMInitializePowerPCAsmPrinter(); - LLVMInitializeRISCVAsmPrinter(); - LLVMInitializeSparcAsmPrinter(); - LLVMInitializeSystemZAsmPrinter(); - LLVMInitializeWebAssemblyAsmPrinter(); - LLVMInitializeX86AsmPrinter(); - LLVMInitializeXCoreAsmPrinter(); -} -pub const initializeAllAsmParsers = LLVMInitializeAllAsmParsers; -fn LLVMInitializeAllAsmParsers() callconv(.C) void { - LLVMInitializeAArch64AsmParser(); - LLVMInitializeAMDGPUAsmParser(); - LLVMInitializeARMAsmParser(); - LLVMInitializeAVRAsmParser(); - LLVMInitializeBPFAsmParser(); - LLVMInitializeHexagonAsmParser(); - LLVMInitializeLanaiAsmParser(); - LLVMInitializeMipsAsmParser(); - LLVMInitializeMSP430AsmParser(); - LLVMInitializePowerPCAsmParser(); - LLVMInitializeRISCVAsmParser(); - LLVMInitializeSparcAsmParser(); - LLVMInitializeSystemZAsmParser(); - LLVMInitializeWebAssemblyAsmParser(); - LLVMInitializeX86AsmParser(); -} - -extern fn ZigLLDLinkCOFF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; -extern fn ZigLLDLinkELF(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; -extern fn ZigLLDLinkMachO(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; -extern fn ZigLLDLinkWasm(argc: c_int, argv: [*:null]const ?[*:0]const u8, can_exit_early: bool) c_int; - -pub const LinkCOFF = ZigLLDLinkCOFF; -pub const LinkELF = ZigLLDLinkELF; -pub const LinkMachO = ZigLLDLinkMachO; -pub const LinkWasm = ZigLLDLinkWasm; - -pub const ObjectFormatType = extern enum(c_int) { - Unknown, - COFF, - ELF, - MachO, - Wasm, - XCOFF, -}; - -pub const GetHostCPUName = LLVMGetHostCPUName; -extern fn LLVMGetHostCPUName() ?[*:0]u8; - -pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; -extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; - -pub const WriteArchive = ZigLLVMWriteArchive; -extern fn ZigLLVMWriteArchive( - archive_name: [*:0]const u8, - file_names_ptr: [*]const [*:0]const u8, - file_names_len: usize, - os_type: OSType, -) bool; - -pub const OSType = extern enum(c_int) { - UnknownOS = 0, - Ananas = 1, - CloudABI = 2, - Darwin = 3, - DragonFly = 4, - FreeBSD = 5, - Fuchsia = 6, - IOS = 7, - KFreeBSD = 8, - Linux = 9, - Lv2 = 10, - MacOSX = 11, - NetBSD = 12, - OpenBSD = 13, - Solaris = 14, - Win32 = 15, - Haiku = 16, - Minix = 17, - RTEMS = 18, - NaCl = 19, - CNK = 20, - AIX = 21, - CUDA = 22, - NVCL = 23, - AMDHSA = 24, - PS4 = 25, - ELFIAMCU = 26, - TvOS = 27, - WatchOS = 28, - Mesa3D = 29, - Contiki = 30, - AMDPAL = 31, - HermitCore = 32, - Hurd = 33, - WASI = 34, - Emscripten = 35, -}; - -pub const ArchType = extern enum(c_int) { - UnknownArch = 0, - arm = 1, - armeb = 2, - aarch64 = 3, - aarch64_be = 4, - aarch64_32 = 5, - arc = 6, - avr = 7, - bpfel = 8, - bpfeb = 9, - hexagon = 10, - mips = 11, - mipsel = 12, - mips64 = 13, - mips64el = 14, - msp430 = 15, - ppc = 16, - ppc64 = 17, - ppc64le = 18, - r600 = 19, - amdgcn = 20, - riscv32 = 21, - riscv64 = 22, - sparc = 23, - sparcv9 = 24, - sparcel = 25, - systemz = 26, - tce = 27, - tcele = 28, - thumb = 29, - thumbeb = 30, - x86 = 31, - x86_64 = 32, - xcore = 33, - nvptx = 34, - nvptx64 = 35, - le32 = 36, - le64 = 37, - amdil = 38, - amdil64 = 39, - hsail = 40, - hsail64 = 41, - spir = 42, - spir64 = 43, - kalimba = 44, - shave = 45, - lanai = 46, - wasm32 = 47, - wasm64 = 48, - renderscript32 = 49, - renderscript64 = 50, - ve = 51, -}; - -pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions; -extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void; - -pub const WriteImportLibrary = ZigLLVMWriteImportLibrary; -extern fn ZigLLVMWriteImportLibrary( - def_path: [*:0]const u8, - arch: ArchType, - output_lib_path: [*c]const u8, - kill_at: bool, -) bool; diff --git a/src/main.zig b/src/main.zig index f026b6b4b4..ac31437f85 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1703,7 +1703,7 @@ fn buildOutputType( if (build_options.have_llvm and emit_asm != .no) { // LLVM has no way to set this non-globally. const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; - @import("llvm_bindings.zig").ParseCommandLineOptions(argv.len, &argv); + @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv); } gimmeMoreOfThoseSweetSweetFileDescriptors(); @@ -2890,7 +2890,7 @@ pub fn punt_to_lld(arena: *Allocator, args: []const []const u8) error{OutOfMemor argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. } const exit_code = rc: { - const llvm = @import("llvm_bindings.zig"); + const llvm = @import("codegen/llvm/bindings.zig"); const argc = @intCast(c_int, argv.len); if (mem.eql(u8, args[1], "ld.lld")) { break :rc llvm.LinkELF(argc, argv.ptr, true); @@ -3275,7 +3275,7 @@ fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !s if (!build_options.have_llvm) fatal("CPU features detection is not yet available for {s} without LLVM extensions", .{@tagName(arch)}); - const llvm = @import("llvm_bindings.zig"); + const llvm = @import("codegen/llvm/bindings.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); const llvm_cpu_features = llvm.GetNativeFeatures(); info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); diff --git a/src/mingw.zig b/src/mingw.zig index d55cc28b2b..0a7669f995 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -405,7 +405,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }); errdefer comp.gpa.free(lib_final_path); - const llvm = @import("llvm_bindings.zig"); + const llvm = @import("codegen/llvm/bindings.zig"); const arch_type = @import("target.zig").archToLLVM(target.cpu.arch); const def_final_path_z = try arena.dupeZ(u8, def_final_path); const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); diff --git a/src/target.zig b/src/target.zig index d33e8d06b0..daac577c7b 100644 --- a/src/target.zig +++ b/src/target.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const llvm = @import("llvm_bindings.zig"); +const llvm = @import("codegen/llvm/bindings.zig"); pub const ArchOsAbi = struct { arch: std.Target.Cpu.Arch, diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig new file mode 100644 index 0000000000..ebb5fc390f --- /dev/null +++ b/test/stage2/llvm.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; +const build_options = @import("build_options"); + +// These tests should work with all platforms, but we're using linux_x64 for +// now for consistency. Will be expanded eventually. +const linux_x64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64); + + case.addCompareOutput( + \\fn add(a: i32, b: i32) i32 { + \\ return a + b; + \\} + \\ + \\export fn main() c_int { + \\ var a: i32 = -5; + \\ const x = add(a, 7); + \\ var y = add(2, 0); + \\ y -= x; + \\ return y; + \\} + , ""); + } + + { + var case = ctx.exeUsingLlvmBackend("hello world", linux_x64); + + case.addCompareOutput( + \\extern fn puts(s: [*:0]const u8) c_int; + \\ + \\export fn main() c_int { + \\ _ = puts("hello world!"); + \\ return 0; + \\} + , "hello world!" ++ std.cstr.line_sep); + } +} diff --git a/test/stage2/llvm_backend.zig b/test/stage2/llvm_backend.zig deleted file mode 100644 index ebb5fc390f..0000000000 --- a/test/stage2/llvm_backend.zig +++ /dev/null @@ -1,43 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; -const build_options = @import("build_options"); - -// These tests should work with all platforms, but we're using linux_x64 for -// now for consistency. Will be expanded eventually. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64); - - case.addCompareOutput( - \\fn add(a: i32, b: i32) i32 { - \\ return a + b; - \\} - \\ - \\export fn main() c_int { - \\ var a: i32 = -5; - \\ const x = add(a, 7); - \\ var y = add(2, 0); - \\ y -= x; - \\ return y; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("hello world", linux_x64); - - case.addCompareOutput( - \\extern fn puts(s: [*:0]const u8) c_int; - \\ - \\export fn main() c_int { - \\ _ = puts("hello world!"); - \\ return 0; - \\} - , "hello world!" ++ std.cstr.line_sep); - } -} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 6e25dc283b..5c21ccd669 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -31,7 +31,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("spu-ii.zig").addCases(ctx); try @import("arm.zig").addCases(ctx); try @import("aarch64.zig").addCases(ctx); - try @import("llvm_backend.zig").addCases(ctx); + try @import("llvm.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); -- cgit v1.2.3 From a9b505fa7774e2e8451bedfa7bea27d7227572e7 Mon Sep 17 00:00:00 2001 From: Jay Petacat Date: Tue, 5 Jan 2021 20:57:18 -0500 Subject: Reduce use of deprecated IO types Related: #4917 --- doc/docgen.zig | 8 ++++---- lib/std/Progress.zig | 2 +- lib/std/atomic/queue.zig | 8 ++++---- lib/std/build.zig | 10 +++++----- lib/std/build/run.zig | 4 ++-- lib/std/child_process.zig | 2 +- lib/std/coff.zig | 8 ++++---- lib/std/crypto/benchmark.zig | 2 +- lib/std/debug.zig | 36 +++++++++++++++++------------------ lib/std/dwarf.zig | 10 +++++----- lib/std/heap/logging_allocator.zig | 2 +- lib/std/io/buffered_atomic_file.zig | 2 +- lib/std/io/fixed_buffer_stream.zig | 2 +- lib/std/io/test.zig | 6 +++--- lib/std/json.zig | 16 ++++++++-------- lib/std/json/write_stream.zig | 2 +- lib/std/net.zig | 4 ++-- lib/std/net/test.zig | 2 +- lib/std/os.zig | 2 +- lib/std/os/test.zig | 6 +++--- lib/std/special/build_runner.zig | 4 ++-- lib/std/unicode/throughput_test.zig | 2 +- lib/std/zig/cross_target.zig | 14 +++++++------- lib/std/zig/parser_test.zig | 6 +++--- lib/std/zig/perf_test.zig | 2 +- lib/std/zig/render.zig | 4 ++-- src/Cache.zig | 2 +- src/Compilation.zig | 8 ++++---- src/DepTokenizer.zig | 2 +- src/Module.zig | 2 +- src/codegen/llvm.zig | 8 ++++---- src/libc_installation.zig | 6 +++--- src/main.zig | 38 ++++++++++++++++++------------------- src/print_env.zig | 10 +++++----- src/print_targets.zig | 10 +++++----- src/test.zig | 2 +- src/translate_c.zig | 2 +- src/zir.zig | 4 ++-- test/compare_output.zig | 30 ++++++++++++++--------------- test/stage1/behavior/bugs/5487.zig | 6 +++--- test/tests.zig | 8 ++++---- tools/update_clang_options.zig | 8 ++++---- tools/update_glibc.zig | 6 +++--- 43 files changed, 159 insertions(+), 159 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/doc/docgen.zig b/doc/docgen.zig index 7e0bedbb6c..90e3e32201 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -40,9 +40,9 @@ pub fn main() !void { var out_file = try fs.cwd().createFile(out_file_name, .{}); defer out_file.close(); - const input_file_bytes = try in_file.inStream().readAllAlloc(allocator, max_doc_file_size); + const input_file_bytes = try in_file.reader().readAllAlloc(allocator, max_doc_file_size); - var buffered_out_stream = io.bufferedOutStream(out_file.writer()); + var buffered_writer = io.bufferedWriter(out_file.writer()); var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); var toc = try genToc(allocator, &tokenizer); @@ -50,8 +50,8 @@ pub fn main() !void { try fs.cwd().makePath(tmp_dir_name); defer fs.cwd().deleteTree(tmp_dir_name) catch {}; - try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.writer(), zig_exe); - try buffered_out_stream.flush(); + try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe); + try buffered_writer.flush(); } const Token = struct { diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 6226e34248..ca9fb8ea1f 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -271,7 +271,7 @@ fn refreshWithHeldLock(self: *Progress) void { pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { const file = self.terminal orelse return; self.refresh(); - file.outStream().print(format, args) catch { + file.writer().print(format, args) catch { self.terminal = null; return; }; diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index 19d04d041a..fa3711cd9f 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -122,7 +122,7 @@ pub fn Queue(comptime T: type) type { /// Dumps the contents of the queue to `stderr`. pub fn dump(self: *Self) void { - self.dumpToStream(std.io.getStdErr().outStream()) catch return; + self.dumpToStream(std.io.getStdErr().writer()) catch return; } /// Dumps the contents of the queue to `stream`. @@ -351,7 +351,7 @@ test "std.atomic.Queue dump" { // Test empty stream fbs.reset(); - try queue.dumpToStream(fbs.outStream()); + try queue.dumpToStream(fbs.writer()); expect(mem.eql(u8, buffer[0..fbs.pos], \\head: (null) \\tail: (null) @@ -367,7 +367,7 @@ test "std.atomic.Queue dump" { queue.put(&node_0); fbs.reset(); - try queue.dumpToStream(fbs.outStream()); + try queue.dumpToStream(fbs.writer()); var expected = try std.fmt.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 @@ -387,7 +387,7 @@ test "std.atomic.Queue dump" { queue.put(&node_1); fbs.reset(); - try queue.dumpToStream(fbs.outStream()); + try queue.dumpToStream(fbs.writer()); expected = try std.fmt.bufPrint(expected_buffer[0..], \\head: 0x{x}=1 diff --git a/lib/std/build.zig b/lib/std/build.zig index 0d17b4753a..cb4cb229e3 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1042,7 +1042,7 @@ pub const Builder = struct { try child.spawn(); - const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size); + const stdout = try child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size); errdefer self.allocator.free(stdout); const term = try child.wait(); @@ -1849,7 +1849,7 @@ pub const LibExeObjStep = struct { } pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { - const out = self.build_options_contents.outStream(); + const out = self.build_options_contents.writer(); switch (T) { []const []const u8 => { out.print("pub const {z}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable; @@ -2295,16 +2295,16 @@ pub const LibExeObjStep = struct { } else { var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); - try mcpu_buffer.outStream().print("-mcpu={s}", .{cross.cpu.model.name}); + try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name}); for (all_features) |feature, i_usize| { const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); const in_cpu_set = populated_cpu_features.isEnabled(i); const in_actual_set = cross.cpu.features.isEnabled(i); if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.outStream().print("-{s}", .{feature.name}); + try mcpu_buffer.writer().print("-{s}", .{feature.name}); } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.outStream().print("+{s}", .{feature.name}); + try mcpu_buffer.writer().print("+{s}", .{feature.name}); } } diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 36a4a1a843..8f8fa2eba0 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -200,7 +200,7 @@ pub const RunStep = struct { switch (self.stdout_action) { .expect_exact, .expect_matches => { - stdout = child.stdout.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; + stdout = child.stdout.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; }, .inherit, .ignore => {}, } @@ -210,7 +210,7 @@ pub const RunStep = struct { switch (self.stderr_action) { .expect_exact, .expect_matches => { - stderr = child.stderr.?.inStream().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; + stderr = child.stderr.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; }, .inherit, .ignore => {}, } diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 4360cc7d73..d37dd9fdf5 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -922,7 +922,7 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { .capable_io_mode = .blocking, .intended_io_mode = .blocking, }; - file.outStream().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; + file.writer().writeIntNative(u64, @intCast(u64, value)) catch return error.SystemResources; } fn readIntFd(fd: i32) !ErrInt { diff --git a/lib/std/coff.zig b/lib/std/coff.zig index fdc2ec5a82..85000bf8cb 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -127,7 +127,7 @@ pub const Coff = struct { pub fn loadHeader(self: *Coff) !void { const pe_pointer_offset = 0x3C; - const in = self.in_file.inStream(); + const in = self.in_file.reader(); var magic: [2]u8 = undefined; try in.readNoEof(magic[0..]); @@ -163,7 +163,7 @@ pub const Coff = struct { } fn loadOptionalHeader(self: *Coff) !void { - const in = self.in_file.inStream(); + const in = self.in_file.reader(); self.pe_header.magic = try in.readIntLittle(u16); // For now we're only interested in finding the reference to the .pdb, // so we'll skip most of this header, which size is different in 32 @@ -206,7 +206,7 @@ pub const Coff = struct { const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; - const in = self.in_file.inStream(); + const in = self.in_file.reader(); try self.in_file.seekTo(file_offset); // Find the correct DebugDirectoryEntry, and where its data is stored. @@ -257,7 +257,7 @@ pub const Coff = struct { try self.sections.ensureCapacity(self.coff_header.number_of_sections); - const in = self.in_file.inStream(); + const in = self.in_file.reader(); var name: [8]u8 = undefined; diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 5b3b837d13..00336aef87 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -314,7 +314,7 @@ fn mode(comptime x: comptime_int) comptime_int { } pub fn main() !void { - const stdout = std.io.getStdOut().outStream(); + const stdout = std.io.getStdOut().writer(); var buffer: [1024]u8 = undefined; var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 073f68da5d..b19f96892f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -517,15 +517,15 @@ fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void { const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; - const signature = try modi.inStream().readIntLittle(u32); + const signature = try modi.reader().readIntLittle(u32); if (signature != 4) return error.InvalidDebugInfo; mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); - try modi.inStream().readNoEof(mod.symbols); + try modi.reader().readNoEof(mod.symbols); mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); - try modi.inStream().readNoEof(mod.subsect_info); + try modi.reader().readNoEof(mod.subsect_info); var sect_offset: usize = 0; var skip_len: usize = undefined; @@ -704,11 +704,11 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf try di.pdb.openFile(di.coff, path); var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; - const version = try pdb_stream.inStream().readIntLittle(u32); - const signature = try pdb_stream.inStream().readIntLittle(u32); - const age = try pdb_stream.inStream().readIntLittle(u32); + const version = try pdb_stream.reader().readIntLittle(u32); + const signature = try pdb_stream.reader().readIntLittle(u32); + const age = try pdb_stream.reader().readIntLittle(u32); var guid: [16]u8 = undefined; - try pdb_stream.inStream().readNoEof(&guid); + try pdb_stream.reader().readNoEof(&guid); if (version != 20000404) // VC70, only value observed by LLVM team return error.UnknownPDBVersion; if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) @@ -716,9 +716,9 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf // We validated the executable and pdb match. const string_table_index = str_tab_index: { - const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); + const name_bytes_len = try pdb_stream.reader().readIntLittle(u32); const name_bytes = try allocator.alloc(u8, name_bytes_len); - try pdb_stream.inStream().readNoEof(name_bytes); + try pdb_stream.reader().readNoEof(name_bytes); const HashTableHeader = packed struct { Size: u32, @@ -728,17 +728,17 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf return cap * 2 / 3 + 1; } }; - const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); + const hash_tbl_hdr = try pdb_stream.reader().readStruct(HashTableHeader); if (hash_tbl_hdr.Capacity == 0) return error.InvalidDebugInfo; if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) return error.InvalidDebugInfo; - const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); + const present = try readSparseBitVector(&pdb_stream.reader(), allocator); if (present.len != hash_tbl_hdr.Size) return error.InvalidDebugInfo; - const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); + const deleted = try readSparseBitVector(&pdb_stream.reader(), allocator); const Bucket = struct { first: u32, @@ -746,8 +746,8 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf }; const bucket_list = try allocator.alloc(Bucket, present.len); for (present) |_| { - const name_offset = try pdb_stream.inStream().readIntLittle(u32); - const name_index = try pdb_stream.inStream().readIntLittle(u32); + const name_offset = try pdb_stream.reader().readIntLittle(u32); + const name_index = try pdb_stream.reader().readIntLittle(u32); const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0)); if (mem.eql(u8, name, "/names")) { break :str_tab_index name_index; @@ -762,7 +762,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf const dbi = di.pdb.dbi; // Dbi Header - const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); + const dbi_stream_header = try dbi.reader().readStruct(pdb.DbiStreamHeader); if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team return error.UnknownPDBVersion; if (dbi_stream_header.Age != age) @@ -776,7 +776,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf // Module Info Substream var mod_info_offset: usize = 0; while (mod_info_offset != mod_info_size) { - const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); + const mod_info = try dbi.reader().readStruct(pdb.ModInfo); var this_record_len: usize = @sizeOf(pdb.ModInfo); const module_name = try dbi.readNullTermString(allocator); @@ -814,14 +814,14 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); var sect_cont_offset: usize = 0; if (section_contrib_size != 0) { - const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); + const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.reader().readIntLittle(u32)); if (ver != pdb.SectionContrSubstreamVersion.Ver60) return error.InvalidDebugInfo; sect_cont_offset += @sizeOf(u32); } while (sect_cont_offset != section_contrib_size) { const entry = try sect_contribs.addOne(); - entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); + entry.* = try dbi.reader().readStruct(pdb.SectionContribEntry); sect_cont_offset += @sizeOf(pdb.SectionContribEntry); if (sect_cont_offset > section_contrib_size) diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 2e732cbc75..6769a139da 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -408,7 +408,7 @@ pub const DwarfInfo = struct { fn scanAllFunctions(di: *DwarfInfo) !void { var stream = io.fixedBufferStream(di.debug_info); - const in = &stream.inStream(); + const in = &stream.reader(); const seekable = &stream.seekableStream(); var this_unit_offset: u64 = 0; @@ -512,7 +512,7 @@ pub const DwarfInfo = struct { fn scanAllCompileUnits(di: *DwarfInfo) !void { var stream = io.fixedBufferStream(di.debug_info); - const in = &stream.inStream(); + const in = &stream.reader(); const seekable = &stream.seekableStream(); var this_unit_offset: u64 = 0; @@ -585,7 +585,7 @@ pub const DwarfInfo = struct { if (di.debug_ranges) |debug_ranges| { if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| { var stream = io.fixedBufferStream(debug_ranges); - const in = &stream.inStream(); + const in = &stream.reader(); const seekable = &stream.seekableStream(); // All the addresses in the list are relative to the value @@ -640,7 +640,7 @@ pub const DwarfInfo = struct { fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { var stream = io.fixedBufferStream(di.debug_abbrev); - const in = &stream.inStream(); + const in = &stream.reader(); const seekable = &stream.seekableStream(); try seekable.seekTo(offset); @@ -691,7 +691,7 @@ pub const DwarfInfo = struct { pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo { var stream = io.fixedBufferStream(di.debug_line); - const in = &stream.inStream(); + const in = &stream.reader(); const seekable = &stream.seekableStream(); const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir); diff --git a/lib/std/heap/logging_allocator.zig b/lib/std/heap/logging_allocator.zig index 47a584bb1d..7920138e9b 100644 --- a/lib/std/heap/logging_allocator.zig +++ b/lib/std/heap/logging_allocator.zig @@ -89,7 +89,7 @@ test "LoggingAllocator" { var allocator_buf: [10]u8 = undefined; var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf)); - const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator; + const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.writer()).allocator; var a = try allocator.alloc(u8, 10); a = allocator.shrink(a, 5); diff --git a/lib/std/io/buffered_atomic_file.zig b/lib/std/io/buffered_atomic_file.zig index 9d65e9d193..66c349d318 100644 --- a/lib/std/io/buffered_atomic_file.zig +++ b/lib/std/io/buffered_atomic_file.zig @@ -38,7 +38,7 @@ pub const BufferedAtomicFile = struct { self.atomic_file = try dir.atomicFile(dest_path, atomic_file_options); errdefer self.atomic_file.deinit(); - self.file_stream = self.atomic_file.file.outStream(); + self.file_stream = self.atomic_file.file.writer(); self.buffered_stream = .{ .unbuffered_writer = self.file_stream }; return self; } diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index b36949e538..1711b6da1b 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -45,7 +45,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { return .{ .context = self }; } - /// Deprecated: use `inStream` + /// Deprecated: use `reader` pub fn inStream(self: *Self) InStream { return .{ .context = self }; } diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 85357ae58a..9fdef0de1d 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -30,8 +30,8 @@ test "write a file, read it, then delete it" { var file = try tmp.dir.createFile(tmp_file_name, .{}); defer file.close(); - var buf_stream = io.bufferedOutStream(file.outStream()); - const st = buf_stream.outStream(); + var buf_stream = io.bufferedWriter(file.writer()); + const st = buf_stream.writer(); try st.print("begin", .{}); try st.writeAll(data[0..]); try st.print("end", .{}); @@ -72,7 +72,7 @@ test "BitStreams with File Stream" { var file = try tmp.dir.createFile(tmp_file_name, .{}); defer file.close(); - var bit_stream = io.bitOutStream(builtin.endian, file.outStream()); + var bit_stream = io.bitWriter(builtin.endian, file.writer()); try bit_stream.writeBits(@as(u2, 1), 1); try bit_stream.writeBits(@as(u5, 2), 2); diff --git a/lib/std/json.zig b/lib/std/json.zig index 87808a7350..5cbdb4319e 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1323,31 +1323,31 @@ test "Value.jsonStringify" { { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); - try @as(Value, .Null).jsonStringify(.{}, fbs.outStream()); + try @as(Value, .Null).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "null"); } { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Bool = true }).jsonStringify(.{}, fbs.outStream()); + try (Value{ .Bool = true }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "true"); } { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Integer = 42 }).jsonStringify(.{}, fbs.outStream()); + try (Value{ .Integer = 42 }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "42"); } { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Float = 42 }).jsonStringify(.{}, fbs.outStream()); + try (Value{ .Float = 42 }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "4.2e+01"); } { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .String = "weeee" }).jsonStringify(.{}, fbs.outStream()); + try (Value{ .String = "weeee" }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "\"weeee\""); } { @@ -1360,7 +1360,7 @@ test "Value.jsonStringify" { }; try (Value{ .Array = Array.fromOwnedSlice(undefined, &vals), - }).jsonStringify(.{}, fbs.outStream()); + }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "[1,2,3]"); } { @@ -1369,7 +1369,7 @@ test "Value.jsonStringify" { var obj = ObjectMap.init(testing.allocator); defer obj.deinit(); try obj.putNoClobber("a", .{ .String = "b" }); - try (Value{ .Object = obj }).jsonStringify(.{}, fbs.outStream()); + try (Value{ .Object = obj }).jsonStringify(.{}, fbs.writer()); testing.expectEqualSlices(u8, fbs.getWritten(), "{\"a\":\"b\"}"); } } @@ -2223,7 +2223,7 @@ test "write json then parse it" { var out_buffer: [1000]u8 = undefined; var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer); - const out_stream = fixed_buffer_stream.outStream(); + const out_stream = fixed_buffer_stream.writer(); var jw = writeStream(out_stream, 4); try jw.beginObject(); diff --git a/lib/std/json/write_stream.zig b/lib/std/json/write_stream.zig index 9322ca5429..b4a8aed84c 100644 --- a/lib/std/json/write_stream.zig +++ b/lib/std/json/write_stream.zig @@ -238,7 +238,7 @@ pub fn writeStream( test "json write stream" { var out_buf: [1024]u8 = undefined; var slice_stream = std.io.fixedBufferStream(&out_buf); - const out = slice_stream.outStream(); + const out = slice_stream.writer(); var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_allocator.deinit(); diff --git a/lib/std/net.zig b/lib/std/net.zig index da35bd88b0..037df76907 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1106,7 +1106,7 @@ fn linuxLookupNameFromHosts( }; defer file.close(); - const stream = std.io.bufferedInStream(file.inStream()).inStream(); + const stream = std.io.bufferedReader(file.reader()).reader(); var line_buf: [512]u8 = undefined; while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { error.StreamTooLong => blk: { @@ -1304,7 +1304,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { }; defer file.close(); - const stream = std.io.bufferedInStream(file.inStream()).inStream(); + const stream = std.io.bufferedReader(file.reader()).reader(); var line_buf: [512]u8 = undefined; while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { error.StreamTooLong => blk: { diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 110c6ec9c0..74ae1ddf4f 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -249,6 +249,6 @@ fn testServer(server: *net.StreamServer) anyerror!void { var client = try server.accept(); - const stream = client.file.outStream(); + const stream = client.file.writer(); try stream.print("hello from server\n", .{}); } diff --git a/lib/std/os.zig b/lib/std/os.zig index e9bf6379cc..42bdf486d7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -191,7 +191,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void { .capable_io_mode = .blocking, .intended_io_mode = .blocking, }; - const stream = file.inStream(); + const stream = file.reader(); stream.readNoEof(buf) catch return error.Unexpected; } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 65dee90e62..0904a8585f 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -475,7 +475,7 @@ test "mmap" { const file = try tmp.dir.createFile(test_out_file, .{}); defer file.close(); - const stream = file.outStream(); + const stream = file.writer(); var i: u32 = 0; while (i < alloc_size / @sizeOf(u32)) : (i += 1) { @@ -499,7 +499,7 @@ test "mmap" { defer os.munmap(data); var mem_stream = io.fixedBufferStream(data); - const stream = mem_stream.inStream(); + const stream = mem_stream.reader(); var i: u32 = 0; while (i < alloc_size / @sizeOf(u32)) : (i += 1) { @@ -523,7 +523,7 @@ test "mmap" { defer os.munmap(data); var mem_stream = io.fixedBufferStream(data); - const stream = mem_stream.inStream(); + const stream = mem_stream.reader(); var i: u32 = alloc_size / 2 / @sizeOf(u32); while (i < alloc_size / @sizeOf(u32)) : (i += 1) { diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index f2fa2dc3b8..0b7baf0fc1 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -57,8 +57,8 @@ pub fn main() !void { var targets = ArrayList([]const u8).init(allocator); - const stderr_stream = io.getStdErr().outStream(); - const stdout_stream = io.getStdOut().outStream(); + const stderr_stream = io.getStdErr().writer(); + const stdout_stream = io.getStdOut().writer(); while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-D")) { diff --git a/lib/std/unicode/throughput_test.zig b/lib/std/unicode/throughput_test.zig index 2676a30cb2..8f9f9d9cb7 100644 --- a/lib/std/unicode/throughput_test.zig +++ b/lib/std/unicode/throughput_test.zig @@ -45,7 +45,7 @@ fn benchmarkCodepointCount(buf: []const u8) !ResultCount { } pub fn main() !void { - const stdout = std.io.getStdOut().outStream(); + const stdout = std.io.getStdOut().writer(); const args = try std.process.argsAlloc(std.heap.page_allocator); diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 060dd83caf..f0849f9a03 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -519,29 +519,29 @@ pub const CrossTarget = struct { var result = std.ArrayList(u8).init(allocator); defer result.deinit(); - try result.outStream().print("{s}-{s}", .{ arch_name, os_name }); + try result.writer().print("{s}-{s}", .{ arch_name, os_name }); // The zig target syntax does not allow specifying a max os version with no min, so // if either are present, we need the min. if (self.os_version_min != null or self.os_version_max != null) { switch (self.getOsVersionMin()) { .none => {}, - .semver => |v| try result.outStream().print(".{}", .{v}), - .windows => |v| try result.outStream().print("{s}", .{v}), + .semver => |v| try result.writer().print(".{}", .{v}), + .windows => |v| try result.writer().print("{s}", .{v}), } } if (self.os_version_max) |max| { switch (max) { .none => {}, - .semver => |v| try result.outStream().print("...{}", .{v}), - .windows => |v| try result.outStream().print("..{s}", .{v}), + .semver => |v| try result.writer().print("...{}", .{v}), + .windows => |v| try result.writer().print("..{s}", .{v}), } } if (self.glibc_version) |v| { - try result.outStream().print("-{s}.{}", .{ @tagName(self.getAbi()), v }); + try result.writer().print("-{s}.{}", .{ @tagName(self.getAbi()), v }); } else if (self.abi) |abi| { - try result.outStream().print("-{s}", .{@tagName(abi)}); + try result.writer().print("-{s}", .{@tagName(abi)}); } return result.toOwnedSlice(); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 63d04f9a80..d7cc1208a2 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3734,7 +3734,7 @@ const maxInt = std.math.maxInt; var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { - const stderr = io.getStdErr().outStream(); + const stderr = io.getStdErr().writer(); const tree = try std.zig.parse(allocator, source); defer tree.deinit(); @@ -3767,8 +3767,8 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b var buffer = std.ArrayList(u8).init(allocator); errdefer buffer.deinit(); - const outStream = buffer.outStream(); - anything_changed.* = try std.zig.render(allocator, outStream, tree); + const writer = buffer.writer(); + anything_changed.* = try std.zig.render(allocator, writer, tree); return buffer.toOwnedSlice(); } fn testTransform(source: []const u8, expected_source: []const u8) !void { diff --git a/lib/std/zig/perf_test.zig b/lib/std/zig/perf_test.zig index e1c58c1469..b111170902 100644 --- a/lib/std/zig/perf_test.zig +++ b/lib/std/zig/perf_test.zig @@ -29,7 +29,7 @@ pub fn main() !void { const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = std.io.getStdOut(); - const stdout = stdout_file.outStream(); + const stdout = stdout_file.writer(); try stdout.print("{:.3} MiB/s, {} KiB used \n", .{ mb_per_sec, memory_used / 1024 }); } diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 6931b19250..b882d71cfd 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -790,7 +790,7 @@ fn renderExpression( const section_exprs = row_exprs[0..section_end]; // Null stream for counting the printed length of each expression - var line_find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream); + var line_find_stream = std.io.findByteOutStream('\n', std.io.null_writer); var counting_stream = std.io.countingOutStream(line_find_stream.writer()); var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer()); @@ -954,7 +954,7 @@ fn renderExpression( const expr_outputs_one_line = blk: { // render field expressions until a LF is found for (field_inits) |field_init| { - var find_stream = std.io.findByteOutStream('\n', std.io.null_out_stream); + var find_stream = std.io.findByteOutStream('\n', std.io.null_writer); var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer()); try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None); diff --git a/src/Cache.zig b/src/Cache.zig index 9f8beaabc7..f5ffb34dbe 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -285,7 +285,7 @@ pub const Manifest = struct { }; } - const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.cache.gpa, manifest_file_size_max); + const file_contents = try self.manifest_file.?.reader().readAllAlloc(self.cache.gpa, manifest_file_size_max); defer self.cache.gpa.free(file_contents); const input_file_count = self.files.items.len; diff --git a/src/Compilation.zig b/src/Compilation.zig index 95c9435aa7..a81926bf19 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1820,7 +1820,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{}); defer out_zig_file.close(); - var bos = std.io.bufferedOutStream(out_zig_file.writer()); + var bos = std.io.bufferedWriter(out_zig_file.writer()); _ = try std.zig.render(comp.gpa, bos.writer(), tree); try bos.flush(); @@ -2750,7 +2750,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 switch (target.os.getVersionRange()) { .none => try buffer.appendSlice(" .none = {} }\n"), - .semver => |semver| try buffer.outStream().print( + .semver => |semver| try buffer.writer().print( \\ .semver = .{{ \\ .min = .{{ \\ .major = {}, @@ -2773,7 +2773,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 semver.max.minor, semver.max.patch, }), - .linux => |linux| try buffer.outStream().print( + .linux => |linux| try buffer.writer().print( \\ .linux = .{{ \\ .range = .{{ \\ .min = .{{ @@ -2807,7 +2807,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 linux.glibc.minor, linux.glibc.patch, }), - .windows => |windows| try buffer.outStream().print( + .windows => |windows| try buffer.writer().print( \\ .windows = .{{ \\ .min = {s}, \\ .max = {s}, diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig index 0fe1310cd8..b246a22bd0 100644 --- a/src/DepTokenizer.zig +++ b/src/DepTokenizer.zig @@ -910,7 +910,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { }, else => { try buffer.appendSlice("ERROR: "); - try token.printError(buffer.outStream()); + try token.printError(buffer.writer()); break; }, } diff --git a/src/Module.zig b/src/Module.zig index 603c97ffee..9707e79e2a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1649,7 +1649,7 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { var msg = std.ArrayList(u8).init(self.gpa); defer msg.deinit(); - try parse_err.render(tree.token_ids, msg.outStream()); + try parse_err.render(tree.token_ids, msg.writer()); const err_msg = try self.gpa.create(Compilation.ErrorMsg); err_msg.* = .{ .msg = msg.toOwnedSlice(), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index df97f7e0ef..9b35c42c6e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -200,7 +200,7 @@ pub const LLVMIRModule = struct { if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message)) { defer llvm.disposeMessage(error_message); - const stderr = std.io.getStdErr().outStream(); + const stderr = std.io.getStdErr().writer(); try stderr.print( \\Zig is expecting LLVM to understand this target: '{s}' \\However LLVM responded with: "{s}" @@ -268,7 +268,7 @@ pub const LLVMIRModule = struct { const dump = self.llvm_module.printToString(); defer llvm.disposeMessage(dump); - const stderr = std.io.getStdErr().outStream(); + const stderr = std.io.getStdErr().writer(); try stderr.writeAll(std.mem.spanZ(dump)); } @@ -278,7 +278,7 @@ pub const LLVMIRModule = struct { defer llvm.disposeMessage(error_message); if (self.llvm_module.verify(.ReturnStatus, &error_message)) { - const stderr = std.io.getStdErr().outStream(); + const stderr = std.io.getStdErr().writer(); try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message}); return error.BrokenLLVMModule; } @@ -296,7 +296,7 @@ pub const LLVMIRModule = struct { )) { defer llvm.disposeMessage(error_message); - const stderr = std.io.getStdErr().outStream(); + const stderr = std.io.getStdErr().writer(); try stderr.print("LLVM failed to emit file: {s}\n", .{error_message}); return error.FailedToEmit; } diff --git a/src/libc_installation.zig b/src/libc_installation.zig index bc317869e8..05c85578ea 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -338,7 +338,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrinkAndFree(0); - try result_buf.outStream().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version }); + try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -384,7 +384,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrinkAndFree(0); - try result_buf.outStream().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir }); + try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -438,7 +438,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrinkAndFree(0); - const stream = result_buf.outStream(); + const stream = result_buf.writer(); try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { diff --git a/src/main.zig b/src/main.zig index cd89f269d0..7829901acc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -196,7 +196,7 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v return cmdInit(gpa, arena, cmd_args, .Lib); } else if (mem.eql(u8, cmd, "targets")) { const info = try detectNativeTargetInfo(arena, .{}); - const stdout = io.getStdOut().outStream(); + const stdout = io.getStdOut().writer(); return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); } else if (mem.eql(u8, cmd, "version")) { try std.io.getStdOut().writeAll(build_options.version ++ "\n"); @@ -1944,8 +1944,8 @@ fn buildOutputType( } } - const stdin = std.io.getStdIn().inStream(); - const stderr = std.io.getStdErr().outStream(); + const stdin = std.io.getStdIn().reader(); + const stderr = std.io.getStdErr().writer(); var repl_buf: [1024]u8 = undefined; while (watch) { @@ -2114,9 +2114,9 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi var zig_file = try o_dir.createFile(translated_zig_basename, .{}); defer zig_file.close(); - var bos = io.bufferedOutStream(zig_file.writer()); - _ = try std.zig.render(comp.gpa, bos.writer(), tree); - try bos.flush(); + var bw = io.bufferedWriter(zig_file.writer()); + _ = try std.zig.render(comp.gpa, bw.writer(), tree); + try bw.flush(); man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)}); @@ -2187,9 +2187,9 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { }; defer libc.deinit(gpa); - var bos = io.bufferedOutStream(io.getStdOut().writer()); - try libc.render(bos.writer()); - try bos.flush(); + var bw = io.bufferedWriter(io.getStdOut().writer()); + try libc.render(bw.writer()); + try bw.flush(); } } @@ -2570,7 +2570,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - const stdout = io.getStdOut().outStream(); + const stdout = io.getStdOut().writer(); try stdout.writeAll(usage_fmt); return cleanExit(); } else if (mem.eql(u8, arg, "--color")) { @@ -2600,7 +2600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { fatal("cannot use --stdin with positional arguments", .{}); } - const stdin = io.getStdIn().inStream(); + const stdin = io.getStdIn().reader(); const source_code = try stdin.readAllAlloc(gpa, max_src_size); defer gpa.free(source_code); @@ -2617,14 +2617,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { process.exit(1); } if (check_flag) { - const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree); + const anything_changed = try std.zig.render(gpa, io.null_writer, tree); const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); process.exit(code); } - var bos = io.bufferedOutStream(io.getStdOut().writer()); - _ = try std.zig.render(gpa, bos.writer(), tree); - try bos.flush(); + var bw = io.bufferedWriter(io.getStdOut().writer()); + _ = try std.zig.render(gpa, bw.writer(), tree); + try bw.flush(); return; } @@ -2774,7 +2774,7 @@ fn fmtPathFile( } if (check_mode) { - const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); + const anything_changed = try std.zig.render(fmt.gpa, io.null_writer, tree); if (anything_changed) { const stdout = io.getStdOut().writer(); try stdout.print("{s}\n", .{file_path}); @@ -2823,11 +2823,11 @@ fn printErrMsgToFile( var text_buf = std.ArrayList(u8).init(gpa); defer text_buf.deinit(); - const out_stream = text_buf.outStream(); - try parse_error.render(tree.token_ids, out_stream); + const writer = text_buf.writer(); + try parse_error.render(tree.token_ids, writer); const text = text_buf.items; - const stream = file.outStream(); + const stream = file.writer(); try stream.print("{s}:{d}:{d}: error: {s}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); if (!color_on) return; diff --git a/src/print_env.zig b/src/print_env.zig index bcf4a983ab..d62e1f62fd 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -20,10 +20,10 @@ pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: std.fs.File.Wri const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa); defer gpa.free(global_cache_dir); - var bos = std.io.bufferedOutStream(stdout); - const bos_stream = bos.outStream(); + var bw = std.io.bufferedWriter(stdout); + const w = bw.writer(); - var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream); + var jws = std.json.WriteStream(@TypeOf(w), 6).init(w); try jws.beginObject(); try jws.objectField("zig_exe"); @@ -42,6 +42,6 @@ pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: std.fs.File.Wri try jws.emitString(build_options.version); try jws.endObject(); - try bos_stream.writeByte('\n'); - try bos.flush(); + try w.writeByte('\n'); + try bw.flush(); } diff --git a/src/print_targets.zig b/src/print_targets.zig index cf55eee516..e24a2294a1 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -26,9 +26,9 @@ pub fn cmdTargets( const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle); defer glibc_abi.destroy(allocator); - var bos = io.bufferedOutStream(stdout); - const bos_stream = bos.outStream(); - var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream); + var bw = io.bufferedWriter(stdout); + const w = bw.writer(); + var jws = std.json.WriteStream(@TypeOf(w), 6).init(w); try jws.beginObject(); @@ -156,6 +156,6 @@ pub fn cmdTargets( try jws.endObject(); - try bos_stream.writeByte('\n'); - return bos.flush(); + try w.writeByte('\n'); + return bw.flush(); } diff --git a/src/test.zig b/src/test.zig index 682fb5078f..2553e57ff4 100644 --- a/src/test.zig +++ b/src/test.zig @@ -738,7 +738,7 @@ pub const TestContext = struct { write_node.activate(); var out_zir = std.ArrayList(u8).init(allocator); defer out_zir.deinit(); - try new_zir_module.writeToStream(allocator, out_zir.outStream()); + try new_zir_module.writeToStream(allocator, out_zir.writer()); write_node.end(); var test_node = update_node.start("assert", 0); diff --git a/src/translate_c.zig b/src/translate_c.zig index 9369c6d4b8..8df7963267 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -5268,7 +5268,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1); const start_index = c.source_buffer.items.len; - try c.source_buffer.outStream().print(format ++ " ", args); + try c.source_buffer.writer().print(format ++ " ", args); c.token_ids.appendAssumeCapacity(token_id); c.token_locs.appendAssumeCapacity(.{ diff --git a/src/zir.zig b/src/zir.zig index 63850d67db..b3d004f1dc 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -1116,7 +1116,7 @@ pub const Module = struct { /// This is a debugging utility for rendering the tree to stderr. pub fn dump(self: Module) void { - self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; + self.writeToStream(std.heap.page_allocator, std.io.getStdErr().writer()) catch {}; } const DeclAndIndex = struct { @@ -3254,7 +3254,7 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8 try write.inst_table.ensureCapacity(@intCast(u32, instructions.len)); - const stderr = std.io.getStdErr().outStream(); + const stderr = std.io.getStdErr().writer(); try stderr.print("{s} {s} {{ // unanalyzed\n", .{ kind, decl_name }); for (instructions) |inst| { diff --git a/test/compare_output.zig b/test/compare_output.zig index 9dc80f202d..a6e9835ea1 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -22,7 +22,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn main() void { \\ privateFunction(); - \\ const stdout = getStdOut().outStream(); + \\ const stdout = getStdOut().writer(); \\ stdout.print("OK 2\n", .{}) catch unreachable; \\} \\ @@ -37,7 +37,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\// purposefully conflicting function with main.zig \\// but it's private so it should be OK \\fn privateFunction() void { - \\ const stdout = getStdOut().outStream(); + \\ const stdout = getStdOut().writer(); \\ stdout.print("OK 1\n", .{}) catch unreachable; \\} \\ @@ -63,7 +63,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { tc.addSourceFile("foo.zig", \\usingnamespace @import("std").io; \\pub fn foo_function() void { - \\ const stdout = getStdOut().outStream(); + \\ const stdout = getStdOut().writer(); \\ stdout.print("OK\n", .{}) catch unreachable; \\} ); @@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn bar_function() void { \\ if (foo_function()) { - \\ const stdout = getStdOut().outStream(); + \\ const stdout = getStdOut().writer(); \\ stdout.print("OK\n", .{}) catch unreachable; \\ } \\} @@ -106,7 +106,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub const a_text = "OK\n"; \\ \\pub fn ok() void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print(b_text, .{}) catch unreachable; \\} ); @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\ \\pub fn main() void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", .{@as(u32, 12), @as(u16, 0x12), @as(u8, 'a')}) catch unreachable; \\} , "Hello, world!\n 12 12 a\n"); @@ -267,7 +267,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ var x_local : i32 = print_ok(x); \\} \\fn print_ok(val: @TypeOf(x)) @TypeOf(foo) { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("OK\n", .{}) catch unreachable; \\ return 0; \\} @@ -349,7 +349,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub fn main() void { \\ const bar = Bar {.field2 = 13,}; \\ const foo = Foo {.field1 = bar,}; - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ if (!foo.method()) { \\ stdout.print("BAD\n", .{}) catch unreachable; \\ } @@ -363,7 +363,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.add("defer with only fallthrough", \\const io = @import("std").io; \\pub fn main() void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("before\n", .{}) catch unreachable; \\ defer stdout.print("defer1\n", .{}) catch unreachable; \\ defer stdout.print("defer2\n", .{}) catch unreachable; @@ -376,7 +376,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\const os = @import("std").os; \\pub fn main() void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("before\n", .{}) catch unreachable; \\ defer stdout.print("defer1\n", .{}) catch unreachable; \\ defer stdout.print("defer2\n", .{}) catch unreachable; @@ -393,7 +393,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ do_test() catch return; \\} \\fn do_test() !void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("before\n", .{}) catch unreachable; \\ defer stdout.print("defer1\n", .{}) catch unreachable; \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; @@ -412,7 +412,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ do_test() catch return; \\} \\fn do_test() !void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print("before\n", .{}) catch unreachable; \\ defer stdout.print("defer1\n", .{}) catch unreachable; \\ errdefer stdout.print("deferErr\n", .{}) catch unreachable; @@ -429,7 +429,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\const io = @import("std").io; \\ \\pub fn main() void { - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ stdout.print(foo_txt, .{}) catch unreachable; \\} , "1234\nabcd\n"); @@ -448,7 +448,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn main() !void { \\ var args_it = std.process.args(); - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ var index: usize = 0; \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { @@ -487,7 +487,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ \\pub fn main() !void { \\ var args_it = std.process.args(); - \\ const stdout = io.getStdOut().outStream(); + \\ const stdout = io.getStdOut().writer(); \\ var index: usize = 0; \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { diff --git a/test/stage1/behavior/bugs/5487.zig b/test/stage1/behavior/bugs/5487.zig index 02fa677a44..bf530a8a02 100644 --- a/test/stage1/behavior/bugs/5487.zig +++ b/test/stage1/behavior/bugs/5487.zig @@ -3,10 +3,10 @@ const io = @import("std").io; pub fn write(_: void, bytes: []const u8) !usize { return 0; } -pub fn outStream() io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write) { - return io.OutStream(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write){ .context = {} }; +pub fn writer() io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write) { + return io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).Fn.return_type.?).ErrorUnion.error_set, write){ .context = {} }; } test "crash" { - _ = io.multiOutStream(.{outStream()}); + _ = io.multiWriter(.{writer()}); } diff --git a/test/tests.zig b/test/tests.zig index 5ee381e5c2..ec6d9e1df8 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -652,9 +652,9 @@ pub const StackTracesContext = struct { } child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); - const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; + const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; defer b.allocator.free(stdout); - const stderrFull = child.stderr.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; + const stderrFull = child.stderr.?.reader().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; defer b.allocator.free(stderrFull); var stderr = stderrFull; @@ -875,8 +875,8 @@ pub const CompileErrorContext = struct { var stdout_buf = ArrayList(u8).init(b.allocator); var stderr_buf = ArrayList(u8).init(b.allocator); - child.stdout.?.inStream().readAllArrayList(&stdout_buf, max_stdout_size) catch unreachable; - child.stderr.?.inStream().readAllArrayList(&stderr_buf, max_stdout_size) catch unreachable; + child.stdout.?.reader().readAllArrayList(&stdout_buf, max_stdout_size) catch unreachable; + child.stderr.?.reader().readAllArrayList(&stderr_buf, max_stdout_size) catch unreachable; const term = child.wait() catch |err| { debug.panic("Unable to spawn {s}: {s}\n", .{ zig_args.items[0], @errorName(err) }); diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index fd64bb062a..ad380e0139 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -388,8 +388,8 @@ pub fn main() anyerror!void { // "W" and "Wl,". So we sort this list in order of descending priority. std.sort.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan); - var stdout_bos = std.io.bufferedOutStream(std.io.getStdOut().outStream()); - const stdout = stdout_bos.outStream(); + var buffered_stdout = std.io.bufferedWriter(std.io.getStdOut().writer()); + const stdout = buffered_stdout.writer(); try stdout.writeAll( \\// This file is generated by tools/update_clang_options.zig. \\// zig fmt: off @@ -469,7 +469,7 @@ pub fn main() anyerror!void { \\ ); - try stdout_bos.flush(); + try buffered_stdout.flush(); } // TODO we should be able to import clang_options.zig but currently this is problematic because it will @@ -611,7 +611,7 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool { } fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { - file.outStream().print( + file.writer().print( \\Usage: {} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project \\ diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index 03b746f0ab..1e23bf0ff8 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -239,7 +239,7 @@ pub fn main() !void { const vers_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "vers.txt" }); const vers_txt_file = try fs.cwd().createFile(vers_txt_path, .{}); defer vers_txt_file.close(); - var buffered = std.io.bufferedOutStream(vers_txt_file.writer()); + var buffered = std.io.bufferedWriter(vers_txt_file.writer()); const vers_txt = buffered.writer(); for (global_ver_list) |name, i| { _ = global_ver_set.put(name, i) catch unreachable; @@ -251,7 +251,7 @@ pub fn main() !void { const fns_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "fns.txt" }); const fns_txt_file = try fs.cwd().createFile(fns_txt_path, .{}); defer fns_txt_file.close(); - var buffered = std.io.bufferedOutStream(fns_txt_file.writer()); + var buffered = std.io.bufferedWriter(fns_txt_file.writer()); const fns_txt = buffered.writer(); for (global_fn_list) |name, i| { const entry = global_fn_set.getEntry(name).?; @@ -282,7 +282,7 @@ pub fn main() !void { const abilist_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "abi.txt" }); const abilist_txt_file = try fs.cwd().createFile(abilist_txt_path, .{}); defer abilist_txt_file.close(); - var buffered = std.io.bufferedOutStream(abilist_txt_file.writer()); + var buffered = std.io.bufferedWriter(abilist_txt_file.writer()); const abilist_txt = buffered.writer(); // first iterate over the abi lists -- cgit v1.2.3 From 56c059077cdaf71220cb44f06902051a34ffd31d Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Fri, 8 Jan 2021 19:28:34 +0100 Subject: stage2: add initial impl of control flow in LLVM backend The following TZIR instrutions have been implemented in the backend: - all cmp operators (lt, lte, gt, gte, eq, neq) - block - br - condbr The following LLVMIR is generated for a simple assert function: ``` define void @assert(i1 %0) { Entry: %1 = alloca i1, align 1 store i1 %0, i1* %1, align 1 %2 = load i1, i1* %1, align 1 %3 = xor i1 %2, true br i1 %3, label %Then, label %Else Then: ; preds = %Entry call void @llvm.debugtrap() unreachable Else: ; preds = %Entry br label %Block Block: ; preds = %Else ret void } ``` See tests for more examples. --- src/codegen/llvm.zig | 177 +++++++++++++++++++++++++++++++++++------- src/codegen/llvm/bindings.zig | 34 ++++++++ test/stage2/llvm.zig | 71 +++++++++++++++++ 3 files changed, 256 insertions(+), 26 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index df97f7e0ef..8ec869a45e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5,6 +5,7 @@ const Compilation = @import("../Compilation.zig"); const llvm = @import("llvm/bindings.zig"); const link = @import("../link.zig"); const log = std.log.scoped(.codegen); +const math = std.math; const Module = @import("../Module.zig"); const TypedValue = @import("../TypedValue.zig"); @@ -154,6 +155,8 @@ pub const LLVMIRModule = struct { /// This stores the LLVM values used in a function, such that they can be /// referred to in other instructions. This table is cleared before every function is generated. + /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks + /// in here, however if a block ends, the instructions can be thrown away. func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. @@ -165,6 +168,18 @@ pub const LLVMIRModule = struct { /// to the top of the function. latest_alloca_inst: ?*const llvm.Value = null, + llvm_func: *const llvm.Value = undefined, + + /// This data structure is used to implement breaking to blocks. + blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { + parent_bb: *const llvm.BasicBlock, + break_bbs: *BreakBasicBlocks, + break_vals: *BreakValues, + }) = .{}, + + const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); + const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); + pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { const self = try allocator.create(LLVMIRModule); errdefer allocator.destroy(self); @@ -252,6 +267,8 @@ pub const LLVMIRModule = struct { self.func_inst_table.deinit(self.gpa); self.gpa.free(self.object_path); + self.blocks.deinit(self.gpa); + allocator.destroy(self); } @@ -349,32 +366,9 @@ pub const LLVMIRModule = struct { self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); self.builder.positionBuilderAtEnd(self.entry_block); self.latest_alloca_inst = null; + self.llvm_func = llvm_func; - const instructions = func.body.instructions; - for (instructions) |inst| { - const opt_llvm_val: ?*const llvm.Value = switch (inst.tag) { - .add => try self.genAdd(inst.castTag(.add).?), - .alloc => try self.genAlloc(inst.castTag(.alloc).?), - .arg => try self.genArg(inst.castTag(.arg).?), - .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), - .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), - .call => try self.genCall(inst.castTag(.call).?), - .intcast => try self.genIntCast(inst.castTag(.intcast).?), - .load => try self.genLoad(inst.castTag(.load).?), - .not => try self.genNot(inst.castTag(.not).?), - .ret => try self.genRet(inst.castTag(.ret).?), - .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), - .store => try self.genStore(inst.castTag(.store).?), - .sub => try self.genSub(inst.castTag(.sub).?), - .unreach => self.genUnreach(inst.castTag(.unreach).?), - .dbg_stmt => blk: { - // TODO: implement debug info - break :blk null; - }, - else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), - }; - if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); - } + try self.genBody(func.body); } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { _ = try self.resolveLLVMFunction(extern_fn.data, src); } else { @@ -382,6 +376,42 @@ pub const LLVMIRModule = struct { } } + fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { + for (body.instructions) |inst| { + const opt_value = switch (inst.tag) { + .add => try self.genAdd(inst.castTag(.add).?), + .alloc => try self.genAlloc(inst.castTag(.alloc).?), + .arg => try self.genArg(inst.castTag(.arg).?), + .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), + .block => try self.genBlock(inst.castTag(.block).?), + .br => try self.genBr(inst.castTag(.br).?), + .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .call => try self.genCall(inst.castTag(.call).?), + .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), + .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), + .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?, .gte), + .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?, .lt), + .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?, .lte), + .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), + .condbr => try self.genCondBr(inst.castTag(.condbr).?), + .intcast => try self.genIntCast(inst.castTag(.intcast).?), + .load => try self.genLoad(inst.castTag(.load).?), + .not => try self.genNot(inst.castTag(.not).?), + .ret => try self.genRet(inst.castTag(.ret).?), + .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), + .store => try self.genStore(inst.castTag(.store).?), + .sub => try self.genSub(inst.castTag(.sub).?), + .unreach => self.genUnreach(inst.castTag(.unreach).?), + .dbg_stmt => blk: { + // TODO: implement debug info + break :blk null; + }, + else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), + }; + if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); + } + } + fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { if (inst.func.value()) |func_value| { const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| @@ -436,6 +466,99 @@ pub const LLVMIRModule = struct { return null; } + fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + if (inst.base.ty.tag() != .bool) + return self.fail(inst.base.src, "TODO implement 'genCmp' for type {}", .{inst.base.ty}); + + const is_signed = inst.base.ty.isSignedInt(); + const operation = switch (op) { + .eq => .EQ, + .neq => .NE, + .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), + .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), + .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), + .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), + }; + + return self.builder.buildICmp(operation, lhs, rhs, ""); + } + + fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value { + const parent_bb = self.context.createBasicBlock("Block"); + + // 5 breaks to a block seems like a reasonable default. + var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); + var break_vals = try BreakValues.initCapacity(self.gpa, 5); + try self.blocks.putNoClobber(self.gpa, inst, .{ + .parent_bb = parent_bb, + .break_bbs = &break_bbs, + .break_vals = &break_vals, + }); + defer { + self.blocks.removeAssertDiscard(inst); + break_bbs.deinit(self.gpa); + break_vals.deinit(self.gpa); + } + + try self.genBody(inst.body); + + self.llvm_func.appendExistingBasicBlock(parent_bb); + self.builder.positionBuilderAtEnd(parent_bb); + + // If the block does not return a value, we dont have to create a phi node. + if (!inst.base.ty.hasCodeGenBits()) return null; + + const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), ""); + phi_node.addIncoming( + break_vals.items.ptr, + break_bbs.items.ptr, + @intCast(c_uint, break_vals.items.len), + ); + return phi_node; + } + + fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { + // Get the block that we want to break to. + var block = self.blocks.get(inst.block).?; + _ = self.builder.buildBr(block.parent_bb); + + // If the break doesn't break a value, then we don't have to add + // the values to the lists. + if (!inst.operand.ty.hasCodeGenBits()) return null; + + // For the phi node, we need the basic blocks and the values of the + // break instructions. + try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); + + const val = try self.resolveInst(inst.operand); + try block.break_vals.append(self.gpa, val); + + return null; + } + + fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value { + const condition_value = try self.resolveInst(inst.condition); + + const then_block = self.context.appendBasicBlock(self.llvm_func, "Then"); + const else_block = self.context.appendBasicBlock(self.llvm_func, "Else"); + { + const prev_block = self.builder.getInsertBlock(); + defer self.builder.positionBuilderAtEnd(prev_block); + + self.builder.positionBuilderAtEnd(then_block); + try self.genBody(inst.then_body); + + self.builder.positionBuilderAtEnd(else_block); + try self.genBody(inst.else_body); + } + _ = self.builder.buildCondBr(condition_value, then_block, else_block); + return null; + } + fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { return self.builder.buildNot(try self.resolveInst(inst.operand), ""); } @@ -509,6 +632,9 @@ pub const LLVMIRModule = struct { /// Use this instead of builder.buildAlloca, because this function makes sure to /// put the alloca instruction at the top of the function! fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { + const prev_block = self.builder.getInsertBlock(); + defer self.builder.positionBuilderAtEnd(prev_block); + if (self.latest_alloca_inst) |latest_alloc| { // builder.positionBuilder adds it before the instruction, // but we want to put it after the last alloca instruction. @@ -521,7 +647,6 @@ pub const LLVMIRModule = struct { self.builder.positionBuilder(self.entry_block, first_inst); } } - defer self.builder.positionBuilderAtEnd(self.entry_block); const val = self.builder.buildAlloca(t, ""); self.latest_alloca_inst = val; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index cd928877b1..8774260a08 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -24,6 +24,9 @@ pub const Context = opaque { pub const constString = LLVMConstStringInContext; extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; + pub const createBasicBlock = LLVMCreateBasicBlockInContext; + extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; + pub const appendBasicBlock = LLVMAppendBasicBlockInContext; extern fn LLVMAppendBasicBlockInContext(C: *const Context, Fn: *const Value, Name: [*:0]const u8) *const BasicBlock; @@ -38,6 +41,12 @@ pub const Value = opaque { pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; + pub const appendExistingBasicBlock = LLVMAppendExistingBasicBlock; + extern fn LLVMAppendExistingBasicBlock(Fn: *const Value, BB: *const BasicBlock) void; + + pub const addIncoming = LLVMAddIncoming; + extern fn LLVMAddIncoming(PhiNode: *const Value, IncomingValues: [*]*const Value, IncomingBlocks: [*]*const BasicBlock, Count: c_uint) void; + pub const getNextInstruction = LLVMGetNextInstruction; extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value; }; @@ -183,6 +192,31 @@ pub const Builder = opaque { pub const buildInBoundsGEP = LLVMBuildInBoundsGEP; extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value; + + pub const buildICmp = LLVMBuildICmp; + extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildBr = LLVMBuildBr; + extern fn LLVMBuildBr(*const Builder, Dest: *const BasicBlock) *const Value; + + pub const buildCondBr = LLVMBuildCondBr; + extern fn LLVMBuildCondBr(*const Builder, If: *const Value, Then: *const BasicBlock, Else: *const BasicBlock) *const Value; + + pub const buildPhi = LLVMBuildPhi; + extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; +}; + +pub const IntPredicate = extern enum { + EQ = 32, + NE = 33, + UGT = 34, + UGE = 35, + ULT = 36, + ULE = 37, + SGT = 38, + SGE = 39, + SLT = 40, + SLE = 41, }; pub const BasicBlock = opaque { diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index ebb5fc390f..eecea3d1f0 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -40,4 +40,75 @@ pub fn addCases(ctx: *TestContext) !void { \\} , "hello world!" ++ std.cstr.line_sep); } + + { + var case = ctx.exeUsingLlvmBackend("simple if statement", linux_x64); + + case.addCompareOutput( + \\fn add(a: i32, b: i32) i32 { + \\ return a + b; + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\ + \\export fn main() c_int { + \\ assert(add(1,2) == 3); + \\ return 0; + \\} + , ""); + } + + { + var case = ctx.exeUsingLlvmBackend("blocks", linux_x64); + + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\ + \\fn foo(ok: bool) i32 { + \\ const val: i32 = blk: { + \\ var x: i32 = 1; + \\ if (!ok) break :blk x + 9; + \\ break :blk x + 19; + \\ }; + \\ return val + 10; + \\} + \\ + \\export fn main() c_int { + \\ assert(foo(false) == 20); + \\ assert(foo(true) == 30); + \\ return 0; + \\} + , ""); + } + + { + var case = ctx.exeUsingLlvmBackend("nested blocks", linux_x64); + + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\ + \\fn foo(ok: bool) i32 { + \\ var val: i32 = blk: { + \\ const val2: i32 = another: { + \\ if (!ok) break :blk 10; + \\ break :another 10; + \\ }; + \\ break :blk val2 + 10; + \\ }; + \\ return val; + \\} + \\ + \\export fn main() c_int { + \\ assert(foo(false) == 10); + \\ assert(foo(true) == 20); + \\ return 0; + \\} + , ""); + } } -- cgit v1.2.3 From e1d8073d2fc0df6fbc5ce983312e3da374a9889b Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sun, 10 Jan 2021 17:36:01 +0100 Subject: stage2: add support for loops in LLVM backend A simple `while(true) {}` loop generates the following LLVMIR: ``` define i32 @main() { Entry: br label %Loop Loop: ; preds = %Loop, %Entry br label %Loop } ``` Also implement TZIR printing for loops and add a corresponding test. --- src/codegen/llvm.zig | 12 ++++++++++++ src/zir.zig | 21 +++++++++++++++++++-- test/stage2/llvm.zig | 21 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d6515f9ec6..5d753c41cb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -396,6 +396,7 @@ pub const LLVMIRModule = struct { .condbr => try self.genCondBr(inst.castTag(.condbr).?), .intcast => try self.genIntCast(inst.castTag(.intcast).?), .load => try self.genLoad(inst.castTag(.load).?), + .loop => try self.genLoop(inst.castTag(.loop).?), .not => try self.genNot(inst.castTag(.not).?), .ret => try self.genRet(inst.castTag(.ret).?), .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), @@ -559,6 +560,17 @@ pub const LLVMIRModule = struct { return null; } + fn genLoop(self: *LLVMIRModule, inst: *Inst.Loop) !?*const llvm.Value { + const loop_block = self.context.appendBasicBlock(self.llvm_func, "Loop"); + _ = self.builder.buildBr(loop_block); + + self.builder.positionBuilderAtEnd(loop_block); + try self.genBody(inst.body); + + _ = self.builder.buildBr(loop_block); + return null; + } + fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { return self.builder.buildNot(try self.resolveInst(inst.operand), ""); } diff --git a/src/zir.zig b/src/zir.zig index 4aee20991c..8d99218b3d 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -2011,11 +2011,15 @@ const DumpTzir = struct { try dtz.fetchInstsAndResolveConsts(condbr.else_body); }, + .loop => { + const loop = inst.castTag(.loop).?; + try dtz.fetchInstsAndResolveConsts(loop.body); + }, + // TODO fill out this debug printing .assembly, .call, .constant, - .loop, .varptr, .switchbr, => {}, @@ -2229,11 +2233,24 @@ const DumpTzir = struct { try writer.writeAll(")\n"); }, + .loop => { + const loop = inst.castTag(.loop).?; + + try writer.writeAll("\n"); + + const old_indent = dtz.indent; + dtz.indent += 2; + try dtz.dumpBody(loop.body, writer); + dtz.indent = old_indent; + + try writer.writeByteNTimes(' ', dtz.indent); + try writer.writeAll(")\n"); + }, + // TODO fill out this debug printing .assembly, .call, .constant, - .loop, .varptr, .switchbr, => { diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index eecea3d1f0..69622714a7 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -111,4 +111,25 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + + { + var case = ctx.exeUsingLlvmBackend("while loops", linux_x64); + + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\ + \\export fn main() c_int { + \\ var sum: u32 = 0; + \\ var i: u32 = 0; + \\ while (i < 5) : (i += 1) { + \\ sum += i; + \\ } + \\ assert(sum == 10); + \\ assert(i == 5); + \\ return 0; + \\} + , ""); + } } -- cgit v1.2.3 From 8c9ac4db978c80246b4872c899b1618b1b195ec2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jan 2021 22:51:01 -0700 Subject: stage2: implement error notes and regress -femit-zir * Implement error notes - note: other symbol exported here - note: previous else prong is here - note: previous '_' prong is here * Add Compilation.CObject.ErrorMsg. This object properly converts to AllErrors.Message when the time comes. * Add Compilation.CObject.failure_retryable. Properly handles out-of-memory and other transient failures. * Introduce Module.SrcLoc which has not only a byte offset but also references the file which the byte offset applies to. * Scope.Block now contains both a pointer to the "owner" Decl and the "source" Decl. As an example, during inline function call, the "owner" will be the Decl of the caller and the "source" will be the Decl of the callee. * Module.ErrorMsg now sports a `file_scope` field so that notes can refer to source locations in a file other than the parent error message. * Some instances where a `*Scope` was stored, now store a `*Scope.Container`. * Some methods in the `Scope` namespace were moved to the more specific type, since there was only an implementation for one particular tag. - `removeDecl` moved to `Scope.Container` - `destroy` moved to `Scope.File` * Two kinds of Scope deleted: - zir_module - decl * astgen: properly use DeclVal / DeclRef. DeclVal was incorrectly changed to be a reference; this commit fixes it. Fewer ZIR instructions processed as a result. - declval_in_module is renamed to declval - previous declval ZIR instruction is deleted; it was only for .zir files. * Test harness: friendlier diagnostics when an unexpected set of errors is encountered. * zir_sema: fix analyzeInstBlockFlat by properly calling resolvingInst on the last zir instruction in the block. Compile log implementation: * Write to a buffer rather than directly to stderr. * Only keep track of 1 callsite per Decl. * No longer mutate the ZIR Inst struct data. * "Compile log statement found" errors are only emitted when there are no other compile errors. -femit-zir and support for .zir source files is regressed. If we wanted to support this again, outputting .zir would need to be done as yet another backend rather than in the haphazard way it was previously implemented. For parsing .zir, it was implemented previously in a way that was not helpful for debugging. We need tighter integration with the test harness for it to be useful; so clearly a rewrite is needed. Given that a rewrite is needed, and it was getting in the way of progress and organization of the rest of stage2, I regressed the feature. --- src/Compilation.zig | 305 +++++--- src/Module.zig | 764 +++++++------------ src/astgen.zig | 77 +- src/codegen.zig | 166 ++-- src/codegen/c.zig | 7 +- src/codegen/llvm.zig | 13 +- src/link/Coff.zig | 6 +- src/link/Elf.zig | 33 +- src/link/MachO.zig | 8 +- src/link/MachO/DebugSymbols.zig | 27 +- src/main.zig | 47 +- src/test.zig | 212 +++--- src/type/Enum.zig | 2 +- src/type/Struct.zig | 2 +- src/type/Union.zig | 2 +- src/zir.zig | 1610 +-------------------------------------- src/zir_sema.zig | 179 ++--- test/stage2/test.zig | 62 +- 18 files changed, 904 insertions(+), 2618 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index 225a91e5d2..ad99e40541 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -51,7 +51,7 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. /// This data is accessed by multiple threads and is protected by `mutex`. -failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{}, +failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{}, keep_source_files_loaded: bool, use_clang: bool, @@ -215,13 +215,29 @@ pub const CObject = struct { }, /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects. failure, + /// A transient failure happened when trying to compile the C Object; it may + /// succeed if we try again. There may be a corresponding ErrorMsg in + /// Compilation.failed_c_objects. If there is not, the failure is out of memory. + failure_retryable, }, + pub const ErrorMsg = struct { + msg: []const u8, + line: u32, + column: u32, + + pub fn destroy(em: *ErrorMsg, gpa: *Allocator) void { + gpa.free(em.msg); + gpa.destroy(em); + em.* = undefined; + } + }; + /// Returns if there was failure. pub fn clearStatus(self: *CObject, gpa: *Allocator) bool { switch (self.status) { .new => return false, - .failure => { + .failure, .failure_retryable => { self.status = .new; return true; }, @@ -240,6 +256,11 @@ pub const CObject = struct { } }; +/// To support incremental compilation, errors are stored in various places +/// so that they can be created and destroyed appropriately. This structure +/// is used to collect all the errors from the various places into one +/// convenient place for API users to consume. It is allocated into 1 heap +/// and freed all at once. pub const AllErrors = struct { arena: std.heap.ArenaAllocator.State, list: []const Message, @@ -251,23 +272,32 @@ pub const AllErrors = struct { column: usize, byte_offset: usize, msg: []const u8, + notes: []Message = &.{}, }, plain: struct { msg: []const u8, }, - pub fn renderToStdErr(self: Message) void { - switch (self) { + pub fn renderToStdErr(msg: Message) void { + return msg.renderToStdErrInner("error"); + } + + fn renderToStdErrInner(msg: Message, kind: []const u8) void { + switch (msg) { .src => |src| { - std.debug.print("{s}:{d}:{d}: error: {s}\n", .{ + std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ src.src_path, src.line + 1, src.column + 1, + kind, src.msg, }); + for (src.notes) |note| { + note.renderToStdErrInner("note"); + } }, .plain => |plain| { - std.debug.print("error: {s}\n", .{plain.msg}); + std.debug.print("{s}: {s}\n", .{ kind, plain.msg }); }, } } @@ -278,20 +308,38 @@ pub const AllErrors = struct { } fn add( + module: *Module, arena: *std.heap.ArenaAllocator, errors: *std.ArrayList(Message), - sub_file_path: []const u8, - source: []const u8, - simple_err_msg: ErrorMsg, + module_err_msg: Module.ErrorMsg, ) !void { - const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); + const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len); + for (notes) |*note, i| { + const module_note = module_err_msg.notes[i]; + const source = try module_note.src_loc.file_scope.getSource(module); + const loc = std.zig.findLineColumn(source, module_note.src_loc.byte_offset); + const sub_file_path = module_note.src_loc.file_scope.sub_file_path; + note.* = .{ + .src = .{ + .src_path = try arena.allocator.dupe(u8, sub_file_path), + .msg = try arena.allocator.dupe(u8, module_note.msg), + .byte_offset = module_note.src_loc.byte_offset, + .line = loc.line, + .column = loc.column, + }, + }; + } + const source = try module_err_msg.src_loc.file_scope.getSource(module); + const loc = std.zig.findLineColumn(source, module_err_msg.src_loc.byte_offset); + const sub_file_path = module_err_msg.src_loc.file_scope.sub_file_path; try errors.append(.{ .src = .{ .src_path = try arena.allocator.dupe(u8, sub_file_path), - .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), - .byte_offset = simple_err_msg.byte_offset, + .msg = try arena.allocator.dupe(u8, module_err_msg.msg), + .byte_offset = module_err_msg.src_loc.byte_offset, .line = loc.line, .column = loc.column, + .notes = notes, }, }); } @@ -849,17 +897,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .ty = struct_ty, }, }; - break :rs &root_scope.base; + break :rs root_scope; } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { - const root_scope = try gpa.create(Module.Scope.ZIRModule); - root_scope.* = .{ - .sub_file_path = root_pkg.root_src_path, - .source = .{ .unloaded = {} }, - .contents = .{ .not_available = {} }, - .status = .never_loaded, - .decls = .{}, - }; - break :rs &root_scope.base; + return error.ZirFilesUnsupported; } else { unreachable; } @@ -1258,32 +1298,23 @@ pub fn update(self: *Compilation) !void { const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm; if (!use_stage1) { if (self.bin_file.options.module) |module| { + module.compile_log_text.shrinkAndFree(module.gpa, 0); module.generation += 1; // TODO Detect which source files changed. // Until then we simulate a full cache miss. Source files could have been loaded for any reason; // to force a refresh we unload now. - if (module.root_scope.cast(Module.Scope.File)) |zig_file| { - zig_file.unload(module.gpa); - module.failed_root_src_file = null; - module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { - error.AnalysisFail => { - assert(self.totalErrorCount() != 0); - }, - error.OutOfMemory => return error.OutOfMemory, - else => |e| { - module.failed_root_src_file = e; - }, - }; - } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { - zir_module.unload(module.gpa); - module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { - error.AnalysisFail => { - assert(self.totalErrorCount() != 0); - }, - else => |e| return e, - }; - } + module.root_scope.unload(module.gpa); + module.failed_root_src_file = null; + module.analyzeContainer(&module.root_scope.root_container) catch |err| switch (err) { + error.AnalysisFail => { + assert(self.totalErrorCount() != 0); + }, + error.OutOfMemory => return error.OutOfMemory, + else => |e| { + module.failed_root_src_file = e; + }, + }; // TODO only analyze imports if they are still referenced for (module.import_table.items()) |entry| { @@ -1359,14 +1390,18 @@ pub fn totalErrorCount(self: *Compilation) usize { module.failed_exports.items().len + module.failed_files.items().len + @boolToInt(module.failed_root_src_file != null); - for (module.compile_log_decls.items()) |entry| { - total += entry.value.items.len; - } } // The "no entry point found" error only counts if there are no other errors. if (total == 0) { - return @boolToInt(self.link_error_flags.no_entry_point_found); + total += @boolToInt(self.link_error_flags.no_entry_point_found); + } + + // Compile log errors only count if there are no other errors. + if (total == 0) { + if (self.bin_file.options.module) |module| { + total += @boolToInt(module.compile_log_decls.items().len != 0); + } } return total; @@ -1382,32 +1417,32 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { for (self.failed_c_objects.items()) |entry| { const c_object = entry.key; const err_msg = entry.value; - try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*); + // TODO these fields will need to be adjusted when we have proper + // C error reporting bubbling up. + try errors.append(.{ + .src = .{ + .src_path = try arena.allocator.dupe(u8, c_object.src.src_path), + .msg = try std.fmt.allocPrint(&arena.allocator, "unable to build C object: {s}", .{ + err_msg.msg, + }), + .byte_offset = 0, + .line = err_msg.line, + .column = err_msg.column, + }, + }); } if (self.bin_file.options.module) |module| { for (module.failed_files.items()) |entry| { - const scope = entry.key; - const err_msg = entry.value; - const source = try scope.getSource(module); - try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); + try AllErrors.add(module, &arena, &errors, entry.value.*); } for (module.failed_decls.items()) |entry| { - const decl = entry.key; - const err_msg = entry.value; - const source = try decl.scope.getSource(module); - try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + try AllErrors.add(module, &arena, &errors, entry.value.*); } for (module.emit_h_failed_decls.items()) |entry| { - const decl = entry.key; - const err_msg = entry.value; - const source = try decl.scope.getSource(module); - try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + try AllErrors.add(module, &arena, &errors, entry.value.*); } for (module.failed_exports.items()) |entry| { - const decl = entry.key.owner_decl; - const err_msg = entry.value; - const source = try decl.scope.getSource(module); - try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + try AllErrors.add(module, &arena, &errors, entry.value.*); } if (module.failed_root_src_file) |err| { const file_path = try module.root_pkg.root_src_directory.join(&arena.allocator, &[_][]const u8{ @@ -1418,15 +1453,6 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { }); try AllErrors.addPlain(&arena, &errors, msg); } - for (module.compile_log_decls.items()) |entry| { - const decl = entry.key; - const path = decl.scope.subFilePath(); - const source = try decl.scope.getSource(module); - for (entry.value.items) |src_loc| { - const err_msg = ErrorMsg{ .byte_offset = src_loc, .msg = "found compile log statement" }; - try AllErrors.add(&arena, &errors, path, source, err_msg); - } - } } if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { @@ -1437,6 +1463,28 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { }); } + if (self.bin_file.options.module) |module| { + const compile_log_items = module.compile_log_decls.items(); + if (errors.items.len == 0 and compile_log_items.len != 0) { + // First one will be the error; subsequent ones will be notes. + const err_msg = Module.ErrorMsg{ + .src_loc = compile_log_items[0].value, + .msg = "found compile log statement", + .notes = try self.gpa.alloc(Module.ErrorMsg, compile_log_items.len - 1), + }; + defer self.gpa.free(err_msg.notes); + + for (compile_log_items[1..]) |entry, i| { + err_msg.notes[i] = .{ + .src_loc = entry.value, + .msg = "also here", + }; + } + + try AllErrors.add(module, &arena, &errors, err_msg); + } + } + assert(errors.items.len == self.totalErrorCount()); return AllErrors{ @@ -1445,6 +1493,11 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { }; } +pub fn getCompileLogOutput(self: *Compilation) []const u8 { + const module = self.bin_file.options.module orelse return &[0]u8{}; + return module.compile_log_text.items; +} + pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { var progress: std.Progress = .{}; var main_progress_node = try progress.start("", 0); @@ -1517,9 +1570,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }, else => { try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( module.gpa, - decl.src(), + decl.srcLoc(), "unable to codegen: {s}", .{@errorName(err)}, )); @@ -1586,9 +1639,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor const module = self.bin_file.options.module.?; self.bin_file.updateDeclLineNumber(module, decl) catch |err| { try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( module.gpa, - decl.src(), + decl.srcLoc(), "unable to update line number: {s}", .{@errorName(err)}, )); @@ -1858,26 +1911,38 @@ fn workerUpdateCObject( comp.updateCObject(c_object, progress_node) catch |err| switch (err) { error.AnalysisFail => return, else => { - { - const lock = comp.mutex.acquire(); - defer lock.release(); - comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1) catch { - fatal("TODO handle this by setting c_object.status = oom failure", .{}); - }; - comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, ErrorMsg.create( - comp.gpa, - 0, - "unable to build C object: {s}", - .{@errorName(err)}, - ) catch { - fatal("TODO handle this by setting c_object.status = oom failure", .{}); - }); - } - c_object.status = .{ .failure = {} }; + comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) { + // Swallowing this error is OK because it's implied to be OOM when + // there is a missing failed_c_objects error message. + error.OutOfMemory => {}, + }; }, }; } +fn reportRetryableCObjectError( + comp: *Compilation, + c_object: *CObject, + err: anyerror, +) error{OutOfMemory}!void { + c_object.status = .failure_retryable; + + const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg); + errdefer comp.gpa.destroy(c_obj_err_msg); + const msg = try std.fmt.allocPrint(comp.gpa, "unable to build C object: {s}", .{@errorName(err)}); + errdefer comp.gpa.free(msg); + c_obj_err_msg.* = .{ + .msg = msg, + .line = 0, + .column = 0, + }; + { + const lock = comp.mutex.acquire(); + defer lock.release(); + try comp.failed_c_objects.putNoClobber(comp.gpa, c_object, c_obj_err_msg); + } +} + fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); @@ -1892,7 +1957,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * // There was previous failure. const lock = comp.mutex.acquire(); defer lock.release(); - comp.failed_c_objects.removeAssertDiscard(c_object); + // If the failure was OOM, there will not be an entry here, so we do + // not assert discard. + _ = comp.failed_c_objects.swapRemove(c_object); } var man = comp.obtainCObjectCacheManifest(); @@ -2343,11 +2410,27 @@ pub fn addCCArgs( fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError { @setCold(true); - const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args); + const err_msg = blk: { + const msg = try std.fmt.allocPrint(comp.gpa, format, args); + errdefer comp.gpa.free(msg); + const err_msg = try comp.gpa.create(CObject.ErrorMsg); + errdefer comp.gpa.destroy(err_msg); + err_msg.* = .{ + .msg = msg, + .line = 0, + .column = 0, + }; + break :blk err_msg; + }; return comp.failCObjWithOwnedErrorMsg(c_object, err_msg); } -fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError { +fn failCObjWithOwnedErrorMsg( + comp: *Compilation, + c_object: *CObject, + err_msg: *CObject.ErrorMsg, +) InnerError { + @setCold(true); { const lock = comp.mutex.acquire(); defer lock.release(); @@ -2361,36 +2444,6 @@ fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *E return error.AnalysisFail; } -pub const ErrorMsg = struct { - byte_offset: usize, - msg: []const u8, - - pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg { - const self = try gpa.create(ErrorMsg); - errdefer gpa.destroy(self); - self.* = try init(gpa, byte_offset, format, args); - return self; - } - - /// Assumes the ErrorMsg struct and msg were both allocated with allocator. - pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { - self.deinit(gpa); - gpa.destroy(self); - } - - pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg { - return ErrorMsg{ - .byte_offset = byte_offset, - .msg = try std.fmt.allocPrint(gpa, format, args), - }; - } - - pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { - gpa.free(self.msg); - self.* = undefined; - } -}; - pub const FileExt = enum { c, cpp, diff --git a/src/Module.zig b/src/Module.zig index 0bdeab68d0..e612f8f759 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -35,8 +35,7 @@ zig_cache_artifact_directory: Compilation.Directory, /// Pointer to externally managed resource. `null` if there is no zig file being compiled. root_pkg: *Package, /// Module owns this resource. -/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. -root_scope: *Scope, +root_scope: *Scope.File, /// It's rare for a decl to be exported, so we save memory by having a sparse map of /// Decl pointers to details about them being exported. /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. @@ -57,19 +56,19 @@ decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_has /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. /// Note that a Decl can succeed but the Fn it represents can fail. In this case, /// a Decl can have a failed_decls entry but have analysis status of success. -failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, +failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, /// When emit_h is non-null, each Decl gets one more compile error slot for /// emit-h failing for that Decl. This table is also how we tell if a Decl has /// failed emit-h or succeeded. -emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, -/// A Decl can have multiple compileLogs, but only one error, so we map a Decl to a the src locs of all the compileLogs -compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, ArrayListUnmanaged(usize)) = .{}, +emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, +/// Keep track of one `@compileLog` callsite per owner Decl. +compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{}, /// Using a map here for consistency with the other fields here. /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. -failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, +failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, /// Using a map here for consistency with the other fields here. /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. -failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{}, +failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, next_anon_name_index: usize = 0, @@ -103,6 +102,8 @@ stage1_flags: packed struct { emit_h: ?Compilation.EmitLoc, +compile_log_text: std.ArrayListUnmanaged(u8) = .{}, + pub const Export = struct { options: std.builtin.ExportOptions, /// Byte offset into the file that contains the export directive. @@ -138,9 +139,9 @@ pub const Decl = struct { /// mapping them to an address in the output file. /// Memory owned by this decl, using Module's allocator. name: [*:0]const u8, - /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`. + /// The direct parent container of the Decl. /// Reference to externally owned memory. - scope: *Scope, + container: *Scope.Container, /// The AST Node decl index or ZIR Inst index that contains this declaration. /// Must be recomputed when the corresponding source file is modified. src_index: usize, @@ -235,31 +236,21 @@ pub const Decl = struct { } } + pub fn srcLoc(self: Decl) SrcLoc { + return .{ + .byte_offset = self.src(), + .file_scope = self.getFileScope(), + }; + } + pub fn src(self: Decl) usize { - switch (self.scope.tag) { - .container => { - const container = @fieldParentPtr(Scope.Container, "base", self.scope); - const tree = container.file_scope.contents.tree; - // TODO Container should have its own decls() - const decl_node = tree.root_node.decls()[self.src_index]; - return tree.token_locs[decl_node.firstToken()].start; - }, - .zir_module => { - const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope); - const module = zir_module.contents.module; - const src_decl = module.decls[self.src_index]; - return src_decl.inst.src; - }, - .file, .block => unreachable, - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .decl => unreachable, - } + const tree = self.container.file_scope.contents.tree; + const decl_node = tree.root_node.decls()[self.src_index]; + return tree.token_locs[decl_node.firstToken()].start; } pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { - return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name)); + return self.container.fullyQualifiedNameHash(mem.spanZ(self.name)); } pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue { @@ -293,9 +284,8 @@ pub const Decl = struct { } } - /// Asserts that the `Decl` is part of AST and not ZIRModule. - pub fn getFileScope(self: *Decl) *Scope.File { - return self.scope.cast(Scope.Container).?.file_scope; + pub fn getFileScope(self: Decl) *Scope.File { + return self.container.file_scope; } pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { @@ -326,7 +316,7 @@ pub const Fn = struct { /// Contains un-analyzed ZIR instructions generated from Zig source AST. /// Even after we finish analysis, the ZIR is kept in memory, so that /// comptime and inline function calls can happen. - zir: zir.Module.Body, + zir: zir.Body, /// undefined unless analysis state is `success`. body: Body, state: Analysis, @@ -373,47 +363,49 @@ pub const Scope = struct { return @fieldParentPtr(T, "base", base); } - /// Asserts the scope has a parent which is a DeclAnalysis and - /// returns the arena Allocator. + /// Returns the arena Allocator associated with the Decl of the Scope. pub fn arena(self: *Scope) *Allocator { switch (self.tag) { .block => return self.cast(Block).?.arena, - .decl => return &self.cast(DeclAnalysis).?.arena.allocator, .gen_zir => return self.cast(GenZIR).?.arena, .local_val => return self.cast(LocalVal).?.gen_zir.arena, .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, - .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, .file => unreachable, .container => unreachable, } } - /// If the scope has a parent which is a `DeclAnalysis`, - /// returns the `Decl`, otherwise returns `null`. - pub fn decl(self: *Scope) ?*Decl { + pub fn ownerDecl(self: *Scope) ?*Decl { + return switch (self.tag) { + .block => self.cast(Block).?.owner_decl, + .gen_zir => self.cast(GenZIR).?.decl, + .local_val => self.cast(LocalVal).?.gen_zir.decl, + .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .file => null, + .container => null, + }; + } + + pub fn srcDecl(self: *Scope) ?*Decl { return switch (self.tag) { - .block => self.cast(Block).?.decl, + .block => self.cast(Block).?.src_decl, .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, - .decl => self.cast(DeclAnalysis).?.decl, - .zir_module => null, .file => null, .container => null, }; } - /// Asserts the scope has a parent which is a ZIRModule or Container and - /// returns it. - pub fn namespace(self: *Scope) *Scope { + /// Asserts the scope has a parent which is a Container and returns it. + pub fn namespace(self: *Scope) *Container { switch (self.tag) { - .block => return self.cast(Block).?.decl.scope, - .gen_zir => return self.cast(GenZIR).?.decl.scope, - .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope, - .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope, - .decl => return self.cast(DeclAnalysis).?.decl.scope, - .file => return &self.cast(File).?.root_container.base, - .zir_module, .container => return self, + .block => return self.cast(Block).?.owner_decl.container, + .gen_zir => return self.cast(GenZIR).?.decl.container, + .local_val => return self.cast(LocalVal).?.gen_zir.decl.container, + .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container, + .file => return &self.cast(File).?.root_container, + .container => return self.cast(Container).?, } } @@ -426,9 +418,7 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, - .decl => unreachable, .file => unreachable, - .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), .container => return self.cast(Container).?.fullyQualifiedNameHash(name), } } @@ -437,12 +427,10 @@ pub const Scope = struct { pub fn tree(self: *Scope) *ast.Tree { switch (self.tag) { .file => return self.cast(File).?.contents.tree, - .zir_module => unreachable, - .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, - .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, - .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, - .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, - .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree, + .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree, + .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.tree, + .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.contents.tree, + .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.contents.tree, .container => return self.cast(Container).?.file_scope.contents.tree, } } @@ -454,38 +442,21 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?, .local_val => return self.cast(LocalVal).?.gen_zir, .local_ptr => return self.cast(LocalPtr).?.gen_zir, - .decl => unreachable, - .zir_module => unreachable, .file => unreachable, .container => unreachable, }; } - /// Asserts the scope has a parent which is a ZIRModule, Container or File and + /// Asserts the scope has a parent which is a Container or File and /// returns the sub_file_path field. pub fn subFilePath(base: *Scope) []const u8 { switch (base.tag) { .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, .file => return @fieldParentPtr(File, "base", base).sub_file_path, - .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, .block => unreachable, .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, - .decl => unreachable, - } - } - - pub fn unload(base: *Scope, gpa: *Allocator) void { - switch (base.tag) { - .file => return @fieldParentPtr(File, "base", base).unload(gpa), - .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), - .block => unreachable, - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .decl => unreachable, - .container => unreachable, } } @@ -493,67 +464,28 @@ pub const Scope = struct { switch (base.tag) { .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), .file => return @fieldParentPtr(File, "base", base).getSource(module), - .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, .block => unreachable, - .decl => unreachable, } } + /// When called from inside a Block Scope, chases the src_decl, not the owner_decl. pub fn getFileScope(base: *Scope) *Scope.File { var cur = base; while (true) { cur = switch (cur.tag) { .container => return @fieldParentPtr(Container, "base", cur).file_scope, .file => return @fieldParentPtr(File, "base", cur), - .zir_module => unreachable, // TODO are zir modules allowed to import packages? .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, - .block => @fieldParentPtr(Block, "base", cur).decl.scope, - .decl => @fieldParentPtr(DeclAnalysis, "base", cur).decl.scope, + .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, }; } } - /// Asserts the scope is a namespace Scope and removes the Decl from the namespace. - pub fn removeDecl(base: *Scope, child: *Decl) void { - switch (base.tag) { - .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), - .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), - .file => unreachable, - .block => unreachable, - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .decl => unreachable, - } - } - - /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it. - pub fn destroy(base: *Scope, gpa: *Allocator) void { - switch (base.tag) { - .file => { - const scope_file = @fieldParentPtr(File, "base", base); - scope_file.deinit(gpa); - gpa.destroy(scope_file); - }, - .zir_module => { - const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base); - scope_zir_module.deinit(gpa); - gpa.destroy(scope_zir_module); - }, - .block => unreachable, - .gen_zir => unreachable, - .local_val => unreachable, - .local_ptr => unreachable, - .decl => unreachable, - .container => unreachable, - } - } - fn name_hash_hash(x: NameHash) u32 { return @truncate(u32, @bitCast(u128, x)); } @@ -563,14 +495,11 @@ pub const Scope = struct { } pub const Tag = enum { - /// .zir source code. - zir_module, /// .zig source code. file, /// struct, enum or union, every .file contains one of these. container, block, - decl, gen_zir, local_val, local_ptr, @@ -657,6 +586,11 @@ pub const Scope = struct { self.* = undefined; } + pub fn destroy(self: *File, gpa: *Allocator) void { + self.deinit(gpa); + gpa.destroy(self); + } + pub fn dumpSrc(self: *File, src: usize) void { const loc = std.zig.findLineColumn(self.source.bytes, src); std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); @@ -681,109 +615,6 @@ pub const Scope = struct { } }; - pub const ZIRModule = struct { - pub const base_tag: Tag = .zir_module; - base: Scope = Scope{ .tag = base_tag }, - /// Relative to the owning package's root_src_dir. - /// Reference to external memory, not owned by ZIRModule. - sub_file_path: []const u8, - source: union(enum) { - unloaded: void, - bytes: [:0]const u8, - }, - contents: union { - not_available: void, - module: *zir.Module, - }, - status: enum { - never_loaded, - unloaded_success, - unloaded_parse_failure, - unloaded_sema_failure, - - loaded_sema_failure, - loaded_success, - }, - - /// Even though .zir files only have 1 module, this set is still needed - /// because of anonymous Decls, which can exist in the global set, but - /// not this one. - decls: ArrayListUnmanaged(*Decl), - - pub fn unload(self: *ZIRModule, gpa: *Allocator) void { - switch (self.status) { - .never_loaded, - .unloaded_parse_failure, - .unloaded_sema_failure, - .unloaded_success, - => {}, - - .loaded_success => { - self.contents.module.deinit(gpa); - gpa.destroy(self.contents.module); - self.contents = .{ .not_available = {} }; - self.status = .unloaded_success; - }, - .loaded_sema_failure => { - self.contents.module.deinit(gpa); - gpa.destroy(self.contents.module); - self.contents = .{ .not_available = {} }; - self.status = .unloaded_sema_failure; - }, - } - switch (self.source) { - .bytes => |bytes| { - gpa.free(bytes); - self.source = .{ .unloaded = {} }; - }, - .unloaded => {}, - } - } - - pub fn deinit(self: *ZIRModule, gpa: *Allocator) void { - self.decls.deinit(gpa); - self.unload(gpa); - self.* = undefined; - } - - pub fn removeDecl(self: *ZIRModule, child: *Decl) void { - for (self.decls.items) |item, i| { - if (item == child) { - _ = self.decls.swapRemove(i); - return; - } - } - } - - pub fn dumpSrc(self: *ZIRModule, src: usize) void { - const loc = std.zig.findLineColumn(self.source.bytes, src); - std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); - } - - pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { - switch (self.source) { - .unloaded => { - const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions( - module.gpa, - self.sub_file_path, - std.math.maxInt(u32), - null, - 1, - 0, - ); - self.source = .{ .bytes = source }; - return source; - }, - .bytes => |bytes| return bytes, - } - } - - pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash { - // ZIR modules only have 1 file with all decls global in the same namespace. - return std.zig.hashSrc(name); - } - }; - /// This is a temporary structure, references to it are valid only /// during semantic analysis of the block. pub const Block = struct { @@ -794,9 +625,14 @@ pub const Scope = struct { /// Maps ZIR to TZIR. Shared to sub-blocks. inst_table: *InstTable, func: ?*Fn, - decl: *Decl, + /// When analyzing an inline function call, owner_decl is the Decl of the caller + /// and src_decl is the Decl of the callee. + /// This Decl owns the arena memory of this Block. + owner_decl: *Decl, + /// This Decl is the Decl according to the Zig source code corresponding to this Block. + src_decl: *Decl, instructions: ArrayListUnmanaged(*Inst), - /// Points to the arena allocator of DeclAnalysis + /// Points to the arena allocator of the Decl. arena: *Allocator, label: ?Label = null, inlining: ?*Inlining, @@ -845,21 +681,12 @@ pub const Scope = struct { } }; - /// This is a temporary structure, references to it are valid only - /// during semantic analysis of the decl. - pub const DeclAnalysis = struct { - pub const base_tag: Tag = .decl; - base: Scope = Scope{ .tag = base_tag }, - decl: *Decl, - arena: std.heap.ArenaAllocator, - }; - /// This is a temporary structure, references to it are valid only /// during semantic analysis of the decl. pub const GenZIR = struct { pub const base_tag: Tag = .gen_zir; base: Scope = Scope{ .tag = base_tag }, - /// Parents can be: `GenZIR`, `ZIRModule`, `File` + /// Parents can be: `GenZIR`, `File` parent: *Scope, decl: *Decl, arena: *Allocator, @@ -905,11 +732,73 @@ pub const Scope = struct { }; }; +/// This struct holds data necessary to construct API-facing `AllErrors.Message`. +/// Its memory is managed with the general purpose allocator so that they +/// can be created and destroyed in response to incremental updates. +/// In some cases, the Scope.File could have been inferred from where the ErrorMsg +/// is stored. For example, if it is stored in Module.failed_decls, then the Scope.File +/// would be determined by the Decl Scope. However, the data structure contains the field +/// anyway so that `ErrorMsg` can be reused for error notes, which may be in a different +/// file than the parent error message. It also simplifies processing of error messages. +pub const ErrorMsg = struct { + src_loc: SrcLoc, + msg: []const u8, + notes: []ErrorMsg = &.{}, + + pub fn create( + gpa: *Allocator, + src_loc: SrcLoc, + comptime format: []const u8, + args: anytype, + ) !*ErrorMsg { + const self = try gpa.create(ErrorMsg); + errdefer gpa.destroy(self); + self.* = try init(gpa, src_loc, format, args); + return self; + } + + /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, + /// as well as all notes. + pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { + self.deinit(gpa); + gpa.destroy(self); + } + + pub fn init( + gpa: *Allocator, + src_loc: SrcLoc, + comptime format: []const u8, + args: anytype, + ) !ErrorMsg { + return ErrorMsg{ + .src_loc = src_loc, + .msg = try std.fmt.allocPrint(gpa, format, args), + }; + } + + pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { + for (self.notes) |*note| { + note.deinit(gpa); + } + gpa.free(self.notes); + gpa.free(self.msg); + self.* = undefined; + } +}; + +/// Canonical reference to a position within a source file. +pub const SrcLoc = struct { + file_scope: *Scope.File, + byte_offset: usize, +}; + pub const InnerError = error{ OutOfMemory, AnalysisFail }; pub fn deinit(self: *Module) void { const gpa = self.gpa; + self.compile_log_text.deinit(gpa); + self.zig_cache_artifact_directory.handle.close(); self.deletion_set.deinit(gpa); @@ -939,9 +828,6 @@ pub fn deinit(self: *Module) void { } self.failed_exports.deinit(gpa); - for (self.compile_log_decls.items()) |*entry| { - entry.value.deinit(gpa); - } self.compile_log_decls.deinit(gpa); for (self.decl_exports.items()) |entry| { @@ -965,7 +851,7 @@ pub fn deinit(self: *Module) void { self.global_error_set.deinit(gpa); for (self.import_table.items()) |entry| { - entry.value.base.destroy(gpa); + entry.value.destroy(gpa); } self.import_table.deinit(gpa); } @@ -978,7 +864,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void { gpa.free(export_list); } -pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { +pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -999,7 +885,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. - self.deleteDeclExports(decl); + mod.deleteDeclExports(decl); // Dependencies will be re-discovered, so we remove them here prior to re-analysis. for (decl.dependencies.items()) |entry| { const dep = entry.key; @@ -1008,7 +894,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { // We don't perform a deletion here, because this Decl or another one // may end up referencing it before the update is complete. dep.deletion_flag = true; - try self.deletion_set.append(self.gpa, dep); + try mod.deletion_set.append(mod.gpa, dep); } } decl.dependencies.clearRetainingCapacity(); @@ -1019,24 +905,21 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { .unreferenced => false, }; - const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| - try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index]) - else - self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => return error.AnalysisFail, - else => { - try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); - self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( - self.gpa, - decl.src(), - "unable to analyze: {s}", - .{@errorName(err)}, - )); - decl.analysis = .sema_failure_retryable; - return error.AnalysisFail; - }, - }; + const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => return error.AnalysisFail, + else => { + decl.analysis = .sema_failure_retryable; + try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1); + mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + mod.gpa, + decl.srcLoc(), + "unable to analyze: {s}", + .{@errorName(err)}, + )); + return error.AnalysisFail; + }, + }; if (subsequent_analysis) { // We may need to chase the dependants and re-analyze them. @@ -1055,8 +938,8 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { .codegen_failure, .codegen_failure_retryable, .complete, - => if (dep.generation != self.generation) { - try self.markOutdatedDecl(dep); + => if (dep.generation != mod.generation) { + try mod.markOutdatedDecl(dep); }, } } @@ -1068,8 +951,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const tracy = trace(@src()); defer tracy.end(); - const container_scope = decl.scope.cast(Scope.Container).?; - const tree = try self.getAstTree(container_scope.file_scope); + const tree = try self.getAstTree(decl.container.file_scope); const ast_node = tree.root_node.decls()[decl.src_index]; switch (ast_node.tag) { .FnProto => { @@ -1085,7 +967,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var fn_type_scope: Scope.GenZIR = .{ .decl = decl, .arena = &fn_type_scope_arena.allocator, - .parent = decl.scope, + .parent = &decl.container.base, }; defer fn_type_scope.instructions.deinit(self.gpa); @@ -1197,7 +1079,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .parent = null, .inst_table = &inst_table, .func = null, - .decl = decl, + .owner_decl = decl, + .src_decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, .inlining = null, @@ -1242,12 +1125,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); - const fn_zir: zir.Module.Body = blk: { + const fn_zir: zir.Body = blk: { // We put the ZIR inside the Decl arena. var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &decl_arena.allocator, - .parent = decl.scope, + .parent = &decl.container.base, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1400,7 +1283,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .parent = null, .inst_table = &decl_inst_table, .func = null, - .decl = decl, + .owner_decl = decl, + .src_decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, .inlining = null, @@ -1444,7 +1328,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &gen_scope_arena.allocator, - .parent = decl.scope, + .parent = &decl.container.base, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1472,7 +1356,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .parent = null, .inst_table = &var_inst_table, .func = null, - .decl = decl, + .owner_decl = decl, + .src_decl = decl, .instructions = .{}, .arena = &gen_scope_arena.allocator, .inlining = null, @@ -1503,7 +1388,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var type_scope: Scope.GenZIR = .{ .decl = decl, .arena = &type_scope_arena.allocator, - .parent = decl.scope, + .parent = &decl.container.base, }; defer type_scope.instructions.deinit(self.gpa); @@ -1584,7 +1469,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &analysis_arena.allocator, - .parent = decl.scope, + .parent = &decl.container.base, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1602,7 +1487,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .parent = null, .inst_table = &inst_table, .func = null, - .decl = decl, + .owner_decl = decl, + .src_decl = decl, .instructions = .{}, .arena = &analysis_arena.allocator, .inlining = null, @@ -1632,44 +1518,6 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void dependee.dependants.putAssumeCapacity(depender, {}); } -fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { - switch (root_scope.status) { - .never_loaded, .unloaded_success => { - try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); - - const source = try root_scope.getSource(self); - - var keep_zir_module = false; - const zir_module = try self.gpa.create(zir.Module); - defer if (!keep_zir_module) self.gpa.destroy(zir_module); - - zir_module.* = try zir.parse(self.gpa, source); - defer if (!keep_zir_module) zir_module.deinit(self.gpa); - - if (zir_module.error_msg) |src_err_msg| { - self.failed_files.putAssumeCapacityNoClobber( - &root_scope.base, - try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{s}", .{src_err_msg.msg}), - ); - root_scope.status = .unloaded_parse_failure; - return error.AnalysisFail; - } - - root_scope.status = .loaded_success; - root_scope.contents = .{ .module = zir_module }; - keep_zir_module = true; - - return zir_module; - }, - - .unloaded_parse_failure, - .unloaded_sema_failure, - => return error.AnalysisFail, - - .loaded_success, .loaded_sema_failure => return root_scope.contents.module, - } -} - pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { const tracy = trace(@src()); defer tracy.end(); @@ -1691,10 +1539,13 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { defer msg.deinit(); try parse_err.render(tree.token_ids, msg.writer()); - const err_msg = try self.gpa.create(Compilation.ErrorMsg); + const err_msg = try self.gpa.create(ErrorMsg); err_msg.* = .{ + .src_loc = .{ + .file_scope = root_scope, + .byte_offset = tree.token_locs[parse_err.loc()].start, + }, .msg = msg.toOwnedSlice(), - .byte_offset = tree.token_locs[parse_err.loc()].start, }; self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); @@ -1753,9 +1604,12 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.swapRemove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{s}'", .{decl.name}); - errdefer err_msg.destroy(self.gpa); - try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); + const msg = try ErrorMsg.create(self.gpa, .{ + .file_scope = container_scope.file_scope, + .byte_offset = tree.token_locs[name_tok].start, + }, "redefinition of '{s}'", .{decl.name}); + errdefer msg.destroy(self.gpa); + try self.failed_decls.putNoClobber(self.gpa, decl, msg); } else { if (!srcHashEql(decl.contents_hash, contents_hash)) { try self.markOutdatedDecl(decl); @@ -1795,7 +1649,10 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.swapRemove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{s}'", .{decl.name}); + const err_msg = try ErrorMsg.create(self.gpa, .{ + .file_scope = container_scope.file_scope, + .byte_offset = name_loc.start, + }, "redefinition of '{s}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else if (!srcHashEql(decl.contents_hash, contents_hash)) { @@ -1840,65 +1697,12 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void } } -pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { - // We may be analyzing it for the first time, or this may be - // an incremental update. This code handles both cases. - const src_module = try self.getSrcModule(root_scope); - - try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len); - try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len); - - var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa); - defer exports_to_resolve.deinit(); - - // Keep track of the decls that we expect to see in this file so that - // we know which ones have been deleted. - var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); - defer deleted_decls.deinit(); - try deleted_decls.ensureCapacity(self.decl_table.items().len); - for (self.decl_table.items()) |entry| { - deleted_decls.putAssumeCapacityNoClobber(entry.value, {}); - } - - for (src_module.decls) |src_decl, decl_i| { - const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); - if (self.decl_table.get(name_hash)) |decl| { - deleted_decls.removeAssertDiscard(decl); - if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { - try self.markOutdatedDecl(decl); - decl.contents_hash = src_decl.contents_hash; - } - } else { - const new_decl = try self.createNewDecl( - &root_scope.base, - src_decl.name, - decl_i, - name_hash, - src_decl.contents_hash, - ); - root_scope.decls.appendAssumeCapacity(new_decl); - if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| { - try exports_to_resolve.append(src_decl); - } - } - } - for (exports_to_resolve.items) |export_decl| { - _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl); - } - // Handle explicitly deleted decls from the source code. Not to be confused - // with when we delete decls because they are no longer referenced. - for (deleted_decls.items()) |entry| { - log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); - try self.deleteDecl(entry.key); - } -} - pub fn deleteDecl(self: *Module, decl: *Decl) !void { try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); // Remove from the namespace it resides in. In the case of an anonymous Decl it will // not be present in the set, and this does nothing. - decl.scope.removeDecl(decl); + decl.container.removeDecl(decl); log.debug("deleting decl '{s}'\n", .{decl.name}); const name_hash = decl.fullyQualifiedNameHash(); @@ -1929,9 +1733,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { if (self.emit_h_failed_decls.swapRemove(decl)) |entry| { entry.value.destroy(self.gpa); } - if (self.compile_log_decls.swapRemove(decl)) |*entry| { - entry.value.deinit(self.gpa); - } + _ = self.compile_log_decls.swapRemove(decl); self.deleteDeclExports(decl); self.comp.bin_file.freeDecl(decl); @@ -1993,7 +1795,8 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { .parent = null, .inst_table = &inst_table, .func = func, - .decl = decl, + .owner_decl = decl, + .src_decl = decl, .instructions = .{}, .arena = &arena.allocator, .inlining = null, @@ -2022,9 +1825,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void { if (self.emit_h_failed_decls.swapRemove(decl)) |entry| { entry.value.destroy(self.gpa); } - if (self.compile_log_decls.swapRemove(decl)) |*entry| { - entry.value.deinit(self.gpa); - } + _ = self.compile_log_decls.swapRemove(decl); decl.analysis = .outdated; } @@ -2046,7 +1847,7 @@ fn allocateNewDecl( new_decl.* = .{ .name = "", - .scope = scope.namespace(), + .container = scope.namespace(), .src_index = src_index, .typed_value = .{ .never_succeeded = {} }, .analysis = .unreferenced, @@ -2129,34 +1930,34 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { } pub fn analyzeExport( - self: *Module, + mod: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl, ) !void { - try self.ensureDeclAnalyzed(exported_decl); + try mod.ensureDeclAnalyzed(exported_decl); const typed_value = exported_decl.typed_value.most_recent.typed_value; switch (typed_value.ty.zigTypeTag()) { .Fn => {}, - else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), + else => return mod.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), } - try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1); - try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1); + try mod.decl_exports.ensureCapacity(mod.gpa, mod.decl_exports.items().len + 1); + try mod.export_owners.ensureCapacity(mod.gpa, mod.export_owners.items().len + 1); - const new_export = try self.gpa.create(Export); - errdefer self.gpa.destroy(new_export); + const new_export = try mod.gpa.create(Export); + errdefer mod.gpa.destroy(new_export); - const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name); - errdefer self.gpa.free(symbol_name); + const symbol_name = try mod.gpa.dupe(u8, borrowed_symbol_name); + errdefer mod.gpa.free(symbol_name); - const owner_decl = scope.decl().?; + const owner_decl = scope.ownerDecl().?; new_export.* = .{ .options = .{ .name = symbol_name }, .src = src, - .link = switch (self.comp.bin_file.tag) { + .link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, .elf => .{ .elf = link.File.Elf.Export{} }, .macho => .{ .macho = link.File.MachO.Export{} }, @@ -2169,48 +1970,53 @@ pub fn analyzeExport( }; // Add to export_owners table. - const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl); + const eo_gop = mod.export_owners.getOrPutAssumeCapacity(owner_decl); if (!eo_gop.found_existing) { eo_gop.entry.value = &[0]*Export{}; } - eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); + eo_gop.entry.value = try mod.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export; - errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); + errdefer eo_gop.entry.value = mod.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); // Add to exported_decl table. - const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl); + const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl); if (!de_gop.found_existing) { de_gop.entry.value = &[0]*Export{}; } - de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); + de_gop.entry.value = try mod.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; - errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); + errdefer de_gop.entry.value = mod.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); - if (self.symbol_exports.get(symbol_name)) |_| { - try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); - self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( - self.gpa, + if (mod.symbol_exports.get(symbol_name)) |other_export| { + new_export.status = .failed_retryable; + try mod.failed_exports.ensureCapacity(mod.gpa, mod.failed_exports.items().len + 1); + const msg = try mod.errMsg( + scope, src, "exported symbol collision: {s}", .{symbol_name}, - )); - // TODO: add a note + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote( + &other_export.owner_decl.container.base, + other_export.src, + msg, + "other symbol here", + .{}, + ); + mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); new_export.status = .failed; return; } - try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); - self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { + try mod.symbol_exports.putNoClobber(mod.gpa, symbol_name, new_export); + mod.comp.bin_file.updateDeclExports(mod, exported_decl, de_gop.entry.value) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1); - self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( - self.gpa, - src, - "unable to export: {s}", - .{@errorName(err)}, - )); new_export.status = .failed_retryable; + try mod.failed_exports.ensureCapacity(mod.gpa, mod.failed_exports.items().len + 1); + const msg = try mod.errMsg(scope, src, "unable to export: {s}", .{@errorName(err)}); + mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); }, }; } @@ -2476,7 +2282,7 @@ pub fn createAnonymousDecl( typed_value: TypedValue, ) !*Decl { const name_index = self.getNextAnonNameIndex(); - const scope_decl = scope.decl().?; + const scope_decl = scope.ownerDecl().?; const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); defer self.gpa.free(name); const name_hash = scope.namespace().fullyQualifiedNameHash(name); @@ -2512,7 +2318,7 @@ pub fn createContainerDecl( decl_arena: *std.heap.ArenaAllocator, typed_value: TypedValue, ) !*Decl { - const scope_decl = scope.decl().?; + const scope_decl = scope.ownerDecl().?; const name = try self.getAnonTypeName(scope, base_token); defer self.gpa.free(name); const name_hash = scope.namespace().fullyQualifiedNameHash(name); @@ -2558,14 +2364,14 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De } pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { - const scope_decl = scope.decl().?; + const scope_decl = scope.ownerDecl().?; try self.declareDeclDependency(scope_decl, decl); self.ensureDeclAnalyzed(decl) catch |err| { if (scope.cast(Scope.Block)) |block| { if (block.func) |func| { func.state = .dependency_failure; } else { - block.decl.analysis = .dependency_failure; + block.owner_decl.analysis = .dependency_failure; } } else { scope_decl.analysis = .dependency_failure; @@ -3217,10 +3023,51 @@ fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *In return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); } -pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { - @setCold(true); - const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); - return self.failWithOwnedErrorMsg(scope, src, err_msg); +/// We don't return a pointer to the new error note because the pointer +/// becomes invalid when you add another one. +pub fn errNote( + mod: *Module, + scope: *Scope, + src: usize, + parent: *ErrorMsg, + comptime format: []const u8, + args: anytype, +) error{OutOfMemory}!void { + const msg = try std.fmt.allocPrint(mod.gpa, format, args); + errdefer mod.gpa.free(msg); + + parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); + parent.notes[parent.notes.len - 1] = .{ + .src_loc = .{ + .file_scope = scope.getFileScope(), + .byte_offset = src, + }, + .msg = msg, + }; +} + +pub fn errMsg( + mod: *Module, + scope: *Scope, + src_byte_offset: usize, + comptime format: []const u8, + args: anytype, +) error{OutOfMemory}!*ErrorMsg { + return ErrorMsg.create(mod.gpa, .{ + .file_scope = scope.getFileScope(), + .byte_offset = src_byte_offset, + }, format, args); +} + +pub fn fail( + mod: *Module, + scope: *Scope, + src_byte_offset: usize, + comptime format: []const u8, + args: anytype, +) InnerError { + const err_msg = try mod.errMsg(scope, src_byte_offset, format, args); + return mod.failWithOwnedErrorMsg(scope, err_msg); } pub fn failTok( @@ -3230,7 +3077,6 @@ pub fn failTok( comptime format: []const u8, args: anytype, ) InnerError { - @setCold(true); const src = scope.tree().token_locs[token_index].start; return self.fail(scope, src, format, args); } @@ -3242,80 +3088,36 @@ pub fn failNode( comptime format: []const u8, args: anytype, ) InnerError { - @setCold(true); const src = scope.tree().token_locs[ast_node.firstToken()].start; return self.fail(scope, src, format, args); } -fn addCompileLog(self: *Module, decl: *Decl, src: usize) error{OutOfMemory}!void { - const entry = try self.compile_log_decls.getOrPutValue(self.gpa, decl, .{}); - try entry.value.append(self.gpa, src); -} - -pub fn failCompileLog( - self: *Module, - scope: *Scope, - src: usize, -) InnerError!void { - switch (scope.tag) { - .decl => { - const decl = scope.cast(Scope.DeclAnalysis).?.decl; - try self.addCompileLog(decl, src); - }, - .block => { - const block = scope.cast(Scope.Block).?; - try self.addCompileLog(block.decl, src); - }, - .gen_zir => { - const gen_zir = scope.cast(Scope.GenZIR).?; - try self.addCompileLog(gen_zir.decl, src); - }, - .local_val => { - const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; - try self.addCompileLog(gen_zir.decl, src); - }, - .local_ptr => { - const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; - try self.addCompileLog(gen_zir.decl, src); - }, - .zir_module, - .file, - .container, - => unreachable, - } -} - -fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError { +pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError { + @setCold(true); { errdefer err_msg.destroy(self.gpa); try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1); try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); } switch (scope.tag) { - .decl => { - const decl = scope.cast(Scope.DeclAnalysis).?.decl; - decl.analysis = .sema_failure; - decl.generation = self.generation; - self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg); - }, .block => { const block = scope.cast(Scope.Block).?; if (block.inlining) |inlining| { if (inlining.shared.caller) |func| { func.state = .sema_failure; } else { - block.decl.analysis = .sema_failure; - block.decl.generation = self.generation; + block.owner_decl.analysis = .sema_failure; + block.owner_decl.generation = self.generation; } } else { if (block.func) |func| { func.state = .sema_failure; } else { - block.decl.analysis = .sema_failure; - block.decl.generation = self.generation; + block.owner_decl.analysis = .sema_failure; + block.owner_decl.generation = self.generation; } } - self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); + self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); }, .gen_zir => { const gen_zir = scope.cast(Scope.GenZIR).?; @@ -3335,11 +3137,6 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com gen_zir.decl.generation = self.generation; self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); }, - .zir_module => { - const zir_module = scope.cast(Scope.ZIRModule).?; - zir_module.status = .loaded_sema_failure; - self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); - }, .file => unreachable, .container => unreachable, } @@ -3671,7 +3468,8 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .inlining = parent_block.inlining, diff --git a/src/astgen.zig b/src/astgen.zig index 53503a0467..4631e46b5d 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -318,7 +318,7 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as // Make a scope to collect generated instructions in the sub-expression. var block_scope: Scope.GenZIR = .{ .parent = parent_scope, - .decl = parent_scope.decl().?, + .decl = parent_scope.ownerDecl().?, .arena = parent_scope.arena(), .instructions = .{}, }; @@ -474,7 +474,7 @@ fn labeledBlockExpr( var block_scope: Scope.GenZIR = .{ .parent = parent_scope, - .decl = parent_scope.decl().?, + .decl = parent_scope.ownerDecl().?, .arena = gen_zir.arena, .instructions = .{}, .break_result_loc = rl, @@ -899,7 +899,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con var gen_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1028,7 +1028,13 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con .ty = Type.initTag(.type), .val = val, }); - return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); + if (rl == .ref) { + return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{}); + } else { + return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ + .decl = decl, + }, .{})); + } } fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { @@ -1084,7 +1090,7 @@ fn orelseCatchExpr( var block_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1266,7 +1272,7 @@ fn boolBinOp( var block_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1412,7 +1418,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn } var block_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1513,7 +1519,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W var expr_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1649,7 +1655,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) var for_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1843,7 +1849,7 @@ fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { var block_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1885,7 +1891,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node var item_scope: Scope.GenZIR = .{ .parent = scope, - .decl = scope.decl().?, + .decl = scope.ownerDecl().?, .arena = scope.arena(), .instructions = .{}, }; @@ -1922,8 +1928,15 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node // Check for else/_ prong, those are handled last. if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { if (else_src) |src| { - return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{}); - // TODO notes "previous else prong is here" + const msg = try mod.errMsg( + scope, + case_src, + "multiple else prongs in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, src, msg, "previous else prong is here", .{}); + return mod.failWithOwnedErrorMsg(scope, msg); } else_src = case_src; special_case = case; @@ -1932,8 +1945,15 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) { if (underscore_src) |src| { - return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{}); - // TODO notes "previous '_' prong is here" + const msg = try mod.errMsg( + scope, + case_src, + "multiple '_' prongs in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); + return mod.failWithOwnedErrorMsg(scope, msg); } underscore_src = case_src; special_case = case; @@ -1942,9 +1962,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node if (else_src) |some_else| { if (underscore_src) |some_underscore| { - return mod.fail(scope, switch_src, "else and '_' prong in switch expression", .{}); - // TODO notes "else prong is here" - // TODO notes "'_' prong is here" + const msg = try mod.errMsg( + scope, + switch_src, + "else and '_' prong in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some_else, msg, "else prong is here", .{}); + try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); + return mod.failWithOwnedErrorMsg(scope, msg); } } @@ -2162,7 +2189,13 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo } if (mod.lookupDeclName(scope, ident_name)) |decl| { - return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); + if (rl == .ref) { + return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{}); + } else { + return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ + .decl = decl, + }, .{})); + } } return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name}); @@ -2927,6 +2960,8 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul return rlWrap(mod, scope, rl, void_inst); } +/// TODO go over all the callsites and see where we can introduce "by-value" ZIR instructions +/// to save ZIR memory. For example, see DeclVal vs DeclRef. fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst { if (rl == .ref) return ptr; @@ -3032,7 +3067,7 @@ pub fn addZIRInstBlock( scope: *Scope, src: usize, tag: zir.Inst.Tag, - body: zir.Module.Body, + body: zir.Body, ) !*zir.Inst.Block { const gen_zir = scope.getGenZIR(); try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); @@ -3070,7 +3105,7 @@ pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: Typ } /// TODO The existence of this function is a workaround for a bug in stage1. -pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop { +pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Body) !*zir.Inst.Loop { const P = std.meta.fieldInfo(zir.Inst.Loop, .positionals).field_type; return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{}); } diff --git a/src/codegen.zig b/src/codegen.zig index 709c91a635..9f2fbaab78 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -9,7 +9,7 @@ const TypedValue = @import("TypedValue.zig"); const link = @import("link.zig"); const Module = @import("Module.zig"); const Compilation = @import("Compilation.zig"); -const ErrorMsg = Compilation.ErrorMsg; +const ErrorMsg = Module.ErrorMsg; const Target = std.Target; const Allocator = mem.Allocator; const trace = @import("tracy.zig").trace; @@ -74,7 +74,7 @@ pub const DebugInfoOutput = union(enum) { pub fn generateSymbol( bin_file: *link.File, - src: usize, + src_loc: Module.SrcLoc, typed_value: TypedValue, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, @@ -87,56 +87,56 @@ pub fn generateSymbol( switch (bin_file.options.target.cpu.arch) { .wasm32 => unreachable, // has its own code path .wasm64 => unreachable, // has its own code path - .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output), - .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output), - .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output), - .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output), - .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output), - .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output), - .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output), - .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output), - //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output), + .arm => return Function(.arm).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .armeb => return Function(.armeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.arc => return Function(.arc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.avr => return Function(.avr).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.mips => return Function(.mips).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.mips64 => return Function(.mips64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.msp430 => return Function(.msp430).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.r600 => return Function(.r600).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.sparc => return Function(.sparc).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.s390x => return Function(.s390x).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.tce => return Function(.tce).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.tcele => return Function(.tcele).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.thumb => return Function(.thumb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.i386 => return Function(.i386).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.xcore => return Function(.xcore).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.le32 => return Function(.le32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.le64 => return Function(.le64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.amdil => return Function(.amdil).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.hsail => return Function(.hsail).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.spir => return Function(.spir).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.spir64 => return Function(.spir64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.shave => return Function(.shave).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.lanai => return Function(.lanai).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), + //.ve => return Function(.ve).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."), } }, @@ -147,7 +147,7 @@ pub fn generateSymbol( try code.ensureCapacity(code.items.len + payload.data.len + 1); code.appendSliceAssumeCapacity(payload.data); const prev_len = code.items.len; - switch (try generateSymbol(bin_file, src, .{ + switch (try generateSymbol(bin_file, src_loc, .{ .ty = typed_value.ty.elemType(), .val = sentinel, }, code, debug_output)) { @@ -165,7 +165,7 @@ pub fn generateSymbol( return Result{ .fail = try ErrorMsg.create( bin_file.allocator, - src, + src_loc, "TODO implement generateSymbol for more kinds of arrays", .{}, ), @@ -200,7 +200,7 @@ pub fn generateSymbol( return Result{ .fail = try ErrorMsg.create( bin_file.allocator, - src, + src_loc, "TODO implement generateSymbol for pointer {}", .{typed_value.val}, ), @@ -217,7 +217,7 @@ pub fn generateSymbol( return Result{ .fail = try ErrorMsg.create( bin_file.allocator, - src, + src_loc, "TODO implement generateSymbol for int type '{}'", .{typed_value.ty}, ), @@ -227,7 +227,7 @@ pub fn generateSymbol( return Result{ .fail = try ErrorMsg.create( bin_file.allocator, - src, + src_loc, "TODO implement generateSymbol for type '{s}'", .{@tagName(t)}, ), @@ -259,7 +259,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { ret_mcv: MCValue, fn_type: Type, arg_index: usize, - src: usize, + src_loc: Module.SrcLoc, stack_align: u32, /// Byte offset within the source file. @@ -428,7 +428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn generateSymbol( bin_file: *link.File, - src: usize, + src_loc: Module.SrcLoc, typed_value: TypedValue, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, @@ -450,19 +450,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try branch_stack.append(.{}); const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: { - if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| { - const tree = container_scope.file_scope.contents.tree; - const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const lbrace_src = tree.token_locs[block.lbrace].start; - const rbrace_src = tree.token_locs[block.rbrace].start; - break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, .source = tree.source }; - } else if (module_fn.owner_decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| { - const byte_off = zir_module.contents.module.decls[module_fn.owner_decl.src_index].inst.src; - break :blk .{ .lbrace_src = byte_off, .rbrace_src = byte_off, .source = zir_module.source.bytes }; - } else { - unreachable; - } + const container_scope = module_fn.owner_decl.container; + const tree = container_scope.file_scope.contents.tree; + const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; + const block = fn_proto.getBodyNode().?.castTag(.Block).?; + const lbrace_src = tree.token_locs[block.lbrace].start; + const rbrace_src = tree.token_locs[block.rbrace].start; + break :blk .{ + .lbrace_src = lbrace_src, + .rbrace_src = rbrace_src, + .source = tree.source, + }; }; var function = Self{ @@ -478,7 +476,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .fn_type = fn_type, .arg_index = 0, .branch_stack = &branch_stack, - .src = src, + .src_loc = src_loc, .stack_align = undefined, .prev_di_pc = 0, .prev_di_src = src_data.lbrace_src, @@ -489,7 +487,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { defer function.stack.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); - var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) { + var call_info = function.resolveCallingConventionValues(src_loc.byte_offset, fn_type) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, else => |e| return e, }; @@ -536,12 +534,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const stack_end = self.max_end_stack; if (stack_end > math.maxInt(i32)) - return self.fail(self.src, "too much stack used in call parameters", .{}); + return self.failSymbol("too much stack used in call parameters", .{}); const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end)); if (self.code.items.len >= math.maxInt(i32)) { - return self.fail(self.src, "unable to perform relocation: jump too far", .{}); + return self.failSymbol("unable to perform relocation: jump too far", .{}); } if (self.exitlude_jump_relocs.items.len == 1) { self.code.items.len -= 5; @@ -598,7 +596,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| { writeInt(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.al, .sp, .sp, op).toU32()); } else { - return self.fail(self.src, "TODO ARM: allow larger stacks", .{}); + return self.failSymbol("TODO ARM: allow larger stacks", .{}); } try self.dbgSetEpilogueBegin(); @@ -624,7 +622,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (math.cast(i26, amt)) |offset| { writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(.al, offset).toU32()); } else |err| { - return self.fail(self.src, "exitlude jump is too large", .{}); + return self.failSymbol("exitlude jump is too large", .{}); } } } @@ -3678,7 +3676,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args); + self.err_msg = try ErrorMsg.create(self.bin_file.allocator, .{ + .file_scope = self.src_loc.file_scope, + .byte_offset = src, + }, format, args); + return error.CodegenFail; + } + + fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + assert(self.err_msg == null); + self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); return error.CodegenFail; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 8c85f482fd..b26f753757 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -114,10 +114,13 @@ pub const DeclGen = struct { module: *Module, decl: *Decl, fwd_decl: std.ArrayList(u8), - error_msg: ?*Compilation.ErrorMsg, + error_msg: ?*Module.ErrorMsg, fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { - dg.error_msg = try Compilation.ErrorMsg.create(dg.module.gpa, src, format, args); + dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{ + .file_scope = dg.decl.getFileScope(), + .byte_offset = src, + }, format, args); return error.AnalysisFail; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5d753c41cb..1edd466d54 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -148,7 +148,7 @@ pub const LLVMIRModule = struct { object_path: []const u8, gpa: *Allocator, - err_msg: ?*Compilation.ErrorMsg = null, + err_msg: ?*Module.ErrorMsg = null, // TODO: The fields below should really move into a different struct, // because they are only valid when generating a function @@ -177,6 +177,8 @@ pub const LLVMIRModule = struct { break_vals: *BreakValues, }) = .{}, + src_loc: Module.SrcLoc, + const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); @@ -254,6 +256,8 @@ pub const LLVMIRModule = struct { .builder = builder, .object_path = object_path, .gpa = gpa, + // TODO move this field into a struct that is only instantiated per gen() call + .src_loc = undefined, }; return self; } @@ -335,6 +339,8 @@ pub const LLVMIRModule = struct { const typed_value = decl.typed_value.most_recent.typed_value; const src = decl.src(); + self.src_loc = decl.srcLoc(); + log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); if (typed_value.val.castTag(.function)) |func_payload| { @@ -853,7 +859,10 @@ pub const LLVMIRModule = struct { pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { @setCold(true); assert(self.err_msg == null); - self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); + self.err_msg = try Module.ErrorMsg.create(self.gpa, .{ + .file_scope = self.src_loc.file_scope, + .byte_offset = src, + }, format, args); return error.CodegenFail; } }; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index f7cd9b69ce..981d4ec3a3 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -670,7 +670,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none); + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none); const code = switch (res) { .externally_managed => |x| x, .appended => code_buffer.items, @@ -732,7 +732,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), ); continue; } @@ -743,7 +743,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}), ); continue; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 8c76a4e967..ee50eb5d94 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2189,22 +2189,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { try dbg_line_buffer.ensureCapacity(26); const line_off: u28 = blk: { - if (decl.scope.cast(Module.Scope.Container)) |container_scope| { - const tree = container_scope.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); - // TODO Look into improving the performance here by adding a token-index-to-line - // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); - break :blk @intCast(u28, line_delta); - } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| { - const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src; - const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off); - break :blk @intCast(u28, line_delta); - } else { - unreachable; - } + const tree = decl.container.file_scope.contents.tree; + const file_ast_decls = tree.root_node.decls(); + // TODO Look into improving the performance here by adding a token-index-to-line + // lookup table. Currently this involves scanning over the source code for newlines. + const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; + const block = fn_proto.getBodyNode().?.castTag(.Block).?; + const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + break :blk @intCast(u28, line_delta); }; const ptr_width_bytes = self.ptrWidthBytes(); @@ -2268,7 +2260,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { } else { // TODO implement .debug_info for global variables } - const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{ + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ .dwarf = .{ .dbg_line = &dbg_line_buffer, .dbg_info = &dbg_info_buffer, @@ -2642,7 +2634,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), ); continue; } @@ -2660,7 +2652,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}), ); continue; }, @@ -2703,8 +2695,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec if (self.llvm_ir_module) |_| return; - const container_scope = decl.scope.cast(Module.Scope.Container).?; - const tree = container_scope.file_scope.contents.tree; + const tree = decl.container.file_scope.contents.tree; const file_ast_decls = tree.root_node.decls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d913a82328..1017405255 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1148,7 +1148,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } const res = if (debug_buffers) |*dbg| - try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{ + try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ .dwarf = .{ .dbg_line = &dbg.dbg_line_buffer, .dbg_info = &dbg.dbg_info_buffer, @@ -1156,7 +1156,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, }) else - try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none); + try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none); const code = switch (res) { .externally_managed => |x| x, @@ -1316,7 +1316,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), ); continue; } @@ -1334,7 +1334,7 @@ pub fn updateDeclExports( try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( exp, - try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), + try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}), ); continue; }, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 81a016ce42..fb7488a12c 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -906,8 +906,7 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M const tracy = trace(@src()); defer tracy.end(); - const container_scope = decl.scope.cast(Module.Scope.Container).?; - const tree = container_scope.file_scope.contents.tree; + const tree = decl.container.file_scope.contents.tree; const file_ast_decls = tree.root_node.decls(); // TODO Look into improving the performance here by adding a token-index-to-line // lookup table. Currently this involves scanning over the source code for newlines. @@ -951,22 +950,14 @@ pub fn initDeclDebugBuffers( try dbg_line_buffer.ensureCapacity(26); const line_off: u28 = blk: { - if (decl.scope.cast(Module.Scope.Container)) |container_scope| { - const tree = container_scope.file_scope.contents.tree; - const file_ast_decls = tree.root_node.decls(); - // TODO Look into improving the performance here by adding a token-index-to-line - // lookup table. Currently this involves scanning over the source code for newlines. - const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; - const block = fn_proto.getBodyNode().?.castTag(.Block).?; - const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); - break :blk @intCast(u28, line_delta); - } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| { - const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src; - const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off); - break :blk @intCast(u28, line_delta); - } else { - unreachable; - } + const tree = decl.container.file_scope.contents.tree; + const file_ast_decls = tree.root_node.decls(); + // TODO Look into improving the performance here by adding a token-index-to-line + // lookup table. Currently this involves scanning over the source code for newlines. + const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?; + const block = fn_proto.getBodyNode().?.castTag(.Block).?; + const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start); + break :blk @intCast(u28, line_delta); }; dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ diff --git a/src/main.zig b/src/main.zig index 867aa348b1..13bea13a5e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -221,7 +221,6 @@ const usage_build_generic = \\ \\Supported file types: \\ .zig Zig source code - \\ .zir Zig Intermediate Representation code \\ .o ELF object file \\ .o MACH-O (macOS) object file \\ .obj COFF (Windows) object file @@ -245,8 +244,6 @@ const usage_build_generic = \\ -fno-emit-bin Do not output machine code \\ -femit-asm[=path] Output .s (assembly code) \\ -fno-emit-asm (default) Do not output .s (assembly code) - \\ -femit-zir[=path] Produce a .zir file with Zig IR - \\ -fno-emit-zir (default) Do not produce a .zir file with Zig IR \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR \\ -femit-h[=path] Generate a C header file (.h) @@ -1631,18 +1628,12 @@ fn buildOutputType( var emit_docs_resolved = try emit_docs.resolve("docs"); defer emit_docs_resolved.deinit(); - const zir_out_path: ?[]const u8 = switch (emit_zir) { - .no => null, - .yes_default_path => blk: { - if (root_src_file) |rsf| { - if (mem.endsWith(u8, rsf, ".zir")) { - break :blk try std.fmt.allocPrint(arena, "{s}.out.zir", .{root_name}); - } - } - break :blk try std.fmt.allocPrint(arena, "{s}.zir", .{root_name}); + switch (emit_zir) { + .no => {}, + .yes_default_path, .yes => { + fatal("The -femit-zir implementation has been intentionally deleted so that it can be rewritten as a proper backend.", .{}); }, - .yes => |p| p, - }; + } const root_pkg: ?*Package = if (root_src_file) |src_path| blk: { if (main_pkg_path) |p| { @@ -1753,7 +1744,7 @@ fn buildOutputType( .dll_export_fns = dll_export_fns, .object_format = object_format, .optimize_mode = optimize_mode, - .keep_source_files_loaded = zir_out_path != null, + .keep_source_files_loaded = false, .clang_argv = clang_argv.items, .lld_argv = lld_argv.items, .lib_dirs = lib_dirs.items, @@ -1845,7 +1836,7 @@ fn buildOutputType( } }; - updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) { + updateModule(gpa, comp, hook) catch |err| switch (err) { error.SemanticAnalyzeFail => if (!watch) process.exit(1), else => |e| return e, }; @@ -1980,7 +1971,7 @@ fn buildOutputType( if (output_mode == .Exe) { try comp.makeBinFileWritable(); } - updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) { + updateModule(gpa, comp, hook) catch |err| switch (err) { error.SemanticAnalyzeFail => continue, else => |e| return e, }; @@ -2003,7 +1994,7 @@ const AfterUpdateHook = union(enum) { update: []const u8, }; -fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void { +fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !void { try comp.update(); var errors = try comp.getAllErrorsAlloc(); @@ -2013,6 +2004,10 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, for (errors.list) |full_err_msg| { full_err_msg.renderToStdErr(); } + const log_text = comp.getCompileLogOutput(); + if (log_text.len != 0) { + std.debug.print("\nCompile Log Output:\n{s}", .{log_text}); + } return error.SemanticAnalyzeFail; } else switch (hook) { .none => {}, @@ -2024,20 +2019,6 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, .{}, ), } - - if (zir_out_path) |zop| { - const module = comp.bin_file.options.module orelse - fatal("-femit-zir with no zig source code", .{}); - var new_zir_module = try zir.emit(gpa, module); - defer new_zir_module.deinit(gpa); - - const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); - defer baf.destroy(); - - try new_zir_module.writeToStream(gpa, baf.writer()); - - try baf.finish(); - } } fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void { @@ -2506,7 +2487,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v }; defer comp.destroy(); - try updateModule(gpa, comp, null, .none); + try updateModule(gpa, comp, .none); try comp.makeBinFileExecutable(); child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( diff --git a/src/test.zig b/src/test.zig index 59927525df..1c9fb57f01 100644 --- a/src/test.zig +++ b/src/test.zig @@ -15,6 +15,8 @@ const CrossTarget = std.zig.CrossTarget; const zig_h = link.File.C.zig_h; +const hr = "=" ** 40; + test "self-hosted" { var ctx = TestContext.init(); defer ctx.deinit(); @@ -29,23 +31,32 @@ const ErrorMsg = union(enum) { msg: []const u8, line: u32, column: u32, + kind: Kind, }, plain: struct { msg: []const u8, + kind: Kind, }, - fn init(other: Compilation.AllErrors.Message) ErrorMsg { + const Kind = enum { + @"error", + note, + }; + + fn init(other: Compilation.AllErrors.Message, kind: Kind) ErrorMsg { switch (other) { .src => |src| return .{ .src = .{ .msg = src.msg, .line = @intCast(u32, src.line), .column = @intCast(u32, src.column), + .kind = kind, }, }, .plain => |plain| return .{ .plain = .{ .msg = plain.msg, + .kind = kind, }, }, } @@ -59,14 +70,15 @@ const ErrorMsg = union(enum) { ) !void { switch (self) { .src => |src| { - return writer.print(":{d}:{d}: error: {s}", .{ + return writer.print(":{d}:{d}: {s}: {s}", .{ src.line + 1, src.column + 1, + @tagName(src.kind), src.msg, }); }, .plain => |plain| { - return writer.print("error: {s}", .{plain.msg}); + return writer.print("{s}: {s}", .{ plain.msg, @tagName(plain.kind) }); }, } } @@ -86,9 +98,6 @@ pub const TestContext = struct { /// effects of the incremental compilation. src: [:0]const u8, case: union(enum) { - /// A transformation update transforms the input and tests against - /// the expected output ZIR. - Transformation: [:0]const u8, /// Check the main binary output file against an expected set of bytes. /// This is most useful with, for example, `-ofmt=c`. CompareObjectFile: []const u8, @@ -139,15 +148,6 @@ pub const TestContext = struct { files: std.ArrayList(File), - /// Adds a subcase in which the module is updated with `src`, and the - /// resulting ZIR is validated against `result`. - pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { - self.updates.append(.{ - .src = src, - .case = .{ .Transformation = result }, - }) catch unreachable; - } - /// Adds a subcase in which the module is updated with `src`, and a C /// header is generated. pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { @@ -182,31 +182,37 @@ pub const TestContext = struct { /// the form `:line:column: error: message`. pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; - for (errors) |e, i| { - if (e[0] != ':') { - array[i] = .{ .plain = .{ .msg = e } }; + for (errors) |err_msg_line, i| { + if (std.mem.startsWith(u8, err_msg_line, "error: ")) { + array[i] = .{ + .plain = .{ .msg = err_msg_line["error: ".len..], .kind = .@"error" }, + }; + continue; + } else if (std.mem.startsWith(u8, err_msg_line, "note: ")) { + array[i] = .{ + .plain = .{ .msg = err_msg_line["note: ".len..], .kind = .note }, + }; continue; } - var cur = e[1..]; - var line_index = std.mem.indexOf(u8, cur, ":"); - if (line_index == null) { - @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n"); - } - const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number"); - cur = cur[line_index.? + 1 ..]; - const column_index = std.mem.indexOf(u8, cur, ":"); - if (column_index == null) { - @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n"); - } - const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number"); - cur = cur[column_index.? + 2 ..]; - if (!std.mem.eql(u8, cur[0..7], "error: ")) { - @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n"); - } - const msg = cur[7..]; + // example: ":1:2: error: bad thing happened" + var it = std.mem.split(err_msg_line, ":"); + _ = it.next() orelse @panic("missing colon"); + const line_text = it.next() orelse @panic("missing line"); + const col_text = it.next() orelse @panic("missing column"); + const kind_text = it.next() orelse @panic("missing 'error'/'note'"); + const msg = it.rest()[1..]; // skip over the space at end of "error: " + + const line = std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number"); + const column = std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number"); + const kind: ErrorMsg.Kind = if (std.mem.eql(u8, kind_text, " error")) + .@"error" + else if (std.mem.eql(u8, kind_text, " note")) + .note + else + @panic("expected 'error'/'note'"); if (line == 0 or column == 0) { - @panic("Invalid test: error line and column must be specified starting at one!"); + @panic("line and column must be specified starting at one"); } array[i] = .{ @@ -214,6 +220,7 @@ pub const TestContext = struct { .msg = msg, .line = line - 1, .column = column - 1, + .kind = kind, }, }; } @@ -689,25 +696,20 @@ pub const TestContext = struct { var all_errors = try comp.getAllErrorsAlloc(); defer all_errors.deinit(allocator); if (all_errors.list.len != 0) { - std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); + std.debug.print("\nErrors occurred updating the compilation:\n{s}\n", .{hr}); for (all_errors.list) |err_msg| { switch (err_msg) { .src => |src| { - std.debug.print(":{d}:{d}: error: {s}\n================\n", .{ - src.line + 1, src.column + 1, src.msg, + std.debug.print(":{d}:{d}: error: {s}\n{s}\n", .{ + src.line + 1, src.column + 1, src.msg, hr, }); }, .plain => |plain| { - std.debug.print("error: {s}\n================\n", .{plain.msg}); + std.debug.print("error: {s}\n{s}\n", .{ plain.msg, hr }); }, } } // TODO print generated C code - //if (comp.bin_file.cast(link.File.C)) |c_file| { - // std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{ - // c_file.main.items, - // }); - //} std.debug.print("Test failed.\n", .{}); std.process.exit(1); } @@ -728,48 +730,74 @@ pub const TestContext = struct { std.testing.expectEqualStrings(expected_output, out); }, - .Transformation => |expected_output| { - update_node.setEstimatedTotalItems(5); - var emit_node = update_node.start("emit", 0); - emit_node.activate(); - var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?); - defer new_zir_module.deinit(allocator); - emit_node.end(); - - var write_node = update_node.start("write", 0); - write_node.activate(); - var out_zir = std.ArrayList(u8).init(allocator); - defer out_zir.deinit(); - try new_zir_module.writeToStream(allocator, out_zir.writer()); - write_node.end(); - + .Error => |case_error_list| { var test_node = update_node.start("assert", 0); test_node.activate(); defer test_node.end(); - std.testing.expectEqualStrings(expected_output, out_zir.items); - }, - .Error => |e| { - var test_node = update_node.start("assert", 0); - test_node.activate(); - defer test_node.end(); - var handled_errors = try arena.alloc(bool, e.len); - for (handled_errors) |*handled| { - handled.* = false; + const handled_errors = try arena.alloc(bool, case_error_list.len); + std.mem.set(bool, handled_errors, false); + + var actual_errors = try comp.getAllErrorsAlloc(); + defer actual_errors.deinit(allocator); + + var any_failed = false; + var notes_to_check = std.ArrayList(*const Compilation.AllErrors.Message).init(allocator); + defer notes_to_check.deinit(); + + for (actual_errors.list) |actual_error| { + for (case_error_list) |case_msg, i| { + const ex_tag: @TagType(@TypeOf(case_msg)) = case_msg; + switch (actual_error) { + .src => |actual_msg| { + for (actual_msg.notes) |*note| { + try notes_to_check.append(note); + } + + if (ex_tag != .src) continue; + + if (actual_msg.line == case_msg.src.line and + actual_msg.column == case_msg.src.column and + std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and + case_msg.src.kind == .@"error") + { + handled_errors[i] = true; + break; + } + }, + .plain => |plain| { + if (ex_tag != .plain) continue; + + if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and + case_msg.plain.kind == .@"error") + { + handled_errors[i] = true; + break; + } + }, + } + } else { + std.debug.print( + "\nUnexpected error:\n{s}\n{}\n{s}", + .{ hr, ErrorMsg.init(actual_error, .@"error"), hr }, + ); + any_failed = true; + } } - var all_errors = try comp.getAllErrorsAlloc(); - defer all_errors.deinit(allocator); - for (all_errors.list) |a| { - for (e) |ex, i| { - const a_tag: @TagType(@TypeOf(a)) = a; - const ex_tag: @TagType(@TypeOf(ex)) = ex; - switch (a) { - .src => |src| { + while (notes_to_check.popOrNull()) |note| { + for (case_error_list) |case_msg, i| { + const ex_tag: @TagType(@TypeOf(case_msg)) = case_msg; + switch (note.*) { + .src => |actual_msg| { + for (actual_msg.notes) |*sub_note| { + try notes_to_check.append(sub_note); + } if (ex_tag != .src) continue; - if (src.line == ex.src.line and - src.column == ex.src.column and - std.mem.eql(u8, ex.src.msg, src.msg)) + if (actual_msg.line == case_msg.src.line and + actual_msg.column == case_msg.src.column and + std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and + case_msg.src.kind == .note) { handled_errors[i] = true; break; @@ -778,7 +806,9 @@ pub const TestContext = struct { .plain => |plain| { if (ex_tag != .plain) continue; - if (std.mem.eql(u8, ex.plain.msg, plain.msg)) { + if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and + case_msg.plain.kind == .note) + { handled_errors[i] = true; break; } @@ -786,23 +816,29 @@ pub const TestContext = struct { } } else { std.debug.print( - "{s}\nUnexpected error:\n================\n{}\n================\nTest failed.\n", - .{ case.name, ErrorMsg.init(a) }, + "\nUnexpected note:\n{s}\n{}\n{s}", + .{ hr, ErrorMsg.init(note.*, .note), hr }, ); - std.process.exit(1); + any_failed = true; } } for (handled_errors) |handled, i| { if (!handled) { - const er = e[i]; std.debug.print( - "{s}\nDid not receive error:\n================\n{}\n================\nTest failed.\n", - .{ case.name, er }, + "\nExpected error not found:\n{s}\n{}\n{s}", + .{ hr, case_error_list[i], hr }, ); - std.process.exit(1); + any_failed = true; } } + + if (any_failed) { + std.debug.print("\nTest case '{s}' failed, update_index={d}.\n", .{ + case.name, update_index, + }); + std.process.exit(1); + } }, .Execution => |expected_stdout| { update_node.setEstimatedTotalItems(4); diff --git a/src/type/Enum.zig b/src/type/Enum.zig index 9b9ec5b319..4dfd5f6e44 100644 --- a/src/type/Enum.zig +++ b/src/type/Enum.zig @@ -21,7 +21,7 @@ pub const Field = struct { }; pub const Zir = struct { - body: zir.Module.Body, + body: zir.Body, inst: *zir.Inst, }; diff --git a/src/type/Struct.zig b/src/type/Struct.zig index d6a591c95e..24e3a0dcad 100644 --- a/src/type/Struct.zig +++ b/src/type/Struct.zig @@ -24,7 +24,7 @@ pub const Field = struct { }; pub const Zir = struct { - body: zir.Module.Body, + body: zir.Body, inst: *zir.Inst, }; diff --git a/src/type/Union.zig b/src/type/Union.zig index 26cc1796c6..5c7acf7d36 100644 --- a/src/type/Union.zig +++ b/src/type/Union.zig @@ -24,7 +24,7 @@ pub const Field = struct { }; pub const Zir = struct { - body: zir.Module.Body, + body: zir.Body, inst: *zir.Inst, }; diff --git a/src/zir.zig b/src/zir.zig index 0ccb09efac..0e7b3a3520 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -12,17 +12,6 @@ const TypedValue = @import("TypedValue.zig"); const ir = @import("ir.zig"); const IrModule = @import("Module.zig"); -/// This struct is relevent only for the ZIR Module text format. It is not used for -/// semantic analysis of Zig source code. -pub const Decl = struct { - name: []const u8, - - /// Hash of slice into the source of the part after the = and before the next instruction. - contents_hash: std.zig.SrcHash, - - inst: *Inst, -}; - /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for /// in-memory, analyzed instructions with types and values. /// We use a table to map these instruction to their respective semantically analyzed @@ -141,15 +130,12 @@ pub const Inst = struct { container_field, /// Declares the beginning of a statement. Used for debug info. dbg_stmt, - /// Represents a pointer to a global decl by name. + /// Represents a pointer to a global decl. declref, /// Represents a pointer to a global decl by string name. declref_str, - /// The syntax `@foo` is equivalent to `declval("foo")`. - /// declval is equivalent to declref followed by deref. + /// Equivalent to a declref followed by deref. declval, - /// Same as declval but the parameter is a `*Module.Decl` rather than a name. - declval_in_module, /// Load the value from a pointer. deref, /// Arithmetic division. Asserts no integer overflow. @@ -419,7 +405,6 @@ pub const Inst = struct { .declref => DeclRef, .declref_str => DeclRefStr, .declval => DeclVal, - .declval_in_module => DeclValInModule, .coerce_result_block_ptr => CoerceResultBlockPtr, .compilelog => CompileLog, .loop => Loop, @@ -496,7 +481,6 @@ pub const Inst = struct { .declref, .declref_str, .declval, - .declval_in_module, .deref, .div, .elemptr, @@ -650,7 +634,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - body: Module.Body, + body: Body, }, kw_args: struct {}, }; @@ -705,7 +689,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - name: []const u8, + decl: *IrModule.Decl, }, kw_args: struct {}, }; @@ -724,16 +708,6 @@ pub const Inst = struct { pub const base_tag = Tag.declval; base: Inst, - positionals: struct { - name: []const u8, - }, - kw_args: struct {}, - }; - - pub const DeclValInModule = struct { - pub const base_tag = Tag.declval_in_module; - base: Inst, - positionals: struct { decl: *IrModule.Decl, }, @@ -758,10 +732,7 @@ pub const Inst = struct { positionals: struct { to_log: []*Inst, }, - kw_args: struct { - /// If we have seen it already so don't make another error - seen: bool = false, - }, + kw_args: struct {}, }; pub const Const = struct { @@ -799,7 +770,7 @@ pub const Inst = struct { base: Inst, positionals: struct { - body: Module.Body, + body: Body, }, kw_args: struct {}, }; @@ -838,7 +809,7 @@ pub const Inst = struct { positionals: struct { fn_type: *Inst, - body: Module.Body, + body: Body, }, kw_args: struct { is_inline: bool = false, @@ -998,8 +969,8 @@ pub const Inst = struct { positionals: struct { condition: *Inst, - then_body: Module.Body, - else_body: Module.Body, + then_body: Body, + else_body: Body, }, kw_args: struct {}, }; @@ -1078,7 +1049,7 @@ pub const Inst = struct { /// List of all individual items and ranges items: []*Inst, cases: []Case, - else_body: Module.Body, + else_body: Body, }, kw_args: struct { /// Pointer to first range if such exists. @@ -1092,7 +1063,7 @@ pub const Inst = struct { pub const Case = struct { item: *Inst, - body: Module.Body, + body: Body, }; }; pub const TypeOfPeer = struct { @@ -1192,6 +1163,10 @@ pub const ErrorMsg = struct { msg: []const u8, }; +pub const Body = struct { + instructions: []*Inst, +}; + pub const Module = struct { decls: []*Decl, arena: std.heap.ArenaAllocator, @@ -1199,6 +1174,15 @@ pub const Module = struct { metadata: std.AutoHashMap(*Inst, MetaData), body_metadata: std.AutoHashMap(*Body, BodyMetaData), + pub const Decl = struct { + name: []const u8, + + /// Hash of slice into the source of the part after the = and before the next instruction. + contents_hash: std.zig.SrcHash, + + inst: *Inst, + }; + pub const MetaData = struct { deaths: ir.Inst.DeathsInt, addr: usize, @@ -1208,10 +1192,6 @@ pub const Module = struct { deaths: []*Inst, }; - pub const Body = struct { - instructions: []*Inst, - }; - pub fn deinit(self: *Module, allocator: *Allocator) void { self.metadata.deinit(); self.body_metadata.deinit(); @@ -1369,7 +1349,7 @@ const Writer = struct { } try stream.writeByte(']'); }, - Module.Body => { + Body => { try stream.writeAll("{\n"); if (self.module.body_metadata.get(param_ptr)) |metadata| { if (metadata.deaths.len > 0) { @@ -1468,8 +1448,6 @@ const Writer = struct { try stream.print("@{s}", .{info.name}); } } else if (inst.cast(Inst.DeclVal)) |decl_val| { - try stream.print("@{s}", .{decl_val.positionals.name}); - } else if (inst.cast(Inst.DeclValInModule)) |decl_val| { try stream.print("@{s}", .{decl_val.positionals.decl.name}); } else { // This should be unreachable in theory, but since ZIR is used for debugging the compiler @@ -1479,502 +1457,6 @@ const Writer = struct { } }; -pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module { - var global_name_map = std.StringHashMap(*Inst).init(allocator); - defer global_name_map.deinit(); - - var parser: Parser = .{ - .allocator = allocator, - .arena = std.heap.ArenaAllocator.init(allocator), - .i = 0, - .source = source, - .global_name_map = &global_name_map, - .decls = .{}, - .unnamed_index = 0, - .block_table = std.StringHashMap(*Inst.Block).init(allocator), - .loop_table = std.StringHashMap(*Inst.Loop).init(allocator), - }; - defer parser.block_table.deinit(); - defer parser.loop_table.deinit(); - errdefer parser.arena.deinit(); - - parser.parseRoot() catch |err| switch (err) { - error.ParseFailure => { - assert(parser.error_msg != null); - }, - else => |e| return e, - }; - - return Module{ - .decls = parser.decls.toOwnedSlice(allocator), - .arena = parser.arena, - .error_msg = parser.error_msg, - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), - }; -} - -const Parser = struct { - allocator: *Allocator, - arena: std.heap.ArenaAllocator, - i: usize, - source: [:0]const u8, - decls: std.ArrayListUnmanaged(*Decl), - global_name_map: *std.StringHashMap(*Inst), - error_msg: ?ErrorMsg = null, - unnamed_index: usize, - block_table: std.StringHashMap(*Inst.Block), - loop_table: std.StringHashMap(*Inst.Loop), - - const Body = struct { - instructions: std.ArrayList(*Inst), - name_map: *std.StringHashMap(*Inst), - }; - - fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body { - var name_map = std.StringHashMap(*Inst).init(self.allocator); - defer name_map.deinit(); - - var body_context = Body{ - .instructions = std.ArrayList(*Inst).init(self.allocator), - .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map, - }; - defer body_context.instructions.deinit(); - - try requireEatBytes(self, "{"); - skipSpace(self); - - while (true) : (self.i += 1) switch (self.source[self.i]) { - ';' => _ = try skipToAndOver(self, '\n'), - '%' => { - self.i += 1; - const ident = try skipToAndOver(self, ' '); - skipSpace(self); - try requireEatBytes(self, "="); - skipSpace(self); - const decl = try parseInstruction(self, &body_context, ident); - const ident_index = body_context.instructions.items.len; - if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{s}'", .{ident}); - } - try body_context.instructions.append(decl.inst); - continue; - }, - ' ', '\n' => continue, - '}' => { - self.i += 1; - break; - }, - else => |byte| return self.failByte(byte), - }; - - // Move the instructions to the arena - const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); - mem.copy(*Inst, instrs, body_context.instructions.items); - return Module.Body{ .instructions = instrs }; - } - - fn parseStringLiteral(self: *Parser) ![]u8 { - const start = self.i; - try self.requireEatBytes("\""); - - while (true) : (self.i += 1) switch (self.source[self.i]) { - '"' => { - self.i += 1; - const span = self.source[start..self.i]; - var bad_index: usize = undefined; - const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) { - error.InvalidCharacter => { - self.i = start + bad_index; - const bad_byte = self.source[self.i]; - return self.fail("invalid string literal character: '{c}'\n", .{bad_byte}); - }, - else => |e| return e, - }; - return parsed; - }, - '\\' => { - self.i += 1; - continue; - }, - 0 => return self.failByte(0), - else => continue, - }; - } - - fn parseIntegerLiteral(self: *Parser) !BigIntConst { - const start = self.i; - if (self.source[self.i] == '-') self.i += 1; - while (true) : (self.i += 1) switch (self.source[self.i]) { - '0'...'9' => continue, - else => break, - }; - const number_text = self.source[start..self.i]; - const base = 10; - // TODO reuse the same array list for this - const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len); - const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len); - defer self.allocator.free(limbs_buffer); - const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len); - const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len); - var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) { - error.InvalidCharacter => { - self.i = start; - return self.fail("invalid digit in integer literal", .{}); - }, - }; - return result.toConst(); - } - - fn parseRoot(self: *Parser) !void { - // The IR format is designed so that it can be tokenized and parsed at the same time. - while (true) { - switch (self.source[self.i]) { - ';' => _ = try skipToAndOver(self, '\n'), - '@' => { - self.i += 1; - const ident = try skipToAndOver(self, ' '); - skipSpace(self); - try requireEatBytes(self, "="); - skipSpace(self); - const decl = try parseInstruction(self, null, ident); - const ident_index = self.decls.items.len; - if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{s}'", .{ident}); - } - try self.decls.append(self.allocator, decl); - }, - ' ', '\n' => self.i += 1, - 0 => break, - else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), - } - } - } - - fn eatByte(self: *Parser, byte: u8) bool { - if (self.source[self.i] != byte) return false; - self.i += 1; - return true; - } - - fn skipSpace(self: *Parser) void { - while (self.source[self.i] == ' ' or self.source[self.i] == '\n') { - self.i += 1; - } - } - - fn requireEatBytes(self: *Parser, bytes: []const u8) !void { - const start = self.i; - for (bytes) |byte| { - if (self.source[self.i] != byte) { - self.i = start; - return self.fail("expected '{s}'", .{bytes}); - } - self.i += 1; - } - } - - fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 { - const start_i = self.i; - while (self.source[self.i] != 0) : (self.i += 1) { - if (self.source[self.i] == byte) { - const result = self.source[start_i..self.i]; - self.i += 1; - return result; - } - } - return self.fail("unexpected EOF", .{}); - } - - /// ParseFailure is an internal error code; handled in `parse`. - const InnerError = error{ ParseFailure, OutOfMemory }; - - fn failByte(self: *Parser, byte: u8) InnerError { - if (byte == 0) { - return self.fail("unexpected EOF", .{}); - } else { - return self.fail("unexpected byte: '{c}'", .{byte}); - } - } - - fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError { - @setCold(true); - self.error_msg = ErrorMsg{ - .byte_offset = self.i, - .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args), - }; - return error.ParseFailure; - } - - fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl { - const contents_start = self.i; - const fn_name = try skipToAndOver(self, '('); - inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { - if (mem.eql(u8, field.name, fn_name)) { - const tag = @field(Inst.Tag, field.name); - return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start); - } - } - return self.fail("unknown instruction '{s}'", .{fn_name}); - } - - fn parseInstructionGeneric( - self: *Parser, - comptime fn_name: []const u8, - comptime InstType: type, - tag: Inst.Tag, - body_ctx: ?*Body, - inst_name: []const u8, - contents_start: usize, - ) InnerError!*Decl { - const inst_specific = try self.arena.allocator.create(InstType); - inst_specific.base = .{ - .src = self.i, - .tag = tag, - }; - - if (InstType == Inst.Block) { - try self.block_table.put(inst_name, inst_specific); - } else if (InstType == Inst.Loop) { - try self.loop_table.put(inst_name, inst_specific); - } - - if (@hasField(InstType, "ty")) { - inst_specific.ty = opt_type orelse { - return self.fail("instruction '" ++ fn_name ++ "' requires type", .{}); - }; - } - - const Positionals = @TypeOf(inst_specific.positionals); - inline for (@typeInfo(Positionals).Struct.fields) |arg_field| { - if (self.source[self.i] == ',') { - self.i += 1; - skipSpace(self); - } else if (self.source[self.i] == ')') { - return self.fail("expected positional parameter '{s}'", .{arg_field.name}); - } - @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric( - self, - arg_field.field_type, - body_ctx, - ); - skipSpace(self); - } - - const KW_Args = @TypeOf(inst_specific.kw_args); - inst_specific.kw_args = .{}; // assign defaults - skipSpace(self); - while (eatByte(self, ',')) { - skipSpace(self); - const name = try skipToAndOver(self, '='); - inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { - const field_name = arg_field.name; - if (mem.eql(u8, name, field_name)) { - const NonOptional = switch (@typeInfo(arg_field.field_type)) { - .Optional => |info| info.child, - else => arg_field.field_type, - }; - @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx); - break; - } - } else { - return self.fail("unrecognized keyword parameter: '{s}'", .{name}); - } - skipSpace(self); - } - try requireEatBytes(self, ")"); - - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = inst_name, - .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]), - .inst = &inst_specific.base, - }; - - return decl; - } - - fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T { - if (@typeInfo(T) == .Enum) { - const start = self.i; - while (true) : (self.i += 1) switch (self.source[self.i]) { - ' ', '\n', ',', ')' => { - const enum_name = self.source[start..self.i]; - return std.meta.stringToEnum(T, enum_name) orelse { - return self.fail("tag '{s}' not a member of enum '{s}'", .{ enum_name, @typeName(T) }); - }; - }, - 0 => return self.failByte(0), - else => continue, - }; - } - switch (T) { - Module.Body => return parseBody(self, body_ctx), - bool => { - const bool_value = switch (self.source[self.i]) { - '0' => false, - '1' => true, - else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}), - }; - self.i += 1; - return bool_value; - }, - []*Inst => { - try requireEatBytes(self, "["); - skipSpace(self); - if (eatByte(self, ']')) return &[0]*Inst{}; - - var instructions = std.ArrayList(*Inst).init(&self.arena.allocator); - while (true) { - skipSpace(self); - try instructions.append(try parseParameterInst(self, body_ctx)); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - try requireEatBytes(self, "]"); - return instructions.toOwnedSlice(); - }, - *Inst => return parseParameterInst(self, body_ctx), - []u8, []const u8 => return self.parseStringLiteral(), - BigIntConst => return self.parseIntegerLiteral(), - usize => { - const big_int = try self.parseIntegerLiteral(); - return big_int.to(usize) catch |err| return self.fail("integer literal: {s}", .{@errorName(err)}); - }, - TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), - *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), - *Inst.Block => { - const name = try self.parseStringLiteral(); - return self.block_table.get(name).?; - }, - *Inst.Loop => { - const name = try self.parseStringLiteral(); - return self.loop_table.get(name).?; - }, - [][]const u8 => { - try requireEatBytes(self, "["); - skipSpace(self); - if (eatByte(self, ']')) return &[0][]const u8{}; - - var strings = std.ArrayList([]const u8).init(&self.arena.allocator); - while (true) { - skipSpace(self); - try strings.append(try self.parseStringLiteral()); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - try requireEatBytes(self, "]"); - return strings.toOwnedSlice(); - }, - []Inst.SwitchBr.Case => { - try requireEatBytes(self, "{"); - skipSpace(self); - if (eatByte(self, '}')) return &[0]Inst.SwitchBr.Case{}; - - var cases = std.ArrayList(Inst.SwitchBr.Case).init(&self.arena.allocator); - while (true) { - const cur = try cases.addOne(); - skipSpace(self); - cur.item = try self.parseParameterGeneric(*Inst, body_ctx); - skipSpace(self); - try requireEatBytes(self, "=>"); - cur.body = try self.parseBody(body_ctx); - skipSpace(self); - if (!eatByte(self, ',')) break; - } - skipSpace(self); - try requireEatBytes(self, "}"); - return cases.toOwnedSlice(); - }, - else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), - } - return self.fail("TODO parse parameter {s}", .{@typeName(T)}); - } - - fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst { - const local_ref = switch (self.source[self.i]) { - '@' => false, - '%' => true, - else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), - }; - const map = if (local_ref) - if (body_ctx) |bc| - bc.name_map - else - return self.fail("referencing a % instruction in global scope", .{}) - else - self.global_name_map; - - self.i += 1; - const name_start = self.i; - while (true) : (self.i += 1) switch (self.source[self.i]) { - 0, ' ', '\n', ',', ')', ']' => break, - else => continue, - }; - const ident = self.source[name_start..self.i]; - return map.get(ident) orelse { - const bad_name = self.source[name_start - 1 .. self.i]; - const src = name_start - 1; - if (local_ref) { - self.i = src; - return self.fail("unrecognized identifier: {s}", .{bad_name}); - } else { - const declval = try self.arena.allocator.create(Inst.DeclVal); - declval.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclVal.base_tag, - }, - .positionals = .{ .name = ident }, - .kw_args = .{}, - }; - return &declval.base; - } - }; - } - - fn generateName(self: *Parser) ![]u8 { - const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.unnamed_index}); - self.unnamed_index += 1; - return result; - } -}; - -pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { - var ctx: EmitZIR = .{ - .allocator = allocator, - .decls = .{}, - .arena = std.heap.ArenaAllocator.init(allocator), - .old_module = old_module, - .next_auto_name = 0, - .names = std.StringArrayHashMap(void).init(allocator), - .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), - .indent = 0, - .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), - .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), - }; - errdefer ctx.metadata.deinit(); - errdefer ctx.body_metadata.deinit(); - defer ctx.block_table.deinit(); - defer ctx.loop_table.deinit(); - defer ctx.decls.deinit(allocator); - defer ctx.names.deinit(); - defer ctx.primitive_table.deinit(); - errdefer ctx.arena.deinit(); - - try ctx.emit(); - - return Module{ - .decls = ctx.decls.toOwnedSlice(allocator), - .arena = ctx.arena, - .metadata = ctx.metadata, - .body_metadata = ctx.body_metadata, - }; -} - /// For debugging purposes, prints a function representation to stderr. pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { const allocator = old_module.gpa; @@ -2374,1052 +1856,14 @@ const DumpTzir = struct { } }; -const EmitZIR = struct { - allocator: *Allocator, - arena: std.heap.ArenaAllocator, - old_module: *const IrModule, - decls: std.ArrayListUnmanaged(*Decl), - names: std.StringArrayHashMap(void), - next_auto_name: usize, - primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl), - indent: usize, - block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block), - loop_table: std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop), - metadata: std.AutoHashMap(*Inst, Module.MetaData), - body_metadata: std.AutoHashMap(*Module.Body, Module.BodyMetaData), - - fn emit(self: *EmitZIR) !void { - // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced - // by the hash table. - var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); - defer src_decls.deinit(); - try src_decls.ensureCapacity(self.old_module.decl_table.items().len); - try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len); - try self.names.ensureCapacity(self.old_module.decl_table.items().len); - - for (self.old_module.decl_table.items()) |entry| { - const decl = entry.value; - src_decls.appendAssumeCapacity(decl); - self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); - } - std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { - fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { - return a.src_index < b.src_index; - } - }).lessThan); - - // Emit all the decls. - for (src_decls.items) |ir_decl| { - switch (ir_decl.analysis) { - .unreferenced => continue, - - .complete => {}, - .codegen_failure => {}, // We still can emit the ZIR. - .codegen_failure_retryable => {}, // We still can emit the ZIR. - - .in_progress => unreachable, - .outdated => unreachable, - - .sema_failure, - .sema_failure_retryable, - .dependency_failure, - => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| { - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = ir_decl.src(), - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, err_msg.msg); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = ir_decl.src(), - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = err_msg.msg, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = mem.spanZ(ir_decl.name), - .contents_hash = undefined, - .inst = &fail_inst.base, - }; - try self.decls.append(self.allocator, decl); - continue; - }, - } - if (self.old_module.export_owners.get(ir_decl)) |exports| { - for (exports) |module_export| { - const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); - const export_inst = try self.arena.allocator.create(Inst.Export); - export_inst.* = .{ - .base = .{ - .src = module_export.src, - .tag = Inst.Export.base_tag, - }, - .positionals = .{ - .symbol_name = symbol_name.inst, - .decl_name = mem.spanZ(module_export.exported_decl.name), - }, - .kw_args = .{}, - }; - _ = try self.emitUnnamedDecl(&export_inst.base); - } - } - const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value); - new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); - } - } - - const ZirBody = struct { - inst_table: *std.AutoHashMap(*ir.Inst, *Inst), - instructions: *std.ArrayList(*Inst), - }; - - fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst { - if (inst.cast(ir.Inst.Constant)) |const_inst| { - const new_inst = if (const_inst.val.castTag(.function)) |func_pl| blk: { - const owner_decl = func_pl.data.owner_decl; - break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); - } else if (const_inst.val.castTag(.decl_ref)) |declref| blk: { - const decl_ref = try self.emitDeclRef(inst.src, declref.data); - try new_body.instructions.append(decl_ref); - break :blk decl_ref; - } else if (const_inst.val.castTag(.variable)) |var_pl| blk: { - const owner_decl = var_pl.data.owner_decl; - break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); - } else blk: { - break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst; - }; - _ = try new_body.inst_table.put(inst, new_inst); - return new_inst; - } else { - return new_body.inst_table.get(inst).?; - } - } - - fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst { - const declval = try self.arena.allocator.create(Inst.DeclVal); - declval.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclVal.base_tag, - }, - .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) }, - .kw_args = .{}, - }; - return &declval.base; - } - - fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl { - const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); - const int_inst = try self.arena.allocator.create(Inst.Int); - int_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Int.base_tag, - }, - .positionals = .{ - .int = val.toBigInt(big_int_space), - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&int_inst.base); - } - - fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst { - const declref_inst = try self.arena.allocator.create(Inst.DeclRef); - declref_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.DeclRef.base_tag, - }, - .positionals = .{ - .name = mem.spanZ(module_decl.name), - }, - .kw_args = .{}, - }; - return &declref_inst.base; - } - - fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl { - var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator); - defer inst_table.deinit(); - - var instructions = std.ArrayList(*Inst).init(self.allocator); - defer instructions.deinit(); - - switch (module_fn.state) { - .queued => unreachable, - .in_progress => unreachable, - .inline_only => unreachable, - .success => { - try self.emitBody(module_fn.body, &inst_table, &instructions); - }, - .sema_failure => { - const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = src, - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, err_msg.msg); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = msg_str, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - try instructions.append(&fail_inst.base); - }, - .dependency_failure => { - const fail_inst = try self.arena.allocator.create(Inst.UnOp); - fail_inst.* = .{ - .base = .{ - .src = src, - .tag = .compileerror, - }, - .positionals = .{ - .operand = blk: { - const msg_str = try self.arena.allocator.dupe(u8, "depends on another failed Decl"); - - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = msg_str, - }, - .kw_args = .{}, - }; - break :blk &str_inst.base; - }, - }, - .kw_args = .{}, - }; - try instructions.append(&fail_inst.base); - }, - } - - const fn_type = try self.emitType(src, ty); - - const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); - mem.copy(*Inst, arena_instrs, instructions.items); - - const fn_inst = try self.arena.allocator.create(Inst.Fn); - fn_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Fn.base_tag, - }, - .positionals = .{ - .fn_type = fn_type.inst, - .body = .{ .instructions = arena_instrs }, - }, - .kw_args = .{ - .is_inline = module_fn.state == .inline_only, - }, - }; - return self.emitUnnamedDecl(&fn_inst.base); - } - - fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl { - const allocator = &self.arena.allocator; - if (typed_value.val.castTag(.decl_ref)) |decl_ref| { - const decl = decl_ref.data; - return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl)); - } else if (typed_value.val.castTag(.variable)) |variable| { - return self.emitTypedValue(src, .{ - .ty = typed_value.ty, - .val = variable.data.init, - }); - } - if (typed_value.val.isUndef()) { - const as_inst = try self.arena.allocator.create(Inst.BinOp); - as_inst.* = .{ - .base = .{ - .tag = .as, - .src = src, - }, - .positionals = .{ - .lhs = (try self.emitType(src, typed_value.ty)).inst, - .rhs = (try self.emitPrimitive(src, .@"undefined")).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&as_inst.base); - } - switch (typed_value.ty.zigTypeTag()) { - .Pointer => { - const ptr_elem_type = typed_value.ty.elemType(); - switch (ptr_elem_type.zigTypeTag()) { - .Array => { - // TODO more checks to make sure this can be emitted as a string literal - //const array_elem_type = ptr_elem_type.elemType(); - //if (array_elem_type.eql(Type.initTag(.u8)) and - // ptr_elem_type.hasSentinel(Value.initTag(.zero))) - //{ - //} - const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { - error.AnalysisFail => unreachable, - else => |e| return e, - }; - return self.emitStringLiteral(src, bytes); - }, - else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {s}", .{@tagName(t)}), - } - }, - .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val), - .Int => { - const as_inst = try self.arena.allocator.create(Inst.BinOp); - as_inst.* = .{ - .base = .{ - .tag = .as, - .src = src, - }, - .positionals = .{ - .lhs = (try self.emitType(src, typed_value.ty)).inst, - .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&as_inst.base); - }, - .Type => { - const ty = try typed_value.val.toType(&self.arena.allocator); - return self.emitType(src, ty); - }, - .Fn => { - const module_fn = typed_value.val.castTag(.function).?.data; - return self.emitFn(module_fn, src, typed_value.ty); - }, - .Array => { - // TODO more checks to make sure this can be emitted as a string literal - //const array_elem_type = ptr_elem_type.elemType(); - //if (array_elem_type.eql(Type.initTag(.u8)) and - // ptr_elem_type.hasSentinel(Value.initTag(.zero))) - //{ - //} - const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { - error.AnalysisFail => unreachable, - else => |e| return e, - }; - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = bytes, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&str_inst.base); - }, - .Void => return self.emitPrimitive(src, .void_value), - .Bool => if (typed_value.val.toBool()) - return self.emitPrimitive(src, .@"true") - else - return self.emitPrimitive(src, .@"false"), - .EnumLiteral => { - const enum_literal = typed_value.val.castTag(.enum_literal).?; - const inst = try self.arena.allocator.create(Inst.Str); - inst.* = .{ - .base = .{ - .src = src, - .tag = .enum_literal, - }, - .positionals = .{ - .bytes = enum_literal.data, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - }, - else => |t| std.debug.panic("TODO implement emitTypedValue for {s}", .{@tagName(t)}), - } - } - - fn emitNoOp(self: *EmitZIR, src: usize, old_inst: *ir.Inst.NoOp, tag: Inst.Tag) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.NoOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{}, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitUnOp( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.UnOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.UnOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitBinOp( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.BinOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.BinOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .lhs = try self.resolveInst(new_body, old_inst.lhs), - .rhs = try self.resolveInst(new_body, old_inst.rhs), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitCast( - self: *EmitZIR, - src: usize, - new_body: ZirBody, - old_inst: *ir.Inst.UnOp, - tag: Inst.Tag, - ) Allocator.Error!*Inst { - const new_inst = try self.arena.allocator.create(Inst.BinOp); - new_inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst, - .rhs = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - return &new_inst.base; - } - - fn emitBody( - self: *EmitZIR, - body: ir.Body, - inst_table: *std.AutoHashMap(*ir.Inst, *Inst), - instructions: *std.ArrayList(*Inst), - ) Allocator.Error!void { - const new_body = ZirBody{ - .inst_table = inst_table, - .instructions = instructions, - }; - for (body.instructions) |inst| { - const new_inst = switch (inst.tag) { - .constant => unreachable, // excluded from function bodies - - .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint), - .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck), - .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid), - .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt), - - .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot), - .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"), - .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint), - .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull), - .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), - .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr), - .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), - .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), - .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), - .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as), - - .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), - .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), - .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store), - .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt), - .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte), - .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq), - .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte), - .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt), - .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq), - .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland), - .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor), - .bitand => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitand).?, .bitand), - .bitor => try self.emitBinOp(inst.src, new_body, inst.castTag(.bitor).?, .bitor), - .xor => try self.emitBinOp(inst.src, new_body, inst.castTag(.xor).?, .xor), - - .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast), - .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast), - .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast), - - .alloc => blk: { - const new_inst = try self.arena.allocator.create(Inst.UnOp); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = .alloc, - }, - .positionals = .{ - .operand = (try self.emitType(inst.src, inst.ty)).inst, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .arg => blk: { - const old_inst = inst.castTag(.arg).?; - const new_inst = try self.arena.allocator.create(Inst.Arg); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = .arg, - }, - .positionals = .{ - .name = try self.arena.allocator.dupe(u8, mem.spanZ(old_inst.name)), - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .block => blk: { - const old_inst = inst.castTag(.block).?; - const new_inst = try self.arena.allocator.create(Inst.Block); - - try self.block_table.put(old_inst, new_inst); - - var block_body = std.ArrayList(*Inst).init(self.allocator); - defer block_body.deinit(); - - try self.emitBody(old_inst.body, inst_table, &block_body); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Block.base_tag, - }, - .positionals = .{ - .body = .{ .instructions = block_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - - break :blk &new_inst.base; - }, - - .loop => blk: { - const old_inst = inst.castTag(.loop).?; - const new_inst = try self.arena.allocator.create(Inst.Loop); - - try self.loop_table.put(old_inst, new_inst); - - var loop_body = std.ArrayList(*Inst).init(self.allocator); - defer loop_body.deinit(); - - try self.emitBody(old_inst.body, inst_table, &loop_body); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Loop.base_tag, - }, - .positionals = .{ - .body = .{ .instructions = loop_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - - break :blk &new_inst.base; - }, - - .brvoid => blk: { - const old_inst = inst.cast(ir.Inst.BrVoid).?; - const new_block = self.block_table.get(old_inst.block).?; - const new_inst = try self.arena.allocator.create(Inst.BreakVoid); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.BreakVoid.base_tag, - }, - .positionals = .{ - .block = new_block, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .br => blk: { - const old_inst = inst.castTag(.br).?; - const new_block = self.block_table.get(old_inst.block).?; - const new_inst = try self.arena.allocator.create(Inst.Break); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Break.base_tag, - }, - .positionals = .{ - .block = new_block, - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .call => blk: { - const old_inst = inst.castTag(.call).?; - const new_inst = try self.arena.allocator.create(Inst.Call); - - const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len); - for (args) |*elem, i| { - elem.* = try self.resolveInst(new_body, old_inst.args[i]); - } - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Call.base_tag, - }, - .positionals = .{ - .func = try self.resolveInst(new_body, old_inst.func), - .args = args, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - - .assembly => blk: { - const old_inst = inst.castTag(.assembly).?; - const new_inst = try self.arena.allocator.create(Inst.Asm); - - const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len); - for (inputs) |*elem, i| { - elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst; - } - - const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len); - for (clobbers) |*elem, i| { - elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst; - } - - const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len); - for (args) |*elem, i| { - elem.* = try self.resolveInst(new_body, old_inst.args[i]); - } - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.Asm.base_tag, - }, - .positionals = .{ - .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst, - .return_type = (try self.emitType(inst.src, inst.ty)).inst, - }, - .kw_args = .{ - .@"volatile" = old_inst.is_volatile, - .output = if (old_inst.output) |o| - (try self.emitStringLiteral(inst.src, o)).inst - else - null, - .inputs = inputs, - .clobbers = clobbers, - .args = args, - }, - }; - break :blk &new_inst.base; - }, - - .condbr => blk: { - const old_inst = inst.castTag(.condbr).?; - - var then_body = std.ArrayList(*Inst).init(self.allocator); - var else_body = std.ArrayList(*Inst).init(self.allocator); - - defer then_body.deinit(); - defer else_body.deinit(); - - const then_deaths = try self.arena.allocator.alloc(*Inst, old_inst.thenDeaths().len); - const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len); - - for (old_inst.thenDeaths()) |death, i| { - then_deaths[i] = try self.resolveInst(new_body, death); - } - for (old_inst.elseDeaths()) |death, i| { - else_deaths[i] = try self.resolveInst(new_body, death); - } - - try self.emitBody(old_inst.then_body, inst_table, &then_body); - try self.emitBody(old_inst.else_body, inst_table, &else_body); - - const new_inst = try self.arena.allocator.create(Inst.CondBr); - - try self.body_metadata.put(&new_inst.positionals.then_body, .{ .deaths = then_deaths }); - try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths }); - - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.CondBr.base_tag, - }, - .positionals = .{ - .condition = try self.resolveInst(new_body, old_inst.condition), - .then_body = .{ .instructions = then_body.toOwnedSlice() }, - .else_body = .{ .instructions = else_body.toOwnedSlice() }, - }, - .kw_args = .{}, - }; - break :blk &new_inst.base; - }, - .switchbr => blk: { - const old_inst = inst.castTag(.switchbr).?; - const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len); - const new_inst = try self.arena.allocator.create(Inst.SwitchBr); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.SwitchBr.base_tag, - }, - .positionals = .{ - .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr), - .cases = cases, - .items = &[_]*Inst{}, // TODO this should actually be populated - .else_body = undefined, // populated below - }, - .kw_args = .{}, - }; - - var body_tmp = std.ArrayList(*Inst).init(self.allocator); - defer body_tmp.deinit(); - - for (old_inst.cases) |*case, i| { - body_tmp.items.len = 0; - - const case_deaths = try self.arena.allocator.alloc(*Inst, old_inst.caseDeaths(i).len); - for (old_inst.caseDeaths(i)) |death, j| { - case_deaths[j] = try self.resolveInst(new_body, death); - } - try self.body_metadata.put(&cases[i].body, .{ .deaths = case_deaths }); - - try self.emitBody(case.body, inst_table, &body_tmp); - const item = (try self.emitTypedValue(inst.src, .{ - .ty = old_inst.target_ptr.ty.elemType(), - .val = case.item, - })).inst; - - cases[i] = .{ - .item = item, - .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) }, - }; - } - { // else - const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len); - for (old_inst.elseDeaths()) |death, j| { - else_deaths[j] = try self.resolveInst(new_body, death); - } - try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths }); - - body_tmp.items.len = 0; - try self.emitBody(old_inst.else_body, inst_table, &body_tmp); - new_inst.positionals.else_body = .{ - .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items), - }; - } - - break :blk &new_inst.base; - }, - .varptr => @panic("TODO"), - }; - try self.metadata.put(new_inst, .{ - .deaths = inst.deaths, - .addr = @ptrToInt(inst), - }); - try instructions.append(new_inst); - try inst_table.put(inst, new_inst); - } - } - - fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl { - switch (ty.tag()) { - .i8 => return self.emitPrimitive(src, .i8), - .u8 => return self.emitPrimitive(src, .u8), - .i16 => return self.emitPrimitive(src, .i16), - .u16 => return self.emitPrimitive(src, .u16), - .i32 => return self.emitPrimitive(src, .i32), - .u32 => return self.emitPrimitive(src, .u32), - .i64 => return self.emitPrimitive(src, .i64), - .u64 => return self.emitPrimitive(src, .u64), - .isize => return self.emitPrimitive(src, .isize), - .usize => return self.emitPrimitive(src, .usize), - .c_short => return self.emitPrimitive(src, .c_short), - .c_ushort => return self.emitPrimitive(src, .c_ushort), - .c_int => return self.emitPrimitive(src, .c_int), - .c_uint => return self.emitPrimitive(src, .c_uint), - .c_long => return self.emitPrimitive(src, .c_long), - .c_ulong => return self.emitPrimitive(src, .c_ulong), - .c_longlong => return self.emitPrimitive(src, .c_longlong), - .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong), - .c_longdouble => return self.emitPrimitive(src, .c_longdouble), - .c_void => return self.emitPrimitive(src, .c_void), - .f16 => return self.emitPrimitive(src, .f16), - .f32 => return self.emitPrimitive(src, .f32), - .f64 => return self.emitPrimitive(src, .f64), - .f128 => return self.emitPrimitive(src, .f128), - .anyerror => return self.emitPrimitive(src, .anyerror), - else => switch (ty.zigTypeTag()) { - .Bool => return self.emitPrimitive(src, .bool), - .Void => return self.emitPrimitive(src, .void), - .NoReturn => return self.emitPrimitive(src, .noreturn), - .Type => return self.emitPrimitive(src, .type), - .ComptimeInt => return self.emitPrimitive(src, .comptime_int), - .ComptimeFloat => return self.emitPrimitive(src, .comptime_float), - .Fn => { - const param_types = try self.allocator.alloc(Type, ty.fnParamLen()); - defer self.allocator.free(param_types); - - ty.fnParamTypes(param_types); - const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len); - for (param_types) |param_type, i| { - emitted_params[i] = (try self.emitType(src, param_type)).inst; - } - - const fntype_inst = try self.arena.allocator.create(Inst.FnType); - fntype_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.FnType.base_tag, - }, - .positionals = .{ - .param_types = emitted_params, - .return_type = (try self.emitType(src, ty.fnReturnType())).inst, - }, - .kw_args = .{ - .cc = ty.fnCallingConvention(), - }, - }; - return self.emitUnnamedDecl(&fntype_inst.base); - }, - .Int => { - const info = ty.intInfo(self.old_module.getTarget()); - const signed = try self.emitPrimitive(src, switch (info.signedness) { - .signed => .@"true", - .unsigned => .@"false", - }); - const bits_val = try Value.Tag.int_u64.create(&self.arena.allocator, info.bits); - const bits = try self.emitComptimeIntVal(src, bits_val); - const inttype_inst = try self.arena.allocator.create(Inst.IntType); - inttype_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.IntType.base_tag, - }, - .positionals = .{ - .signed = signed.inst, - .bits = bits.inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inttype_inst.base); - }, - .Pointer => { - if (ty.isSinglePointer()) { - const inst = try self.arena.allocator.create(Inst.UnOp); - const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type; - inst.* = .{ - .base = .{ - .src = src, - .tag = tag, - }, - .positionals = .{ - .operand = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - } else { - std.debug.panic("TODO implement emitType for {}", .{ty}); - } - }, - .Optional => { - var buf: Type.Payload.ElemType = undefined; - const inst = try self.arena.allocator.create(Inst.UnOp); - inst.* = .{ - .base = .{ - .src = src, - .tag = .optional_type, - }, - .positionals = .{ - .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&inst.base); - }, - .Array => { - var len_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = ty.arrayLen(), - }; - const len = Value.initPayload(&len_pl.base); - - const inst = if (ty.sentinel()) |sentinel| blk: { - const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel); - inst.* = .{ - .base = .{ - .src = src, - .tag = .array_type, - }, - .positionals = .{ - .len = (try self.emitTypedValue(src, .{ - .ty = Type.initTag(.usize), - .val = len, - })).inst, - .sentinel = (try self.emitTypedValue(src, .{ - .ty = ty.elemType(), - .val = sentinel, - })).inst, - .elem_type = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - break :blk &inst.base; - } else blk: { - const inst = try self.arena.allocator.create(Inst.BinOp); - inst.* = .{ - .base = .{ - .src = src, - .tag = .array_type, - }, - .positionals = .{ - .lhs = (try self.emitTypedValue(src, .{ - .ty = Type.initTag(.usize), - .val = len, - })).inst, - .rhs = (try self.emitType(src, ty.elemType())).inst, - }, - .kw_args = .{}, - }; - break :blk &inst.base; - }; - return self.emitUnnamedDecl(inst); - }, - else => std.debug.panic("TODO implement emitType for {}", .{ty}), - }, - } - } - - fn autoName(self: *EmitZIR) ![]u8 { - while (true) { - const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.next_auto_name}); - self.next_auto_name += 1; - const gop = try self.names.getOrPut(proposed_name); - if (!gop.found_existing) { - gop.entry.value = {}; - return proposed_name; - } - } - } - - fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl { - const gop = try self.primitive_table.getOrPut(tag); - if (!gop.found_existing) { - const primitive_inst = try self.arena.allocator.create(Inst.Primitive); - primitive_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Primitive.base_tag, - }, - .positionals = .{ - .tag = tag, - }, - .kw_args = .{}, - }; - gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base); - } - return gop.entry.value; - } - - fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { - const str_inst = try self.arena.allocator.create(Inst.Str); - str_inst.* = .{ - .base = .{ - .src = src, - .tag = Inst.Str.base_tag, - }, - .positionals = .{ - .bytes = str, - }, - .kw_args = .{}, - }; - return self.emitUnnamedDecl(&str_inst.base); - } - - fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl { - const decl = try self.arena.allocator.create(Decl); - decl.* = .{ - .name = try self.autoName(), - .contents_hash = undefined, - .inst = inst, - }; - try self.decls.append(self.allocator, decl); - return decl; - } -}; - /// For debugging purposes, like dumpFn but for unanalyzed zir blocks pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8, instructions: []*Inst) !void { var fib = std.heap.FixedBufferAllocator.init(&[_]u8{}); var module = Module{ - .decls = &[_]*Decl{}, + .decls = &[_]*Module.Decl{}, .arena = std.heap.ArenaAllocator.init(&fib.allocator), .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(&fib.allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(&fib.allocator), + .body_metadata = std.AutoHashMap(*Body, Module.BodyMetaData).init(&fib.allocator), }; var write = Writer{ .module = &module, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 6302ab29e4..36eb5f4239 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -63,7 +63,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?), .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?), .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?), - .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?), @@ -166,7 +165,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! } } -pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Module.Body) !void { +pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Body) !void { const tracy = trace(@src()); defer tracy.end(); @@ -183,7 +182,7 @@ pub fn analyzeBodyValueAsType( mod: *Module, block_scope: *Scope.Block, zir_result_inst: *zir.Inst, - body: zir.Module.Body, + body: zir.Body, ) !Type { try analyzeBody(mod, block_scope, body); const result_inst = block_scope.inst_table.get(zir_result_inst).?; @@ -191,84 +190,6 @@ pub fn analyzeBodyValueAsType( return val.toType(block_scope.base.arena()); } -pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool { - var decl_scope: Scope.DeclAnalysis = .{ - .decl = decl, - .arena = std.heap.ArenaAllocator.init(mod.gpa), - }; - errdefer decl_scope.arena.deinit(); - - decl.analysis = .in_progress; - - const typed_value = try analyzeConstInst(mod, &decl_scope.base, src_decl.inst); - const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); - - var prev_type_has_bits = false; - var type_changed = true; - - if (decl.typedValueManaged()) |tvm| { - prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); - type_changed = !tvm.typed_value.ty.eql(typed_value.ty); - - tvm.deinit(mod.gpa); - } - - arena_state.* = decl_scope.arena.state; - decl.typed_value = .{ - .most_recent = .{ - .typed_value = typed_value, - .arena = arena_state, - }, - }; - decl.analysis = .complete; - decl.generation = mod.generation; - if (typed_value.ty.hasCodeGenBits()) { - // We don't fully codegen the decl until later, but we do need to reserve a global - // offset table index for it. This allows us to codegen decls out of dependency order, - // increasing how many computations can be done in parallel. - try mod.comp.bin_file.allocateDeclIndexes(decl); - try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - } else if (prev_type_has_bits) { - mod.comp.bin_file.freeDecl(decl); - } - - return type_changed; -} - -pub fn resolveZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { - const zir_module = mod.root_scope.cast(Scope.ZIRModule).?; - const entry = zir_module.contents.module.findDecl(src_decl.name).?; - return resolveZirDeclHavingIndex(mod, scope, src_decl, entry.index); -} - -fn resolveZirDeclHavingIndex(mod: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { - const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); - const decl = mod.decl_table.get(name_hash).?; - decl.src_index = src_index; - try mod.ensureDeclAnalyzed(decl); - return decl; -} - -/// Declares a dependency on the decl. -fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { - const decl = try resolveZirDecl(mod, scope, src_decl); - switch (decl.analysis) { - .unreferenced => unreachable, - .in_progress => unreachable, - .outdated => unreachable, - - .dependency_failure, - .sema_failure, - .sema_failure_retryable, - .codegen_failure, - .codegen_failure_retryable, - => return error.AnalysisFail, - - .complete => {}, - } - return decl; -} - pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst { const block = scope.cast(Scope.Block).?; return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses! @@ -640,22 +561,28 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In } fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst { - std.debug.print("| ", .{}); - for (inst.positionals.to_log) |item, i| { - const to_log = try resolveInst(mod, scope, item); - if (to_log.value()) |val| { - std.debug.print("{}", .{val}); + var managed = mod.compile_log_text.toManaged(mod.gpa); + defer mod.compile_log_text = managed.moveToUnmanaged(); + const writer = managed.writer(); + + for (inst.positionals.to_log) |arg_inst, i| { + if (i != 0) try writer.print(", ", .{}); + + const arg = try resolveInst(mod, scope, arg_inst); + if (arg.value()) |val| { + try writer.print("@as({}, {})", .{ arg.ty, val }); } else { - std.debug.print("(runtime value)", .{}); + try writer.print("@as({}, [runtime value])", .{arg.ty}); } - if (i != inst.positionals.to_log.len - 1) std.debug.print(", ", .{}); } - std.debug.print("\n", .{}); - if (!inst.kw_args.seen) { + try writer.print("\n", .{}); - // so that we do not give multiple compile errors if it gets evaled twice - inst.kw_args.seen = true; - try mod.failCompileLog(scope, inst.base.src); + const gop = try mod.compile_log_decls.getOrPut(mod.gpa, scope.ownerDecl().?); + if (!gop.found_existing) { + gop.entry.value = .{ + .file_scope = scope.getFileScope(), + .byte_offset = inst.base.src, + }; } return mod.constVoid(scope, inst.base.src); } @@ -705,7 +632,8 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .inlining = parent_block.inlining, @@ -732,7 +660,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .label = null, @@ -744,13 +673,14 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c try analyzeBody(mod, &child_block, inst.positionals.body); - try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); - - // comptime blocks won't generate any runtime values - if (child_block.instructions.items.len == 0) - return mod.constVoid(scope, inst.base.src); + // Move the analyzed instructions into the parent block arena. + const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); + try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); - return parent_block.instructions.items[parent_block.instructions.items.len - 1]; + // The result of a flat block is the last instruction. + const zir_inst_list = inst.positionals.body.instructions; + const last_zir_inst = zir_inst_list[zir_inst_list.len - 1]; + return resolveInst(mod, scope, last_zir_inst); } fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { @@ -775,7 +705,8 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, // TODO @as here is working around a stage1 miscompilation bug :( @@ -890,22 +821,15 @@ fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); + return mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl); } fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - const decl = try analyzeDeclVal(mod, scope, inst); - const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl); - return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); -} - -fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const decl = inst.positionals.decl; - return mod.analyzeDeclRef(scope, inst.base.src, decl); + const decl_ref = try mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl); + // TODO look into avoiding the call to analyzeDeref here + return mod.analyzeDeref(scope, inst.base.src, decl_ref, inst.base.src); } fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { @@ -1032,9 +956,8 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError .parent = null, .inst_table = &inst_table, .func = module_fn, - // Note that we pass the caller's Decl, not the callee. This causes - // compile errors to be attached (correctly) to the caller's Decl. - .decl = scope.decl().?, + .owner_decl = scope.ownerDecl().?, + .src_decl = module_fn.owner_decl, .instructions = .{}, .arena = scope.arena(), .label = null, @@ -1069,7 +992,7 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued, .zir = fn_inst.positionals.body, .body = undefined, - .owner_decl = scope.decl().?, + .owner_decl = scope.ownerDecl().?, }; return mod.constInst(scope, fn_inst.base.src, .{ .ty = fn_type, @@ -1391,7 +1314,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr return mod.analyzeDeclRef(scope, fieldptr.base.src, decl); } - if (&container_scope.file_scope.base == mod.root_scope) { + if (container_scope.file_scope == mod.root_scope) { return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{s}'", .{field_name}); } else { return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); @@ -1606,7 +1529,8 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .inlining = parent_block.inlining, @@ -2182,7 +2106,8 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .inlining = parent_block.inlining, @@ -2196,7 +2121,8 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE .parent = parent_block, .inst_table = parent_block.inst_table, .func = parent_block.func, - .decl = parent_block.decl, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, .instructions = .{}, .arena = parent_block.arena, .inlining = parent_block.inlining, @@ -2294,17 +2220,6 @@ fn analyzeBreak( } else unreachable; } -fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { - const decl_name = inst.positionals.name; - const zir_module = scope.namespace().cast(Scope.ZIRModule).?; - const src_decl = zir_module.contents.module.findDecl(decl_name) orelse - return mod.fail(scope, inst.base.src, "use of undeclared identifier '{s}'", .{decl_name}); - - const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl); - - return decl; -} - fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); diff --git a/test/stage2/test.zig b/test/stage2/test.zig index f25f07adbf..cb5e9bebbb 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -36,7 +36,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", linux_x64); - case.addError("", &[_][]const u8{"no entry point found"}); + case.addError("", &[_][]const u8{"error: no entry point found"}); // Incorrect return type case.addError( @@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", macosx_x64); - case.addError("", &[_][]const u8{"no entry point found"}); + case.addError("", &[_][]const u8{"error: no entry point found"}); // Incorrect return type case.addError( @@ -1243,24 +1243,46 @@ pub fn addCases(ctx: *TestContext) !void { \\} , &[_][]const u8{":3:9: error: redefinition of 'testing'"}); } - ctx.compileError("compileLog", linux_x64, - \\export fn _start() noreturn { - \\ const b = true; - \\ var f: u32 = 1; - \\ @compileLog(b, 20, f, x); - \\ @compileLog(1000); - \\ var bruh: usize = true; - \\ unreachable; - \\} - \\fn x() void {} - , &[_][]const u8{ - ":4:3: error: found compile log statement", - ":5:3: error: found compile log statement", - ":6:21: error: expected usize, found bool", - }); - // TODO if this is here it invalidates the compile error checker: - // "| true, 20, (runtime value), (function)" - // "| 1000" + + { + // TODO make the test harness support checking the compile log output too + var case = ctx.obj("@compileLog", linux_x64); + // The other compile error prevents emission of a "found compile log" statement. + case.addError( + \\export fn _start() noreturn { + \\ const b = true; + \\ var f: u32 = 1; + \\ @compileLog(b, 20, f, x); + \\ @compileLog(1000); + \\ var bruh: usize = true; + \\ unreachable; + \\} + \\export fn other() void { + \\ @compileLog(1234); + \\} + \\fn x() void {} + , &[_][]const u8{ + ":6:23: error: expected usize, found bool", + }); + + // Now only compile log statements remain. One per Decl. + case.addError( + \\export fn _start() noreturn { + \\ const b = true; + \\ var f: u32 = 1; + \\ @compileLog(b, 20, f, x); + \\ @compileLog(1000); + \\ unreachable; + \\} + \\export fn other() void { + \\ @compileLog(1234); + \\} + \\fn x() void {} + , &[_][]const u8{ + ":11:8: error: found compile log statement", + ":4:5: note: also here", + }); + } { var case = ctx.obj("extern variable has no type", linux_x64); -- cgit v1.2.3 From 02c138fe7011346ebab5e4b24ba0f8575bb52173 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 18 Jan 2021 23:47:25 +0100 Subject: SPIR-V: Add glsl450 and vulkan spir-v operating system definitions --- lib/std/target.zig | 18 +++++++++++++++--- lib/std/zig/cross_target.zig | 4 ++++ src/codegen/llvm.zig | 5 +++++ src/link/SpirV.zig | 20 ++++++++++++++++++++ src/target.zig | 4 ++-- src/type.zig | 2 ++ 6 files changed, 48 insertions(+), 5 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/lib/std/target.zig b/lib/std/target.zig index b3e0f8afdd..66b5f560c1 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -57,7 +57,9 @@ pub const Target = struct { wasi, emscripten, uefi, - opencl, // SPIR-V on OpenCL + opencl, + glsl450, + vulkan, other, pub fn isDarwin(tag: Tag) bool { @@ -249,7 +251,9 @@ pub const Target = struct { .wasi, .emscripten, .uefi, - .opencl, + .opencl, // TODO: OpenCL versions + .glsl450, // TODO: GLSL versions + .vulkan, .other, => return .{ .none = {} }, @@ -406,6 +410,8 @@ pub const Target = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => false, }; @@ -497,7 +503,9 @@ pub const Target = struct { .wasi, .emscripten, => return .musl, - .opencl, // TODO: Where should this go? + .opencl, // TODO: SPIR-V ABIs with Linkage capability + .glsl450, + .vulkan, => return .none, } } @@ -1367,6 +1375,8 @@ pub const Target = struct { .windows, .emscripten, .opencl, + .glsl450, + .vulkan, .other, => return false, else => return true, @@ -1547,6 +1557,8 @@ pub const Target = struct { .emscripten, .wasi, .opencl, + .glsl450, + .vulkan, .other, => return result, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 1e9066b90b..c34dcc2bd3 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -131,6 +131,8 @@ pub const CrossTarget = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => { self.os_version_min = .{ .none = {} }; @@ -732,6 +734,8 @@ pub const CrossTarget = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => return error.InvalidOperatingSystemVersion, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1edd466d54..df6a58b1e2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -69,6 +69,8 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .renderscript64 => "renderscript64", .ve => "ve", .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, + .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV, + .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV, }; // TODO Add a sub-arch for some architectures depending on CPU features. @@ -109,6 +111,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .wasi => "wasi", .emscripten => "emscripten", .uefi => "windows", + .opencl => return error.LLVMBackendDoesNotSupportOpenCL, + .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450, + .vulkan => return error.LLVMBackendDoesNotSupportVulkan, .other => "unknown", }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 207e460174..68edfab845 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -12,6 +12,8 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); +//! SPIR-V Documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html + pub const FnData = struct { id: ?u32 = null, code: std.ArrayListUnmanaged(u32) = .{}, @@ -32,6 +34,22 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .allocator = gpa, }, }; + + // TODO: Figure out where to put all of these + switch (options.target.cpu.arch) { + .spirv32, .spirv64 => {}, + else => return error.TODOArchNotSupported, + } + + switch (options.target.os.tag) { + .opencl, .glsl450, .vulkan => {}, + else => return error.TODOOsNotSupported, + } + + if (options.target.abi != .none) { + return error.TODOAbiNotSupported; + } + return spirv; } @@ -119,6 +137,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { switch (decl.typed_value) { .most_recent => |tvm| { const fn_data = &decl.fn_link.spirv; + + // TODO: This could probably be more efficient. for (fn_data.code.items) |word| { try writer.writeIntLittle(u32, word); } diff --git a/src/target.zig b/src/target.zig index c3df682ce0..e167520f89 100644 --- a/src/target.zig +++ b/src/target.zig @@ -189,7 +189,7 @@ pub fn supportsStackProbing(target: std.Target) bool { pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { return switch (os_tag) { - .freestanding, .other => .UnknownOS, + .freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS, .windows, .uefi => .Win32, .ananas => .Ananas, .cloudabi => .CloudABI, @@ -280,7 +280,7 @@ pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { .renderscript32 => .renderscript32, .renderscript64 => .renderscript64, .ve => .ve, - .spu_2 => .UnknownArch, + .spu_2, .spirv32, .spirv64 => .UnknownArch, }; } diff --git a/src/type.zig b/src/type.zig index cb2448aa1a..e0190cdd37 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3598,6 +3598,8 @@ pub const CType = enum { .hermit, .hurd, .opencl, + .glsl450, + .vulkan, => @panic("TODO specify the C integer and float type sizes for this OS"), } } -- cgit v1.2.3