diff --git a/build.zig b/build.zig index 9912364..a05bd3b 100644 --- a/build.zig +++ b/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); fn run_process_and_capture_stdout(b: *std.Build, argv: []const []const u8) ![]const u8 { const result = std.process.Child.run(.{ @@ -17,11 +18,115 @@ fn run_process_and_capture_stdout(b: *std.Build, argv: []const []const u8) ![]co return result.stdout; } +fn file_find_in_path(allocator: std.mem.Allocator, file_name: []const u8, path_env: []const u8, extension: []const u8) ?[]const u8 { + const path_env_separator = switch (builtin.os.tag) { + .windows => ';', + else => ':', + }; + const path_separator = switch (builtin.os.tag) { + .windows => '\\', + else => '/', + }; + var env_it = std.mem.splitScalar(u8, path_env, path_env_separator); + const result: ?[]const u8 = while (env_it.next()) |dir_path| { + const full_path = std.mem.concatWithSentinel(allocator, u8, &.{ dir_path, &[1]u8{path_separator}, file_name, extension }, 0) catch unreachable; + const file = std.fs.cwd().openFile(full_path, .{}) catch continue; + file.close(); + break full_path; + } else null; + return result; +} + +fn executable_find_in_path(allocator: std.mem.Allocator, file_name: []const u8, path_env: []const u8) ?[]const u8 { + const extension = switch (builtin.os.tag) { + .windows => ".exe", + else => "", + }; + return file_find_in_path(allocator, file_name, path_env, extension); +} + +const LLVM = struct { + module: *std.Build.Module, + + fn setup(b: *std.Build, path: []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !LLVM { + var llvm_libs = std.ArrayList([]const u8).init(b.allocator); + var flags = std.ArrayList([]const u8).init(b.allocator); + const llvm_config_path = if (b.option([]const u8, "llvm_prefix", "LLVM prefix")) |llvm_prefix| blk: { + const full_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/bin/llvm-config" }); + const f = std.fs.cwd().openFile(full_path, .{}) catch return error.llvm_not_found; + f.close(); + break :blk full_path; + } else executable_find_in_path(b.allocator, "llvm-config", path) orelse return error.llvm_not_found; + const llvm_components_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--components" }); + var it = std.mem.splitScalar(u8, llvm_components_result, ' '); + var args = std.ArrayList([]const u8).init(b.allocator); + try args.append(llvm_config_path); + try args.append("--libs"); + while (it.next()) |component| { + try args.append(std.mem.trimRight(u8, component, "\n")); + } + const llvm_libs_result = try run_process_and_capture_stdout(b, args.items); + it = std.mem.splitScalar(u8, llvm_libs_result, ' '); + + while (it.next()) |lib| { + const llvm_lib = std.mem.trimLeft(u8, std.mem.trimRight(u8, lib, "\n"), "-l"); + try llvm_libs.append(llvm_lib); + } + + const llvm_cxx_flags_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--cxxflags" }); + it = std.mem.splitScalar(u8, llvm_cxx_flags_result, ' '); + while (it.next()) |flag| { + const llvm_cxx_flag = std.mem.trimRight(u8, flag, "\n"); + try flags.append(llvm_cxx_flag); + } + + const llvm_lib_dir = std.mem.trimRight(u8, try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--libdir" }), "\n"); + + if (optimize != .ReleaseSmall) { + try flags.append("-g"); + } + + try flags.append("-fno-rtti"); + + const llvm = b.createModule(.{ + .target = target, + .optimize = optimize, + }); + + llvm.addLibraryPath(.{ .cwd_relative = llvm_lib_dir }); + + llvm.addCSourceFiles(.{ + .files = &.{"src/llvm.cpp"}, + .flags = flags.items, + }); + llvm.addIncludePath(.{ .cwd_relative = "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/14.2.1/../../../../include/c++/14.2.1" }); + llvm.addIncludePath(.{ .cwd_relative = "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/14.2.1/../../../../include/c++/14.2.1/x86_64-pc-linux-gnu" }); + + llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); + llvm.linkSystemLibrary("unwind", .{}); + llvm.linkSystemLibrary("z", .{}); + + for (llvm_libs.items) |llvm_lib| { + llvm.linkSystemLibrary(llvm_lib, .{}); + } + + return LLVM{ + .module = llvm, + }; + } + + fn link(llvm: LLVM, target: *std.Build.Step.Compile) void { + if (target.root_module != llvm.module) { + target.root_module.addImport("llvm", llvm.module); + } + } +}; + pub fn build(b: *std.Build) !void { - const ci = b.option(bool, "ci", ""); - _ = &ci; const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const env = try std.process.getEnvMap(b.allocator); + const path = env.get("PATH") orelse unreachable; const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), @@ -29,33 +134,14 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); - var llvm_libs = std.ArrayList([]const u8).init(b.allocator); - { - const llvm_components_result = try run_process_and_capture_stdout(b, &.{ "llvm-config", "--components" }); - var it = std.mem.splitScalar(u8, llvm_components_result, ' '); - var args = std.ArrayList([]const u8).init(b.allocator); - try args.append("llvm-config"); - try args.append("--libs"); - while (it.next()) |component| { - try args.append(std.mem.trim(u8, component, "\n")); - } - const llvm_libs_result = try run_process_and_capture_stdout(b, args.items); - it = std.mem.splitScalar(u8, llvm_libs_result, ' '); - - while (it.next()) |component| { - const llvm_lib = std.mem.trim(u8, std.mem.trim(u8, component, "\n"), "-l"); - try llvm_libs.append(llvm_lib); - } - } + const llvm = try LLVM.setup(b, path, target, optimize); const exe = b.addExecutable(.{ .name = "bloat-buster", .root_module = exe_mod, }); - exe.linkLibC(); - for (llvm_libs.items) |llvm_lib| { - exe.linkSystemLibrary(llvm_lib); - } + + llvm.link(exe); b.installArtifact(exe); @@ -70,6 +156,7 @@ pub fn build(b: *std.Build) !void { const exe_unit_tests = b.addTest(.{ .root_module = exe_mod, }); + llvm.link(exe_unit_tests); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); const test_step = b.step("test", "Run unit tests"); diff --git a/src/LLVM.zig b/src/LLVM.zig new file mode 100644 index 0000000..e6aec7f --- /dev/null +++ b/src/LLVM.zig @@ -0,0 +1,280 @@ +const lib = @import("lib.zig"); +const Arena = lib.Arena; +const api = @import("llvm_api.zig"); +pub const Architecture = enum { + X86, +}; + +pub const TargetInitializerOptions = struct { + asm_parser: bool = true, + asm_printer: bool = true, + disassembler: bool = false, +}; + +const targets = [@typeInfo(Architecture).@"enum".fields.len]type{ + api.get_initializer(.X86), +}; + +pub const Context = opaque { + pub const create = api.LLVMContextCreate; + pub fn create_module(context: *Context, name: [:0]const u8) *Module { + return api.llvm_context_create_module(context, name.ptr, name.len); + } + pub const create_builder = api.LLVMCreateBuilderInContext; + pub fn create_basic_block(context: *Context, name: []const u8, parent: *Function) *BasicBlock { + return api.llvm_context_create_basic_block(context, name.ptr, name.len, parent); + } +}; + +pub const BasicBlock = opaque {}; + +pub const Module = opaque { + pub const create_di_builder = api.LLVMCreateDIBuilder; + + pub fn to_string(module: *Module) []const u8 { + var result: []const u8 = undefined; + api.llvm_module_to_string(module, &result.ptr, &result.len); + return result; + } + + const FunctionCreate = struct { + type: *Type.Function, + linkage: LinkageType, + address_space: c_uint = 0, + name: []const u8, + }; + + pub fn create_function(module: *Module, create: FunctionCreate) *Function { + return api.llvm_module_create_function(module, create.type, create.linkage, create.address_space, create.name.ptr, create.name.len); + } + + pub fn verify(module: *Module) VerifyResult { + var result: VerifyResult = undefined; + result.success = api.llvm_module_verify(module, &result.error_message.ptr, &result.error_message.len); + return result; + } +}; + +pub const VerifyResult = struct { + error_message: []const u8, + success: bool, +}; + +pub const Builder = opaque { + pub const position_at_end = api.LLVMPositionBuilderAtEnd; + + pub const create_ret = api.LLVMBuildRet; + + pub fn create_ret_void(builder: *Builder) void { + builder.create_ret(null); + } +}; + +pub const Function = opaque { + pub fn verify(function: *Function) VerifyResult { + var result: VerifyResult = undefined; + result.success = api.llvm_function_verify(function, &result.error_message.ptr, &result.error_message.len); + return result; + } +}; + +pub const Constant = opaque { + pub fn to_value(constant: *Constant) *Value { + return @ptrCast(constant); + } + + pub const Integer = opaque { + pub fn to_value(constant: *Constant.Integer) *Value { + return @ptrCast(constant); + } + }; +}; + +pub const Value = opaque {}; + +pub const DI = struct { + pub const Builder = opaque { + pub fn create_file(builder: *DI.Builder, file_name: []const u8, directory_name: []const u8) *File { + return api.LLVMCreateDIBuilder(builder, file_name.ptr, file_name.len, directory_name.ptr, directory_name.len); + } + }; + pub const File = opaque {}; + + const Flags = enum(c_int) { + _, + const Zero = 0; + const Private = 1; + const Protected = 2; + const Public = 3; + const FwdDecl = 1 << 2; + const AppleBlock = 1 << 3; + const ReservedBit4 = 1 << 4; + const Virtual = 1 << 5; + const Artificial = 1 << 6; + const Explicit = 1 << 7; + const Prototyped = 1 << 8; + const ObjcClassComplete = 1 << 9; + const ObjectPointer = 1 << 10; + const Vector = 1 << 11; + const StaticMember = 1 << 12; + const LValueReference = 1 << 13; + const RValueReference = 1 << 14; + const Reserved = 1 << 15; + const SingleInheritance = 1 << 16; + const MultipleInheritance = 2 << 16; + const VirtualInheritance = 3 << 16; + const IntroducedVirtual = 1 << 18; + const BitField = 1 << 19; + const NoReturn = 1 << 20; + const TypePassByValue = 1 << 22; + const TypePassByReference = 1 << 23; + const EnumClass = 1 << 24; + const Thunk = 1 << 25; + const NonTrivial = 1 << 26; + const BigEndian = 1 << 27; + const LittleEndian = 1 << 28; + const IndirectVirtualBase = (1 << 2) | (1 << 5); + const Accessibility = Private | Protected | Public; + const PtrToMemberRep = SingleInheritance | MultipleInheritance | VirtualInheritance; + }; +}; + +pub const Type = opaque { + pub const Function = opaque { + pub fn get(return_type: *Type, parameter_types: []const *Type, is_var_args: c_int) *Type.Function { + return api.LLVMFunctionType(return_type, parameter_types.ptr, @intCast(parameter_types.len), is_var_args); + } + }; + + pub const Integer = opaque { + pub const get_constant = api.LLVMConstInt; + pub fn to_type(integer: *Type.Integer) *Type { + return @ptrCast(integer); + } + }; +}; + +pub const LinkageType = enum(c_int) { + ExternalLinkage, + AvailableExternallyLinkage, + LinkOnceAnyLinkage, + LinkOnceODRLinkage, + WeakAnyLinkage, + WeakODRLinkage, + AppendingLinkage, + InternalLinkage, + PrivateLinkage, + ExternalWeakLinkage, + CommonLinkage, +}; + +pub const DwarfSourceLanguage = enum(c_int) { + c17 = 0x2c, +}; +pub const DwarfEmissionKind = enum(c_int) { + none, + full, + line_tables_only, +}; + +pub const Thread = struct { + context: *Context, + i1: Integer, + i8: Integer, + i16: Integer, + i32: Integer, + i64: Integer, + i128: Integer, + + pub const Integer = struct { + type: *Type.Integer, + zero: *Constant.Integer, + }; + + pub fn initialize(thread: *Thread) void { + const context = Context.create(); + const type_i1 = api.LLVMInt1TypeInContext(context); + const type_i8 = api.LLVMInt8TypeInContext(context); + const type_i16 = api.LLVMInt16TypeInContext(context); + const type_i32 = api.LLVMInt32TypeInContext(context); + const type_i64 = api.LLVMInt64TypeInContext(context); + const type_i128 = api.LLVMInt128TypeInContext(context); + const zero_i1 = type_i1.get_constant(0, 0); + const zero_i8 = type_i8.get_constant(0, 0); + const zero_i16 = type_i16.get_constant(0, 0); + const zero_i32 = type_i32.get_constant(0, 0); + const zero_i64 = type_i64.get_constant(0, 0); + const zero_i128 = type_i128.get_constant(0, 0); + + thread.* = .{ + .context = context, + .i1 = .{ + .type = type_i1, + .zero = zero_i1, + }, + .i8 = .{ + .type = type_i8, + .zero = zero_i8, + }, + .i16 = .{ + .type = type_i16, + .zero = zero_i16, + }, + .i32 = .{ + .type = type_i32, + .zero = zero_i32, + }, + .i64 = .{ + .type = type_i64, + .zero = zero_i64, + }, + .i128 = .{ + .type = type_i128, + .zero = zero_i128, + }, + }; + } +}; + +pub var threads: []Thread = undefined; + +// This is meant to call globally, only once per execution +pub fn initialize_all() void { + threads = lib.global.arena.allocate(Thread, lib.global.thread_count); + inline for (targets) |target| { + target.initialize(.{}); + } +} + +pub fn experiment() void { + const thread = &threads[0]; + thread.initialize(); + const module = thread.context.create_module("first_module"); + const builder = thread.context.create_builder(); + // const di_builder = module.create_di_builder(); + const return_type = thread.i32.type; + const return_value = thread.i32.zero; + // const return_value = thread. + const function_type = Type.Function.get(return_type.to_type(), &.{}, 0); + const function = module.create_function(.{ + .type = function_type, + .linkage = .ExternalLinkage, + .name = "main", + }); + const entry_basic_block = thread.context.create_basic_block("entry", function); + builder.position_at_end(entry_basic_block); + builder.create_ret(return_value.to_value()); + const function_verify = function.verify(); + if (!function_verify.success) { + unreachable; + } + const module_verify = module.verify(); + if (!module_verify.success) { + unreachable; + } + + const module_z = api.LLVMPrintModuleToString(module); + _ = module_z; + const module_string = module.to_string(); + lib.print_string(module_string); +} diff --git a/src/lib.zig b/src/lib.zig index 5bb9039..366eea0 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -3,6 +3,10 @@ const VariableArguments = @import("std").builtin.VaList; extern "c" fn IsDebuggerPresent() bool; extern "c" fn __errno_location() *c_int; +test { + _ = @import("lib_test.zig"); +} + pub const KB = 1024; pub const MB = 1024 * 1024; pub const GB = 1024 * 1024 * 1024; @@ -59,48 +63,6 @@ pub fn value_from_flag(value: anytype, flag: anytype) @TypeOf(value) { return result; } -test "value_from_flag" { - const std = @import("std"); - const expect = std.testing.expect; - - try expect(value_from_flag(1, 1) == 1); - try expect(value_from_flag(2, true) == 2); - try expect(value_from_flag(3, false) == 0); - try expect(value_from_flag(3, true) == 3); - try expect(value_from_flag(3, 1) == 3); - - try expect(value_from_flag(0xffff, 1) == 0xffff); - try expect(value_from_flag(0xffff, 0) == 0); - try expect(value_from_flag(0xffff, true) == 0xffff); - try expect(value_from_flag(0xffff, false) == 0); - - try expect(value_from_flag(0xffffffff, 1) == 0xffffffff); - try expect(value_from_flag(0xffffffff, 0) == 0); - try expect(value_from_flag(0xffffffff, true) == 0xffffffff); - try expect(value_from_flag(0xffffffff, false) == 0); - - try expect(value_from_flag(0xffffffffffffffff, 1) == 0xffffffffffffffff); - try expect(value_from_flag(0xffffffffffffffff, 0) == 0); - try expect(value_from_flag(0xffffffffffffffff, true) == 0xffffffffffffffff); - try expect(value_from_flag(0xffffffffffffffff, false) == 0); - - const a: u32 = 1235; - const b_true: bool = true; - const b_false: bool = false; - const u_true: u1 = 1; - const u_false: u1 = 0; - try expect(value_from_flag(a, b_true) == a); - try expect(value_from_flag(a, b_false) == 0); - try expect(value_from_flag(a, u_true) == a); - try expect(value_from_flag(a, u_false) == 0); - - const b: u64 = 0xffffffffffffffff; - try expect(value_from_flag(b, b_true) == b); - try expect(value_from_flag(b, b_false) == 0); - try expect(value_from_flag(b, u_true) == b); - try expect(value_from_flag(b, u_false) == 0); -} - pub const Error = enum(c_int) { SUCCESS = 0, PERM = 1, @@ -302,7 +264,37 @@ pub const os = struct { return result; } + pub fn get_cpu_count() usize { + switch (builtin.os.tag) { + .windows => @compileError("TODO"), + else => { + var cpu_set: posix.cpu_set_t = undefined; + const result = posix.sched_getaffinity(0, @sizeOf(posix.cpu_set_t), &cpu_set); + assert(result == 0); + const cpu_count = posix.CPU_COUNT(cpu_set); + return cpu_count; + }, + } + } + const linux = struct { + pub const CPU_SETSIZE = 128; + pub const cpu_set_t = [CPU_SETSIZE / @sizeOf(usize)]usize; + pub const cpu_count_t = @Type(.{ + .int = .{ + .signedness = .unsigned, + .bits = log2_int(@as(u64, CPU_SETSIZE * 8)), + }, + }); + + pub fn CPU_COUNT(set: cpu_set_t) cpu_count_t { + var sum: cpu_count_t = 0; + for (set) |x| { + sum += @popCount(x); + } + return sum; + } + const FileDescriptor = c_int; fn fd_is_valid(fd: FileDescriptor) bool { @@ -414,6 +406,7 @@ pub const os = struct { extern "c" fn fstat(fd: system.FileDescriptor, stat: *Stat) c_int; extern "c" fn read(fd: system.FileDescriptor, pointer: [*]u8, byte_count: usize) isize; extern "c" fn write(fd: system.FileDescriptor, pointer: [*]const u8, byte_count: usize) isize; + extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; const mode_t = usize; @@ -490,6 +483,17 @@ pub const os = struct { libc.exit(1); } } + + pub fn get_stdout() File { + return switch (builtin.os.tag) { + .windows => @compileError("TODO"), + else => { + return File{ + .fd = 1, + }; + }, + }; + } }; pub const libc = struct { @@ -586,6 +590,18 @@ pub const Arena = struct { return result; } + pub fn allocate(arena: *Arena, T: type, count: usize) []T { + const result = arena.allocate_bytes(@sizeOf(T) * count, @alignOf(T)); + const t_ptr: [*]T = @alignCast(@ptrCast(result)); + const t_len = count; + return t_ptr[0..t_len]; + } + + pub fn allocate_one(arena: *Arena, T: type) *T { + const result = arena.allocate(T, 1); + return &result[0]; + } + pub fn join_string(arena: *Arena, pieces: []const []const u8) [:0]u8 { var size: u64 = 0; for (pieces) |piece| { @@ -2424,6 +2440,20 @@ pub fn format(buffer_pointer: [*]u8, buffer_length: usize, format_string: [*:0]c return byte_count; } +pub const GlobalState = struct { + arena: *Arena, + thread_count: usize, + + pub fn initialize() void { + const thread_count = os.get_cpu_count(); + global = .{ + .arena = Arena.initialize_default(2 * MB), + .thread_count = thread_count, + }; + } +}; +pub var global: GlobalState = undefined; + pub const parse = struct { fn integer_decimal(str: []const u8) u64 { var value: u64 = 0; @@ -2436,3 +2466,19 @@ pub const parse = struct { return value; } }; + +fn vprint(format_string: [*:0]const u8, args: *VariableArguments) void { + var buffer: [16 * 1024]u8 = undefined; + const slice = format_va(&buffer, format_string, args); + print_string(slice); +} + +pub fn print(format_string: [*:0]const u8, ...) callconv(.C) void { + const args = @cVaStart(); + vprint(format_string, &args); + @cVaEnd(&args); +} + +pub fn print_string(str: []const u8) void { + os.get_stdout().write(str); +} diff --git a/src/lib_test.zig b/src/lib_test.zig new file mode 100644 index 0000000..707ba94 --- /dev/null +++ b/src/lib_test.zig @@ -0,0 +1,44 @@ +const lib = @import("lib.zig"); + +test "value_from_flag" { + const std = @import("std"); + const expect = std.testing.expect; + const value_from_flag = lib.value_from_flag; + + try expect(value_from_flag(1, 1) == 1); + try expect(value_from_flag(2, true) == 2); + try expect(value_from_flag(3, false) == 0); + try expect(value_from_flag(3, true) == 3); + try expect(value_from_flag(3, 1) == 3); + + try expect(value_from_flag(0xffff, 1) == 0xffff); + try expect(value_from_flag(0xffff, 0) == 0); + try expect(value_from_flag(0xffff, true) == 0xffff); + try expect(value_from_flag(0xffff, false) == 0); + + try expect(value_from_flag(0xffffffff, 1) == 0xffffffff); + try expect(value_from_flag(0xffffffff, 0) == 0); + try expect(value_from_flag(0xffffffff, true) == 0xffffffff); + try expect(value_from_flag(0xffffffff, false) == 0); + + try expect(value_from_flag(0xffffffffffffffff, 1) == 0xffffffffffffffff); + try expect(value_from_flag(0xffffffffffffffff, 0) == 0); + try expect(value_from_flag(0xffffffffffffffff, true) == 0xffffffffffffffff); + try expect(value_from_flag(0xffffffffffffffff, false) == 0); + + const a: u32 = 1235; + const b_true: bool = true; + const b_false: bool = false; + const u_true: u1 = 1; + const u_false: u1 = 0; + try expect(value_from_flag(a, b_true) == a); + try expect(value_from_flag(a, b_false) == 0); + try expect(value_from_flag(a, u_true) == a); + try expect(value_from_flag(a, u_false) == 0); + + const b: u64 = 0xffffffffffffffff; + try expect(value_from_flag(b, b_true) == b); + try expect(value_from_flag(b, b_false) == 0); + try expect(value_from_flag(b, u_true) == b); + try expect(value_from_flag(b, u_false) == 0); +} diff --git a/src/llvm.cpp b/src/llvm.cpp new file mode 100644 index 0000000..6e41734 --- /dev/null +++ b/src/llvm.cpp @@ -0,0 +1,104 @@ + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" + +#define EXPORT extern "C" +#define fn static + +using namespace llvm; + +EXPORT Module* llvm_context_create_module(LLVMContext& context, const char* name_pointer, size_t name_length) +{ + auto name = StringRef(name_pointer, name_length); + return new Module(name, context); +} + +EXPORT Value* llvm_builder_create_add(IRBuilder<>& builder, Value* left, Value* right, bool nuw, bool nsw) +{ + auto* result = builder.CreateAdd(left, right, "", nuw, nsw); + return result; +} + +EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, const char* name_pointer, size_t name_length) +{ + auto name = StringRef(name_pointer, name_length); + auto* function = Function::Create(function_type, linkage_type, address_space, name, module); + return function; +} + +EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, const char* name_pointer, size_t name_length, bool is_packed) +{ + auto types = ArrayRef(type_pointer, type_count); + auto name = StringRef(name_pointer, name_length); + auto* struct_type = StructType::create(context, types, name, is_packed); + return struct_type; +} + +EXPORT StructType* llvm_context_get_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, bool is_packed) +{ + auto types = ArrayRef(type_pointer, type_count); + auto* struct_type = StructType::get(context, types, is_packed); + return struct_type; +} + +EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, const char* name_pointer, size_t name_length, Function* parent) +{ + auto name = StringRef(name_pointer, name_length); + auto* basic_block = BasicBlock::Create(context, name, parent); + return basic_block; +} + +fn void stream_to_string(raw_string_ostream& stream, const char** message_pointer, size_t* message_length) +{ + // No need to call stream.flush(); because it's string-based + stream.flush(); + + auto string = stream.str(); + auto length = string.length(); + + char* result = 0; + if (length) + { + result = new char[length]; + memcpy(result, string.c_str(), length); + } + + *message_pointer = result; + *message_length = length; +} + +EXPORT bool llvm_function_verify(Function& function, const char** message_pointer, size_t* message_length) +{ + std::string message_buffer; + raw_string_ostream message_stream(message_buffer); + + bool result = verifyFunction(function, &message_stream); + auto size = message_stream.str().size(); + stream_to_string(message_stream, message_pointer, message_length); + + // We invert the condition because LLVM conventions are just stupid + return !result; +} + +EXPORT bool llvm_module_verify(const Module& module, const char** message_pointer, size_t* message_length) +{ + std::string message_buffer; + raw_string_ostream message_stream(message_buffer); + + bool result = verifyModule(module, &message_stream); + stream_to_string(message_stream, message_pointer, message_length); + + // We invert the condition because LLVM conventions are just stupid + return !result; +} + +EXPORT void llvm_module_to_string(Module* module, const char** module_pointer, size_t* module_length) +{ + std::string buffer; + raw_string_ostream stream(buffer); + module->print(stream, nullptr); + + stream_to_string(stream, module_pointer, module_length); +} + diff --git a/src/llvm_api.zig b/src/llvm_api.zig new file mode 100644 index 0000000..b9b7f2b --- /dev/null +++ b/src/llvm_api.zig @@ -0,0 +1,110 @@ +const llvm = @import("LLVM.zig"); + +const Bool = c_int; + +pub extern fn llvm_context_create_module(context: *llvm.Context, name_pointer: [*]const u8, name_length: usize) *llvm.Module; +pub extern fn LLVMContextCreate() *llvm.Context; +pub extern fn LLVMCreateBuilderInContext(context: *llvm.Context) *llvm.Builder; + +// Module +pub extern fn llvm_module_create_function(module: *llvm.Module, function_type: *llvm.Type.Function, linkage_type: llvm.LinkageType, address_space: c_uint, name_pointer: [*]const u8, name_length: usize) *llvm.Function; +pub extern fn llvm_context_create_basic_block(context: *llvm.Context, name_pointer: [*]const u8, name_length: usize, parent: *llvm.Function) *llvm.BasicBlock; + +pub extern fn llvm_function_verify(function: *llvm.Function, message_pointer: *[*]const u8, message_length: *usize) bool; +pub extern fn llvm_module_verify(module: *llvm.Module, message_pointer: *[*]const u8, message_length: *usize) bool; + +pub extern fn llvm_module_to_string(module: *llvm.Module, module_pointer: *[*]const u8, module_length: *usize) void; +pub extern fn LLVMPrintModuleToString(module: *llvm.Module) [*:0]const u8; + +// Builder API +pub extern fn LLVMPositionBuilderAtEnd(builder: *llvm.Builder, basic_block: *llvm.BasicBlock) void; +pub extern fn LLVMBuildRet(builder: *llvm.Builder, value: ?*llvm.Value) void; + +// TYPES +// Types: integers +pub extern fn LLVMInt1TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMInt8TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMInt16TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMInt32TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMInt64TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMInt128TypeInContext(context: *llvm.Context) *llvm.Type.Integer; +pub extern fn LLVMIntTypeInContext(context: *llvm.Context, bit_count: c_uint) *llvm.Type.Integer; + +// Types: floating point +pub extern fn LLVMHalfTypeInContext(context: *llvm.Context) *llvm.Type; +pub extern fn LLVMBFloatTypeInContext(context: *llvm.Context) *llvm.Type; +pub extern fn LLVMFloatTypeInContext(context: *llvm.Context) *llvm.Type; +pub extern fn LLVMDoubleTypeInContext(context: *llvm.Context) *llvm.Type; +pub extern fn LLVMFP128TypeInContext(context: *llvm.Context) *llvm.Type; + +// Types: functions +pub extern fn LLVMFunctionType(return_type: *llvm.Type, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: c_uint, is_var_arg: Bool) *llvm.Type.Function; +pub extern fn LLVMIsFunctionVarArg(function_type: *llvm.Type.Function) Bool; +pub extern fn LLVMGetReturnType(function_type: *llvm.Type.Function) *llvm.Type; +pub extern fn LLVMCountParamTypes(function_type: *llvm.Type.Function) c_uint; +pub extern fn LLVMGetParamTypes(function_type: *llvm.Type.Function, types: [*]*llvm.Type) void; + +// Types: struct +pub extern fn llvm_context_create_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, name_pointer: [*]const u8, name_length: usize, is_packed: bool) *llvm.Type.Struct; +pub extern fn llvm_context_get_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, is_packed: bool) *llvm.Type.Struct; + +// Types: arrays +pub extern fn LLVMArrayType2(element_type: *llvm.Type, element_count: u64) *llvm.Type.Array; + +// Types: pointers +pub extern fn LLVMPointerTypeInContext(context: *llvm.Context, address_space: c_uint) *llvm.Type.Pointer; + +// Types: vectors +pub extern fn LLVMVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.FixedVector; +pub extern fn LLVMScalableVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.ScalableVector; + +// VALUES +pub extern fn LLVMConstInt(type: *llvm.Type.Integer, value: c_ulonglong, sign_extend: Bool) *llvm.Constant.Integer; + +// Debug info API +pub extern fn LLVMCreateDIBuilder(module: *llvm.Module) *llvm.DI.Builder; +pub extern fn LLVMDIBuilderCreateFile(builder: *llvm.DI.Builder, file_name_pointer: [*]const u8, file_name_length: usize, directory_name_pointer: [*]const u8, directory_name_length: usize) *llvm.DI.File; +pub extern fn LLVMDIBuilderCreateCompileUnit(builder: *llvm.DI.Builder, language: llvm.DwarfSourceLanguage, file: *llvm.DI.File, producer_name_pointer: [*]const u8, producer_name_length: usize, optimized: Bool, flags_pointer: [*]const u8, flags_length: usize, runtime_version: c_uint, split_name_pointer: [*]const u8, split_name_length: usize, dwarf_emission_kind: llvm.DwarfEmissionKind, debug_with_offset_id: c_uint, split_debug_inlining: Bool, debug_info_for_profiling: Bool, sysroot_pointer: [*]const u8, sysroot_length: usize, sdk_pointer: [*]const u8, sdk_length: usize) *llvm.DI.CompileUnit; +pub extern fn LLVMDIBuilderCreateFunction(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line_number: c_uint, type: *llvm.DI.Type.Subroutine, local_to_unit: Bool, is_definition: Bool, scope_line: c_uint, flags: llvm.DI.Flags, is_optimized: Bool) *llvm.DI.Subprogram; + +pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type { + const arch_name = @tagName(llvm_arch); + return struct { + pub const initialize_target_info = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "TargetInfo", + }); + pub const initialize_target = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "Target", + }); + pub const initialize_target_mc = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "TargetMC", + }); + pub const initialize_asm_printer = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "AsmPrinter", + }); + pub const initialize_asm_parser = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "AsmParser", + }); + pub const initialize_disassembler = @extern(*const fn () callconv(.C) void, .{ + .name = "LLVMInitialize" ++ arch_name ++ "Disassembler", + }); + + pub fn initialize(options: llvm.TargetInitializerOptions) void { + initialize_target_info(); + initialize_target(); + initialize_target_mc(); + + if (options.asm_printer) { + initialize_asm_printer(); + } + + if (options.asm_parser) { + initialize_asm_parser(); + } + + if (options.disassembler) { + initialize_disassembler(); + } + } + }; +} diff --git a/src/main.zig b/src/main.zig index 07983e2..44d8658 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,4 +1,6 @@ const lib = @import("lib.zig"); +const llvm = @import("LLVM.zig"); +const Arena = lib.Arena; pub const panic = struct { const abort = lib.os.abort; @@ -133,19 +135,13 @@ pub const panic = struct { } }; -pub fn main() callconv(.C) c_int { - const arena = lib.Arena.initialize_default(2 * lib.MB); - _ = arena.allocate_bytes(1024, 1); - _ = arena.join_string(&.{ "foo", "fa" }); - arena.reset(); - lib.file.write(".zig-cache/foo", "fafu", .{}); - const a = lib.file.read(arena, ".zig-cache/foo"); - lib.assert(lib.string.equal(a, "fafu")); +var global_persistent_arena: *Arena = undefined; - var buffer: [100]u8 = undefined; - const written_character_count = lib.format(&buffer, buffer.len, "fjaksdjkasd {u64}\n", @as(u64, 123123)); - const result_str = buffer[0..written_character_count]; - lib.assert(lib.string.equal(result_str, "fjaksdjkasd 123123\n")); +pub fn main() callconv(.C) c_int { + lib.GlobalState.initialize(); + + llvm.initialize_all(); + llvm.experiment(); return 0; } @@ -159,4 +155,5 @@ comptime { test { _ = lib; + _ = llvm; }