aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig125
-rw-r--r--src/Sema.zig601
-rw-r--r--src/astgen.zig126
-rw-r--r--src/zir.zig75
4 files changed, 512 insertions, 415 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 55e301c21c..d535a6d580 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -477,7 +477,7 @@ pub const Scope = struct {
switch (scope.tag) {
.file => return &scope.cast(File).?.tree,
.block => return &scope.cast(Block).?.src_decl.container.file_scope.tree,
- .gen_zir => return &scope.cast(GenZir).?.zir_code.decl.container.file_scope.tree,
+ .gen_zir => return scope.cast(GenZir).?.tree(),
.local_val => return &scope.cast(LocalVal).?.gen_zir.zir_code.decl.container.file_scope.tree,
.local_ptr => return &scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container.file_scope.tree,
.container => return &scope.cast(Container).?.file_scope.tree,
@@ -983,6 +983,30 @@ pub const Scope = struct {
return gz.zir_code.decl.nodeSrcLoc(node_index);
}
+ pub fn tree(gz: *const GenZir) *const ast.Tree {
+ return &gz.zir_code.decl.container.file_scope.tree;
+ }
+
+ pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
+ try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len +
+ @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
+ const zir_datas = gz.zir_code.instructions.items(.data);
+ zir_datas[inst].bool_br.payload_index = gz.zir_code.addExtraAssumeCapacity(
+ zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
+ );
+ gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items);
+ }
+
+ pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void {
+ try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len +
+ @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
+ const zir_datas = gz.zir_code.instructions.items(.data);
+ zir_datas[inst].pl_node.payload_index = gz.zir_code.addExtraAssumeCapacity(
+ zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
+ );
+ gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items);
+ }
+
pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
param_types: []const zir.Inst.Ref,
ret_ty: zir.Inst.Ref,
@@ -1044,73 +1068,62 @@ pub const Scope = struct {
return new_index + gz.zir_code.ref_start_index;
}
- pub fn addCondBr(
+ pub fn addCall(
gz: *GenZir,
- condition: zir.Inst.Ref,
- then_body: []const zir.Inst.Ref,
- else_body: []const zir.Inst.Ref,
+ tag: zir.Inst.Tag,
+ callee: zir.Inst.Ref,
+ args: []const zir.Inst.Ref,
/// Absolute node index. This function does the conversion to offset from Decl.
abs_node_index: ast.Node.Index,
- ) !zir.Inst.Ref {
+ ) !zir.Inst.Index {
+ assert(callee != 0);
+ assert(abs_node_index != 0);
const gpa = gz.zir_code.gpa;
try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
- @typeInfo(zir.Inst.CondBr).Struct.fields.len + then_body.len + else_body.len);
+ @typeInfo(zir.Inst.Call).Struct.fields.len + args.len);
- const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.CondBr{
- .condition = condition,
- .then_body_len = @intCast(u32, then_body.len),
- .else_body_len = @intCast(u32, else_body.len),
+ const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{
+ .callee = callee,
+ .args_len = @intCast(u32, args.len),
});
- gz.zir_code.extra.appendSliceAssumeCapacity(then_body);
- gz.zir_code.extra.appendSliceAssumeCapacity(else_body);
+ gz.zir_code.extra.appendSliceAssumeCapacity(args);
const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
gz.zir_code.instructions.appendAssumeCapacity(.{
- .tag = .condbr,
+ .tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.zir_code.decl.nodeIndexToRelative(abs_node_index),
.payload_index = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
-
return new_index + gz.zir_code.ref_start_index;
}
- pub fn addCall(
+ /// Note that this returns a `zir.Inst.Index` not a ref.
+ /// Leaves the `payload_index` field undefined.
+ pub fn addBoolBr(
gz: *GenZir,
tag: zir.Inst.Tag,
- callee: zir.Inst.Ref,
- args: []const zir.Inst.Ref,
- /// Absolute node index. This function does the conversion to offset from Decl.
- abs_node_index: ast.Node.Index,
+ lhs: zir.Inst.Ref,
) !zir.Inst.Index {
- assert(callee != 0);
- assert(abs_node_index != 0);
+ assert(lhs != 0);
const gpa = gz.zir_code.gpa;
try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
- try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
- @typeInfo(zir.Inst.Call).Struct.fields.len + args.len);
-
- const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{
- .callee = callee,
- .args_len = @intCast(u32, args.len),
- });
- gz.zir_code.extra.appendSliceAssumeCapacity(args);
const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
gz.zir_code.instructions.appendAssumeCapacity(.{
.tag = tag,
- .data = .{ .pl_node = .{
- .src_node = gz.zir_code.decl.nodeIndexToRelative(abs_node_index),
- .payload_index = payload_index,
+ .data = .{ .bool_br = .{
+ .lhs = lhs,
+ .payload_index = undefined,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
- return new_index + gz.zir_code.ref_start_index;
+ return new_index;
}
pub fn addInt(gz: *GenZir, integer: u64) !zir.Inst.Ref {
@@ -1291,6 +1304,20 @@ pub const Scope = struct {
return new_index;
}
+ /// Note that this returns a `zir.Inst.Index` not a ref.
+ /// Leaves the `payload_index` field undefined.
+ pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {
+ const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
+ try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
+ .tag = .condbr,
+ .data = .{ .pl_node = .{
+ .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
+ .payload_index = undefined,
+ } },
+ });
+ return new_index;
+ }
+
pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
const gpa = gz.zir_code.gpa;
try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
@@ -1409,9 +1436,9 @@ pub const WipZirCode = struct {
.bitcast_result_ptr,
.bit_or,
.block,
- .block_flat,
.block_comptime,
- .block_comptime_flat,
+ .bool_br_and,
+ .bool_br_or,
.bool_not,
.bool_and,
.bool_or,
@@ -1461,9 +1488,6 @@ pub const WipZirCode = struct {
.ret_type,
.shl,
.shr,
- .store,
- .store_to_block_ptr,
- .store_to_inferred_ptr,
.str,
.sub,
.subwrap,
@@ -1497,7 +1521,6 @@ pub const WipZirCode = struct {
.slice_sentinel,
.import,
.typeof_peer,
- .resolve_inferred_alloc,
=> return false,
.breakpoint,
@@ -1509,6 +1532,7 @@ pub const WipZirCode = struct {
.ensure_err_payload_void,
.@"break",
.break_void_tok,
+ .break_flat,
.condbr,
.compile_error,
.ret_node,
@@ -1517,6 +1541,10 @@ pub const WipZirCode = struct {
.@"unreachable",
.loop,
.elided,
+ .store,
+ .store_to_block_ptr,
+ .store_to_inferred_ptr,
+ .resolve_inferred_alloc,
=> return true,
}
}
@@ -2150,7 +2178,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
};
defer block_scope.instructions.deinit(mod.gpa);
- try sema.root(&block_scope);
+ _ = try sema.root(&block_scope);
decl.analysis = .complete;
decl.generation = mod.generation;
@@ -2338,6 +2366,7 @@ fn astgenAndSemaFn(
const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);
};
+ _ = try fn_type_scope.addUnNode(.break_flat, fn_type_inst, 0);
// We need the memory for the Type to go into the arena for the Decl
var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
@@ -2370,7 +2399,7 @@ fn astgenAndSemaFn(
};
defer block_scope.instructions.deinit(mod.gpa);
- const fn_type = try fn_type_sema.rootAsType(&block_scope, fn_type_inst);
+ const fn_type = try fn_type_sema.rootAsType(&block_scope);
if (body_node == 0) {
if (!is_extern) {
return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{});
@@ -2650,6 +2679,7 @@ fn astgenAndSemaVarDecl(
init_result_loc,
var_decl.ast.init_node,
);
+ _ = try gen_scope.addUnNode(.break_flat, init_inst, var_decl.ast.init_node);
var code = try gen_scope.finish();
defer code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
@@ -2676,10 +2706,9 @@ fn astgenAndSemaVarDecl(
};
defer block_scope.instructions.deinit(mod.gpa);
- try sema.root(&block_scope);
-
+ const init_inst_zir_ref = try sema.root(&block_scope);
// The result location guarantees the type coercion.
- const analyzed_init_inst = try sema.resolveInst(init_inst);
+ const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
// The is_comptime in the Scope.Block guarantees the result is comptime-known.
const val = analyzed_init_inst.value().?;
@@ -2713,6 +2742,8 @@ fn astgenAndSemaVarDecl(
defer type_scope.instructions.deinit(mod.gpa);
const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
+ _ = try type_scope.addUnNode(.break_flat, var_type, 0);
+
var code = try type_scope.finish();
defer code.deinit(mod.gpa);
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
@@ -2739,7 +2770,7 @@ fn astgenAndSemaVarDecl(
};
defer block_scope.instructions.deinit(mod.gpa);
- const ty = try sema.rootAsType(&block_scope, var_type);
+ const ty = try sema.rootAsType(&block_scope);
break :vi .{
.ty = try ty.copy(&decl_arena.allocator),
@@ -3328,7 +3359,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void {
func.state = .in_progress;
log.debug("set {s} to in_progress", .{decl.name});
- try sema.root(&inner_block);
+ _ = try sema.root(&inner_block);
const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items);
func.state = .success;
diff --git a/src/Sema.zig b/src/Sema.zig
index 3b257b666e..7ad18ac66e 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -53,172 +53,230 @@ const InnerError = Module.InnerError;
const Decl = Module.Decl;
const LazySrcLoc = Module.LazySrcLoc;
-pub fn root(sema: *Sema, root_block: *Scope.Block) !void {
+pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len];
return sema.analyzeBody(root_block, root_body);
}
-pub fn rootAsType(sema: *Sema, root_block: *Scope.Block, result_inst: zir.Inst.Ref) !Type {
- const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len];
- try sema.analyzeBody(root_block, root_body);
-
+/// Assumes that `root_block` ends with `break_flat`.
+pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
+ const zir_inst_ref = try sema.root(root_block);
// Source location is unneeded because resolveConstValue must have already
// been successfully called when coercing the value to a type, from the
// result location.
- return sema.resolveType(root_block, .unneeded, result_inst);
-}
-
-pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !void {
- const tracy = trace(@src());
- defer tracy.end();
+ return sema.resolveType(root_block, .unneeded, zir_inst_ref);
+}
+
+/// ZIR instructions which are always `noreturn` return this. This matches the
+/// return type of `analyzeBody` so that we can tail call them.
+/// Only appropriate to return when the instruction is known to be NoReturn
+/// solely based on the ZIR tag.
+const always_noreturn: InnerError!zir.Inst.Ref = @as(zir.Inst.Index, 0);
+
+/// This function is the main loop of `Sema` and it can be used in two different ways:
+/// * The traditional way where there are N breaks out of the block and peer type
+/// resolution is done on the break operands. In this case, the `zir.Inst.Index`
+/// part of the return value will be `undefined`, and callsites should ignore it,
+/// finding the block result value via the block scope.
+/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_flat`
+/// instruction. In this case, the `zir.Inst.Index` part of the return value will be
+/// the block result value. No block scope needs to be created for this strategy.
+pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !zir.Inst.Index {
+ // No tracy calls here, to avoid interfering with the tail call mechanism.
const map = block.sema.inst_map;
const tags = block.sema.code.instructions.items(.tag);
- // TODO: As an optimization, look into making these switch prongs directly jump
- // to the next one, rather than detouring through the loop condition.
- // Also, look into leaving only the "noreturn" loop break condition, and removing
- // the iteration based one. Better yet, have an extra entry in the tags array as a
- // sentinel, so that exiting the loop is just another jump table prong.
- // Related: https://github.com/ziglang/zig/issues/8220
- for (body) |zir_inst| {
- map[zir_inst] = switch (tags[zir_inst]) {
- .alloc => try sema.zirAlloc(block, zir_inst),
- .alloc_mut => try sema.zirAllocMut(block, zir_inst),
- .alloc_inferred => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_const)),
- .alloc_inferred_mut => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_mut)),
- .bitcast_ref => try sema.zirBitcastRef(block, zir_inst),
- .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, zir_inst),
- .block => try sema.zirBlock(block, zir_inst, false),
- .block_comptime => try sema.zirBlock(block, zir_inst, true),
- .block_flat => try sema.zirBlockFlat(block, zir_inst, false),
- .block_comptime_flat => try sema.zirBlockFlat(block, zir_inst, true),
- .@"break" => try sema.zirBreak(block, zir_inst),
- .break_void_tok => try sema.zirBreakVoidTok(block, zir_inst),
- .breakpoint => try sema.zirBreakpoint(block, zir_inst),
- .call => try sema.zirCall(block, zir_inst, .auto),
- .call_compile_time => try sema.zirCall(block, zir_inst, .compile_time),
- .call_none => try sema.zirCallNone(block, zir_inst),
- .coerce_result_ptr => try sema.zirCoerceResultPtr(block, zir_inst),
- .compile_error => try sema.zirCompileError(block, zir_inst),
- .compile_log => try sema.zirCompileLog(block, zir_inst),
- .@"const" => try sema.zirConst(block, zir_inst),
- .dbg_stmt_node => try sema.zirDbgStmtNode(block, zir_inst),
- .decl_ref => try sema.zirDeclRef(block, zir_inst),
- .decl_val => try sema.zirDeclVal(block, zir_inst),
+ // We use a while(true) loop here to avoid a redundant way of breaking out of
+ // the loop. The only way to break out of the loop is with a `noreturn`
+ // instruction.
+ // TODO: As an optimization, make sure the codegen for these switch prongs
+ // directly jump to the next one, rather than detouring through the loop
+ // continue expression. Related: https://github.com/ziglang/zig/issues/8220
+ var i: usize = 0;
+ while (true) : (i += 1) {
+ const inst = body[i];
+ map[inst] = switch (tags[inst]) {
.elided => continue,
- .ensure_result_used => try sema.zirEnsureResultUsed(block, zir_inst),
- .ensure_result_non_error => try sema.zirEnsureResultNonError(block, zir_inst),
- .indexable_ptr_len => try sema.zirIndexablePtrLen(block, zir_inst),
- .ref => try sema.zirRef(block, zir_inst),
- .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, zir_inst),
- .ret_ptr => try sema.zirRetPtr(block, zir_inst),
- .ret_type => try sema.zirRetType(block, zir_inst),
- .store_to_block_ptr => try sema.zirStoreToBlockPtr(block, zir_inst),
- .store_to_inferred_ptr => try sema.zirStoreToInferredPtr(block, zir_inst),
- .ptr_type_simple => try sema.zirPtrTypeSimple(block, zir_inst),
- .ptr_type => try sema.zirPtrType(block, zir_inst),
- .store => try sema.zirStore(block, zir_inst),
- .set_eval_branch_quota => try sema.zirSetEvalBranchQuota(block, zir_inst),
- .str => try sema.zirStr(block, zir_inst),
- .int => try sema.zirInt(block, zir_inst),
- .int_type => try sema.zirIntType(block, zir_inst),
- .loop => try sema.zirLoop(block, zir_inst),
- .param_type => try sema.zirParamType(block, zir_inst),
- .ptrtoint => try sema.zirPtrtoint(block, zir_inst),
- .field_ptr => try sema.zirFieldPtr(block, zir_inst),
- .field_val => try sema.zirFieldVal(block, zir_inst),
- .field_ptr_named => try sema.zirFieldPtrNamed(block, zir_inst),
- .field_val_named => try sema.zirFieldValNamed(block, zir_inst),
- .deref_node => try sema.zirDerefNode(block, zir_inst),
- .as => try sema.zirAs(block, zir_inst),
- .as_node => try sema.zirAsNode(block, zir_inst),
- .@"asm" => try sema.zirAsm(block, zir_inst, false),
- .asm_volatile => try sema.zirAsm(block, zir_inst, true),
- .@"unreachable" => try sema.zirUnreachable(block, zir_inst),
- .ret_coerce => try sema.zirRetTok(block, zir_inst, true),
- .ret_tok => try sema.zirRetTok(block, zir_inst, false),
- .ret_node => try sema.zirRetNode(block, zir_inst),
- .fn_type => try sema.zirFnType(block, zir_inst, false),
- .fn_type_cc => try sema.zirFnTypeCc(block, zir_inst, false),
- .fn_type_var_args => try sema.zirFnType(block, zir_inst, true),
- .fn_type_cc_var_args => try sema.zirFnTypeCc(block, zir_inst, true),
- .intcast => try sema.zirIntcast(block, zir_inst),
- .bitcast => try sema.zirBitcast(block, zir_inst),
- .floatcast => try sema.zirFloatcast(block, zir_inst),
- .elem_ptr => try sema.zirElemPtr(block, zir_inst),
- .elem_ptr_node => try sema.zirElemPtrNode(block, zir_inst),
- .elem_val => try sema.zirElemVal(block, zir_inst),
- .elem_val_node => try sema.zirElemValNode(block, zir_inst),
- .add => try sema.zirArithmetic(block, zir_inst),
- .addwrap => try sema.zirArithmetic(block, zir_inst),
- .sub => try sema.zirArithmetic(block, zir_inst),
- .subwrap => try sema.zirArithmetic(block, zir_inst),
+
+ .add => try sema.zirArithmetic(block, inst),
+ .addwrap => try sema.zirArithmetic(block, inst),
+ .alloc => try sema.zirAlloc(block, inst),
+ .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
+ .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
+ .alloc_mut => try sema.zirAllocMut(block, inst),
+ .array_cat => try sema.zirArrayCat(block, inst),
+ .array_mul => try sema.zirArrayMul(block, inst),
+ .array_type => try sema.zirArrayType(block, inst),
+ .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
+ .as => try sema.zirAs(block, inst),
+ .as_node => try sema.zirAsNode(block, inst),
+ .@"asm" => try sema.zirAsm(block, inst, false),
+ .asm_volatile => try sema.zirAsm(block, inst, true),
+ .bit_and => try sema.zirBitwise(block, inst),
+ .bit_not => try sema.zirBitNot(block, inst),
+ .bit_or => try sema.zirBitwise(block, inst),
+ .bitcast => try sema.zirBitcast(block, inst),
+ .bitcast_ref => try sema.zirBitcastRef(block, inst),
+ .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
+ .block => try sema.zirBlock(block, inst, false),
+ .block_comptime => try sema.zirBlock(block, inst, true),
+ .bool_not => try sema.zirBoolNot(block, inst),
+ .bool_and => try sema.zirBoolOp(block, inst, false),
+ .bool_or => try sema.zirBoolOp(block, inst, true),
+ .bool_br_and => try sema.zirBoolBr(block, inst, false),
+ .bool_br_or => try sema.zirBoolBr(block, inst, true),
+ .call => try sema.zirCall(block, inst, .auto),
+ .call_compile_time => try sema.zirCall(block, inst, .compile_time),
+ .call_none => try sema.zirCallNone(block, inst),
+ .cmp_eq => try sema.zirCmp(block, inst, .eq),
+ .cmp_gt => try sema.zirCmp(block, inst, .gt),
+ .cmp_gte => try sema.zirCmp(block, inst, .gte),
+ .cmp_lt => try sema.zirCmp(block, inst, .lt),
+ .cmp_lte => try sema.zirCmp(block, inst, .lte),
+ .cmp_neq => try sema.zirCmp(block, inst, .neq),
+ .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst),
+ .@"const" => try sema.zirConst(block, inst),
+ .decl_ref => try sema.zirDeclRef(block, inst),
+ .decl_val => try sema.zirDeclVal(block, inst),
+ .deref_node => try sema.zirDerefNode(block, inst),
+ .div => try sema.zirArithmetic(block, inst),
+ .elem_ptr => try sema.zirElemPtr(block, inst),
+ .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
+ .elem_val => try sema.zirElemVal(block, inst),
+ .elem_val_node => try sema.zirElemValNode(block, inst),
+ .enum_literal => try sema.zirEnumLiteral(block, inst),
+ .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst),
+ .err_union_code => try sema.zirErrUnionCode(block, inst),
+ .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
+ .err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true),
+ .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, true),
+ .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst, false),
+ .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false),
+ .error_set => try sema.zirErrorSet(block, inst),
+ .error_union_type => try sema.zirErrorUnionType(block, inst),
+ .error_value => try sema.zirErrorValue(block, inst),
+ .field_ptr => try sema.zirFieldPtr(block, inst),
+ .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
+ .field_val => try sema.zirFieldVal(block, inst),
+ .field_val_named => try sema.zirFieldValNamed(block, inst),
+ .floatcast => try sema.zirFloatcast(block, inst),
+ .fn_type => try sema.zirFnType(block, inst, false),
+ .fn_type_cc => try sema.zirFnTypeCc(block, inst, false),
+ .fn_type_cc_var_args => try sema.zirFnTypeCc(block, inst, true),
+ .fn_type_var_args => try sema.zirFnType(block, inst, true),
+ .import => try sema.zirImport(block, inst),
+ .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst),
+ .int => try sema.zirInt(block, inst),
+ .int_type => try sema.zirIntType(block, inst),
+ .intcast => try sema.zirIntcast(block, inst),
+ .is_err => try sema.zirIsErr(block, inst),
+ .is_err_ptr => try sema.zirIsErrPtr(block, inst),
+ .is_non_null => try sema.zirIsNull(block, inst, true),
+ .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
+ .is_null => try sema.zirIsNull(block, inst, false),
+ .is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
+ .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
+ .mod_rem => try sema.zirArithmetic(block, inst),
+ .mul => try sema.zirArithmetic(block, inst),
+ .mulwrap => try sema.zirArithmetic(block, inst),
.negate => @panic("TODO"),
.negate_wrap => @panic("TODO"),
- .mul => try sema.zirArithmetic(block, zir_inst),
- .mulwrap => try sema.zirArithmetic(block, zir_inst),
- .div => try sema.zirArithmetic(block, zir_inst),
- .mod_rem => try sema.zirArithmetic(block, zir_inst),
- .array_cat => try sema.zirArrayCat(block, zir_inst),
- .array_mul => try sema.zirArrayMul(block, zir_inst),
- .bit_and => try sema.zirBitwise(block, zir_inst),
- .bit_not => try sema.zirBitNot(block, zir_inst),
- .bit_or => try sema.zirBitwise(block, zir_inst),
- .xor => try sema.zirBitwise(block, zir_inst),
- .shl => try sema.zirShl(block, zir_inst),
- .shr => try sema.zirShr(block, zir_inst),
- .cmp_lt => try sema.zirCmp(block, zir_inst, .lt),
- .cmp_lte => try sema.zirCmp(block, zir_inst, .lte),
- .cmp_eq => try sema.zirCmp(block, zir_inst, .eq),
- .cmp_gte => try sema.zirCmp(block, zir_inst, .gte),
- .cmp_gt => try sema.zirCmp(block, zir_inst, .gt),
- .cmp_neq => try sema.zirCmp(block, zir_inst, .neq),
- .condbr => try sema.zirCondbr(block, zir_inst),
- .is_null => try sema.zirIsNull(block, zir_inst, false),
- .is_non_null => try sema.zirIsNull(block, zir_inst, true),
- .is_null_ptr => try sema.zirIsNullPtr(block, zir_inst, false),
- .is_non_null_ptr => try sema.zirIsNullPtr(block, zir_inst, true),
- .is_err => try sema.zirIsErr(block, zir_inst),
- .is_err_ptr => try sema.zirIsErrPtr(block, zir_inst),
- .bool_not => try sema.zirBoolNot(block, zir_inst),
- .typeof => try sema.zirTypeof(block, zir_inst),
- .typeof_peer => try sema.zirTypeofPeer(block, zir_inst),
- .optional_type => try sema.zirOptionalType(block, zir_inst),
- .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, zir_inst),
- .optional_payload_safe => try sema.zirOptionalPayload(block, zir_inst, true),
- .optional_payload_unsafe => try sema.zirOptionalPayload(block, zir_inst, false),
- .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, true),
- .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, false),
- .err_union_payload_safe => try sema.zirErrUnionPayload(block, zir_inst, true),
- .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, zir_inst, false),
- .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, true),
- .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, false),
- .err_union_code => try sema.zirErrUnionCode(block, zir_inst),
- .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, zir_inst),
- .ensure_err_payload_void => try sema.zirEnsureErrPayloadVoid(block, zir_inst),
- .array_type => try sema.zirArrayType(block, zir_inst),
- .array_type_sentinel => try sema.zirArrayTypeSentinel(block, zir_inst),
- .enum_literal => try sema.zirEnumLiteral(block, zir_inst),
- .enum_literal_small => try sema.zirEnumLiteralSmall(block, zir_inst),
- .merge_error_sets => try sema.zirMergeErrorSets(block, zir_inst),
- .error_union_type => try sema.zirErrorUnionType(block, zir_inst),
- .error_set => try sema.zirErrorSet(block, zir_inst),
- .error_value => try sema.zirErrorValue(block, zir_inst),
- .slice_start => try sema.zirSliceStart(block, zir_inst),
- .slice_end => try sema.zirSliceEnd(block, zir_inst),
- .slice_sentinel => try sema.zirSliceSentinel(block, zir_inst),
- .import => try sema.zirImport(block, zir_inst),
- .bool_and => try sema.zirBoolOp(block, zir_inst, false),
- .bool_or => try sema.zirBoolOp(block, zir_inst, true),
+ .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
+ .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true),
+ .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
+ .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
+ .optional_type => try sema.zirOptionalType(block, inst),
+ .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, inst),
+ .param_type => try sema.zirParamType(block, inst),
+ .ptr_type => try sema.zirPtrType(block, inst),
+ .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
+ .ptrtoint => try sema.zirPtrtoint(block, inst),
+ .ref => try sema.zirRef(block, inst),
+ .ret_ptr => try sema.zirRetPtr(block, inst),
+ .ret_type => try sema.zirRetType(block, inst),
+ .shl => try sema.zirShl(block, inst),
+ .shr => try sema.zirShr(block, inst),
+ .slice_end => try sema.zirSliceEnd(block, inst),
+ .slice_sentinel => try sema.zirSliceSentinel(block, inst),
+ .slice_start => try sema.zirSliceStart(block, inst),
+ .str => try sema.zirStr(block, inst),
+ .sub => try sema.zirArithmetic(block, inst),
+ .subwrap => try sema.zirArithmetic(block, inst),
+ .typeof => try sema.zirTypeof(block, inst),
+ .typeof_peer => try sema.zirTypeofPeer(block, inst),
+ .xor => try sema.zirBitwise(block, inst),
// TODO
- //.switchbr => try sema.zirSwitchBr(block, zir_inst, false),
- //.switchbr_ref => try sema.zirSwitchBr(block, zir_inst, true),
- //.switch_range => try sema.zirSwitchRange(block, zir_inst),
+ //.switchbr => try sema.zirSwitchBr(block, inst, false),
+ //.switchbr_ref => try sema.zirSwitchBr(block, inst, true),
+ //.switch_range => try sema.zirSwitchRange(block, inst),
+
+ // Instructions that we know to *always* be noreturn based solely on their tag.
+ // These functions match the return type of analyzeBody so that we can
+ // tail call them here.
+ .condbr => return sema.zirCondbr(block, inst),
+ .@"break" => return sema.zirBreak(block, inst),
+ .break_void_tok => return sema.zirBreakVoidTok(block, inst),
+ .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
+ .compile_error => return sema.zirCompileError(block, inst),
+ .ret_coerce => return sema.zirRetTok(block, inst, true),
+ .ret_node => return sema.zirRetNode(block, inst),
+ .ret_tok => return sema.zirRetTok(block, inst, false),
+ .@"unreachable" => return sema.zirUnreachable(block, inst),
+ .loop => return sema.zirLoop(block, inst),
+
+ // Instructions that we know can *never* be noreturn based solely on
+ // their tag. We avoid needlessly checking if they are noreturn and
+ // continue the loop.
+ // We also know that they cannot be referenced later, so we avoid
+ // putting them into the map.
+ .breakpoint => {
+ try sema.zirBreakpoint(block, inst);
+ continue;
+ },
+ .dbg_stmt_node => {
+ try sema.zirDbgStmtNode(block, inst);
+ continue;
+ },
+ .ensure_err_payload_void => {
+ try sema.zirEnsureErrPayloadVoid(block, inst);
+ continue;
+ },
+ .ensure_result_non_error => {
+ try sema.zirEnsureResultNonError(block, inst);
+ continue;
+ },
+ .ensure_result_used => {
+ try sema.zirEnsureResultUsed(block, inst);
+ continue;
+ },
+ .compile_log => {
+ try sema.zirCompileLog(block, inst);
+ continue;
+ },
+ .set_eval_branch_quota => {
+ try sema.zirSetEvalBranchQuota(block, inst);
+ continue;
+ },
+ .store => {
+ try sema.zirStore(block, inst);
+ continue;
+ },
+ .store_to_block_ptr => {
+ try sema.zirStoreToBlockPtr(block, inst);
+ continue;
+ },
+ .store_to_inferred_ptr => {
+ try sema.zirStoreToInferredPtr(block, inst);
+ continue;
+ },
+ .resolve_inferred_alloc => {
+ try sema.zirResolveInferredAlloc(block, inst);
+ continue;
+ },
};
- if (map[zir_inst].ty.isNoReturn()) {
- break;
- }
+ if (map[inst].ty.isNoReturn())
+ return always_noreturn;
}
}
@@ -392,7 +450,7 @@ fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
return sema.mod.constType(sema.arena, src, ret_type);
}
-fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -400,12 +458,12 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) I
const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
switch (operand.ty.zigTypeTag()) {
- .Void, .NoReturn => return sema.mod.constVoid(sema.arena, .unneeded),
+ .Void, .NoReturn => return,
else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}),
}
}
-fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -414,7 +472,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Inde
const src = inst_data.src();
switch (operand.ty.zigTypeTag()) {
.ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}),
- else => return sema.mod.constVoid(sema.arena, .unneeded),
+ else => return,
}
}
@@ -508,11 +566,7 @@ fn zirAllocInferred(
return result;
}
-fn zirResolveInferredAlloc(
- sema: *Sema,
- block: *Scope.Block,
- inst: zir.Inst.Index,
-) InnerError!*Inst {
+fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -536,15 +590,9 @@ fn zirResolveInferredAlloc(
// Change it to a normal alloc.
ptr.ty = final_ptr_ty;
ptr.tag = .alloc;
-
- return sema.mod.constVoid(sema.arena, .unneeded);
}
-fn zirStoreToBlockPtr(
- sema: *Sema,
- block: *Scope.Block,
- inst: zir.Inst.Index,
-) InnerError!*Inst {
+fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -560,11 +608,7 @@ fn zirStoreToBlockPtr(
return sema.storePtr(block, src, bitcasted_ptr, value);
}
-fn zirStoreToInferredPtr(
- sema: *Sema,
- block: *Scope.Block,
- inst: zir.Inst.Index,
-) InnerError!*Inst {
+fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -583,21 +627,16 @@ fn zirStoreToInferredPtr(
return sema.storePtr(block, src, bitcasted_ptr, value);
}
-fn zirSetEvalBranchQuota(
- sema: *Sema,
- block: *Scope.Block,
- inst: zir.Inst.Index,
-) InnerError!*Inst {
+fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
try sema.requireFunctionBlock(block, src);
const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);
if (sema.branch_quota < quota)
sema.branch_quota = quota;
- return sema.mod.constVoid(sema.arena, .unneeded);
}
-fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -677,7 +716,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In
return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
}
-fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -688,7 +727,7 @@ fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inner
return sema.mod.fail(&block.base, src, "{s}", .{msg});
}
-fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
defer sema.mod.compile_log_text = managed.moveToUnmanaged();
const writer = managed.writer();
@@ -711,10 +750,9 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
if (!gop.found_existing) {
gop.entry.value = inst_data.src().toSrcLoc(&block.base);
}
- return sema.mod.constVoid(sema.arena, .unneeded);
}
-fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -746,41 +784,13 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
};
defer child_block.instructions.deinit(sema.gpa);
- try sema.analyzeBody(&child_block, body);
+ _ = try sema.analyzeBody(&child_block, body);
// Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
try parent_block.instructions.append(sema.gpa, &loop_inst.base);
loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items) };
- return &loop_inst.base;
-}
-
-fn zirBlockFlat(
- sema: *Sema,
- parent_block: *Scope.Block,
- inst: zir.Inst.Index,
- is_comptime: bool,
-) InnerError!*Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
- const body = sema.code.extra[extra.end..][0..extra.data.operands_len];
-
- var child_block = parent_block.makeSubBlock();
- defer child_block.instructions.deinit(sema.gpa);
- child_block.is_comptime = child_block.is_comptime or is_comptime;
-
- try sema.analyzeBody(&child_block, body);
-
- // Move the analyzed instructions into the parent block arena.
- const copied_instructions = try sema.arena.dupe(*Inst, child_block.instructions.items);
- try parent_block.instructions.appendSlice(sema.gpa, copied_instructions);
-
- // The result of a flat block is the last instruction.
- return sema.inst_map[body[body.len - 1]];
+ return always_noreturn;
}
fn zirBlock(
@@ -794,8 +804,8 @@ fn zirBlock(
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
- const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index);
- const body = sema.code.extra[extra.end..][0..extra.data.operands_len];
+ const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
+ const body = sema.code.extra[extra.end..][0..extra.data.body_len];
// Reserve space for a Block instruction so that generated Break instructions can
// point to it, even if it doesn't end up getting used because the code ends up being
@@ -833,7 +843,7 @@ fn zirBlock(
defer merges.results.deinit(sema.gpa);
defer merges.br_list.deinit(sema.gpa);
- try sema.analyzeBody(&child_block, body);
+ _ = try sema.analyzeBody(&child_block, body);
return sema.analyzeBlockBody(parent_block, &child_block, merges);
}
@@ -919,17 +929,17 @@ fn analyzeBlockBody(
return &merges.block_inst.base;
}
-fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
const src_node = sema.code.instructions.items(.data)[inst].node;
const src: LazySrcLoc = .{ .node_offset = src_node };
try sema.requireRuntimeBlock(block, src);
- return block.addNoOp(src, Type.initTag(.void), .breakpoint);
+ _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
}
-fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -939,7 +949,7 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*
return sema.analyzeBreak(block, sema.src, zir_block, operand);
}
-fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -955,7 +965,7 @@ fn analyzeBreak(
src: LazySrcLoc,
zir_block: zir.Inst.Index,
operand: *Inst,
-) InnerError!*Inst {
+) InnerError!zir.Inst.Ref {
var block = start_block;
while (true) {
if (block.label) |*label| {
@@ -981,26 +991,24 @@ fn analyzeBreak(
try block.instructions.append(sema.gpa, &br.base);
try label.merges.results.append(sema.gpa, operand);
try label.merges.br_list.append(sema.gpa, br);
- return &br.base;
+ return always_noreturn;
}
}
block = block.parent.?;
}
}
-fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
- if (block.is_comptime) {
- return sema.mod.constVoid(sema.arena, .unneeded);
- }
+ if (block.is_comptime) return;
const src_node = sema.code.instructions.items(.data)[inst].node;
const src: LazySrcLoc = .{ .node_offset = src_node };
const src_loc = src.toSrcLoc(&block.base);
const abs_byte_off = try src_loc.byteOffset();
- return block.addDbgStmt(src, abs_byte_off);
+ _ = try block.addDbgStmt(src, abs_byte_off);
}
fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -1185,7 +1193,7 @@ fn analyzeCall(
// This will have return instructions analyzed as break instructions to
// the block_inst above.
- try sema.root(&child_block);
+ _ = try sema.root(&child_block);
return sema.analyzeBlockBody(block, &child_block, merges);
}
@@ -1638,7 +1646,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) In
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
}
-fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -1650,7 +1658,6 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Inde
if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
return sema.mod.fail(&block.base, src, "expression value is ignored", .{});
}
- return sema.mod.constVoid(sema.arena, .unneeded);
}
fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: bool) InnerError!*Inst {
@@ -2067,7 +2074,7 @@ fn zirSwitchBr(
parent_block: *Scope.Block,
inst: zir.Inst.Index,
ref: bool,
-) InnerError!*Inst {
+) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2087,18 +2094,18 @@ fn zirSwitchBr(
const item = try sema.resolveConstValue(parent_block, case_src, casted);
if (target_val.eql(item)) {
- try sema.analyzeBody(parent_block, case.body);
- return sema.mod.constNoReturn(sema.arena, inst.base.src);
+ _ = try sema.analyzeBody(parent_block, case.body);
+ return always_noreturn;
}
}
- try sema.analyzeBody(parent_block, inst.positionals.else_body);
- return sema.mod.constNoReturn(sema.arena, inst.base.src);
+ _ = try sema.analyzeBody(parent_block, inst.positionals.else_body);
+ return always_noreturn;
}
if (inst.positionals.cases.len == 0) {
// no cases just analyze else_branch
- try sema.analyzeBody(parent_block, inst.positionals.else_body);
- return sema.mod.constNoReturn(sema.arena, inst.base.src);
+ _ = try sema.analyzeBody(parent_block, inst.positionals.else_body);
+ return always_noreturn;
}
try sema.requireRuntimeBlock(parent_block, inst.base.src);
@@ -2122,7 +2129,7 @@ fn zirSwitchBr(
const casted = try sema.coerce(block, target.ty, resolved, resolved_src);
const item = try sema.resolveConstValue(parent_block, case_src, casted);
- try sema.analyzeBody(&case_block, case.body);
+ _ = try sema.analyzeBody(&case_block, case.body);
cases[i] = .{
.item = item,
@@ -2131,7 +2138,7 @@ fn zirSwitchBr(
}
case_block.instructions.items.len = 0;
- try sema.analyzeBody(&case_block, inst.positionals.else_body);
+ _ = try sema.analyzeBody(&case_block, inst.positionals.else_body);
const else_body: ir.Body = .{
.instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
@@ -2719,6 +2726,75 @@ fn zirBoolOp(
return block.addBinOp(src, bool_type, tag, lhs, rhs);
}
+fn zirBoolBr(
+ sema: *Sema,
+ parent_block: *Scope.Block,
+ inst: zir.Inst.Index,
+ is_bool_or: bool,
+) InnerError!*Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const inst_data = sema.code.instructions.items(.data)[inst].bool_br;
+ const src: LazySrcLoc = .unneeded;
+ const lhs = try sema.resolveInst(inst_data.lhs);
+ const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
+ const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+
+ if (try sema.resolveDefinedValue(parent_block, src, lhs)) |lhs_val| {
+ if (lhs_val.toBool() == is_bool_or) {
+ return sema.mod.constBool(sema.arena, src, is_bool_or);
+ }
+ // comptime-known left-hand side. No need for a block here; the result
+ // is simply the rhs expression. Here we rely on there only being 1
+ // break instruction (`break_flat`).
+ const zir_inst_ref = try sema.analyzeBody(parent_block, body);
+ return sema.resolveInst(zir_inst_ref);
+ }
+
+ const block_inst = try sema.arena.create(Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = Inst.Block.base_tag,
+ .ty = Type.initTag(.bool),
+ .src = src,
+ },
+ .body = undefined,
+ };
+
+ var child_block = parent_block.makeSubBlock();
+ defer child_block.instructions.deinit(sema.gpa);
+
+ var then_block = child_block.makeSubBlock();
+ defer then_block.instructions.deinit(sema.gpa);
+
+ var else_block = child_block.makeSubBlock();
+ defer else_block.instructions.deinit(sema.gpa);
+
+ const lhs_block = if (is_bool_or) &then_block else &else_block;
+ const rhs_block = if (is_bool_or) &else_block else &then_block;
+
+ const lhs_result = try sema.mod.constInst(sema.arena, src, .{
+ .ty = Type.initTag(.bool),
+ .val = if (is_bool_or) Value.initTag(.bool_true) else Value.initTag(.bool_false),
+ });
+ _ = try lhs_block.addBr(src, block_inst, lhs_result);
+
+ const rhs_result_zir_ref = try sema.analyzeBody(rhs_block, body);
+ const rhs_result = try sema.resolveInst(rhs_result_zir_ref);
+ _ = try rhs_block.addBr(src, block_inst, rhs_result);
+
+ const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
+ const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) };
+ _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body);
+
+ block_inst.body = .{
+ .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items),
+ };
+ try parent_block.instructions.append(sema.gpa, &block_inst.base);
+ return &block_inst.base;
+}
+
fn zirIsNull(
sema: *Sema,
block: *Scope.Block,
@@ -2770,7 +2846,11 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
return sema.analyzeIsErr(block, src, loaded);
}
-fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirCondbr(
+ sema: *Sema,
+ parent_block: *Scope.Block,
+ inst: zir.Inst.Index,
+) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2787,8 +2867,8 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne
if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
const body = if (cond_val.toBool()) then_body else else_body;
- try sema.analyzeBody(parent_block, body);
- return sema.mod.constNoReturn(sema.arena, src);
+ _ = try sema.analyzeBody(parent_block, body);
+ return always_noreturn;
}
var true_block: Scope.Block = .{
@@ -2800,7 +2880,7 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne
.is_comptime = parent_block.is_comptime,
};
defer true_block.instructions.deinit(sema.gpa);
- try sema.analyzeBody(&true_block, then_body);
+ _ = try sema.analyzeBody(&true_block, then_body);
var false_block: Scope.Block = .{
.parent = parent_block,
@@ -2811,14 +2891,15 @@ fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) Inne
.is_comptime = parent_block.is_comptime,
};
defer false_block.instructions.deinit(sema.gpa);
- try sema.analyzeBody(&false_block, else_body);
+ _ = try sema.analyzeBody(&false_block, else_body);
const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, true_block.instructions.items) };
const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, false_block.instructions.items) };
- return parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
+ _ = try parent_block.addCondBr(src, cond, tzir_then_body, tzir_else_body);
+ return always_noreturn;
}
-fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2830,7 +2911,8 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE
if (safety_check and block.wantSafety()) {
return sema.safetyPanic(block, src, .unreach);
} else {
- return block.addNoOp(src, Type.initTag(.noreturn), .unreach);
+ _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
+ return always_noreturn;
}
}
@@ -2839,7 +2921,7 @@ fn zirRetTok(
block: *Scope.Block,
inst: zir.Inst.Index,
need_coercion: bool,
-) InnerError!*Inst {
+) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2850,7 +2932,7 @@ fn zirRetTok(
return sema.analyzeRet(block, operand, src, need_coercion);
}
-fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2867,22 +2949,24 @@ fn analyzeRet(
operand: *Inst,
src: LazySrcLoc,
need_coercion: bool,
-) InnerError!*Inst {
+) InnerError!zir.Inst.Ref {
if (block.inlining) |inlining| {
// We are inlining a function call; rewrite the `ret` as a `break`.
try inlining.merges.results.append(sema.gpa, operand);
- const br = try block.addBr(src, inlining.merges.block_inst, operand);
- return &br.base;
+ _ = try block.addBr(src, inlining.merges.block_inst, operand);
+ return always_noreturn;
}
if (need_coercion) {
if (sema.func) |func| {
const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
const casted_operand = try sema.coerce(block, fn_ty.fnReturnType(), operand, src);
- return block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
+ _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
+ return always_noreturn;
}
}
- return block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
+ _ = try block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
+ return always_noreturn;
}
fn floatOpAllowed(tag: zir.Inst.Tag) bool {
@@ -3051,10 +3135,11 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
try parent_block.instructions.append(sema.gpa, &block_inst.base);
}
-fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !*Inst {
+fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Ref {
// TODO Once we have a panic function to call, call it here instead of breakpoint.
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
- return block.addNoOp(src, Type.initTag(.noreturn), .unreach);
+ _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
+ return always_noreturn;
}
fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
@@ -3405,20 +3490,20 @@ fn storePtr(
src: LazySrcLoc,
ptr: *Inst,
uncasted_value: *Inst,
-) !*Inst {
+) !void {
if (ptr.ty.isConstPtr())
return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
const elem_ty = ptr.ty.elemType();
const value = try sema.coerce(block, elem_ty, uncasted_value, uncasted_value.src);
if (elem_ty.onePossibleValue() != null)
- return sema.mod.constVoid(sema.arena, .unneeded);
+ return;
// TODO handle comptime pointer writes
// TODO handle if the element type requires comptime
try sema.requireRuntimeBlock(block, src);
- return block.addBinOp(src, Type.initTag(.void), .store, ptr, value);
+ _ = try block.addBinOp(src, Type.initTag(.void), .store, ptr, value);
}
fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
diff --git a/src/astgen.zig b/src/astgen.zig
index 81382c73cc..bc1349048f 100644
--- a/src/astgen.zig
+++ b/src/astgen.zig
@@ -370,8 +370,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
.array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
.array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
- .bool_and => return boolBinOp(mod, scope, rl, node, .bool_and),
- .bool_or => return boolBinOp(mod, scope, rl, node, .bool_or),
+ .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and),
+ .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or),
.bool_not => return boolNot(mod, scope, rl, node),
.bit_not => return bitNot(mod, scope, rl, node),
@@ -425,8 +425,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
.field_access => return fieldAccess(mod, scope, rl, node),
.float_literal => return floatLiteral(mod, scope, rl, node),
- .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)),
- .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)),
+ .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),
+ .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),
.while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
.while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),
@@ -923,6 +923,7 @@ fn labeledBlockExpr(
// so that break statements can reference it.
const gz = parent_scope.getGenZir();
const block_inst = try gz.addBlock(zir_tag, block_node);
+ try gz.instructions.append(mod.gpa, block_inst);
var block_scope: Scope.GenZir = .{
.parent = parent_scope,
@@ -946,8 +947,6 @@ fn labeledBlockExpr(
return mod.failTok(parent_scope, label_token, "unused block label", .{});
}
- try gz.instructions.append(mod.gpa, block_inst);
-
const zir_tags = gz.zir_code.instructions.items(.tag);
const zir_datas = gz.zir_code.instructions.items(.data);
@@ -961,7 +960,7 @@ fn labeledBlockExpr(
}
// TODO technically not needed since we changed the tag to break_void but
// would be better still to elide the ones that are in this list.
- try copyBodyNoEliding(block_inst, block_scope);
+ try block_scope.setBlockBody(block_inst);
return gz.zir_code.ref_start_index + block_inst;
},
@@ -975,7 +974,7 @@ fn labeledBlockExpr(
// TODO technically not needed since we changed the tag to elided but
// would be better still to elide the ones that are in this list.
}
- try copyBodyNoEliding(block_inst, block_scope);
+ try block_scope.setBlockBody(block_inst);
const block_ref = gz.zir_code.ref_start_index + block_inst;
switch (rl) {
.ref => return block_ref,
@@ -1635,8 +1634,8 @@ fn finishThenElseBlock(
});
}
assert(!strat.elide_store_to_block_ptr_instructions);
- try copyBodyNoEliding(then_body, then_scope.*);
- try copyBodyNoEliding(else_body, else_scope.*);
+ try then_scope.setBlockBody(then_body);
+ try else_scope.setBlockBody(else_body);
return &main_block.base;
},
.break_operand => {
@@ -1662,8 +1661,8 @@ fn finishThenElseBlock(
try copyBodyWithElidedStoreBlockPtr(then_body, then_scope.*);
try copyBodyWithElidedStoreBlockPtr(else_body, else_scope.*);
} else {
- try copyBodyNoEliding(then_body, then_scope.*);
- try copyBodyNoEliding(else_body, else_scope.*);
+ try then_scope.setBlockBody(then_body);
+ try else_scope.setBlockBody(else_body);
}
switch (rl) {
.ref => return &main_block.base,
@@ -1801,95 +1800,49 @@ fn boolBinOp(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
- infix_node: ast.Node.Index,
- kind: enum { bool_and, bool_or },
+ node: ast.Node.Index,
+ zir_tag: zir.Inst.Tag,
) InnerError!zir.Inst.Ref {
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const bool_type = @enumToInt(zir.Const.bool_type);
const gz = scope.getGenZir();
+ const node_datas = gz.tree().nodes.items(.data);
+ const bool_type = @enumToInt(zir.Const.bool_type);
- const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[infix_node].lhs);
-
- const block_inst = try gz.addBlock(.block, infix_node);
- const block_ref = gz.zir_code.ref_start_index + block_inst;
- var block_scope: Scope.GenZir = .{
- .parent = scope,
- .zir_code = gz.zir_code,
- .force_comptime = gz.force_comptime,
- };
- defer block_scope.instructions.deinit(mod.gpa);
+ const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[node].lhs);
+ const bool_br = try gz.addBoolBr(zir_tag, lhs);
var rhs_scope: Scope.GenZir = .{
- .parent = &block_scope.base,
+ .parent = scope,
.zir_code = gz.zir_code,
.force_comptime = gz.force_comptime,
};
defer rhs_scope.instructions.deinit(mod.gpa);
- const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[infix_node].rhs);
- _ = try rhs_scope.addBin(.@"break", block_inst, rhs);
-
- // TODO: should we have zir.Const instructions for `break true` and `break false`?
- const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
- const break_true_false_ref = new_index + gz.zir_code.ref_start_index;
- try gz.zir_code.instructions.append(gz.zir_code.gpa, .{ .tag = .@"break", .data = .{ .bin = .{
- .lhs = block_inst,
- .rhs = switch (kind) {
- .bool_and => @enumToInt(zir.Const.bool_false),
- .bool_or => @enumToInt(zir.Const.bool_true),
- },
- } } });
-
- switch (kind) {
- // if lhs // AND
- // break rhs
- // else
- // break false
- .bool_and => _ = try block_scope.addCondBr(
- lhs,
- rhs_scope.instructions.items,
- &[_]zir.Inst.Ref{break_true_false_ref},
- infix_node,
- ),
- // if lhs // OR
- // break true
- // else
- // break rhs
- .bool_or => _ = try block_scope.addCondBr(
- lhs,
- &[_]zir.Inst.Ref{break_true_false_ref},
- rhs_scope.instructions.items,
- infix_node,
- ),
- }
+ const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[node].rhs);
+ _ = try rhs_scope.addUnNode(.break_flat, rhs, node);
+ try rhs_scope.setBoolBrBody(bool_br);
- try gz.instructions.append(mod.gpa, block_inst);
- try copyBodyNoEliding(block_inst, block_scope);
-
- return rvalue(mod, scope, rl, block_ref, infix_node);
+ const block_ref = gz.zir_code.ref_start_index + bool_br;
+ return rvalue(mod, scope, rl, block_ref, node);
}
fn ifExpr(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
+ node: ast.Node.Index,
if_full: ast.full.If,
) InnerError!zir.Inst.Ref {
if (true) @panic("TODO update for zir-memory-layout");
+ const parent_gz = scope.getGenZir();
var block_scope: Scope.GenZir = .{
.parent = scope,
- .decl = scope.ownerDecl().?,
- .arena = scope.arena(),
+ .zir_code = parent_gz.zir_code,
.force_comptime = scope.isComptime(),
.instructions = .{},
};
setBlockResultLoc(&block_scope, rl);
defer block_scope.instructions.deinit(mod.gpa);
- const tree = scope.tree();
- const main_tokens = tree.nodes.items(.main_token);
-
- const if_src = token_starts[if_full.ast.if_token];
+ const tree = parent_gz.tree();
const cond = c: {
// TODO https://github.com/ziglang/zig/issues/7929
@@ -1898,23 +1851,16 @@ fn ifExpr(
} else if (if_full.payload_token) |payload_token| {
return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
} else {
- const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr);
+ const bool_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.bool_type) };
+ break :c try expr(mod, &block_scope.base, bool_rl, if_full.ast.cond_expr);
}
};
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
+ const condbr = try block_scope.addCondBr(node);
- const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
- .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items),
- });
+ const block = try parent_gz.addBlock(.block, node);
+ try parent_gz.instructions.append(mod.gpa, block);
+ try block_scope.setBlockBody(block);
const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
var then_scope: Scope.GenZir = .{
@@ -1990,12 +1936,6 @@ fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZir)
assert(dst_index == body.instructions.len);
}
-fn copyBodyNoEliding(block_inst: zir.Inst.Index, gz: Module.Scope.GenZir) !void {
- const zir_datas = gz.zir_code.instructions.items(.data);
- zir_datas[block_inst].pl_node.payload_index = @intCast(u32, gz.zir_code.extra.items.len);
- try gz.zir_code.extra.appendSlice(gz.zir_code.gpa, gz.instructions.items);
-}
-
fn whileExpr(
mod: *Module,
scope: *Scope,
diff --git a/src/zir.zig b/src/zir.zig
index 12ad5fabb0..a057e0df2e 100644
--- a/src/zir.zig
+++ b/src/zir.zig
@@ -99,11 +99,7 @@ pub const Code = struct {
try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name });
const root_body = code.extra[code.root_start..][0..code.root_len];
- for (root_body) |inst| {
- try stderr.print(" %{d} ", .{inst});
- try writer.writeInstToStream(stderr, inst);
- try stderr.writeByte('\n');
- }
+ try writer.writeBody(stderr, root_body);
try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name });
}
@@ -451,16 +447,10 @@ pub const Inst = struct {
/// Bitwise OR. `|`
bit_or,
/// A labeled block of code, which can return a value.
- /// Uses the `pl_node` union field. Payload is `MultiOp`.
+ /// Uses the `pl_node` union field. Payload is `Block`.
block,
- /// A block of code, which can return a value. There are no instructions that break out of
- /// this block; it is implied that the final instruction is the result.
- /// Uses the `pl_node` union field. Payload is `MultiOp`.
- block_flat,
/// Same as `block` but additionally makes the inner instructions execute at comptime.
block_comptime,
- /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
- block_comptime_flat,
/// Boolean AND. See also `bit_and`.
/// Uses the `pl_node` union field. Payload is `Bin`.
bool_and,
@@ -470,6 +460,14 @@ pub const Inst = struct {
/// Boolean OR. See also `bit_or`.
/// Uses the `pl_node` union field. Payload is `Bin`.
bool_or,
+ /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
+ /// is a block, which is evaluated if `lhs` is `true`.
+ /// Uses the `bool_br` union field.
+ bool_br_and,
+ /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
+ /// is a block, which is evaluated if `lhs` is `false`.
+ /// Uses the `bool_br` union field.
+ bool_br_or,
/// Return a value from a block.
/// Uses the `bin` union field: `lhs` is `Index` to the block (*not* `Ref`!),
/// `rhs` is operand.
@@ -480,6 +478,12 @@ pub const Inst = struct {
/// Uses the `un_tok` union field.
/// Note that the block operand is a `Index`, not `Ref`.
break_void_tok,
+ /// Return a value from a block. This is a special form that is only valid
+ /// when there is exactly 1 break from a block (this one). This instruction
+ /// allows using the return value from `Sema.analyzeBody`. The block is
+ /// assumed to be the direct parent of this instruction.
+ /// Uses the `un_node` union field. The AST node is unused.
+ break_flat,
/// Uses the `node` union field.
breakpoint,
/// Function call with modifier `.auto`.
@@ -637,7 +641,7 @@ pub const Inst = struct {
/// A labeled block of code that loops forever. At the end of the body it is implied
/// to repeat; no explicit "repeat" instruction terminates loop bodies.
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
- /// The payload is `MultiOp`.
+ /// The payload is `Block`.
loop,
/// Merge two error sets into one, `E1 || E2`.
merge_error_sets,
@@ -886,9 +890,9 @@ pub const Inst = struct {
.bitcast_result_ptr,
.bit_or,
.block,
- .block_flat,
.block_comptime,
- .block_comptime_flat,
+ .bool_br_and,
+ .bool_br_or,
.bool_not,
.bool_and,
.bool_or,
@@ -988,6 +992,7 @@ pub const Inst = struct {
.@"break",
.break_void_tok,
+ .break_flat,
.condbr,
.compile_error,
.ret_node,
@@ -1127,6 +1132,11 @@ pub const Inst = struct {
/// For `fn_type_cc` this points to `FnTypeCc` in `extra`.
payload_index: u32,
},
+ bool_br: struct {
+ lhs: Ref,
+ /// Points to a `Block`.
+ payload_index: u32,
+ },
param_type: struct {
callee: Ref,
param_index: u32,
@@ -1191,6 +1201,12 @@ pub const Inst = struct {
operands_len: u32,
};
+ /// This data is stored inside extra, with trailing operands according to `body_len`.
+ /// Each operand is an `Index`.
+ pub const Block = struct {
+ body_len: u32,
+ };
+
/// Stored inside extra, with trailing arguments according to `args_len`.
/// Each argument is a `Ref`.
pub const Call = struct {
@@ -1342,6 +1358,7 @@ const Writer = struct {
.err_union_payload_unsafe_ptr,
.err_union_code,
.err_union_code_ptr,
+ .break_flat,
=> try self.writeUnNode(stream, inst),
.break_void_tok,
@@ -1358,6 +1375,10 @@ const Writer = struct {
.ensure_err_payload_void,
=> try self.writeUnTok(stream, inst),
+ .bool_br_and,
+ .bool_br_or,
+ => try self.writeBoolBr(stream, inst),
+
.array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
.@"const" => try self.writeConst(stream, inst),
.param_type => try self.writeParamType(stream, inst),
@@ -1370,9 +1391,7 @@ const Writer = struct {
.@"asm",
.asm_volatile,
.block,
- .block_flat,
.block_comptime,
- .block_comptime_flat,
.call,
.call_compile_time,
.compile_log,
@@ -1618,6 +1637,19 @@ const Writer = struct {
return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
}
+ fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].bool_br;
+ const extra = self.code.extraData(Inst.Block, inst_data.payload_index);
+ const body = self.code.extra[extra.end..][0..extra.data.body_len];
+ try self.writeInstRef(stream, inst_data.lhs);
+ try stream.writeAll(", {\n");
+ self.indent += 2;
+ try self.writeBody(stream, body);
+ self.indent -= 2;
+ try stream.writeByteNTimes(' ', self.indent);
+ try stream.writeAll("})");
+ }
+
fn writeFnTypeCc(
self: *Writer,
stream: anytype,
@@ -1713,4 +1745,13 @@ const Writer = struct {
@tagName(src), delta_line.line + 1, delta_line.column + 1,
});
}
+
+ fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void {
+ for (body) |inst| {
+ try stream.writeByteNTimes(' ', self.indent);
+ try stream.print("%{d} ", .{inst});
+ try self.writeInstToStream(stream, inst);
+ try stream.writeByte('\n');
+ }
+ }
};