zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit b1cfa923bee5210fd78c7508d1af92dde3361c8c (tree)
parent 31d1ec4c2fd0d1e07e0020b19b7bca8196d7879c
Author: Timon Kruiper <timonkruiper@gmail.com>
Date:   Wed,  6 Jan 2021 01:27:06 +0100

stage2: rename and move files related to LLVM backend

Diffstat:
MCMakeLists.txt | 4++--
Msrc/Compilation.zig | 2+-
Asrc/codegen/llvm.zig | 722+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/codegen/llvm/bindings.zig | 571+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/link.zig | 2+-
Msrc/link/Coff.zig | 2+-
Msrc/link/Elf.zig | 2+-
Dsrc/llvm_backend.zig | 722-------------------------------------------------------------------------------
Dsrc/llvm_bindings.zig | 574-------------------------------------------------------------------------------
Msrc/main.zig | 6+++---
Msrc/mingw.zig | 2+-
Msrc/target.zig | 2+-
Rtest/stage2/llvm_backend.zig -> test/stage2/llvm.zig | 0
Mtest/stage2/test.zig | 2+-
14 files changed, 1305 insertions(+), 1308 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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_backend.zig b/test/stage2/llvm.zig diff --git 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);