stage2: switch emit zir

master
Vexu 2020-10-13 18:08:15 +03:00
parent 11998d2972
commit 2020ca640e
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
6 changed files with 127 additions and 48 deletions

View File

@ -2098,6 +2098,29 @@ pub fn addCall(
return &inst.base;
}
pub fn addSwitchBr(
self: *Module,
block: *Scope.Block,
src: usize,
target_ptr: *Inst,
cases: []Inst.SwitchBr.Case,
else_body: ?Module.Body,
) !*Inst {
const inst = try block.arena.create(Inst.SwitchBr);
inst.* = .{
.base = .{
.tag = .switchbr,
.ty = Type.initTag(.noreturn),
.src = src,
},
.target_ptr = target_ptr,
.cases = cases,
.@"else" = else_body,
};
try block.instructions.append(self.gpa, &inst.base);
return &inst.base;
}
pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
const const_inst = try scope.arena().create(Inst.Constant);
const_inst.* = .{

View File

@ -1573,8 +1573,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
const tree = scope.tree();
const switch_src = tree.token_locs[switch_node.switch_token].start;
const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
const cases = try scope.arena().alloc(zir.Inst.Switch.Case, switch_node.cases_len);
var kw_args: std.meta.fieldInfo(zir.Inst.Switch, "kw_args").field_type = .{};
const cases = try scope.arena().alloc(zir.Inst.SwitchBr.Case, switch_node.cases_len);
var kw_args: std.meta.fieldInfo(zir.Inst.SwitchBr, "kw_args").field_type = .{};
// first we gather all the switch items and check else/'_' prongs
var case_index: usize = 0;
@ -1643,7 +1643,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
}
// Then we add the switch instruction to finish the block.
_ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.Switch, .{
_ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
.target_ptr = target_ptr,
.cases = cases,
}, kw_args);

View File

@ -786,7 +786,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
.@"switch" => return self.genSwitch(inst.castTag(.@"switch").?),
.switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
}
}
@ -1990,7 +1990,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return @bitCast(MCValue, inst.codegen.mcv);
}
fn genSwitch(self: *Self, inst: *ir.Inst.Switch) !MCValue {
fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue {
switch (arch) {
else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),
}

View File

@ -91,7 +91,7 @@ pub const Inst = struct {
intcast,
unwrap_optional,
wrap_optional,
@"switch",
switchbr,
pub fn Type(tag: Tag) type {
return switch (tag) {
@ -138,7 +138,7 @@ pub const Inst = struct {
.constant => Constant,
.loop => Loop,
.varptr => VarPtr,
.@"switch" => Switch,
.switchbr => SwitchBr,
};
}
@ -461,26 +461,45 @@ pub const Inst = struct {
}
};
pub const Switch = struct {
pub const base_tag = Tag.@"switch";
pub const SwitchBr = struct {
pub const base_tag = Tag.switchbr;
base: Inst,
target_ptr: *Inst,
cases: []Case,
@"else": ?Body,
/// Set of instructions whose lifetimes end at the start of one of the cases.
/// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count].
deaths: [*]*Inst = undefined,
else_index: u32 = 0,
else_deaths: u32 = 0,
pub const Case = struct {
items: []Value,
body: Body,
index: u32 = 0,
deaths: u32 = 0,
};
pub fn operandCount(self: *const Switch) usize {
pub fn operandCount(self: *const SwitchBr) usize {
return 1;
}
pub fn getOperand(self: *const Switch, index: usize) ?*Inst {
return self.target_ptr;
pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
var i = index;
if (i < 1)
return self.target_ptr;
i -= 1;
return null;
}
pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
const case = self.cases[case_index];
return (self.deaths + case.index)[0..case.deaths];
}
pub fn elseDeaths(self: *const SwitchBr) []*Inst {
return (self.deaths + self.else_deaths)[0..self.else_deaths];
}
// TODO case body deaths
};
};

View File

@ -273,7 +273,7 @@ pub const Inst = struct {
/// Enum literal
enum_literal,
/// A switch expression.
@"switch",
switchbr,
/// A range in a switch case, `lhs...rhs`.
/// Only checks that `lhs >= rhs` if they are ints or floats, everything else is
/// validated by the .switch instruction.
@ -396,7 +396,7 @@ pub const Inst = struct {
.enum_literal => EnumLiteral,
.error_set => ErrorSet,
.slice => Slice,
.@"switch" => Switch,
.switchbr => SwitchBr,
};
}
@ -513,7 +513,7 @@ pub const Inst = struct {
.unreach_nocheck,
.@"unreachable",
.loop,
.@"switch",
.switchbr,
=> true,
};
}
@ -998,8 +998,8 @@ pub const Inst = struct {
},
};
pub const Switch = struct {
pub const base_tag = Tag.@"switch";
pub const SwitchBr = struct {
pub const base_tag = Tag.switchbr;
base: Inst,
positionals: struct {
@ -1275,24 +1275,24 @@ const Writer = struct {
}
try stream.writeByte(']');
},
[]Inst.Switch.Case => {
[]Inst.SwitchBr.Case => {
if (param.len == 0) {
return stream.writeAll("{}");
}
try stream.writeAll("{\n");
self.indent += 2;
for (param) |*case, i| {
if (i != 0) {
try stream.writeAll(",\n");
}
try stream.writeByteNTimes(' ', self.indent);
self.indent += 2;
try self.writeParamToStream(stream, &case.items);
try stream.writeAll(" => ");
try self.writeParamToStream(stream, &case.body);
self.indent -= 2;
}
try stream.writeByte('\n');
self.indent -= 2;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeByteNTimes(' ', self.indent - 2);
try stream.writeByte('}');
},
else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
@ -1707,12 +1707,12 @@ const Parser = struct {
try requireEatBytes(self, "]");
return strings.toOwnedSlice();
},
[]Inst.Switch.Case => {
[]Inst.SwitchBr.Case => {
try requireEatBytes(self, "{");
skipSpace(self);
if (eatByte(self, '}')) return &[0]Inst.Switch.Case{};
if (eatByte(self, '}')) return &[0]Inst.SwitchBr.Case{};
var cases = std.ArrayList(Inst.Switch.Case).init(&self.arena.allocator);
var cases = std.ArrayList(Inst.SwitchBr.Case).init(&self.arena.allocator);
while (true) {
const cur = try cases.addOne();
skipSpace(self);
@ -1824,7 +1824,7 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
.arena = std.heap.ArenaAllocator.init(allocator),
.old_module = &old_module,
.next_auto_name = 0,
.names = std.StringHashMap(void).init(allocator),
.names = std.StringArrayHashMap(void).init(allocator),
.primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
.indent = 0,
.block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
@ -2547,11 +2547,58 @@ const EmitZIR = struct {
};
break :blk &new_inst.base;
},
.switchbr => blk: {
const old_inst = inst.castTag(.switchbr).?;
const case_count = old_inst.cases.len + @boolToInt(old_inst.@"else" != null);
const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, case_count);
const new_inst = try self.arena.allocator.create(Inst.SwitchBr);
new_inst.* = .{
.base = .{
.src = inst.src,
.tag = Inst.SwitchBr.base_tag,
},
.positionals = .{
.target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
.cases = cases,
},
.kw_args = .{
.special_case = if (old_inst.@"else" != null) .@"else" else .none,
.support_range = null,
},
};
.varptr => @panic("TODO"),
.@"switch" => {
@panic("TODO");
var body_tmp = std.ArrayList(*Inst).init(self.allocator);
defer body_tmp.deinit();
for (old_inst.cases) |case, i| {
body_tmp.items.len = 0;
try self.emitBody(case.body, inst_table, &body_tmp);
const items = try self.arena.allocator.alloc(*Inst, case.items.len);
for (case.items) |item, j| {
items[j] = (try self.emitTypedValue(inst.src, .{
.ty = old_inst.target_ptr.ty.elemType(),
.val = item,
})).inst;
}
cases[i] = .{
.items = items,
.body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
};
}
if (old_inst.@"else") |some| {
body_tmp.items.len = 0;
try self.emitBody(some, inst_table, &body_tmp);
cases[cases.len - 1] = .{
.items = &[0]*Inst{},
.body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
};
}
break :blk &new_inst.base;
},
.varptr => @panic("TODO"),
};
try self.metadata.put(new_inst, .{
.deaths = inst.deaths,

View File

@ -135,7 +135,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
.slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
.import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),
.@"switch" => return analyzeInstSwitch(mod, scope, old_inst.castTag(.@"switch").?),
.switchbr => return analyzeInstSwitchBr(mod, scope, old_inst.castTag(.switchbr).?),
.switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
}
}
@ -1228,7 +1228,7 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In
return mod.constVoid(scope, inst.base.src);
}
fn analyzeInstSwitch(mod: *Module, scope: *Scope, inst: *zir.Inst.Switch) InnerError!*Inst {
fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst {
const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr);
const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
try validateSwitch(mod, scope, target, inst);
@ -1239,17 +1239,7 @@ fn analyzeInstSwitch(mod: *Module, scope: *Scope, inst: *zir.Inst.Switch) InnerE
const case_count = inst.positionals.cases.len - @boolToInt(inst.kw_args.special_case != .none);
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
const switch_inst = try parent_block.arena.create(Inst.Switch);
switch_inst.* = .{
.base = .{
.tag = Inst.Switch.base_tag,
.ty = Type.initTag(.noreturn),
.src = inst.base.src,
},
.target_ptr = target_ptr,
.@"else" = null,
.cases = try parent_block.arena.alloc(Inst.Switch.Case, case_count),
};
const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, case_count);
var case_block: Scope.Block = .{
.parent = parent_block,
@ -1281,25 +1271,25 @@ fn analyzeInstSwitch(mod: *Module, scope: *Scope, inst: *zir.Inst.Switch) InnerE
try analyzeBody(mod, &case_block.base, case.body);
switch_inst.cases[i] = .{
cases[i] = .{
.items = try parent_block.arena.dupe(Value, items_tmp.items),
.body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
};
}
if (inst.kw_args.special_case != .none) {
const else_body = if (inst.kw_args.special_case != .none) blk: {
case_block.instructions.items.len = 0;
try analyzeBody(mod, &case_block.base, inst.positionals.cases[case_count].body);
switch_inst.@"else" = .{
break: blk Body{
.instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
};
}
} else null;
return &switch_inst.base;
return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
}
fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Switch) InnerError!void {
fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
// validate usage of '_' prongs
if (inst.kw_args.special_case == .underscore and target.ty.zigTypeTag() != .Enum) {
return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});