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