Function finish [src]

Prototype

pub fn finish(self: *WipFunction) Allocator.Error!void

Parameters

self: *WipFunction

Source

pub fn finish(self: *WipFunction) Allocator.Error!void { const gpa = self.builder.gpa; const function = self.function.ptr(self.builder); const params_len = self.function.typeOf(self.builder).functionParameters(self.builder).len; 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: struct { items: []Instruction.Index, fn map(instructions: @This(), val: Value) Value { if (val == .none) return .none; return switch (val.unwrap()) { .instruction => |instruction| instructions.items[ @intFromEnum(instruction) ].toValue(), .constant => |constant| constant.toValue(), .metadata => |metadata| metadata.toValue(), }; } } = .{ .items = try gpa.alloc(Instruction.Index, self.instructions.len) }; defer gpa.free(instructions.items); const names = try gpa.alloc(String, final_instructions_len); errdefer gpa.free(names); const value_indices = try gpa.alloc(u32, final_instructions_len); errdefer gpa.free(value_indices); var debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .empty; errdefer debug_locations.deinit(gpa); try debug_locations.ensureUnusedCapacity(gpa, @intCast(self.debug_locations.count())); const debug_values = try gpa.alloc(Instruction.Index, self.debug_values.count()); errdefer gpa.free(debug_values); var wip_extra: struct { index: Instruction.ExtraIndex = 0, items: []u32, fn addExtra(wip_extra: *@This(), extra: anytype) Instruction.ExtraIndex { const result = wip_extra.index; inline for (@typeInfo(@TypeOf(extra)).@"struct".fields) |field| { const value = @field(extra, field.name); wip_extra.items[wip_extra.index] = switch (field.type) { u32 => value, Alignment, AtomicOrdering, Block.Index, FunctionAttributes, Type, Value, Instruction.BrCond.Weights, => @intFromEnum(value), MemoryAccessInfo, Instruction.Alloca.Info, Instruction.Call.Info, => @bitCast(value), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; wip_extra.index += 1; } return result; } fn appendSlice(wip_extra: *@This(), slice: anytype) void { if (@typeInfo(@TypeOf(slice)).pointer.child == Value) @compileError("use appendMappedValues"); const data: []const u32 = @ptrCast(slice); @memcpy(wip_extra.items[wip_extra.index..][0..data.len], data); wip_extra.index += @intCast(data.len); } fn appendMappedValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void { for (wip_extra.items[wip_extra.index..][0..vals.len], vals) |*extra, val| extra.* = @intFromEnum(ctx.map(val)); wip_extra.index += @intCast(vals.len); } fn finish(wip_extra: *const @This()) []const u32 { assert(wip_extra.index == wip_extra.items.len); return wip_extra.items; } } = .{ .items = try gpa.alloc(u32, self.extra.items.len) }; errdefer gpa.free(wip_extra.items); gpa.free(function.blocks); function.blocks = &.{}; gpa.free(function.names[0..function.instructions.len]); function.debug_locations.deinit(gpa); function.debug_locations = .{}; gpa.free(function.debug_values); function.debug_values = &.{}; gpa.free(function.extra); function.extra = &.{}; function.instructions.shrinkRetainingCapacity(0); try function.instructions.setCapacity(gpa, final_instructions_len); errdefer function.instructions.shrinkRetainingCapacity(0); { var final_instruction_index: Instruction.Index = @enumFromInt(0); for (0..params_len) |param_index| { instructions.items[param_index] = final_instruction_index; final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); } for (blocks, self.blocks.items) |*final_block, current_block| { assert(current_block.incoming == current_block.branches); final_block.instruction = final_instruction_index; final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); for (current_block.instructions.items) |instruction| { instructions.items[@intFromEnum(instruction)] = final_instruction_index; final_instruction_index = @enumFromInt(@intFromEnum(final_instruction_index) + 1); } } } var wip_name: struct { next_name: String = @enumFromInt(0), next_unique_name: std.AutoHashMap(String, String), builder: *Builder, fn map(wip_name: *@This(), name: String, sep: []const u8) Allocator.Error!String { switch (name) { .none => return .none, .empty => { assert(wip_name.next_name != .none); defer wip_name.next_name = @enumFromInt(@intFromEnum(wip_name.next_name) + 1); return wip_name.next_name; }, _ => { assert(!name.isAnon()); const gop = try wip_name.next_unique_name.getOrPut(name); if (!gop.found_existing) { gop.value_ptr.* = @enumFromInt(0); return name; } while (true) { gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); const unique_name = try wip_name.builder.fmt("{f}{s}{f}", .{ name.fmtRaw(wip_name.builder), sep, gop.value_ptr.fmtRaw(wip_name.builder), }); const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); if (!unique_gop.found_existing) { unique_gop.value_ptr.* = @enumFromInt(0); return unique_name; } } }, } } } = .{ .next_unique_name = std.AutoHashMap(String, String).init(gpa), .builder = self.builder, }; defer wip_name.next_unique_name.deinit(); var value_index: u32 = 0; for (0..params_len) |param_index| { const old_argument_index: Instruction.Index = @enumFromInt(param_index); const new_argument_index: Instruction.Index = @enumFromInt(function.instructions.len); const argument = self.instructions.get(@intFromEnum(old_argument_index)); assert(argument.tag == .arg); assert(argument.data == param_index); value_indices[function.instructions.len] = value_index; value_index += 1; function.instructions.appendAssumeCapacity(argument); names[@intFromEnum(new_argument_index)] = try wip_name.map( if (self.strip) .empty else self.names.items[@intFromEnum(old_argument_index)], ".", ); if (self.debug_locations.get(old_argument_index)) |location| { debug_locations.putAssumeCapacity(new_argument_index, location); } if (self.debug_values.getIndex(old_argument_index)) |index| { debug_values[index] = new_argument_index; } } for (self.blocks.items) |current_block| { const new_block_index: Instruction.Index = @enumFromInt(function.instructions.len); value_indices[function.instructions.len] = value_index; function.instructions.appendAssumeCapacity(.{ .tag = .block, .data = current_block.incoming, }); names[@intFromEnum(new_block_index)] = try wip_name.map(current_block.name, ""); for (current_block.instructions.items) |old_instruction_index| { const new_instruction_index: Instruction.Index = @enumFromInt(function.instructions.len); var instruction = self.instructions.get(@intFromEnum(old_instruction_index)); switch (instruction.tag) { .add, .@"add nsw", .@"add nuw", .@"add nuw nsw", .@"and", .ashr, .@"ashr exact", .fadd, .@"fadd fast", .@"fcmp false", .@"fcmp fast false", .@"fcmp fast oeq", .@"fcmp fast oge", .@"fcmp fast ogt", .@"fcmp fast ole", .@"fcmp fast olt", .@"fcmp fast one", .@"fcmp fast ord", .@"fcmp fast true", .@"fcmp fast ueq", .@"fcmp fast uge", .@"fcmp fast ugt", .@"fcmp fast ule", .@"fcmp fast ult", .@"fcmp fast une", .@"fcmp fast uno", .@"fcmp oeq", .@"fcmp oge", .@"fcmp ogt", .@"fcmp ole", .@"fcmp olt", .@"fcmp one", .@"fcmp ord", .@"fcmp true", .@"fcmp ueq", .@"fcmp uge", .@"fcmp ugt", .@"fcmp ule", .@"fcmp ult", .@"fcmp une", .@"fcmp uno", .fdiv, .@"fdiv fast", .fmul, .@"fmul fast", .frem, .@"frem fast", .fsub, .@"fsub fast", .@"icmp eq", .@"icmp ne", .@"icmp sge", .@"icmp sgt", .@"icmp sle", .@"icmp slt", .@"icmp uge", .@"icmp ugt", .@"icmp ule", .@"icmp ult", .lshr, .@"lshr exact", .mul, .@"mul nsw", .@"mul nuw", .@"mul nuw nsw", .@"or", .sdiv, .@"sdiv exact", .shl, .@"shl nsw", .@"shl nuw", .@"shl nuw nsw", .srem, .sub, .@"sub nsw", .@"sub nuw", .@"sub nuw nsw", .udiv, .@"udiv exact", .urem, .xor, => { const extra = self.extraData(Instruction.Binary, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Binary{ .lhs = instructions.map(extra.lhs), .rhs = instructions.map(extra.rhs), }); }, .addrspacecast, .bitcast, .fpext, .fptosi, .fptoui, .fptrunc, .inttoptr, .ptrtoint, .sext, .sitofp, .trunc, .uitofp, .zext, => { const extra = self.extraData(Instruction.Cast, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Cast{ .val = instructions.map(extra.val), .type = extra.type, }); }, .alloca, .@"alloca inalloca", => { const extra = self.extraData(Instruction.Alloca, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Alloca{ .type = extra.type, .len = instructions.map(extra.len), .info = extra.info, }); }, .arg, .block, => unreachable, .atomicrmw => { const extra = self.extraData(Instruction.AtomicRmw, instruction.data); instruction.data = wip_extra.addExtra(Instruction.AtomicRmw{ .info = extra.info, .ptr = instructions.map(extra.ptr), .val = instructions.map(extra.val), }); }, .br, .fence, .@"ret void", .@"unreachable", => {}, .br_cond => { const extra = self.extraData(Instruction.BrCond, instruction.data); instruction.data = wip_extra.addExtra(Instruction.BrCond{ .cond = instructions.map(extra.cond), .then = extra.then, .@"else" = extra.@"else", .weights = extra.weights, }); }, .call, .@"call fast", .@"musttail call", .@"musttail call fast", .@"notail call", .@"notail call fast", .@"tail call", .@"tail call fast", => { var extra = self.extraDataTrail(Instruction.Call, instruction.data); const args = extra.trail.next(extra.data.args_len, Value, self); instruction.data = wip_extra.addExtra(Instruction.Call{ .info = extra.data.info, .attributes = extra.data.attributes, .ty = extra.data.ty, .callee = instructions.map(extra.data.callee), .args_len = extra.data.args_len, }); wip_extra.appendMappedValues(args, instructions); }, .cmpxchg, .@"cmpxchg weak", => { const extra = self.extraData(Instruction.CmpXchg, instruction.data); instruction.data = wip_extra.addExtra(Instruction.CmpXchg{ .info = extra.info, .ptr = instructions.map(extra.ptr), .cmp = instructions.map(extra.cmp), .new = instructions.map(extra.new), }); }, .extractelement => { const extra = self.extraData(Instruction.ExtractElement, instruction.data); instruction.data = wip_extra.addExtra(Instruction.ExtractElement{ .val = instructions.map(extra.val), .index = instructions.map(extra.index), }); }, .extractvalue => { var extra = self.extraDataTrail(Instruction.ExtractValue, instruction.data); const indices = extra.trail.next(extra.data.indices_len, u32, self); instruction.data = wip_extra.addExtra(Instruction.ExtractValue{ .val = instructions.map(extra.data.val), .indices_len = extra.data.indices_len, }); wip_extra.appendSlice(indices); }, .fneg, .@"fneg fast", .ret, => instruction.data = @intFromEnum(instructions.map(@enumFromInt(instruction.data))), .getelementptr, .@"getelementptr inbounds", => { var extra = self.extraDataTrail(Instruction.GetElementPtr, instruction.data); const indices = extra.trail.next(extra.data.indices_len, Value, self); instruction.data = wip_extra.addExtra(Instruction.GetElementPtr{ .type = extra.data.type, .base = instructions.map(extra.data.base), .indices_len = extra.data.indices_len, }); wip_extra.appendMappedValues(indices, instructions); }, .indirectbr => { var extra = self.extraDataTrail(Instruction.IndirectBr, instruction.data); const targets = extra.trail.next(extra.data.targets_len, Block.Index, self); instruction.data = wip_extra.addExtra(Instruction.IndirectBr{ .addr = instructions.map(extra.data.addr), .targets_len = extra.data.targets_len, }); wip_extra.appendSlice(targets); }, .insertelement => { const extra = self.extraData(Instruction.InsertElement, instruction.data); instruction.data = wip_extra.addExtra(Instruction.InsertElement{ .val = instructions.map(extra.val), .elem = instructions.map(extra.elem), .index = instructions.map(extra.index), }); }, .insertvalue => { var extra = self.extraDataTrail(Instruction.InsertValue, instruction.data); const indices = extra.trail.next(extra.data.indices_len, u32, self); instruction.data = wip_extra.addExtra(Instruction.InsertValue{ .val = instructions.map(extra.data.val), .elem = instructions.map(extra.data.elem), .indices_len = extra.data.indices_len, }); wip_extra.appendSlice(indices); }, .load, .@"load atomic", => { const extra = self.extraData(Instruction.Load, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Load{ .type = extra.type, .ptr = instructions.map(extra.ptr), .info = extra.info, }); }, .phi, .@"phi fast", => { const incoming_len = current_block.incoming; var extra = self.extraDataTrail(Instruction.Phi, instruction.data); const incoming_vals = extra.trail.next(incoming_len, Value, self); const incoming_blocks = extra.trail.next(incoming_len, Block.Index, self); instruction.data = wip_extra.addExtra(Instruction.Phi{ .type = extra.data.type, }); wip_extra.appendMappedValues(incoming_vals, instructions); wip_extra.appendSlice(incoming_blocks); }, .select, .@"select fast", => { const extra = self.extraData(Instruction.Select, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Select{ .cond = instructions.map(extra.cond), .lhs = instructions.map(extra.lhs), .rhs = instructions.map(extra.rhs), }); }, .shufflevector => { const extra = self.extraData(Instruction.ShuffleVector, instruction.data); instruction.data = wip_extra.addExtra(Instruction.ShuffleVector{ .lhs = instructions.map(extra.lhs), .rhs = instructions.map(extra.rhs), .mask = instructions.map(extra.mask), }); }, .store, .@"store atomic", => { const extra = self.extraData(Instruction.Store, instruction.data); instruction.data = wip_extra.addExtra(Instruction.Store{ .val = instructions.map(extra.val), .ptr = instructions.map(extra.ptr), .info = extra.info, }); }, .@"switch" => { var extra = self.extraDataTrail(Instruction.Switch, instruction.data); const case_vals = extra.trail.next(extra.data.cases_len, Constant, self); const case_blocks = extra.trail.next(extra.data.cases_len, Block.Index, self); instruction.data = wip_extra.addExtra(Instruction.Switch{ .val = instructions.map(extra.data.val), .default = extra.data.default, .cases_len = extra.data.cases_len, .weights = extra.data.weights, }); wip_extra.appendSlice(case_vals); wip_extra.appendSlice(case_blocks); }, .va_arg => { const extra = self.extraData(Instruction.VaArg, instruction.data); instruction.data = wip_extra.addExtra(Instruction.VaArg{ .list = instructions.map(extra.list), .type = extra.type, }); }, } function.instructions.appendAssumeCapacity(instruction); names[@intFromEnum(new_instruction_index)] = try wip_name.map(if (self.strip) if (old_instruction_index.hasResultWip(self)) .empty else .none else self.names.items[@intFromEnum(old_instruction_index)], "."); if (self.debug_locations.get(old_instruction_index)) |location| { debug_locations.putAssumeCapacity(new_instruction_index, location); } if (self.debug_values.getIndex(old_instruction_index)) |index| { debug_values[index] = new_instruction_index; } value_indices[@intFromEnum(new_instruction_index)] = value_index; if (old_instruction_index.hasResultWip(self)) value_index += 1; } } assert(function.instructions.len == final_instructions_len); function.extra = wip_extra.finish(); function.blocks = blocks; function.names = names.ptr; function.value_indices = value_indices.ptr; function.strip = self.strip; function.debug_locations = debug_locations; function.debug_values = debug_values; }