Merge pull request #7700 from FireFox317/more-stage2-stuff-llvm

stage2: improvements to LLVM backend
This commit is contained in:
Andrew Kelley
2021-01-06 16:06:32 -08:00
committed by GitHub
12 changed files with 360 additions and 214 deletions

View File

@@ -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/C/zig.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"

View File

@@ -2150,7 +2150,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) {

View File

@@ -1,17 +1,18 @@
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 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 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;
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) {
@@ -138,23 +139,32 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
pub const LLVMIRModule = struct {
module: *Module,
llvm_module: *const llvm.ModuleRef,
target_machine: *const llvm.TargetMachineRef,
builder: *const llvm.BuilderRef,
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.ValueRef) = .{},
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.ValueRef = &[_]*const llvm.ValueRef{},
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);
@@ -172,19 +182,22 @@ pub const LLVMIRModule = struct {
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.ModuleRef.createWithName(root_nameZ.ptr);
errdefer llvm_module.disposeModule();
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_ref: *const llvm.TargetRef = undefined;
if (llvm.TargetRef.getTargetFromTriple(llvm_target_triple.ptr, &target_ref, &error_message)) {
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();
@@ -204,8 +217,8 @@ pub const LLVMIRModule = struct {
}
const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive;
const target_machine = llvm.TargetMachineRef.createTargetMachine(
target_ref,
const target_machine = llvm.TargetMachine.create(
target,
llvm_target_triple.ptr,
"",
"",
@@ -213,14 +226,15 @@ pub const LLVMIRModule = struct {
.Static,
.Default,
);
errdefer target_machine.disposeTargetMachine();
errdefer target_machine.dispose();
const builder = llvm.BuilderRef.createBuilder();
errdefer builder.disposeBuilder();
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,
@@ -230,9 +244,10 @@ pub const LLVMIRModule = struct {
}
pub fn deinit(self: *LLVMIRModule, allocator: *Allocator) void {
self.builder.disposeBuilder();
self.target_machine.disposeTargetMachine();
self.llvm_module.disposeModule();
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);
@@ -262,7 +277,7 @@ pub const LLVMIRModule = struct {
// verifyModule always allocs the error_message even if there is no error
defer llvm.disposeMessage(error_message);
if (self.llvm_module.verifyModule(.ReturnStatus, &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;
@@ -288,8 +303,7 @@ pub const LLVMIRModule = struct {
}
pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
const typed_value = decl.typed_value.most_recent.typed_value;
self.gen(module, typed_value, decl.src()) catch |err| switch (err) {
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.?);
@@ -300,15 +314,20 @@ pub const LLVMIRModule = struct {
};
}
fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void {
if (typed_value.val.castTag(.function)) |func_inst| {
const func = func_inst.data;
fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void {
const typed_value = decl.typed_value.most_recent.typed_value;
const src = decl.src();
const llvm_func = try self.resolveLLVMFunction(func, 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.ValueRef, fn_param_len);
var args = try self.gpa.alloc(*const llvm.Value, fn_param_len);
defer self.gpa.free(args);
for (args) |*arg, i| {
@@ -327,12 +346,13 @@ pub const LLVMIRModule = struct {
bb.deleteBasicBlock();
}
const entry_block = llvm_func.appendBasicBlock("Entry");
self.builder.positionBuilderAtEnd(entry_block);
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.ValueRef = switch (inst.tag) {
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).?),
@@ -355,70 +375,77 @@ pub const LLVMIRModule = struct {
};
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 {
return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty});
_ = try self.resolveGlobalDecl(decl, src);
}
}
fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef {
fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value {
if (inst.func.value()) |func_value| {
if (func_value.castTag(.function)) |func_payload| {
const func = func_payload.data;
const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src);
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 num_args = inst.args.len;
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 llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args);
defer self.gpa.free(llvm_param_vals);
const num_args = inst.args.len;
for (inst.args) |arg, i| {
llvm_param_vals[i] = try self.resolveInst(arg);
}
const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, num_args);
defer self.gpa.free(llvm_param_vals);
// 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;
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", .{});
}
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{});
}
fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef {
fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
_ = self.builder.buildRetVoid();
return null;
}
fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
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.ValueRef {
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.ValueRef {
fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.Value {
_ = self.builder.buildUnreachable();
return null;
}
fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
const lhs = try self.resolveInst(inst.lhs);
const rhs = try self.resolveInst(inst.rhs);
@@ -431,7 +458,7 @@ pub const LLVMIRModule = struct {
self.builder.buildNUWAdd(lhs, rhs, "");
}
fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
const lhs = try self.resolveInst(inst.lhs);
const rhs = try self.resolveInst(inst.rhs);
@@ -444,7 +471,7 @@ pub const LLVMIRModule = struct {
self.builder.buildNUWSub(lhs, rhs, "");
}
fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value {
const val = try self.resolveInst(inst.operand);
const signed = inst.base.ty.isSignedInt();
@@ -453,51 +480,73 @@ pub const LLVMIRModule = struct {
return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, "");
}
fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef {
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.ValueRef {
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.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), "");
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.ValueRef {
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.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), "");
return self.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src));
}
fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef {
/// 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.ValueRef {
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.ValueRef {
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.ValueRef {
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
@@ -506,7 +555,7 @@ pub const LLVMIRModule = struct {
return self.llvm_module.getIntrinsicDeclaration(id, null, 0);
}
fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef {
fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.Value {
if (inst.value()) |val| {
return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val });
}
@@ -515,7 +564,7 @@ pub const LLVMIRModule = struct {
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) !*const llvm.ValueRef {
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())
@@ -538,16 +587,89 @@ pub const LLVMIRModule = struct {
}
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}),
}
}
/// If the llvm function does not exist, create it
fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef {
// TODO: do we want to store this in our own datastructure?
if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn;
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}),
}
}
const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty;
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();
@@ -556,44 +678,39 @@ pub const LLVMIRModule = struct {
defer self.gpa.free(fn_param_types);
zig_fn_type.fnParamTypes(fn_param_types);
const llvm_param = try self.gpa.alloc(*const llvm.TypeRef, fn_param_len);
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.TypeRef.functionType(
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.owner_decl.name, fn_type);
const llvm_fn = self.llvm_module.addFunction(func.name, fn_type);
if (return_type.tag() == .noreturn) {
llvm_fn.addFnAttr("noreturn");
self.addFnAttr(llvm_fn, "noreturn");
}
return llvm_fn;
}
fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef {
switch (t.zigTypeTag()) {
.Void => return llvm.voidType(),
.NoReturn => return llvm.voidType(),
.Int => {
const info = t.intInfo(self.module.getTarget());
return llvm.intType(info.bits);
},
.Bool => return llvm.intType(1),
.Pointer => {
const pointer = t.castPointer().?;
const elem_type = try self.getLLVMType(pointer.data, src);
return elem_type.pointerType(0);
},
else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
}
// 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 } {

View File

@@ -1,79 +1,100 @@
//! 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 LLVMAttributeIndex = c_uint;
pub const AttributeIndex = c_uint;
pub const ValueRef = opaque {
/// 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 ValueRef, Idx: LLVMAttributeIndex, A: *const AttributeRef) void;
pub const appendBasicBlock = LLVMAppendBasicBlock;
extern fn LLVMAppendBasicBlock(Fn: *const ValueRef, Name: [*:0]const u8) *const BasicBlockRef;
extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void;
pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
extern fn LLVMGetFirstBasicBlock(Fn: *const ValueRef) ?*const BasicBlockRef;
extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;
// Helper functions
// TODO: Do we want to put these functions here? It allows for convienient function calls
// on ValueRef: llvm_fn.addFnAttr("noreturn")
fn addAttr(val: *const ValueRef, index: LLVMAttributeIndex, name: []const u8) void {
const kind_id = getEnumAttributeKindForName(name.ptr, name.len);
assert(kind_id != 0);
const llvm_attr = ContextRef.getGlobal().createEnumAttribute(kind_id, 0);
val.addAttributeAtIndex(index, llvm_attr);
}
pub fn addFnAttr(val: *const ValueRef, attr_name: []const u8) void {
// TODO: improve this API, `addAttr(-1, attr_name)`
val.addAttr(std.math.maxInt(LLVMAttributeIndex), attr_name);
}
pub const getNextInstruction = LLVMGetNextInstruction;
extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value;
};
pub const TypeRef = opaque {
pub const Type = opaque {
pub const functionType = LLVMFunctionType;
extern fn LLVMFunctionType(ReturnType: *const TypeRef, ParamTypes: ?[*]*const TypeRef, ParamCount: c_uint, IsVarArg: LLVMBool) *const TypeRef;
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 TypeRef) *const ValueRef;
extern fn LLVMConstNull(Ty: *const Type) *const Value;
pub const constAllOnes = LLVMConstAllOnes;
extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef;
extern fn LLVMConstAllOnes(Ty: *const Type) *const Value;
pub const constInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef;
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 TypeRef) *const ValueRef;
extern fn LLVMGetUndef(Ty: *const Type) *const Value;
pub const pointerType = LLVMPointerType;
extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef;
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 ModuleRef = opaque {
pub const createWithName = LLVMModuleCreateWithName;
extern fn LLVMModuleCreateWithName(ModuleID: [*:0]const u8) *const ModuleRef;
pub const Module = opaque {
pub const createWithName = LLVMModuleCreateWithNameInContext;
extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *const Context) *const Module;
pub const disposeModule = LLVMDisposeModule;
extern fn LLVMDisposeModule(*const ModuleRef) void;
pub const dispose = LLVMDisposeModule;
extern fn LLVMDisposeModule(*const Module) void;
pub const verifyModule = LLVMVerifyModule;
extern fn LLVMVerifyModule(*const ModuleRef, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;
pub const verify = LLVMVerifyModule;
extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) LLVMBool;
pub const addFunction = LLVMAddFunction;
extern fn LLVMAddFunction(*const ModuleRef, Name: [*:0]const u8, FunctionTy: *const TypeRef) *const ValueRef;
extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
pub const getNamedFunction = LLVMGetNamedFunction;
extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef;
extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration;
extern fn LLVMGetIntrinsicDeclaration(Mod: *const ModuleRef, ID: c_uint, ParamTypes: ?[*]*const TypeRef, ParamCount: usize) *const ValueRef;
extern fn LLVMGetIntrinsicDeclaration(Mod: *const Module, ID: c_uint, ParamTypes: ?[*]*const Type, ParamCount: usize) *const Value;
pub const printToString = LLVMPrintModuleToString;
extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8;
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;
@@ -89,111 +110,106 @@ pub const VerifierFailureAction = extern enum {
};
pub const constNeg = LLVMConstNeg;
extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef;
extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;
pub const voidType = LLVMVoidType;
extern fn LLVMVoidType() *const TypeRef;
pub const setInitializer = LLVMSetInitializer;
extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void;
pub const getParam = LLVMGetParam;
extern fn LLVMGetParam(Fn: *const ValueRef, Index: c_uint) *const ValueRef;
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 AttributeRef = opaque {};
pub const Attribute = opaque {};
pub const ContextRef = opaque {
pub const createEnumAttribute = LLVMCreateEnumAttribute;
extern fn LLVMCreateEnumAttribute(*const ContextRef, KindID: c_uint, Val: u64) *const AttributeRef;
pub const Builder = opaque {
pub const dispose = LLVMDisposeBuilder;
extern fn LLVMDisposeBuilder(Builder: *const Builder) void;
pub const getGlobal = LLVMGetGlobalContext;
extern fn LLVMGetGlobalContext() *const ContextRef;
};
pub const intType = LLVMIntType;
extern fn LLVMIntType(NumBits: c_uint) *const TypeRef;
pub const BuilderRef = opaque {
pub const createBuilder = LLVMCreateBuilder;
extern fn LLVMCreateBuilder() *const BuilderRef;
pub const disposeBuilder = LLVMDisposeBuilder;
extern fn LLVMDisposeBuilder(Builder: *const BuilderRef) 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 BuilderRef, Block: *const BasicBlockRef) void;
extern fn LLVMPositionBuilderAtEnd(Builder: *const Builder, Block: *const BasicBlock) void;
pub const getInsertBlock = LLVMGetInsertBlock;
extern fn LLVMGetInsertBlock(Builder: *const BuilderRef) *const BasicBlockRef;
extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock;
pub const buildCall = LLVMBuildCall;
extern fn LLVMBuildCall(*const BuilderRef, Fn: *const ValueRef, Args: ?[*]*const ValueRef, NumArgs: c_uint, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, *const TypeRef, Fn: *const ValueRef, Args: [*]*const ValueRef, NumArgs: c_uint, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef) *const ValueRef;
extern fn LLVMBuildRetVoid(*const Builder) *const Value;
pub const buildRet = LLVMBuildRet;
extern fn LLVMBuildRet(*const BuilderRef, V: *const ValueRef) *const ValueRef;
extern fn LLVMBuildRet(*const Builder, V: *const Value) *const Value;
pub const buildUnreachable = LLVMBuildUnreachable;
extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef;
extern fn LLVMBuildUnreachable(*const Builder) *const Value;
pub const buildAlloca = LLVMBuildAlloca;
extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
extern fn LLVMBuildAlloca(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value;
pub const buildStore = LLVMBuildStore;
extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef;
extern fn LLVMBuildStore(*const Builder, Val: *const Value, Ptr: *const Value) *const Value;
pub const buildLoad = LLVMBuildLoad;
extern fn LLVMBuildLoad(*const BuilderRef, PointerVal: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value;
pub const buildNot = LLVMBuildNot;
extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
pub const buildNSWAdd = LLVMBuildNSWAdd;
extern fn LLVMBuildNSWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, IsSigned: LLVMBool, Name: [*:0]const u8) *const ValueRef;
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 BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef;
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 BasicBlockRef = opaque {
pub const BasicBlock = opaque {
pub const deleteBasicBlock = LLVMDeleteBasicBlock;
extern fn LLVMDeleteBasicBlock(BB: *const BasicBlockRef) void;
extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;
pub const getFirstInstruction = LLVMGetFirstInstruction;
extern fn LLVMGetFirstInstruction(BB: *const BasicBlock) ?*const Value;
};
pub const TargetMachineRef = opaque {
pub const createTargetMachine = LLVMCreateTargetMachine;
pub const TargetMachine = opaque {
pub const create = LLVMCreateTargetMachine;
extern fn LLVMCreateTargetMachine(
T: *const TargetRef,
T: *const Target,
Triple: [*:0]const u8,
CPU: [*:0]const u8,
Features: [*:0]const u8,
Level: CodeGenOptLevel,
Reloc: RelocMode,
CodeModel: CodeMode,
) *const TargetMachineRef;
) *const TargetMachine;
pub const disposeTargetMachine = LLVMDisposeTargetMachine;
extern fn LLVMDisposeTargetMachine(T: *const TargetMachineRef) void;
pub const dispose = LLVMDisposeTargetMachine;
extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
pub const emitToFile = LLVMTargetMachineEmitToFile;
extern fn LLVMTargetMachineEmitToFile(*const TargetMachineRef, M: *const ModuleRef, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool;
extern fn LLVMTargetMachineEmitToFile(*const TargetMachine, M: *const Module, Filename: [*:0]const u8, codegen: CodeGenFileType, ErrorMessage: *[*:0]const u8) LLVMBool;
};
pub const CodeMode = extern enum {
@@ -228,9 +244,9 @@ pub const CodeGenFileType = extern enum {
ObjectFile,
};
pub const TargetRef = opaque {
pub const getTargetFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const TargetRef, ErrorMessage: *[*:0]const u8) LLVMBool;
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;

View File

@@ -570,7 +570,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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -27,4 +27,17 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "");
}
{
var case = ctx.exeUsingLlvmBackend("hello world", linux_x64);
case.addCompareOutput(
\\extern fn puts(s: [*:0]const u8) c_int;
\\
\\export fn main() c_int {
\\ _ = puts("hello world!");
\\ return 0;
\\}
, "hello world!" ++ std.cstr.line_sep);
}
}

View File

@@ -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);