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