feat: code generation for switch statements
This commit is contained in:
parent
e5e2b7c559
commit
fac5a968e3
3 changed files with 195 additions and 14 deletions
|
|
@ -735,29 +735,103 @@ fn multiIfStmt(
|
||||||
const branch = branch_list[0];
|
const branch = branch_list[0];
|
||||||
const body_node = branch.data.bin.rhs.?;
|
const body_node = branch.data.bin.rhs.?;
|
||||||
try blockStmt(parent_block, scope, body_node);
|
try blockStmt(parent_block, scope, body_node);
|
||||||
return @enumFromInt(0);
|
return .none;
|
||||||
}
|
}
|
||||||
_ = try ifChain(parent_block, scope, branch_list);
|
_ = try ifChain(parent_block, scope, branch_list);
|
||||||
return @enumFromInt(0);
|
return .none;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn switchStmt(
|
||||||
|
parent_block: *GenIr,
|
||||||
|
scope: *Scope,
|
||||||
|
stmt_node: *const Ast.Node,
|
||||||
|
) InnerError!Ir.Inst.Ref {
|
||||||
|
const astgen = parent_block.astgen;
|
||||||
|
const gpa = astgen.gpa;
|
||||||
|
const switch_stmt = stmt_node.data.switch_stmt;
|
||||||
|
|
||||||
|
try validateSwitchProngs(parent_block, stmt_node);
|
||||||
|
|
||||||
|
const cond_inst = try expr(parent_block, scope, switch_stmt.condition_expr);
|
||||||
|
const switch_br = try parent_block.makeBlockInst(.switch_br);
|
||||||
|
var case_indexes: std.ArrayListUnmanaged(u32) = .empty;
|
||||||
|
try case_indexes.ensureUnusedCapacity(gpa, switch_stmt.cases.len);
|
||||||
|
defer case_indexes.deinit(gpa);
|
||||||
|
|
||||||
|
const switch_cases = switch_stmt.cases[0 .. switch_stmt.cases.len - 1];
|
||||||
|
for (switch_cases) |case_stmt| {
|
||||||
|
// TODO: Maybe make this non-nullable
|
||||||
|
const case_expr = case_stmt.data.bin.lhs.?;
|
||||||
|
const operand: Ir.Inst.Ref = switch (case_expr.tag) {
|
||||||
|
.number_literal => try numberLiteral(parent_block, case_expr),
|
||||||
|
.true_literal => .bool_true,
|
||||||
|
.false_literal => .bool_false,
|
||||||
|
else => return parent_block.fail(.invalid_switch_case, case_stmt),
|
||||||
|
};
|
||||||
|
var case_block = parent_block.makeSubBlock();
|
||||||
|
defer case_block.unstack();
|
||||||
|
_ = try blockStmt(&case_block, scope, case_stmt.data.bin.rhs.?);
|
||||||
|
_ = try case_block.addBreak(.@"break", switch_br);
|
||||||
|
|
||||||
|
const body = case_block.instructionsSlice();
|
||||||
|
const case_extra_len = @typeInfo(Ir.Inst.SwitchBr.Case).@"struct".fields.len + body.len;
|
||||||
|
try astgen.extra.ensureUnusedCapacity(gpa, case_extra_len);
|
||||||
|
|
||||||
|
const extra_index = astgen.addExtraAssumeCapacity(
|
||||||
|
Ir.Inst.SwitchBr.Case{
|
||||||
|
.operand = operand,
|
||||||
|
.body_len = @intCast(body.len),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
astgen.appendBlockBody(body);
|
||||||
|
case_indexes.appendAssumeCapacity(extra_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
try parent_block.instructions.append(gpa, switch_br);
|
||||||
|
|
||||||
|
const else_branch = switch_stmt.cases[switch_stmt.cases.len - 1];
|
||||||
|
var case_block = parent_block.makeSubBlock();
|
||||||
|
defer case_block.unstack();
|
||||||
|
_ = try blockStmt(&case_block, scope, else_branch.data.bin.rhs.?);
|
||||||
|
_ = try case_block.addBreak(.@"break", switch_br);
|
||||||
|
|
||||||
|
const else_body = case_block.instructionsSlice();
|
||||||
|
const extra_len =
|
||||||
|
@typeInfo(Ir.Inst.SwitchBr).@"struct".fields.len + case_indexes.items.len + else_body.len;
|
||||||
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
|
astgen.instructions.items[@intFromEnum(switch_br)].data.payload = .{
|
||||||
|
.payload_index = astgen.addExtraAssumeCapacity(
|
||||||
|
Ir.Inst.SwitchBr{
|
||||||
|
.operand = cond_inst,
|
||||||
|
.cases_len = @intCast(switch_cases.len),
|
||||||
|
.else_body_len = @intCast(else_body.len),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
|
||||||
|
astgen.appendBlockBody(else_body);
|
||||||
|
return switch_br.toRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contentExpr(block: *GenIr, scope: *Scope, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn contentExpr(block: *GenIr, scope: *Scope, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
// FIXME: This is a placeholder until we figure out what this function should be returning.
|
// FIXME: This is a placeholder until we figure out what this function should be returning.
|
||||||
var last_inst: Ir.Inst.Ref = undefined;
|
|
||||||
// TODO: Make sure that this is not nullable.
|
// TODO: Make sure that this is not nullable.
|
||||||
const node_list = expr_node.data.list.items.?;
|
const node_list = expr_node.data.list.items.?;
|
||||||
for (node_list) |child_node| {
|
for (node_list) |child_node| {
|
||||||
last_inst = switch (child_node.tag) {
|
switch (child_node.tag) {
|
||||||
.string_literal => try stringLiteral(block, child_node),
|
.string_literal => {
|
||||||
.inline_logic_expr => try inlineLogicExpr(block, scope, child_node),
|
const result = try stringLiteral(block, child_node);
|
||||||
.if_stmt => try ifStmt(block, scope, child_node),
|
_ = try block.addUnaryNode(.content_push, result);
|
||||||
.multi_if_stmt => try multiIfStmt(block, scope, child_node),
|
},
|
||||||
//.switch_stmt => try switchStmt(block, scope, child_node),
|
.inline_logic_expr => _ = try inlineLogicExpr(block, scope, child_node),
|
||||||
|
.if_stmt => _ = try ifStmt(block, scope, child_node),
|
||||||
|
.multi_if_stmt => _ = try multiIfStmt(block, scope, child_node),
|
||||||
|
.switch_stmt => _ = try switchStmt(block, scope, child_node),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
}
|
||||||
last_inst = try block.addUnaryNode(.content_push, last_inst);
|
|
||||||
}
|
}
|
||||||
return last_inst;
|
return .none;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contentStmt(gen: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn contentStmt(gen: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
|
|
|
||||||
52
src/Ir.zig
52
src/Ir.zig
|
|
@ -72,6 +72,7 @@ pub const Inst = struct {
|
||||||
block,
|
block,
|
||||||
condbr,
|
condbr,
|
||||||
@"break",
|
@"break",
|
||||||
|
switch_br,
|
||||||
content_push,
|
content_push,
|
||||||
content_flush,
|
content_flush,
|
||||||
};
|
};
|
||||||
|
|
@ -126,6 +127,17 @@ pub const Inst = struct {
|
||||||
then_body_len: u32,
|
then_body_len: u32,
|
||||||
else_body_len: u32,
|
else_body_len: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SwitchBr = struct {
|
||||||
|
operand: Ref,
|
||||||
|
cases_len: u32,
|
||||||
|
else_body_len: u32,
|
||||||
|
|
||||||
|
pub const Case = struct {
|
||||||
|
operand: Ref,
|
||||||
|
body_len: u32,
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Global = struct {
|
pub const Global = struct {
|
||||||
|
|
@ -277,6 +289,43 @@ const Render = struct {
|
||||||
try io_w.writeAll(")");
|
try io_w.writeAll(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn renderSwitchBr(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const switch_node = inst.data.payload;
|
||||||
|
const switch_extra = ir.extraData(Inst.SwitchBr, switch_node.payload_index);
|
||||||
|
const cases_slice = ir.bodySlice(switch_extra.end, switch_extra.data.cases_len);
|
||||||
|
|
||||||
|
try io_w.print("{s}(", .{@tagName(inst.tag)});
|
||||||
|
try renderInstRef(r, switch_extra.data.operand);
|
||||||
|
try io_w.print(", \n", .{});
|
||||||
|
for (cases_slice) |case_index| {
|
||||||
|
const case_extra = ir.extraData(Inst.SwitchBr.Case, @intFromEnum(case_index));
|
||||||
|
const body_slice = ir.bodySlice(case_extra.end, case_extra.data.body_len);
|
||||||
|
const old_len = try r.prefix.pushChildPrefix(r.gpa);
|
||||||
|
defer r.prefix.restore(old_len);
|
||||||
|
|
||||||
|
try r.prefix.writeIndent(io_w);
|
||||||
|
try renderInstRef(r, case_extra.data.operand);
|
||||||
|
try io_w.print(" = ", .{});
|
||||||
|
try renderBodyInner(r, ir, body_slice);
|
||||||
|
try io_w.writeAll(",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const else_body = ir.bodySlice(
|
||||||
|
switch_extra.end + switch_extra.data.cases_len,
|
||||||
|
switch_extra.data.else_body_len,
|
||||||
|
);
|
||||||
|
{
|
||||||
|
const old_len = try r.prefix.pushChildPrefix(r.gpa);
|
||||||
|
defer r.prefix.restore(old_len);
|
||||||
|
|
||||||
|
try r.prefix.writeIndent(io_w);
|
||||||
|
try io_w.print("else = ", .{});
|
||||||
|
try renderBodyInner(r, ir, else_body);
|
||||||
|
}
|
||||||
|
try io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
fn renderKnotDecl(r: *Render, ir: Ir, inst: Inst) Error!void {
|
fn renderKnotDecl(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
const io_w = r.writer;
|
const io_w = r.writer;
|
||||||
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
||||||
|
|
@ -340,6 +389,7 @@ const Render = struct {
|
||||||
},
|
},
|
||||||
.condbr => try r.renderCondbr(ir, inst),
|
.condbr => try r.renderCondbr(ir, inst),
|
||||||
.@"break" => try r.renderBreak(ir, inst),
|
.@"break" => try r.renderBreak(ir, inst),
|
||||||
|
.switch_br => try r.renderSwitchBr(ir, inst),
|
||||||
.alloc => try r.renderSimple(inst),
|
.alloc => try r.renderSimple(inst),
|
||||||
.load => try r.renderUnary(inst),
|
.load => try r.renderUnary(inst),
|
||||||
.store => try r.renderBinary(inst),
|
.store => try r.renderBinary(inst),
|
||||||
|
|
@ -366,7 +416,7 @@ const Render = struct {
|
||||||
try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes });
|
try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes });
|
||||||
},
|
},
|
||||||
.content_push => try r.renderUnary(inst),
|
.content_push => try r.renderUnary(inst),
|
||||||
.content_flush => try r.renderSimple(inst),
|
.content_flush => try r.renderUnary(inst),
|
||||||
}
|
}
|
||||||
try io_w.writeAll("\n");
|
try io_w.writeAll("\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
src/Sema.zig
59
src/Sema.zig
|
|
@ -174,6 +174,59 @@ fn irBlock(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
||||||
return blockBodyInner(sema, chunk, body);
|
return blockBodyInner(sema, chunk, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
||||||
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||||
|
const extra = sema.ir.extraData(Ir.Inst.SwitchBr, data.payload_index);
|
||||||
|
const cases_slice = sema.ir.bodySlice(extra.end, extra.data.cases_len);
|
||||||
|
|
||||||
|
var case_labels: std.ArrayListUnmanaged(usize) = .empty;
|
||||||
|
try case_labels.ensureUnusedCapacity(sema.gpa, cases_slice.len + 1);
|
||||||
|
defer case_labels.deinit(sema.gpa);
|
||||||
|
|
||||||
|
// TODO: Do something with this value?
|
||||||
|
//const condition = chunk.resolveInst(extra.data.operand);
|
||||||
|
const exit_label = try chunk.addLabel();
|
||||||
|
|
||||||
|
const cmp_var = chunk.knot.stack_size;
|
||||||
|
chunk.knot.stack_size += 1;
|
||||||
|
_ = try chunk.addConstOp(.store, @intCast(cmp_var));
|
||||||
|
|
||||||
|
for (cases_slice) |case_index| {
|
||||||
|
const case_extra = sema.ir.extraData(Ir.Inst.SwitchBr.Case, @intFromEnum(case_index));
|
||||||
|
const case_expr = chunk.resolveInst(case_extra.data.operand);
|
||||||
|
const case_label_index = try chunk.addLabel();
|
||||||
|
case_labels.appendAssumeCapacity(case_label_index);
|
||||||
|
|
||||||
|
_ = try chunk.addConstOp(.load, @intCast(cmp_var));
|
||||||
|
_ = try chunk.doLoad(case_expr);
|
||||||
|
_ = try chunk.addByteOp(.cmp_eq);
|
||||||
|
_ = try chunk.addFixup(.jmp_t, case_label_index);
|
||||||
|
_ = try chunk.addByteOp(.pop);
|
||||||
|
}
|
||||||
|
|
||||||
|
const else_label = try chunk.addLabel();
|
||||||
|
try chunk.addFixup(.jmp, else_label);
|
||||||
|
|
||||||
|
for (cases_slice, case_labels.items) |case_index, label_index| {
|
||||||
|
const case_extra = sema.ir.extraData(Ir.Inst.SwitchBr.Case, @intFromEnum(case_index));
|
||||||
|
const case_body = sema.ir.bodySlice(case_extra.end, case_extra.data.body_len);
|
||||||
|
|
||||||
|
chunk.setLabel(label_index);
|
||||||
|
_ = try chunk.addByteOp(.pop);
|
||||||
|
try blockBodyInner(sema, chunk, case_body);
|
||||||
|
try chunk.addFixup(.jmp, exit_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
const else_body = sema.ir.bodySlice(
|
||||||
|
extra.end + extra.data.cases_len,
|
||||||
|
extra.data.else_body_len,
|
||||||
|
);
|
||||||
|
|
||||||
|
chunk.setLabel(else_label);
|
||||||
|
try blockBodyInner(sema, chunk, else_body);
|
||||||
|
chunk.setLabel(exit_label);
|
||||||
|
}
|
||||||
|
|
||||||
fn irContentPush(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
fn irContentPush(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||||
const lhs = chunk.resolveInst(data.lhs);
|
const lhs = chunk.resolveInst(data.lhs);
|
||||||
|
|
@ -264,6 +317,10 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
|
||||||
},
|
},
|
||||||
.decl_var => unreachable, // handled in declaration()
|
.decl_var => unreachable, // handled in declaration()
|
||||||
.decl_knot => unreachable, // handled in declaration()
|
.decl_knot => unreachable, // handled in declaration()
|
||||||
|
.switch_br => {
|
||||||
|
try irSwitchBr(sema, chunk, inst);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
.alloc => try irAlloc(sema, chunk, inst),
|
.alloc => try irAlloc(sema, chunk, inst),
|
||||||
.store => {
|
.store => {
|
||||||
try irStore(sema, chunk, inst);
|
try irStore(sema, chunk, inst);
|
||||||
|
|
@ -534,7 +591,7 @@ pub fn compile(gpa: std.mem.Allocator, ir: *const Ir) !CompiledStory {
|
||||||
};
|
};
|
||||||
defer sema.deinit();
|
defer sema.deinit();
|
||||||
|
|
||||||
try file(&sema, @enumFromInt(0));
|
try file(&sema, .file_inst);
|
||||||
return .{
|
return .{
|
||||||
.constants = try sema.constants.toOwnedSlice(gpa),
|
.constants = try sema.constants.toOwnedSlice(gpa),
|
||||||
.globals = try sema.globals.toOwnedSlice(gpa),
|
.globals = try sema.globals.toOwnedSlice(gpa),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue