stage2: switch comptime execution

master
Vexu 2020-10-20 22:00:30 +03:00
parent 12e4c648cc
commit 769d5a9c43
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
5 changed files with 82 additions and 39 deletions

View File

@ -2122,16 +2122,18 @@ pub fn addSwitchBr(
src: usize,
target_ptr: *Inst,
cases: []Inst.SwitchBr.Case,
else_body: ir.Body,
) !*Inst {
const inst = try block.arena.create(Inst.SwitchBr);
inst.* = .{
.base = .{
.tag = .switchbr,
.ty = Type.initTag(.void),
.ty = Type.initTag(.noreturn),
.src = src,
},
.target_ptr = target_ptr,
.cases = cases,
.else_body = else_body,
};
try block.instructions.append(self.gpa, &inst.base);
return &inst.base;

View File

@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
defer block_scope.instructions.deinit(mod.gpa);
var item_scope: Scope.GenZIR = .{
.parent = scope,
.decl = scope.decl().?,
.arena = scope.arena(),
.instructions = .{},
};
defer item_scope.instructions.deinit(mod.gpa);
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);
@ -1598,6 +1590,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
.target_ptr = target_ptr,
.cases = undefined, // populated below
.items = &[_]*zir.Inst{}, // populated below
.else_body = undefined, // populated below
}, .{})).castTag(.switchbr).?;
var items = std.ArrayList(*zir.Inst).init(mod.gpa);
@ -1611,7 +1604,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
});
// then add block containing the switch.
const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
.instructions = undefined, // populated below
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
});
// Most result location types can be forwarded directly; however
@ -1622,6 +1615,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
.inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
};
var item_scope: Scope.GenZIR = .{
.parent = scope,
.decl = scope.decl().?,
.arena = scope.arena(),
.instructions = .{},
};
defer item_scope.instructions.deinit(mod.gpa);
var case_scope: Scope.GenZIR = .{
.parent = scope,
.decl = block_scope.decl,
@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
defer case_scope.instructions.deinit(mod.gpa);
var else_scope: Scope.GenZIR = .{
.parent = scope,
.decl = block_scope.decl,
.arena = block_scope.arena,
.instructions = .{},
};
defer else_scope.instructions.deinit(mod.gpa);
// first we gather all the switch items and check else/'_' prongs
var else_src: ?usize = null;
var underscore_src: ?usize = null;
@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
if (first_range == null) first_range = range_inst;
// target >= start and target <= end
const range_start_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_gte, target, start);
const range_end_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_lte, target, end);
const range_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .booland, range_start_ok, range_end_ok);
const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start);
const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end);
const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok);
if (any_ok) |some| {
any_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .boolor, some, range_ok);
any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok);
} else {
any_ok = range_ok;
}
@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
const item_inst = try expr(mod, &item_scope.base, .none, item);
try items.append(item_inst);
const cpm_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .cmp_eq, target, item_inst);
const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
if (any_ok) |some| {
any_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .boolor, some, cpm_ok);
any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok);
} else {
any_ok = cpm_ok;
}
}
const condbr = try addZIRInstSpecial(mod, &block_scope.base, case_src, zir.Inst.CondBr, .{
const condbr = try addZIRInstSpecial(mod, &else_scope.base, case_src, zir.Inst.CondBr, .{
.condition = any_ok.?,
.then_body = undefined, // populated below
.else_body = undefined, // populated below
@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
}
// Generate else block or a break last to finish the block.
if (special_case) |case| {
try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
} else {
// Not handling all possible cases is a compile error.
_ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck);
}
// All items have been generated, add the instructions to the comptime block.
item_block.positionals.body = .{
.instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
@ -1765,18 +1782,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
switch_inst.kw_args.range = first_range;
// Generate else block or a break last to finish the block.
if (special_case) |case| {
try switchCaseExpr(mod, &block_scope.base, case_rl, block, case);
} else {
// Not handling all possible cases is a compile error.
_ = try addZIRNoOp(mod, &block_scope.base, switch_src, .unreach_nocheck);
}
// Set block instructions now that it is finished.
block.positionals.body = .{
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
switch_inst.positionals.else_body = .{
.instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
};
return &block.base;
}

View File

@ -472,8 +472,11 @@ pub const Inst = struct {
target_ptr: *Inst,
cases: []Case,
/// 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].
/// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
deaths: [*]*Inst = undefined,
else_index: u32 = 0,
else_deaths: u32 = 0,
else_body: Body,
pub const Case = struct {
item: Value,
@ -498,6 +501,9 @@ pub const Inst = struct {
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_index)[0..self.else_deaths];
}
};
};

View File

@ -509,7 +509,6 @@ pub const Inst = struct {
.slice,
.slice_start,
.import,
.switchbr,
.switch_range,
=> false,
@ -522,6 +521,7 @@ pub const Inst = struct {
.unreach_nocheck,
.@"unreachable",
.loop,
.switchbr,
=> true,
};
}
@ -1012,9 +1012,10 @@ pub const Inst = struct {
positionals: struct {
target_ptr: *Inst,
cases: []Case,
/// List of all individual items and ranges
items: []*Inst,
cases: []Case,
else_body: Module.Body,
},
kw_args: struct {
/// Pointer to first range if such exists.
@ -2569,6 +2570,7 @@ const EmitZIR = struct {
.target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
.cases = cases,
.items = &[_]*Inst{}, // TODO this should actually be populated
.else_body = undefined, // populated below
},
.kw_args = .{},
};
@ -2590,6 +2592,12 @@ const EmitZIR = struct {
.body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
};
}
body_tmp.items.len = 0;
try self.emitBody(old_inst.else_body, inst_table, &body_tmp);
new_inst.positionals.else_body = .{
.instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items),
};
break :blk &new_inst.base;
},
.varptr => @panic("TODO"),

View File

@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
try validateSwitch(mod, scope, target, inst);
// TODO comptime execution
if (try mod.resolveDefinedValue(scope, target)) |target_val| {
for (inst.positionals.cases) |case| {
const resolved = try resolveInst(mod, scope, case.item);
const casted = try mod.coerce(scope, target.ty, resolved);
const item = try mod.resolveConstValue(scope, casted);
if (target_val.eql(item)) {
try analyzeBody(mod, scope, case.body);
return mod.constNoReturn(scope, inst.base.src);
}
}
try analyzeBody(mod, scope, inst.positionals.else_body);
return mod.constNoReturn(scope, inst.base.src);
}
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
@ -1253,7 +1266,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
};
defer case_block.instructions.deinit(mod.gpa);
for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| {
for (inst.positionals.cases) |case, i| {
// Reset without freeing.
case_block.instructions.items.len = 0;
@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
};
}
return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases);
case_block.instructions.items.len = 0;
try analyzeBody(mod, &case_block.base, inst.positionals.else_body);
const else_body: ir.Body = .{
.instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
};
return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
}
fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
@ -1354,14 +1374,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
false_count += 1;
}
if (true_count > 1 or false_count > 1) {
if (true_count + false_count > 2) {
return mod.fail(scope, item.src, "duplicate switch value", .{});
}
}
if ((true_count == 0 or false_count == 0) and inst.kw_args.special_prong != .@"else") {
if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
}
if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") {
if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
}
},
@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
try analyzeBody(mod, scope, body.*);
return mod.constVoid(scope, inst.base.src);
return mod.constNoReturn(scope, inst.base.src);
}
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);