diff options
Diffstat (limited to 'src/codegen/llvm')
| -rw-r--r-- | src/codegen/llvm/Builder.zig | 374 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 2 |
2 files changed, 295 insertions, 81 deletions
diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 09ed51315a..69f4ab5d71 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -1,5 +1,6 @@ gpa: Allocator, use_lib_llvm: bool, +strip: bool, llvm: if (build_options.have_llvm) struct { context: *llvm.Context, @@ -12,33 +13,33 @@ llvm: if (build_options.have_llvm) struct { constants: std.ArrayListUnmanaged(*llvm.Value) = .{}, } else void, -source_filename: String = .none, -data_layout: String = .none, -target_triple: String = .none, - -string_map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, -string_bytes: std.ArrayListUnmanaged(u8) = .{}, -string_indices: std.ArrayListUnmanaged(u32) = .{}, - -types: std.AutoArrayHashMapUnmanaged(String, Type) = .{}, -next_unnamed_type: String = @enumFromInt(0), -next_unique_type_id: std.AutoHashMapUnmanaged(String, u32) = .{}, -type_map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, -type_items: std.ArrayListUnmanaged(Type.Item) = .{}, -type_extra: std.ArrayListUnmanaged(u32) = .{}, - -globals: std.AutoArrayHashMapUnmanaged(String, Global) = .{}, -next_unnamed_global: String = @enumFromInt(0), -next_replaced_global: String = .none, -next_unique_global_id: std.AutoHashMapUnmanaged(String, u32) = .{}, -aliases: std.ArrayListUnmanaged(Alias) = .{}, -variables: std.ArrayListUnmanaged(Variable) = .{}, -functions: std.ArrayListUnmanaged(Function) = .{}, - -constant_map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, -constant_items: std.MultiArrayList(Constant.Item) = .{}, -constant_extra: std.ArrayListUnmanaged(u32) = .{}, -constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb) = .{}, +source_filename: String, +data_layout: String, +target_triple: String, + +string_map: std.AutoArrayHashMapUnmanaged(void, void), +string_bytes: std.ArrayListUnmanaged(u8), +string_indices: std.ArrayListUnmanaged(u32), + +types: std.AutoArrayHashMapUnmanaged(String, Type), +next_unnamed_type: String, +next_unique_type_id: std.AutoHashMapUnmanaged(String, u32), +type_map: std.AutoArrayHashMapUnmanaged(void, void), +type_items: std.ArrayListUnmanaged(Type.Item), +type_extra: std.ArrayListUnmanaged(u32), + +globals: std.AutoArrayHashMapUnmanaged(String, Global), +next_unnamed_global: String, +next_replaced_global: String, +next_unique_global_id: std.AutoHashMapUnmanaged(String, u32), +aliases: std.ArrayListUnmanaged(Alias), +variables: std.ArrayListUnmanaged(Variable), +functions: std.ArrayListUnmanaged(Function), + +constant_map: std.AutoArrayHashMapUnmanaged(void, void), +constant_items: std.MultiArrayList(Constant.Item), +constant_extra: std.ArrayListUnmanaged(u32), +constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb), pub const expected_fields_len = 32; pub const expected_gep_indices_len = 8; @@ -46,6 +47,7 @@ pub const expected_gep_indices_len = 8; pub const Options = struct { allocator: Allocator, use_lib_llvm: bool = false, + strip: bool = true, name: []const u8 = &.{}, target: std.Target = builtin.target, triple: []const u8 = &.{}, @@ -986,9 +988,11 @@ pub const Variable = struct { pub const Function = struct { global: Global.Index, - body: ?void = null, - instructions: std.ArrayListUnmanaged(Instruction) = .{}, - blocks: std.ArrayListUnmanaged(Block) = .{}, + blocks: []const Block = &.{}, + instructions: std.MultiArrayList(Instruction) = .{}, + names: ?[*]const String = null, + metadata: ?[*]const Metadata = null, + extra: []const u32 = &.{}, pub const Index = enum(u32) { none = std.math.maxInt(u32), @@ -1007,28 +1011,236 @@ pub const Function = struct { } }; + pub const Block = struct { + instruction: Instruction.Index, + + pub const Index = WipFunction.Block.Index; + }; + pub const Instruction = struct { tag: Tag, + data: u32, pub const Tag = enum { arg, block, + @"ret void", + ret, }; - pub const Index = enum(u32) { _ }; + pub const Index = enum(u32) { + _, + + pub fn name(self: Instruction.Index, function: *const Function) String { + return if (function.names) |names| + names[@intFromEnum(self)] + else + @enumFromInt(@intFromEnum(self)); + } + }; }; + pub fn deinit(self: *Function, gpa: Allocator) void { + gpa.free(self.extra); + if (self.metadata) |metadata| gpa.free(metadata[0..self.instructions.len]); + if (self.names) |names| gpa.free(names[0..self.instructions.len]); + self.instructions.deinit(gpa); + self.* = undefined; + } +}; + +pub const WipFunction = struct { + builder: *Builder, + function: Function.Index, + llvm: if (build_options.have_llvm) struct { + builder: *llvm.Builder, + blocks: std.ArrayListUnmanaged(*llvm.BasicBlock), + instructions: std.ArrayListUnmanaged(*llvm.Value), + } else void, + cursor: Cursor, + blocks: std.ArrayListUnmanaged(Block), + instructions: std.MultiArrayList(Instruction), + names: std.ArrayListUnmanaged(String), + metadata: std.ArrayListUnmanaged(Metadata), + extra: std.ArrayListUnmanaged(u32), + + pub const Cursor = struct { block: Block.Index, instruction: u32 = 0 }; + pub const Block = struct { - body: std.ArrayListUnmanaged(Instruction.Index) = .{}, + name: String, + incoming: u32, + instructions: std.ArrayListUnmanaged(Instruction.Index), - pub const Index = enum(u32) { _ }; + const Index = enum(u32) { + _, + + pub fn toLlvm(self: Index, wip: *const WipFunction) *llvm.BasicBlock { + assert(wip.builder.useLibLlvm()); + return wip.llvm.blocks.items[@intFromEnum(self)]; + } + }; }; - pub fn deinit(self: *Function, gpa: Allocator) void { - self.instructions.deinit(gpa); - self.blocks.deinit(gpa); + pub const Instruction = Function.Instruction; + + pub fn init(builder: *Builder, function: Function.Index) WipFunction { + if (builder.useLibLlvm()) { + const llvm_function = function.toLlvm(builder); + while (llvm_function.getFirstBasicBlock()) |bb| bb.deleteBasicBlock(); + } + return .{ + .builder = builder, + .function = function, + .llvm = if (builder.useLibLlvm()) .{ + .builder = builder.llvm.context.createBuilder(), + .blocks = .{}, + .instructions = .{}, + } else undefined, + .cursor = undefined, + .blocks = .{}, + .instructions = .{}, + .names = .{}, + .metadata = .{}, + .extra = .{}, + }; + } + + pub fn block(self: *WipFunction, name: []const u8) Allocator.Error!Block.Index { + try self.blocks.ensureUnusedCapacity(self.builder.gpa, 1); + if (self.builder.useLibLlvm()) try self.llvm.blocks.ensureUnusedCapacity(self.builder.gpa, 1); + + const index: Block.Index = @enumFromInt(self.blocks.items.len); + const final_name = if (self.builder.strip) .empty else try self.builder.string(name); + self.blocks.appendAssumeCapacity(.{ .name = final_name, .incoming = 0, .instructions = .{} }); + if (self.builder.useLibLlvm()) self.llvm.blocks.appendAssumeCapacity( + self.builder.llvm.context.appendBasicBlock( + self.function.toLlvm(self.builder), + final_name.toSlice(self.builder).?, + ), + ); + return index; + } + + pub fn retVoid(self: *WipFunction) Allocator.Error!void { + _ = try self.addInst(.{ .tag = .@"ret void", .data = undefined }, .none); + if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( + self.llvm.builder.buildRetVoid(), + ); + } + + pub fn finish(self: *WipFunction) Allocator.Error!void { + const gpa = self.builder.gpa; + const function = self.function.ptr(self.builder); + const final_instructions_len = self.blocks.items.len + self.instructions.len; + + const blocks = try gpa.alloc(Function.Block, self.blocks.items.len); + errdefer gpa.free(blocks); + + const instructions = try gpa.alloc(Instruction.Index, self.instructions.len); + defer gpa.free(instructions); + + const names = if (self.builder.strip) null else try gpa.alloc(String, final_instructions_len); + errdefer if (names) |new_names| gpa.free(new_names); + + const metadata = + if (self.builder.strip) null else try gpa.alloc(Metadata, final_instructions_len); + errdefer if (metadata) |new_metadata| gpa.free(new_metadata); + + gpa.free(function.blocks); + function.blocks = &.{}; + if (function.names) |old_names| gpa.free(old_names[0..function.instructions.len]); + function.names = null; + if (function.metadata) |old_metadata| gpa.free(old_metadata[0..function.instructions.len]); + function.metadata = null; + + function.instructions.shrinkRetainingCapacity(0); + try function.instructions.setCapacity(gpa, final_instructions_len); + errdefer function.instructions.shrinkRetainingCapacity(0); + + { + var final_instruction: Instruction.Index = @enumFromInt(0); + for (blocks, self.blocks.items) |*final_block, current_block| { + final_block.instruction = final_instruction; + final_instruction = @enumFromInt(@intFromEnum(final_instruction) + 1); + for (current_block.instructions.items) |instruction| { + instructions[@intFromEnum(instruction)] = final_instruction; + final_instruction = @enumFromInt(@intFromEnum(final_instruction) + 1); + } + } + } + + var next_name: String = @enumFromInt(0); + for (self.blocks.items) |current_block| { + const block_instruction: Instruction.Index = @enumFromInt(function.instructions.len); + function.instructions.appendAssumeCapacity(.{ + .tag = .block, + .data = current_block.incoming, + }); + if (names) |new_names| + new_names[@intFromEnum(block_instruction)] = switch (current_block.name) { + .empty => name: { + const name = next_name; + next_name = @enumFromInt(@intFromEnum(name) + 1); + break :name name; + }, + else => |name| name, + }; + for (current_block.instructions.items) |instruction_index| { + var instruction = self.instructions.get(@intFromEnum(instruction_index)); + switch (instruction.tag) { + .block => unreachable, + .@"ret void" => {}, + else => unreachable, + } + function.instructions.appendAssumeCapacity(instruction); + } + } + + function.extra = try self.extra.toOwnedSlice(gpa); + function.blocks = blocks; + function.names = if (names) |new_names| new_names.ptr else null; + function.metadata = if (metadata) |new_metadata| new_metadata.ptr else null; + } + + pub fn deinit(self: *WipFunction) void { + self.extra.deinit(self.builder.gpa); + self.instructions.deinit(self.builder.gpa); + for (self.blocks.items) |*b| b.instructions.deinit(self.builder.gpa); + self.blocks.deinit(self.builder.gpa); + if (self.builder.useLibLlvm()) self.llvm.builder.dispose(); self.* = undefined; } + + fn addInst( + self: *WipFunction, + instruction: Instruction, + name: String, + ) Allocator.Error!Instruction.Index { + const block_instructions = &self.blocks.items[@intFromEnum(self.cursor.block)].instructions; + try self.instructions.ensureUnusedCapacity(self.builder.gpa, 1); + try self.names.ensureUnusedCapacity(self.builder.gpa, 1); + try block_instructions.ensureUnusedCapacity(self.builder.gpa, 1); + if (self.builder.useLibLlvm()) { + try self.llvm.instructions.ensureUnusedCapacity(self.builder.gpa, 1); + + if (false) self.llvm.builder.positionBuilder( + self.cursor.block.toLlvm(self), + if (self.cursor.instruction < block_instructions.items.len) + self.llvm.instructions.items[ + @intFromEnum(block_instructions.items[self.cursor.instruction]) + ] + else + null, + ); + } + + const index: Instruction.Index = @enumFromInt(self.instructions.len); + self.instructions.appendAssumeCapacity(instruction); + self.names.appendAssumeCapacity(name); + block_instructions.insertAssumeCapacity(self.cursor.instruction, index); + self.cursor.instruction += 1; + return index; + } }; pub const FloatCondition = enum(u4) { @@ -1696,6 +1908,8 @@ pub const Value = enum(u32) { } }; +pub const Metadata = enum(u32) { _ }; + pub const InitError = error{ InvalidLlvmTriple, } || Allocator.Error; @@ -1704,7 +1918,37 @@ pub fn init(options: Options) InitError!Builder { var self = Builder{ .gpa = options.allocator, .use_lib_llvm = options.use_lib_llvm, + .strip = options.strip, + .llvm = undefined, + + .source_filename = .none, + .data_layout = .none, + .target_triple = .none, + + .string_map = .{}, + .string_bytes = .{}, + .string_indices = .{}, + + .types = .{}, + .next_unnamed_type = @enumFromInt(0), + .next_unique_type_id = .{}, + .type_map = .{}, + .type_items = .{}, + .type_extra = .{}, + + .globals = .{}, + .next_unnamed_global = @enumFromInt(0), + .next_replaced_global = .none, + .next_unique_global_id = .{}, + .aliases = .{}, + .variables = .{}, + .functions = .{}, + + .constant_map = .{}, + .constant_items = .{}, + .constant_extra = .{}, + .constant_limbs = .{}, }; if (self.useLibLlvm()) self.llvm = .{ .context = llvm.Context.create() }; errdefer self.deinit(); @@ -1726,7 +1970,7 @@ pub fn init(options: Options) InitError!Builder { var error_message: [*:0]const u8 = undefined; var target: *llvm.Target = undefined; if (llvm.Target.getFromTriple( - self.target_triple.toSlice(&self).?.ptr, + self.target_triple.toSlice(&self).?, &target, &error_message, ).toBool()) { @@ -1739,7 +1983,7 @@ pub fn init(options: Options) InitError!Builder { return InitError.InvalidLlvmTriple; } self.llvm.target = target; - self.llvm.module.?.setTarget(self.target_triple.toSlice(&self).?.ptr); + self.llvm.module.?.setTarget(self.target_triple.toSlice(&self).?); } } @@ -2448,7 +2692,7 @@ pub fn dump(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocator try writer.print( \\{s}{}{}{}{} {} {}( , .{ - if (function.body) |_| "define" else "declare", + if (function.instructions.len > 0) "define" else "declare", global.linkage, global.preemption, global.visibility, @@ -2469,48 +2713,18 @@ pub fn dump(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocator else => unreachable, } try writer.print("){}{}", .{ global.unnamed_addr, global.alignment }); - if (function.body) |_| { - try writer.writeAll(" {\n ret "); - void: { - try writer.print("{%}", .{switch (extra.data.ret) { - .void => |tag| { - try writer.writeAll(@tagName(tag)); - break :void; - }, - inline .half, - .bfloat, - .float, - .double, - .fp128, - .x86_fp80, - => |tag| try @field(Builder, @tagName(tag) ++ "Const")(self, 0.0), - .ppc_fp128 => try self.ppc_fp128Const(.{ 0.0, 0.0 }), - .x86_amx, - .x86_mmx, - .label, - .metadata, - => unreachable, - .token => Constant.none, - else => switch (extra.data.ret.tag(self)) { - .simple, - .function, - .vararg_function, - => unreachable, - .integer => try self.intConst(extra.data.ret, 0), - .pointer => try self.nullConst(extra.data.ret), - .target, - .vector, - .scalable_vector, - .small_array, - .array, - .structure, - .packed_structure, - .named_structure, - => try self.zeroInitConst(extra.data.ret), - }, - }.fmt(self)}); + if (function.instructions.len > 0) { + try writer.writeAll(" {\n"); + for (0..function.instructions.len) |index| { + const instruction_index: Function.Instruction.Index = @enumFromInt(index); + const instruction = function.instructions.get(index); + switch (instruction.tag) { + .block => try writer.print("{}:\n", .{instruction_index.name(&function).fmt(self)}), + .@"ret void" => |tag| try writer.print(" {s}\n", .{@tagName(tag)}), + else => unreachable, + } } - try writer.writeAll("\n}"); + try writer.writeByte('}'); } try writer.writeAll("\n\n"); } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index c3c471fc2e..adc6223830 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -621,7 +621,7 @@ pub const Builder = opaque { extern fn LLVMPositionBuilder( Builder: *Builder, Block: *BasicBlock, - Instr: *Value, + Instr: ?*Value, ) void; pub const positionBuilderAtEnd = LLVMPositionBuilderAtEnd; |
