mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 00:08:56 +00:00
Sema: reimplement runtime switch
Now supports multiple items pointing to the same body. This is a common pattern even when using a jump table, with multiple cases pointing to the same block of code. In the case of a range specified, the items are moved to branches in the else body. A future improvement may make it possible to have jump table items as well as ranges pointing to the same block of code.
This commit is contained in:
parent
caa0de545e
commit
ea902ffe8f
@ -352,9 +352,10 @@ pub const SwitchBr = struct {
|
||||
else_body_len: u32,
|
||||
|
||||
/// Trailing:
|
||||
/// * item: Inst.Ref // for each `items_len`.
|
||||
/// * instruction index for each `body_len`.
|
||||
pub const Case = struct {
|
||||
item: Inst.Ref,
|
||||
items_len: u32,
|
||||
body_len: u32,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1300,6 +1300,10 @@ pub const Scope = struct {
|
||||
}
|
||||
|
||||
pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
|
||||
return Air.indexToRef(try block.addInstAsIndex(inst));
|
||||
}
|
||||
|
||||
pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
|
||||
const sema = block.sema;
|
||||
const gpa = sema.gpa;
|
||||
|
||||
@ -1309,7 +1313,7 @@ pub const Scope = struct {
|
||||
const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
|
||||
sema.air_instructions.appendAssumeCapacity(inst);
|
||||
block.instructions.appendAssumeCapacity(result_index);
|
||||
return Air.indexToRef(result_index);
|
||||
return result_index;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
306
src/Sema.zig
306
src/Sema.zig
@ -4170,159 +4170,201 @@ fn analyzeSwitch(
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
|
||||
// TODO when reworking AIR memory layout make multi cases get generated as cases,
|
||||
// not as part of the "else" block.
|
||||
return mod.fail(&block.base, src, "TODO rework runtime switch Sema", .{});
|
||||
//const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
|
||||
var cases_extra: std.ArrayListUnmanaged(u32) = .{};
|
||||
defer cases_extra.deinit(gpa);
|
||||
|
||||
//var case_block = child_block.makeSubBlock();
|
||||
//case_block.runtime_loop = null;
|
||||
//case_block.runtime_cond = operand.src;
|
||||
//case_block.runtime_index += 1;
|
||||
//defer case_block.instructions.deinit(gpa);
|
||||
try cases_extra.ensureTotalCapacity(gpa, (scalar_cases_len + multi_cases_len) *
|
||||
@typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2);
|
||||
|
||||
//var extra_index: usize = special.end;
|
||||
var case_block = child_block.makeSubBlock();
|
||||
case_block.runtime_loop = null;
|
||||
case_block.runtime_cond = operand_src;
|
||||
case_block.runtime_index += 1;
|
||||
defer case_block.instructions.deinit(gpa);
|
||||
|
||||
//var scalar_i: usize = 0;
|
||||
//while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
// const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
// extra_index += 1;
|
||||
// const body_len = sema.code.extra[extra_index];
|
||||
// extra_index += 1;
|
||||
// const body = sema.code.extra[extra_index..][0..body_len];
|
||||
// extra_index += body_len;
|
||||
var extra_index: usize = special.end;
|
||||
|
||||
// case_block.instructions.shrinkRetainingCapacity(0);
|
||||
// const item = sema.resolveInst(item_ref);
|
||||
// // We validate these above; these two calls are guaranteed to succeed.
|
||||
// const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = sema.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
|
||||
// _ = try sema.analyzeBody(&case_block, body);
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
const item = sema.resolveInst(item_ref);
|
||||
// `item` is already guaranteed to be constant known.
|
||||
|
||||
// cases[scalar_i] = .{
|
||||
// .item = item_val,
|
||||
// .body = .{ .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items) },
|
||||
// };
|
||||
//}
|
||||
_ = try sema.analyzeBody(&case_block, body);
|
||||
|
||||
//var first_else_body: Body = undefined;
|
||||
//var prev_condbr: ?*Inst.CondBr = null;
|
||||
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
|
||||
cases_extra.appendAssumeCapacity(1); // items_len
|
||||
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
|
||||
cases_extra.appendAssumeCapacity(@enumToInt(item));
|
||||
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
|
||||
}
|
||||
|
||||
//var multi_i: usize = 0;
|
||||
//while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
// const items_len = sema.code.extra[extra_index];
|
||||
// extra_index += 1;
|
||||
// const ranges_len = sema.code.extra[extra_index];
|
||||
// extra_index += 1;
|
||||
// const body_len = sema.code.extra[extra_index];
|
||||
// extra_index += 1;
|
||||
// const items = sema.code.refSlice(extra_index, items_len);
|
||||
// extra_index += items_len;
|
||||
var is_first = true;
|
||||
var prev_cond_br: Air.Inst.Index = undefined;
|
||||
var first_else_body: []const Air.Inst.Index = &.{};
|
||||
defer gpa.free(first_else_body);
|
||||
var prev_then_body: []const Air.Inst.Index = &.{};
|
||||
defer gpa.free(prev_then_body);
|
||||
|
||||
// case_block.instructions.shrinkRetainingCapacity(0);
|
||||
var multi_i: usize = 0;
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const items = sema.code.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
|
||||
// var any_ok: ?Air.Inst.Index = null;
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
|
||||
// for (items) |item_ref| {
|
||||
// const item = sema.resolveInst(item_ref);
|
||||
// _ = try sema.resolveConstValue(&child_block, item.src, item);
|
||||
var any_ok: Air.Inst.Ref = .none;
|
||||
|
||||
// const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
|
||||
// if (any_ok) |some| {
|
||||
// any_ok = try case_block.addBinOp(.bool_or, some, cmp_ok);
|
||||
// } else {
|
||||
// any_ok = cmp_ok;
|
||||
// }
|
||||
// }
|
||||
// If there are any ranges, we have to put all the items into the
|
||||
// else prong. Otherwise, we can take advantage of multiple items
|
||||
// mapping to the same body.
|
||||
if (ranges_len == 0) {
|
||||
const body = sema.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
_ = try sema.analyzeBody(&case_block, body);
|
||||
|
||||
// var range_i: usize = 0;
|
||||
// while (range_i < ranges_len) : (range_i += 1) {
|
||||
// const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
// extra_index += 1;
|
||||
// const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
// extra_index += 1;
|
||||
try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
|
||||
case_block.instructions.items.len);
|
||||
|
||||
// const item_first = sema.resolveInst(first_ref);
|
||||
// const item_last = sema.resolveInst(last_ref);
|
||||
cases_extra.appendAssumeCapacity(1); // items_len
|
||||
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
|
||||
|
||||
// _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
|
||||
// _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
|
||||
for (items) |item_ref| {
|
||||
const item = sema.resolveInst(item_ref);
|
||||
cases_extra.appendAssumeCapacity(@enumToInt(item));
|
||||
}
|
||||
|
||||
// // operand >= first and operand <= last
|
||||
// const range_first_ok = try case_block.addBinOp(
|
||||
// .cmp_gte,
|
||||
// operand,
|
||||
// item_first,
|
||||
// );
|
||||
// const range_last_ok = try case_block.addBinOp(
|
||||
// .cmp_lte,
|
||||
// operand,
|
||||
// item_last,
|
||||
// );
|
||||
// const range_ok = try case_block.addBinOp(
|
||||
// .bool_and,
|
||||
// range_first_ok,
|
||||
// range_last_ok,
|
||||
// );
|
||||
// if (any_ok) |some| {
|
||||
// any_ok = try case_block.addBinOp(.bool_or, some, range_ok);
|
||||
// } else {
|
||||
// any_ok = range_ok;
|
||||
// }
|
||||
// }
|
||||
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
|
||||
} else {
|
||||
for (items) |item_ref| {
|
||||
const item = sema.resolveInst(item_ref);
|
||||
const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
|
||||
if (any_ok != .none) {
|
||||
any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
|
||||
} else {
|
||||
any_ok = cmp_ok;
|
||||
}
|
||||
}
|
||||
|
||||
// const new_condbr = try sema.arena.create(Inst.CondBr);
|
||||
// new_condbr.* = .{
|
||||
// .base = .{
|
||||
// .tag = .condbr,
|
||||
// .ty = Type.initTag(.noreturn),
|
||||
// .src = src,
|
||||
// },
|
||||
// .condition = any_ok.?,
|
||||
// .then_body = undefined,
|
||||
// .else_body = undefined,
|
||||
// };
|
||||
// try case_block.instructions.append(gpa, &new_condbr.base);
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
// const cond_body: Body = .{
|
||||
// .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
|
||||
// };
|
||||
const item_first = sema.resolveInst(first_ref);
|
||||
const item_last = sema.resolveInst(last_ref);
|
||||
|
||||
// case_block.instructions.shrinkRetainingCapacity(0);
|
||||
// const body = sema.code.extra[extra_index..][0..body_len];
|
||||
// extra_index += body_len;
|
||||
// _ = try sema.analyzeBody(&case_block, body);
|
||||
// new_condbr.then_body = .{
|
||||
// .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
|
||||
// };
|
||||
// if (prev_condbr) |condbr| {
|
||||
// condbr.else_body = cond_body;
|
||||
// } else {
|
||||
// first_else_body = cond_body;
|
||||
// }
|
||||
// prev_condbr = new_condbr;
|
||||
//}
|
||||
// operand >= first and operand <= last
|
||||
const range_first_ok = try case_block.addBinOp(
|
||||
.cmp_gte,
|
||||
operand,
|
||||
item_first,
|
||||
);
|
||||
const range_last_ok = try case_block.addBinOp(
|
||||
.cmp_lte,
|
||||
operand,
|
||||
item_last,
|
||||
);
|
||||
const range_ok = try case_block.addBinOp(
|
||||
.bool_and,
|
||||
range_first_ok,
|
||||
range_last_ok,
|
||||
);
|
||||
if (any_ok != .none) {
|
||||
any_ok = try case_block.addBinOp(.bool_or, any_ok, range_ok);
|
||||
} else {
|
||||
any_ok = range_ok;
|
||||
}
|
||||
}
|
||||
|
||||
//const final_else_body: Body = blk: {
|
||||
// if (special.body.len != 0) {
|
||||
// case_block.instructions.shrinkRetainingCapacity(0);
|
||||
// _ = try sema.analyzeBody(&case_block, special.body);
|
||||
// const else_body: Body = .{
|
||||
// .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
|
||||
// };
|
||||
// if (prev_condbr) |condbr| {
|
||||
// condbr.else_body = else_body;
|
||||
// break :blk first_else_body;
|
||||
// } else {
|
||||
// break :blk else_body;
|
||||
// }
|
||||
// } else {
|
||||
// break :blk .{ .instructions = &.{} };
|
||||
// }
|
||||
//};
|
||||
const new_cond_br = try case_block.addInstAsIndex(.{ .tag = .cond_br, .data = .{
|
||||
.pl_op = .{
|
||||
.operand = any_ok,
|
||||
.payload = undefined,
|
||||
},
|
||||
} });
|
||||
var cond_body = case_block.instructions.toOwnedSlice(gpa);
|
||||
defer gpa.free(cond_body);
|
||||
|
||||
//_ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
|
||||
//return sema.analyzeBlockBody(block, src, &child_block, merges);
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
const body = sema.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
_ = try sema.analyzeBody(&case_block, body);
|
||||
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
first_else_body = cond_body;
|
||||
cond_body = &.{};
|
||||
} else {
|
||||
try sema.air_extra.ensureUnusedCapacity(
|
||||
gpa,
|
||||
@typeInfo(Air.CondBr).Struct.fields.len + prev_then_body.len + cond_body.len,
|
||||
);
|
||||
|
||||
sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
|
||||
sema.addExtraAssumeCapacity(Air.CondBr{
|
||||
.then_body_len = @intCast(u32, prev_then_body.len),
|
||||
.else_body_len = @intCast(u32, cond_body.len),
|
||||
});
|
||||
sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
|
||||
sema.air_extra.appendSliceAssumeCapacity(cond_body);
|
||||
}
|
||||
prev_then_body = case_block.instructions.toOwnedSlice(gpa);
|
||||
prev_cond_br = new_cond_br;
|
||||
}
|
||||
}
|
||||
|
||||
var final_else_body: []const Air.Inst.Index = &.{};
|
||||
if (special.body.len != 0) {
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
_ = try sema.analyzeBody(&case_block, special.body);
|
||||
|
||||
if (is_first) {
|
||||
final_else_body = case_block.instructions.items;
|
||||
} else {
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, prev_then_body.len +
|
||||
@typeInfo(Air.CondBr).Struct.fields.len + case_block.instructions.items.len);
|
||||
|
||||
sema.air_instructions.items(.data)[prev_cond_br].pl_op.payload =
|
||||
sema.addExtraAssumeCapacity(Air.CondBr{
|
||||
.then_body_len = @intCast(u32, prev_then_body.len),
|
||||
.else_body_len = @intCast(u32, case_block.instructions.items.len),
|
||||
});
|
||||
sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
|
||||
sema.air_extra.appendSliceAssumeCapacity(case_block.instructions.items);
|
||||
final_else_body = first_else_body;
|
||||
}
|
||||
}
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
|
||||
cases_extra.items.len);
|
||||
|
||||
_ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{
|
||||
.operand = operand,
|
||||
.payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
|
||||
.cases_len = @intCast(u32, scalar_cases_len + multi_cases_len),
|
||||
.else_body_len = @intCast(u32, final_else_body.len),
|
||||
}),
|
||||
} } });
|
||||
sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
|
||||
sema.air_extra.appendSliceAssumeCapacity(final_else_body);
|
||||
|
||||
return sema.analyzeBlockBody(block, src, &child_block, merges);
|
||||
}
|
||||
|
||||
fn resolveSwitchItemVal(
|
||||
|
||||
@ -1282,44 +1282,49 @@ pub const Context = struct {
|
||||
// result type is always 'noreturn'
|
||||
const blocktype = wasm.block_empty;
|
||||
|
||||
const signedness: std.builtin.Signedness = blk: {
|
||||
// by default we tell the operand type is unsigned (i.e. bools and enum values)
|
||||
if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
|
||||
_ = valtype;
|
||||
_ = blocktype;
|
||||
_ = target;
|
||||
_ = else_body;
|
||||
return self.fail("TODO implement wasm codegen for switch", .{});
|
||||
//const signedness: std.builtin.Signedness = blk: {
|
||||
// // by default we tell the operand type is unsigned (i.e. bools and enum values)
|
||||
// if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
|
||||
|
||||
// incase of an actual integer, we emit the correct signedness
|
||||
break :blk target_ty.intInfo(self.target).signedness;
|
||||
};
|
||||
for (cases) |case_idx| {
|
||||
const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
|
||||
const case_body = self.air.extra[case.end..][0..case.data.body_len];
|
||||
// // incase of an actual integer, we emit the correct signedness
|
||||
// break :blk target_ty.intInfo(self.target).signedness;
|
||||
//};
|
||||
//for (cases) |case_idx| {
|
||||
// const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
|
||||
// const case_body = self.air.extra[case.end..][0..case.data.body_len];
|
||||
|
||||
// create a block for each case, when the condition does not match we break out of it
|
||||
try self.startBlock(.block, blocktype, null);
|
||||
try self.emitWValue(target);
|
||||
// // create a block for each case, when the condition does not match we break out of it
|
||||
// try self.startBlock(.block, blocktype, null);
|
||||
// try self.emitWValue(target);
|
||||
|
||||
const val = self.air.value(case.data.item).?;
|
||||
try self.emitConstant(val, target_ty);
|
||||
const opcode = buildOpcode(.{
|
||||
.valtype1 = valtype,
|
||||
.op = .ne, // not equal because we jump out the block if it does not match the condition
|
||||
.signedness = signedness,
|
||||
});
|
||||
try self.code.append(wasm.opcode(opcode));
|
||||
try self.code.append(wasm.opcode(.br_if));
|
||||
try leb.writeULEB128(self.code.writer(), @as(u32, 0));
|
||||
// const val = self.air.value(case.data.item).?;
|
||||
// try self.emitConstant(val, target_ty);
|
||||
// const opcode = buildOpcode(.{
|
||||
// .valtype1 = valtype,
|
||||
// .op = .ne, // not equal because we jump out the block if it does not match the condition
|
||||
// .signedness = signedness,
|
||||
// });
|
||||
// try self.code.append(wasm.opcode(opcode));
|
||||
// try self.code.append(wasm.opcode(.br_if));
|
||||
// try leb.writeULEB128(self.code.writer(), @as(u32, 0));
|
||||
|
||||
// emit our block code
|
||||
try self.genBody(case_body);
|
||||
// // emit our block code
|
||||
// try self.genBody(case_body);
|
||||
|
||||
// end the block we created earlier
|
||||
try self.endBlock();
|
||||
}
|
||||
// // end the block we created earlier
|
||||
// try self.endBlock();
|
||||
//}
|
||||
|
||||
// finally, emit the else case if it exists. Here we will not have to
|
||||
// check for a condition, so also no need to emit a block.
|
||||
try self.genBody(else_body);
|
||||
//// finally, emit the else case if it exists. Here we will not have to
|
||||
//// check for a condition, so also no need to emit a block.
|
||||
//try self.genBody(else_body);
|
||||
|
||||
return .none;
|
||||
//return .none;
|
||||
}
|
||||
|
||||
fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user