From b2b87b590011d8df52874e3f9bd1f88d1b0189d1 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 00:34:44 +0100 Subject: SPIR-V: Linking and codegen setup --- src/Module.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index fa9722814e..464124c7b9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1622,7 +1622,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // in `Decl` to notice that the line number did not change. self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } } else { @@ -1855,6 +1855,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.TextBlock.empty }, .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .fn_link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, @@ -1862,6 +1863,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.SrcFn.empty }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = null }, + .spirv => .{ .spirv = .{} }, }, .generation = 0, .is_pub = false, @@ -1959,6 +1961,7 @@ pub fn analyzeExport( .macho => .{ .macho = link.File.MachO.Export{} }, .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .owner_decl = owner_decl, .exported_decl = exported_decl, -- cgit v1.2.3 From b7452fe35f514d4c04aae4582bc8071bc9e70f1b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jan 2021 20:37:44 -0700 Subject: stage2: rework astgen result locations Motivating test case: ```zig export fn _start() noreturn { var x: u64 = 1; var y: u32 = 2; var thing: u32 = 1; const result = if (thing == 1) x else y; exit(); } ``` The main idea here is for astgen to output ideal ZIR depending on whether or not the sub-expressions of a block consume the result location. Here, neither `x` nor `y` consume the result location of the conditional expression block, and so the ZIR should communicate the result of the condbr using break instructions, not with the result location pointer. With this commit, this is accomplished: ``` %22 = alloc_inferred() %23 = block({ %24 = const(TypedValue{ .ty = type, .val = bool}) %25 = deref(%18) %26 = const(TypedValue{ .ty = comptime_int, .val = 1}) %27 = cmp_eq(%25, %26) %28 = as(%24, %27) %29 = condbr(%28, { %30 = deref(%4) < there is no longer a store instruction here > %31 = break("label_23", %30) }, { %32 = deref(%11) < there is no longer a store instruction here > %33 = break("label_23", %32) }) }) %34 = store_to_inferred_ptr(%22, %23) <-- the store is only here %35 = resolve_inferred_alloc(%22) ``` However if the result location gets consumed, the break instructions change to break_void, and the result value is communicated only by the stores, not by the break instructions. Implementation: * The GenZIR scope that conditional branches uses now has an optional result location pointer field and a count of how many times the result location ended up being an rvalue (not consumed). * When rvalue() is called on a result location for a block, it increments this counter. After generating the branches of a block, astgen for the conditional branch checks this count and if it is 2 then the store_to_block_ptr instructions are elided and it calls rvalue() using the block result (which will account for peer type resolution on the break operands). astgen has many functions disabled until they can be reworked with these new semantics. That will be done before merging the branch. There are some new rules for astgen to follow regarding result locations and what you are allowed/required to do depending on which one is passed to expr(). See the updated doc comments of ResultLoc for details. I also changed naming conventions of stuff in this commit, sorry about that. --- src/Module.zig | 9 +- src/astgen.zig | 402 ++++++++++++++++++++++++-------------- src/codegen.zig | 24 +-- src/ir.zig | 16 +- src/zir.zig | 155 +++++++-------- src/zir_sema.zig | 572 ++++++++++++++++++++++++++++--------------------------- 6 files changed, 649 insertions(+), 529 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index fa9722814e..2dc84a93a9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -697,6 +697,13 @@ pub const Scope = struct { continue_block: ?*zir.Inst.Block = null, /// only valid if label != null or (continue_block and break_block) != null break_result_loc: astgen.ResultLoc = undefined, + /// When a block has a pointer result location, here it is. + rl_ptr: ?*zir.Inst = null, + /// Keeps track of how many branches of a block did not actually + /// consume the result location. astgen uses this to figure out + /// whether to rely on break instructions or writing to the result + /// pointer for the result instruction. + rvalue_rl_count: usize = 0, pub const Label = struct { token: ast.TokenIndex, @@ -1171,7 +1178,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) { const src = tree.token_locs[body_block.rbrace].start; - _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); + _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .return_void); } if (std.builtin.mode == .Debug and self.comp.verbose_ir) { diff --git a/src/astgen.zig b/src/astgen.zig index a74b83de44..617937aa82 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -14,25 +14,30 @@ const InnerError = Module.InnerError; pub const ResultLoc = union(enum) { /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the - /// expression should be generated. + /// expression should be generated. The result instruction from the expression must + /// be ignored. discard, /// The expression has an inferred type, and it will be evaluated as an rvalue. none, /// The expression must generate a pointer rather than a value. For example, the left hand side /// of an assignment uses this kind of result location. ref, - /// The expression will be type coerced into this type, but it will be evaluated as an rvalue. + /// The expression will be coerced into this type, but it will be evaluated as an rvalue. ty: *zir.Inst, - /// The expression must store its result into this typed pointer. + /// The expression must store its result into this typed pointer. The result instruction + /// from the expression must be ignored. ptr: *zir.Inst, /// The expression must store its result into this allocation, which has an inferred type. + /// The result instruction from the expression must be ignored. inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(), /// The expression must store its result into this pointer, which is a typed pointer that /// has been bitcasted to whatever the expression's type is. + /// The result instruction from the expression must be ignored. bitcasted_ptr: *zir.Inst.UnOp, /// There is a pointer for the expression to store its result into, however, its type /// is inferred based on peer type resolution for a `zir.Inst.Block`. - block_ptr: *zir.Inst.Block, + /// The result instruction from the expression must be ignored. + block_ptr: *Module.Scope.GenZIR, }; pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { @@ -179,6 +184,9 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { } /// Turn Zig AST into untyped ZIR istructions. +/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the +/// result instruction can be used to inspect whether it is isNoReturn() but that is it, +/// it must otherwise not be used. pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { switch (node.tag) { .Root => unreachable, // Top-level declaration. @@ -197,20 +205,20 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .FieldInitializer => unreachable, // Handled explicitly. .ContainerField => unreachable, // Handled explicitly. - .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)), - .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)), - .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)), - .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)), - .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)), - .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)), - .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)), - .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)), - .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)), - .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)), - .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)), - .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)), - .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)), - .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)), + .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)), + .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)), + .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)), + .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)), + .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)), + .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)), + .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)), + .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)), + .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)), + .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)), + .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)), + .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)), + .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)), + .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)), .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add), .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap), @@ -220,8 +228,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap), .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div), .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem), - .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand), - .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor), + .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and), + .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or), .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl), .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr), .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor), @@ -239,15 +247,15 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), - .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), - .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), - .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), - .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), + .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), + .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), + .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), + .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), - .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), - .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), - .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)), + .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), + .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), + .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)), .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?), .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?), .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?), @@ -255,34 +263,34 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), .Period => return field(mod, scope, rl, node.castTag(.Period).?), - .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), - .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), - .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), - .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), - .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), - .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), - .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), + .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), + .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), + .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), + .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), + .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), + .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), + .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), - .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), + .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block), - .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), - .Continue => return rlWrap(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)), - .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), + .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), + .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)), + .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)), .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr), - .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), - .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), - .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), - .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), - .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), - .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), - .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), - .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), - .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), - .ErrorSetDecl => return rlWrap(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), - .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), + .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)), + .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)), + .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)), + .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), + .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), + .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), + .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), + .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), + .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), + .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), + .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), .For => return forExpr(mod, scope, rl, node.castTag(.For).?), .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?), - .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)), + .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)), .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), @@ -341,6 +349,9 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as } fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { + if (true) { + @panic("TODO reimplement this"); + } const tree = parent_scope.tree(); const src = tree.token_locs[node.ltoken].start; @@ -563,8 +574,8 @@ fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statement scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator); }, .Assign => try assign(mod, scope, statement.castTag(.Assign).?), - .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand), - .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor), + .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and), + .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or), .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl), .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr), .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor), @@ -644,6 +655,7 @@ fn varDecl( // Namespace vars shadowing detection if (mod.lookupDeclName(scope, ident_name)) |_| { + // TODO add note for other definition return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } const init_node = node.getInitNode() orelse @@ -751,14 +763,14 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr .val = Value.initTag(.bool_type), }); const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs); - return addZIRUnOp(mod, scope, src, .boolnot, operand); + return addZIRUnOp(mod, scope, src, .bool_not, operand); } fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; const operand = try expr(mod, scope, .none, node.rhs); - return addZIRUnOp(mod, scope, src, .bitnot, operand); + return addZIRUnOp(mod, scope, src, .bit_not, operand); } fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { @@ -1101,7 +1113,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con if (rl == .ref) { return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{}); } else { - return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ + return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ .decl = decl, }, .{})); } @@ -1200,6 +1212,9 @@ fn orelseCatchExpr( rhs: *ast.Node, payload_node: ?*ast.Node, ) InnerError!*zir.Inst { + if (true) { + @panic("TODO reimplement this"); + } const tree = scope.tree(); const src = tree.token_locs[op_token].start; @@ -1308,7 +1323,7 @@ pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleI .field_name = field_name, }); } - return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{ + return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{ .object = try expr(mod, scope, .none, node.lhs), .field_name = field_name, })); @@ -1338,7 +1353,7 @@ fn namedField( .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), }); } - return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{ + return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{ .object = try expr(mod, scope, .none, params[0]), .field_name = try comptimeExpr(mod, scope, string_rl, params[1]), })); @@ -1359,7 +1374,7 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array .index = try expr(mod, scope, index_rl, node.index_expr), }); } - return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ + return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{ .array = try expr(mod, scope, .none, node.lhs), .index = try expr(mod, scope, index_rl, node.index_expr), })); @@ -1416,7 +1431,7 @@ fn simpleBinOp( const rhs = try expr(mod, scope, .none, infix_node.rhs); const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); - return rlWrap(mod, scope, rl, result); + return rvalue(mod, scope, rl, result); } fn boolBinOp( @@ -1498,7 +1513,7 @@ fn boolBinOp( condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) }; } - return rlWrap(mod, scope, rl, &block.base); + return rvalue(mod, scope, rl, &block.base); } const CondKind = union(enum) { @@ -1578,6 +1593,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn cond_kind = .{ .err_union = null }; } } + const block_branch_count = 2; // then and else var block_scope: Scope.GenZIR = .{ .parent = scope, .decl = scope.ownerDecl().?, @@ -1600,6 +1616,33 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); + // Depending on whether the result location is a pointer or value, different + // ZIR needs to be generated. In the former case we rely on storing to the + // pointer to communicate the result, and use breakvoid; in the latter case + // the block break instructions will have the result values. + // One more complication: when the result location is a pointer, we detect + // the scenario where the result location is not consumed. In this case + // we emit ZIR for the block break instructions to have the result values, + // and then rvalue() on that to pass the value to the result location. + const branch_rl: ResultLoc = switch (rl) { + .discard, .none, .ty, .ptr, .ref => rl, + + .inferred_ptr => |ptr| blk: { + block_scope.rl_ptr = &ptr.base; + break :blk .{ .block_ptr = &block_scope }; + }, + + .bitcasted_ptr => |ptr| blk: { + block_scope.rl_ptr = &ptr.base; + break :blk .{ .block_ptr = &block_scope }; + }, + + .block_ptr => |parent_block_scope| blk: { + block_scope.rl_ptr = parent_block_scope.rl_ptr.?; + break :blk .{ .block_ptr = &block_scope }; + }, + }; + const then_src = tree.token_locs[if_node.body.lastToken()].start; var then_scope: Scope.GenZIR = .{ .parent = scope, @@ -1612,25 +1655,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn // declare payload to the then_scope const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); - // Most result location types can be forwarded directly; however - // if we need to write to a pointer which has an inferred type, - // proper type inference requires peer type resolution on the if's - // branches. - const branch_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .ref => rl, - .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, - }; - const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body); - if (!then_result.tag.isNoReturn()) { - _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{ - .block = block, - .operand = then_result, - }, .{}); - } - condbr.positionals.then_body = .{ - .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items), - }; + // We hold off on the break instructions as well as copying the then/else + // instructions into place until we know whether to keep store_to_block_ptr + // instructions or not. var else_scope: Scope.GenZIR = .{ .parent = scope, @@ -1640,34 +1668,127 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn }; defer else_scope.instructions.deinit(mod.gpa); - if (if_node.@"else") |else_node| { - const else_src = tree.token_locs[else_node.body.lastToken()].start; + var else_src: usize = undefined; + var else_sub_scope: *Module.Scope = undefined; + const else_result: ?*zir.Inst = if (if_node.@"else") |else_node| blk: { + else_src = tree.token_locs[else_node.body.lastToken()].start; // declare payload to the then_scope - const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); + else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); + + break :blk try expr(mod, else_sub_scope, branch_rl, else_node.body); + } else blk: { + else_src = tree.token_locs[if_node.lastToken()].start; + else_sub_scope = &else_scope.base; + block_scope.rvalue_rl_count += 1; + break :blk null; + }; - const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body); - if (!else_result.tag.isNoReturn()) { - _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{ - .block = block, - .operand = else_result, - }, .{}); - } - } else { - // TODO Optimization opportunity: we can avoid an allocation and a memcpy here - // by directly allocating the body for this one instruction. - const else_src = tree.token_locs[if_node.lastToken()].start; - _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{ - .block = block, - }, .{}); + // We now have enough information to decide whether the result instruction should + // be communicated via result location pointer or break instructions. + const Strategy = enum { + /// Both branches will use break_void; result location is used to communicate the + /// result instruction. + break_void, + /// Use break statements to pass the block result value, and call rvalue() at + /// the end depending on rl. Also elide the store_to_block_ptr instructions + /// depending on rl. + break_operand, + }; + var elide_store_to_block_ptr_instructions = false; + const strategy: Strategy = switch (rl) { + // In this branch there will not be any store_to_block_ptr instructions. + .discard, .none, .ty, .ref => .break_operand, + // The pointer got passed through to the sub-expressions, so we will use + // break_void here. + // In this branch there will not be any store_to_block_ptr instructions. + .ptr => .break_void, + .inferred_ptr, .bitcasted_ptr, .block_ptr => blk: { + if (block_scope.rvalue_rl_count == 2) { + // Neither prong of the if consumed the result location, so we can + // use break instructions to create an rvalue. + elide_store_to_block_ptr_instructions = true; + break :blk Strategy.break_operand; + } else { + // Allow the store_to_block_ptr instructions to remain so that + // semantic analysis can turn them into bitcasts. + break :blk Strategy.break_void; + } + }, + }; + switch (strategy) { + .break_void => { + if (!then_result.tag.isNoReturn()) { + _ = try addZIRNoOp(mod, then_sub_scope, then_src, .break_void); + } + if (else_result) |inst| { + if (!inst.tag.isNoReturn()) { + _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void); + } + } else { + _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void); + } + assert(!elide_store_to_block_ptr_instructions); + try copyBodyNoEliding(&condbr.positionals.then_body, then_scope); + try copyBodyNoEliding(&condbr.positionals.else_body, else_scope); + return &block.base; + }, + .break_operand => { + if (!then_result.tag.isNoReturn()) { + _ = try addZirInstTag(mod, then_sub_scope, then_src, .@"break", .{ + .block = block, + .operand = then_result, + }); + } + if (else_result) |inst| { + if (!inst.tag.isNoReturn()) { + _ = try addZirInstTag(mod, else_sub_scope, else_src, .@"break", .{ + .block = block, + .operand = inst, + }); + } + } else { + _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void); + } + if (elide_store_to_block_ptr_instructions) { + try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope); + try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope); + } else { + try copyBodyNoEliding(&condbr.positionals.then_body, then_scope); + try copyBodyNoEliding(&condbr.positionals.else_body, else_scope); + } + switch (rl) { + .ref => return &block.base, + else => return rvalue(mod, scope, rl, &block.base), + } + }, } - condbr.positionals.else_body = .{ - .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), +} + +/// Expects to find exactly 1 .store_to_block_ptr instruction. +fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZIR) !void { + body.* = .{ + .instructions = try scope.arena.alloc(*zir.Inst, scope.instructions.items.len - 1), }; + var dst_index: usize = 0; + for (scope.instructions.items) |src_inst| { + if (src_inst.tag != .store_to_block_ptr) { + body.instructions[dst_index] = src_inst; + dst_index += 1; + } + } + assert(dst_index == body.instructions.len); +} - return &block.base; +fn copyBodyNoEliding(body: *zir.Body, scope: Module.Scope.GenZIR) !void { + body.* = .{ + .instructions = try scope.arena.dupe(*zir.Inst, scope.instructions.items), + }; } fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst { + if (true) { + @panic("TODO reimplement this"); + } var cond_kind: CondKind = .bool; if (while_node.payload) |_| cond_kind = .{ .optional = null }; if (while_node.@"else") |else_node| { @@ -1821,6 +1942,9 @@ fn forExpr( rl: ResultLoc, for_node: *ast.Node.For, ) InnerError!*zir.Inst { + if (true) { + @panic("TODO reimplement this"); + } if (for_node.label) |label| { try checkLabelRedefinition(mod, scope, label); } @@ -2017,6 +2141,9 @@ fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { } fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { + if (true) { + @panic("TODO reimplement this"); + } var block_scope: Scope.GenZIR = .{ .parent = scope, .decl = scope.ownerDecl().?, @@ -2186,10 +2313,10 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node // target >= start and target <= end const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start); const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end); - const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok); + const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok); + any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); } else { any_ok = range_ok; } @@ -2201,7 +2328,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok); + any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); } else { any_ok = cpm_ok; } @@ -2238,7 +2365,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node try switchCaseExpr(mod, &else_scope.base, case_rl, block, case); } else { // Not handling all possible cases is a compile error. - _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck); + _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); } // All items have been generated, add the instructions to the comptime block. @@ -2288,7 +2415,7 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE return addZIRUnOp(mod, scope, src, .@"return", operand); } } else { - return addZIRNoOp(mod, scope, src, .returnvoid); + return addZIRNoOp(mod, scope, src, .return_void); } } @@ -2305,7 +2432,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo if (getSimplePrimitiveValue(ident_name)) |typed_value| { const result = try addZIRInstConst(mod, scope, src, typed_value); - return rlWrap(mod, scope, rl, result); + return rvalue(mod, scope, rl, result); } if (ident_name.len >= 2) integer: { @@ -2327,7 +2454,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type), 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), else => { - return rlWrap(mod, scope, rl, try addZIRInstConst(mod, scope, src, .{ + return rvalue(mod, scope, rl, try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = try Value.Tag.int_type.create(scope.arena(), .{ .signed = is_signed, @@ -2340,7 +2467,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo .ty = Type.initTag(.type), .val = val, }); - return rlWrap(mod, scope, rl, result); + return rvalue(mod, scope, rl, result); } } @@ -2351,7 +2478,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo .local_val => { const local_val = s.cast(Scope.LocalVal).?; if (mem.eql(u8, local_val.name, ident_name)) { - return rlWrap(mod, scope, rl, local_val.inst); + return rvalue(mod, scope, rl, local_val.inst); } s = local_val.parent; }, @@ -2360,7 +2487,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo if (mem.eql(u8, local_ptr.name, ident_name)) { if (rl == .ref) return local_ptr.ptr; const loaded = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr); - return rlWrap(mod, scope, rl, loaded); + return rvalue(mod, scope, rl, loaded); } s = local_ptr.parent; }, @@ -2373,7 +2500,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo if (rl == .ref) { return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{}); } else { - return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ + return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{ .decl = decl, }, .{})); } @@ -2590,7 +2717,7 @@ fn simpleCast( const dest_type = try typeExpr(mod, scope, params[0]); const rhs = try expr(mod, scope, .none, params[1]); const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); - return rlWrap(mod, scope, rl, result); + return rvalue(mod, scope, rl, result); } fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { @@ -2634,11 +2761,11 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I // TODO here we should be able to resolve the inference; we now have a type for the result. return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); }, - .block_ptr => |block_ptr| { - const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{ + .block_ptr => |block_scope| { + const casted_block_ptr = try addZirInstTag(mod, scope, src, .coerce_result_block_ptr, .{ .dest_type = dest_type, - .block = block_ptr, - }, .{}); + .block_ptr = block_scope.rl_ptr.?, + }); return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]); }, } @@ -2703,7 +2830,7 @@ fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerE const src = tree.token_locs[call.builtin_token].start; const params = call.params(); const target = try expr(mod, scope, .none, params[0]); - return addZIRUnOp(mod, scope, src, .compileerror, target); + return addZIRUnOp(mod, scope, src, .compile_error, target); } fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { @@ -2728,12 +2855,12 @@ fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCal return mod.failTok(scope, call.builtin_token, "expected at least 1 argument, found 0", .{}); } if (params.len == 1) { - return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0]))); + return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0]))); } var items = try arena.alloc(*zir.Inst, params.len); for (params) |param, param_i| items[param_i] = try expr(mod, scope, .none, param); - return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); + return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); } fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { const tree = scope.tree(); @@ -2756,7 +2883,7 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built // Also, some builtins have a variable number of parameters. if (mem.eql(u8, builtin_name, "@ptrToInt")) { - return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call)); + return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call)); } else if (mem.eql(u8, builtin_name, "@as")) { return as(mod, scope, rl, call); } else if (mem.eql(u8, builtin_name, "@floatCast")) { @@ -2769,9 +2896,9 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built return typeOf(mod, scope, rl, call); } else if (mem.eql(u8, builtin_name, "@breakpoint")) { const src = tree.token_locs[call.builtin_token].start; - return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); + return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); } else if (mem.eql(u8, builtin_name, "@import")) { - return rlWrap(mod, scope, rl, try import(mod, scope, call)); + return rvalue(mod, scope, rl, try import(mod, scope, call)); } else if (mem.eql(u8, builtin_name, "@compileError")) { return compileError(mod, scope, call); } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) { @@ -2806,13 +2933,13 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In .args = args, }, .{}); // TODO function call with result location - return rlWrap(mod, scope, rl, result); + return rvalue(mod, scope, rl, result); } fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[unreach_node.token].start; - return addZIRNoOp(mod, scope, src, .@"unreachable"); + return addZIRNoOp(mod, scope, src, .unreachable_safe); } fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { @@ -3099,7 +3226,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { /// result locations must call this function on their result. /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. /// If the `ResultLoc` is `ty`, it will coerce the result to the type. -fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst { +fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst { switch (rl) { .none => return result, .discard => { @@ -3113,42 +3240,31 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr }, .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result), .ptr => |ptr_inst| { - const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{ - .ptr = ptr_inst, - .value = result, - }, .{}); - _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result); - return casted_result; + _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, result); + return result; }, .bitcasted_ptr => |bitcasted_ptr| { - return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{}); + return mod.fail(scope, result.src, "TODO implement rvalue .bitcasted_ptr", .{}); }, .inferred_ptr => |alloc| { _ = try addZIRBinOp(mod, scope, result.src, .store_to_inferred_ptr, &alloc.base, result); return result; }, - .block_ptr => |block_ptr| { - return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{}); + .block_ptr => |block_scope| { + block_scope.rvalue_rl_count += 1; + _ = try addZIRBinOp(mod, scope, result.src, .store_to_block_ptr, block_scope.rl_ptr.?, result); + return result; }, } } -fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst { +fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst { const src = scope.tree().token_locs[node.firstToken()].start; const void_inst = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value), }); - return rlWrap(mod, scope, rl, void_inst); -} - -/// TODO go over all the callsites and see where we can introduce "by-value" ZIR instructions -/// to save ZIR memory. For example, see DeclVal vs DeclRef. -/// Do not add additional callsites to this function. -fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst { - if (rl == .ref) return ptr; - - return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr)); + return rvalue(mod, scope, rl, void_inst); } pub fn addZirInstTag( diff --git a/src/codegen.zig b/src/codegen.zig index 1ca2bb2abe..a7b067f7e1 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -840,14 +840,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .arg => return self.genArg(inst.castTag(.arg).?), .assembly => return self.genAsm(inst.castTag(.assembly).?), .bitcast => return self.genBitCast(inst.castTag(.bitcast).?), - .bitand => return self.genBitAnd(inst.castTag(.bitand).?), - .bitor => return self.genBitOr(inst.castTag(.bitor).?), + .bit_and => return self.genBitAnd(inst.castTag(.bit_and).?), + .bit_or => return self.genBitOr(inst.castTag(.bit_or).?), .block => return self.genBlock(inst.castTag(.block).?), .br => return self.genBr(inst.castTag(.br).?), .breakpoint => return self.genBreakpoint(inst.src), .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), - .booland => return self.genBoolOp(inst.castTag(.booland).?), - .boolor => return self.genBoolOp(inst.castTag(.boolor).?), + .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?), + .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?), .call => return self.genCall(inst.castTag(.call).?), .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt), .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte), @@ -1097,7 +1097,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.base.isUnused()) return MCValue.dead; switch (arch) { - .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitand), + .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_and), else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}), } } @@ -1107,7 +1107,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.base.isUnused()) return MCValue.dead; switch (arch) { - .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bitor), + .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_or), else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}), } } @@ -1371,10 +1371,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32()); } }, - .booland, .bitand => { + .bool_and, .bit_and => { writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, dst_reg, operand).toU32()); }, - .boolor, .bitor => { + .bool_or, .bit_or => { writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, dst_reg, operand).toU32()); }, .not, .xor => { @@ -2464,14 +2464,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (arch) { .x86_64 => switch (inst.base.tag) { // lhs AND rhs - .booland => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20), + .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20), // lhs OR rhs - .boolor => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08), + .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08), else => unreachable, // Not a boolean operation }, .arm, .armeb => switch (inst.base.tag) { - .booland => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .booland), - .boolor => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .boolor), + .bool_and => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_and), + .bool_or => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_or), else => unreachable, // Not a boolean operation }, else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}), diff --git a/src/ir.zig b/src/ir.zig index 89698bdd84..b1147871f4 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -56,9 +56,9 @@ pub const Inst = struct { alloc, arg, assembly, - bitand, + bit_and, bitcast, - bitor, + bit_or, block, br, breakpoint, @@ -85,8 +85,8 @@ pub const Inst = struct { is_err, // *E!T => bool is_err_ptr, - booland, - boolor, + bool_and, + bool_or, /// Read a value from a pointer. load, loop, @@ -147,10 +147,10 @@ pub const Inst = struct { .cmp_gt, .cmp_neq, .store, - .booland, - .boolor, - .bitand, - .bitor, + .bool_and, + .bool_or, + .bit_and, + .bit_or, .xor, => BinOp, diff --git a/src/zir.zig b/src/zir.zig index 9e5830e79a..07fc64b65a 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -59,7 +59,7 @@ pub const Inst = struct { /// Inline assembly. @"asm", /// Bitwise AND. `&` - bitand, + bit_and, /// TODO delete this instruction, it has no purpose. bitcast, /// An arbitrary typed pointer is pointer-casted to a new Pointer. @@ -71,9 +71,9 @@ pub const Inst = struct { /// The new result location pointer has an inferred type. bitcast_result_ptr, /// Bitwise NOT. `~` - bitnot, + bit_not, /// Bitwise OR. `|` - bitor, + bit_or, /// A labeled block of code, which can return a value. block, /// A block of code, which can return a value. There are no instructions that break out of @@ -83,17 +83,17 @@ pub const Inst = struct { block_comptime, /// Same as `block_flat` but additionally makes the inner instructions execute at comptime. block_comptime_flat, - /// Boolean AND. See also `bitand`. - booland, - /// Boolean NOT. See also `bitnot`. - boolnot, - /// Boolean OR. See also `bitor`. - boolor, + /// Boolean AND. See also `bit_and`. + bool_and, + /// Boolean NOT. See also `bit_not`. + bool_not, + /// Boolean OR. See also `bit_or`. + bool_or, /// Return a value from a `Block`. @"break", breakpoint, /// Same as `break` but without an operand; the operand is assumed to be the void value. - breakvoid, + break_void, /// Function call. call, /// `<` @@ -116,12 +116,10 @@ pub const Inst = struct { /// result location pointer, whose type is inferred by peer type resolution on the /// `Block`'s corresponding `break` instructions. coerce_result_block_ptr, - /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. - coerce_to_ptr_elem, /// Emit an error message and fail compilation. - compileerror, + compile_error, /// Log compile time variables and emit an error message. - compilelog, + compile_log, /// Conditional branch. Splits control flow based on a boolean condition value. condbr, /// Special case, has no textual representation. @@ -135,11 +133,11 @@ pub const Inst = struct { /// Declares the beginning of a statement. Used for debug info. dbg_stmt, /// Represents a pointer to a global decl. - declref, + decl_ref, /// Represents a pointer to a global decl by string name. - declref_str, - /// Equivalent to a declref followed by deref. - declval, + decl_ref_str, + /// Equivalent to a decl_ref followed by deref. + decl_val, /// Load the value from a pointer. deref, /// Arithmetic division. Asserts no integer overflow. @@ -185,7 +183,7 @@ pub const Inst = struct { /// can hold the same mathematical value. intcast, /// Make an integer type out of signedness and bit count. - inttype, + int_type, /// Return a boolean false if an optional is null. `x != null` is_non_null, /// Return a boolean true if an optional is null. `x == null` @@ -232,7 +230,7 @@ pub const Inst = struct { /// Sends control flow back to the function's callee. Takes an operand as the return value. @"return", /// Same as `return` but there is no operand; the operand is implicitly the void value. - returnvoid, + return_void, /// Changes the maximum number of backwards branches that compile-time /// code execution can use before giving up and making a compile error. set_eval_branch_quota, @@ -270,6 +268,10 @@ pub const Inst = struct { /// Write a value to a pointer. For loading, see `deref`. store, /// Same as `store` but the type of the value being stored will be used to infer + /// the block type. The LHS is a block instruction, whose result location is + /// being stored to. + store_to_block_ptr, + /// Same as `store` but the type of the value being stored will be used to infer /// the pointer type. store_to_inferred_ptr, /// String Literal. Makes an anonymous Decl and then takes a pointer to it. @@ -286,11 +288,11 @@ pub const Inst = struct { typeof_peer, /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler /// will assume the correctness of this instruction. - unreach_nocheck, + unreachable_unsafe, /// Asserts control-flow will not reach this instruction. In safety-checked modes, /// this will generate a call to the panic function unless it can be proven unreachable /// by the compiler. - @"unreachable", + unreachable_safe, /// Bitwise XOR. `^` xor, /// Create an optional type '?T' @@ -352,17 +354,17 @@ pub const Inst = struct { .alloc_inferred_mut, .breakpoint, .dbg_stmt, - .returnvoid, + .return_void, .ret_ptr, .ret_type, - .unreach_nocheck, - .@"unreachable", + .unreachable_unsafe, + .unreachable_safe, => NoOp, .alloc, .alloc_mut, - .boolnot, - .compileerror, + .bool_not, + .compile_error, .deref, .@"return", .is_null, @@ -400,7 +402,7 @@ pub const Inst = struct { .err_union_code_ptr, .ensure_err_payload_void, .anyframe_type, - .bitnot, + .bit_not, .import, .set_eval_branch_quota, .indexable_ptr_len, @@ -411,10 +413,10 @@ pub const Inst = struct { .array_cat, .array_mul, .array_type, - .bitand, - .bitor, - .booland, - .boolor, + .bit_and, + .bit_or, + .bool_and, + .bool_or, .div, .mod_rem, .mul, @@ -422,6 +424,7 @@ pub const Inst = struct { .shl, .shr, .store, + .store_to_block_ptr, .store_to_inferred_ptr, .sub, .subwrap, @@ -452,19 +455,18 @@ pub const Inst = struct { .arg => Arg, .array_type_sentinel => ArrayTypeSentinel, .@"break" => Break, - .breakvoid => BreakVoid, + .break_void => BreakVoid, .call => Call, - .coerce_to_ptr_elem => CoerceToPtrElem, - .declref => DeclRef, - .declref_str => DeclRefStr, - .declval => DeclVal, + .decl_ref => DeclRef, + .decl_ref_str => DeclRefStr, + .decl_val => DeclVal, .coerce_result_block_ptr => CoerceResultBlockPtr, - .compilelog => CompileLog, + .compile_log => CompileLog, .loop => Loop, .@"const" => Const, .str => Str, .int => Int, - .inttype => IntType, + .int_type => IntType, .field_ptr, .field_val => Field, .field_ptr_named, .field_val_named => FieldNamed, .@"asm" => Asm, @@ -508,18 +510,18 @@ pub const Inst = struct { .arg, .as, .@"asm", - .bitand, + .bit_and, .bitcast, .bitcast_ref, .bitcast_result_ptr, - .bitor, + .bit_or, .block, .block_flat, .block_comptime, .block_comptime_flat, - .boolnot, - .booland, - .boolor, + .bool_not, + .bool_and, + .bool_or, .breakpoint, .call, .cmp_lt, @@ -530,12 +532,11 @@ pub const Inst = struct { .cmp_neq, .coerce_result_ptr, .coerce_result_block_ptr, - .coerce_to_ptr_elem, .@"const", .dbg_stmt, - .declref, - .declref_str, - .declval, + .decl_ref, + .decl_ref_str, + .decl_val, .deref, .div, .elem_ptr, @@ -552,7 +553,7 @@ pub const Inst = struct { .fntype, .int, .intcast, - .inttype, + .int_type, .is_non_null, .is_null, .is_non_null_ptr, @@ -579,6 +580,7 @@ pub const Inst = struct { .mut_slice_type, .const_slice_type, .store, + .store_to_block_ptr, .store_to_inferred_ptr, .str, .sub, @@ -602,7 +604,7 @@ pub const Inst = struct { .merge_error_sets, .anyframe_type, .error_union_type, - .bitnot, + .bit_not, .error_set, .slice, .slice_start, @@ -611,20 +613,20 @@ pub const Inst = struct { .typeof_peer, .resolve_inferred_alloc, .set_eval_branch_quota, - .compilelog, + .compile_log, .enum_type, .union_type, .struct_type, => false, .@"break", - .breakvoid, + .break_void, .condbr, - .compileerror, + .compile_error, .@"return", - .returnvoid, - .unreach_nocheck, - .@"unreachable", + .return_void, + .unreachable_unsafe, + .unreachable_safe, .loop, .switchbr, .container_field_named, @@ -717,7 +719,7 @@ pub const Inst = struct { }; pub const BreakVoid = struct { - pub const base_tag = Tag.breakvoid; + pub const base_tag = Tag.break_void; base: Inst, positionals: struct { @@ -739,19 +741,8 @@ pub const Inst = struct { }, }; - pub const CoerceToPtrElem = struct { - pub const base_tag = Tag.coerce_to_ptr_elem; - base: Inst, - - positionals: struct { - ptr: *Inst, - value: *Inst, - }, - kw_args: struct {}, - }; - pub const DeclRef = struct { - pub const base_tag = Tag.declref; + pub const base_tag = Tag.decl_ref; base: Inst, positionals: struct { @@ -761,7 +752,7 @@ pub const Inst = struct { }; pub const DeclRefStr = struct { - pub const base_tag = Tag.declref_str; + pub const base_tag = Tag.decl_ref_str; base: Inst, positionals: struct { @@ -771,7 +762,7 @@ pub const Inst = struct { }; pub const DeclVal = struct { - pub const base_tag = Tag.declval; + pub const base_tag = Tag.decl_val; base: Inst, positionals: struct { @@ -786,13 +777,13 @@ pub const Inst = struct { positionals: struct { dest_type: *Inst, - block: *Block, + block_ptr: *Inst, }, kw_args: struct {}, }; pub const CompileLog = struct { - pub const base_tag = Tag.compilelog; + pub const base_tag = Tag.compile_log; base: Inst, positionals: struct { @@ -905,7 +896,7 @@ pub const Inst = struct { }; pub const IntType = struct { - pub const base_tag = Tag.inttype; + pub const base_tag = Tag.int_type; base: Inst, positionals: struct { @@ -1641,10 +1632,10 @@ const DumpTzir = struct { .cmp_gt, .cmp_neq, .store, - .booland, - .boolor, - .bitand, - .bitor, + .bool_and, + .bool_or, + .bit_and, + .bit_or, .xor, => { const bin_op = inst.cast(ir.Inst.BinOp).?; @@ -1753,10 +1744,10 @@ const DumpTzir = struct { .cmp_gt, .cmp_neq, .store, - .booland, - .boolor, - .bitand, - .bitor, + .bool_and, + .bool_or, + .bit_and, + .bit_or, .xor, => { const bin_op = inst.cast(ir.Inst.BinOp).?; diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 0caaa2a03f..ca8255df94 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -28,144 +28,134 @@ const Decl = Module.Decl; pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { switch (old_inst.tag) { - .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?), - .alloc_mut => return analyzeInstAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?), - .alloc_inferred => return analyzeInstAllocInferred( - mod, - scope, - old_inst.castTag(.alloc_inferred).?, - .inferred_alloc_const, - ), - .alloc_inferred_mut => return analyzeInstAllocInferred( - mod, - scope, - old_inst.castTag(.alloc_inferred_mut).?, - .inferred_alloc_mut, - ), - .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?), - .bitcast_ref => return bitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?), - .bitcast_result_ptr => return bitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?), - .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false), - .block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true), - .block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false), - .block_comptime_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true), - .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?), - .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?), - .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?), - .call => return call(mod, scope, old_inst.castTag(.call).?), - .coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?), - .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?), - .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?), - .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?), - .compilelog => return analyzeInstCompileLog(mod, scope, old_inst.castTag(.compilelog).?), - .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?), - .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?), - .declref => return declRef(mod, scope, old_inst.castTag(.declref).?), - .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?), - .declval => return declVal(mod, scope, old_inst.castTag(.declval).?), - .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), - .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), - .indexable_ptr_len => return indexablePtrLen(mod, scope, old_inst.castTag(.indexable_ptr_len).?), - .ref => return ref(mod, scope, old_inst.castTag(.ref).?), - .resolve_inferred_alloc => return analyzeInstResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?), - .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), - .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?), - .store_to_inferred_ptr => return analyzeInstStoreToInferredPtr(mod, scope, old_inst.castTag(.store_to_inferred_ptr).?), - .single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One), - .single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One), - .many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many), - .many_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many), - .c_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C), - .c_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C), - .const_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice), - .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice), - .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?), - .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?), - .set_eval_branch_quota => return analyzeInstSetEvalBranchQuota(mod, scope, old_inst.castTag(.set_eval_branch_quota).?), - .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?), - .int => return analyzeInstInt(mod, scope, old_inst.castTag(.int).?), - .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?), - .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?), - .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?), - .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?), - .field_ptr => return fieldPtr(mod, scope, old_inst.castTag(.field_ptr).?), - .field_val => return fieldVal(mod, scope, old_inst.castTag(.field_val).?), - .field_ptr_named => return fieldPtrNamed(mod, scope, old_inst.castTag(.field_ptr_named).?), - .field_val_named => return fieldValNamed(mod, scope, old_inst.castTag(.field_val_named).?), - .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), - .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), - .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), - .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true), - .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false), - .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), - .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), - .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?), - .@"export" => return analyzeInstExport(mod, scope, old_inst.castTag(.@"export").?), - .primitive => return analyzeInstPrimitive(mod, scope, old_inst.castTag(.primitive).?), - .fntype => return analyzeInstFnType(mod, scope, old_inst.castTag(.fntype).?), - .intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?), - .bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?), - .floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?), - .elem_ptr => return elemPtr(mod, scope, old_inst.castTag(.elem_ptr).?), - .elem_val => return elemVal(mod, scope, old_inst.castTag(.elem_val).?), - .add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?), - .addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?), - .sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?), - .subwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.subwrap).?), - .mul => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mul).?), - .mulwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mulwrap).?), - .div => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.div).?), - .mod_rem => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mod_rem).?), - .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), - .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), - .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), - .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?), - .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), - .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), - .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), - .shr => return analyzeInstShr(mod, scope, old_inst.castTag(.shr).?), - .cmp_lt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt), - .cmp_lte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte), - .cmp_eq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq), - .cmp_gte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte), - .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt), - .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq), - .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), - .is_null => return isNull(mod, scope, old_inst.castTag(.is_null).?, false), - .is_non_null => return isNull(mod, scope, old_inst.castTag(.is_non_null).?, true), - .is_null_ptr => return isNullPtr(mod, scope, old_inst.castTag(.is_null_ptr).?, false), - .is_non_null_ptr => return isNullPtr(mod, scope, old_inst.castTag(.is_non_null_ptr).?, true), - .is_err => return isErr(mod, scope, old_inst.castTag(.is_err).?), - .is_err_ptr => return isErrPtr(mod, scope, old_inst.castTag(.is_err_ptr).?), - .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), - .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), - .typeof_peer => return analyzeInstTypeOfPeer(mod, scope, old_inst.castTag(.typeof_peer).?), - .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), - .optional_payload_safe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), - .optional_payload_unsafe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), - .optional_payload_safe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true), - .optional_payload_unsafe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_unsafe_ptr).?, false), - .err_union_payload_safe => return errorUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_safe).?, true), - .err_union_payload_unsafe => return errorUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_unsafe).?, false), - .err_union_payload_safe_ptr => return errorUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_safe_ptr).?, true), - .err_union_payload_unsafe_ptr => return errorUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_unsafe_ptr).?, false), - .err_union_code => return errorUnionCode(mod, scope, old_inst.castTag(.err_union_code).?), - .err_union_code_ptr => return errorUnionCodePtr(mod, scope, old_inst.castTag(.err_union_code_ptr).?), - .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), - .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), - .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), - .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), - .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?), - .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?), - .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?), - .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?), - .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), - .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), - .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?), - .switchbr => return analyzeInstSwitchBr(mod, scope, old_inst.castTag(.switchbr).?), - .switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), - .booland => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.booland).?), - .boolor => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.boolor).?), + .alloc => return zirAlloc(mod, scope, old_inst.castTag(.alloc).?), + .alloc_mut => return zirAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?), + .alloc_inferred => return zirAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?, .inferred_alloc_const), + .alloc_inferred_mut => return zirAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred_mut).?, .inferred_alloc_mut), + .arg => return zirArg(mod, scope, old_inst.castTag(.arg).?), + .bitcast_ref => return zirBitcastRef(mod, scope, old_inst.castTag(.bitcast_ref).?), + .bitcast_result_ptr => return zirBitcastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?), + .block => return zirBlock(mod, scope, old_inst.castTag(.block).?, false), + .block_comptime => return zirBlock(mod, scope, old_inst.castTag(.block_comptime).?, true), + .block_flat => return zirBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false), + .block_comptime_flat => return zirBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true), + .@"break" => return zirBreak(mod, scope, old_inst.castTag(.@"break").?), + .breakpoint => return zirBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?), + .break_void => return zirBreakVoid(mod, scope, old_inst.castTag(.break_void).?), + .call => return zirCall(mod, scope, old_inst.castTag(.call).?), + .coerce_result_block_ptr => return zirCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?), + .coerce_result_ptr => return zirCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?), + .compile_error => return zirCompileError(mod, scope, old_inst.castTag(.compile_error).?), + .compile_log => return zirCompileLog(mod, scope, old_inst.castTag(.compile_log).?), + .@"const" => return zirConst(mod, scope, old_inst.castTag(.@"const").?), + .dbg_stmt => return zirDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?), + .decl_ref => return zirDeclRef(mod, scope, old_inst.castTag(.decl_ref).?), + .decl_ref_str => return zirDeclRefStr(mod, scope, old_inst.castTag(.decl_ref_str).?), + .decl_val => return zirDeclVal(mod, scope, old_inst.castTag(.decl_val).?), + .ensure_result_used => return zirEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), + .ensure_result_non_error => return zirEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), + .indexable_ptr_len => return zirIndexablePtrLen(mod, scope, old_inst.castTag(.indexable_ptr_len).?), + .ref => return zirRef(mod, scope, old_inst.castTag(.ref).?), + .resolve_inferred_alloc => return zirResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?), + .ret_ptr => return zirRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), + .ret_type => return zirRetType(mod, scope, old_inst.castTag(.ret_type).?), + .store_to_block_ptr => return zirStoreToBlockPtr(mod, scope, old_inst.castTag(.store_to_block_ptr).?), + .store_to_inferred_ptr => return zirStoreToInferredPtr(mod, scope, old_inst.castTag(.store_to_inferred_ptr).?), + .single_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One), + .single_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One), + .many_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many), + .many_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many), + .c_const_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C), + .c_mut_ptr_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C), + .const_slice_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice), + .mut_slice_type => return zirSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice), + .ptr_type => return zirPtrType(mod, scope, old_inst.castTag(.ptr_type).?), + .store => return zirStore(mod, scope, old_inst.castTag(.store).?), + .set_eval_branch_quota => return zirSetEvalBranchQuota(mod, scope, old_inst.castTag(.set_eval_branch_quota).?), + .str => return zirStr(mod, scope, old_inst.castTag(.str).?), + .int => return zirInt(mod, scope, old_inst.castTag(.int).?), + .int_type => return zirIntType(mod, scope, old_inst.castTag(.int_type).?), + .loop => return zirLoop(mod, scope, old_inst.castTag(.loop).?), + .param_type => return zirParamType(mod, scope, old_inst.castTag(.param_type).?), + .ptrtoint => return zirPtrtoint(mod, scope, old_inst.castTag(.ptrtoint).?), + .field_ptr => return zirFieldPtr(mod, scope, old_inst.castTag(.field_ptr).?), + .field_val => return zirFieldVal(mod, scope, old_inst.castTag(.field_val).?), + .field_ptr_named => return zirFieldPtrNamed(mod, scope, old_inst.castTag(.field_ptr_named).?), + .field_val_named => return zirFieldValNamed(mod, scope, old_inst.castTag(.field_val_named).?), + .deref => return zirDeref(mod, scope, old_inst.castTag(.deref).?), + .as => return zirAs(mod, scope, old_inst.castTag(.as).?), + .@"asm" => return zirAsm(mod, scope, old_inst.castTag(.@"asm").?), + .unreachable_safe => return zirUnreachable(mod, scope, old_inst.castTag(.unreachable_safe).?, true), + .unreachable_unsafe => return zirUnreachable(mod, scope, old_inst.castTag(.unreachable_unsafe).?, false), + .@"return" => return zirReturn(mod, scope, old_inst.castTag(.@"return").?), + .return_void => return zirReturnVoid(mod, scope, old_inst.castTag(.return_void).?), + .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), + .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), + .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), + .fntype => return zirFnType(mod, scope, old_inst.castTag(.fntype).?), + .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), + .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), + .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), + .elem_ptr => return zirElemPtr(mod, scope, old_inst.castTag(.elem_ptr).?), + .elem_val => return zirElemVal(mod, scope, old_inst.castTag(.elem_val).?), + .add => return zirArithmetic(mod, scope, old_inst.castTag(.add).?), + .addwrap => return zirArithmetic(mod, scope, old_inst.castTag(.addwrap).?), + .sub => return zirArithmetic(mod, scope, old_inst.castTag(.sub).?), + .subwrap => return zirArithmetic(mod, scope, old_inst.castTag(.subwrap).?), + .mul => return zirArithmetic(mod, scope, old_inst.castTag(.mul).?), + .mulwrap => return zirArithmetic(mod, scope, old_inst.castTag(.mulwrap).?), + .div => return zirArithmetic(mod, scope, old_inst.castTag(.div).?), + .mod_rem => return zirArithmetic(mod, scope, old_inst.castTag(.mod_rem).?), + .array_cat => return zirArrayCat(mod, scope, old_inst.castTag(.array_cat).?), + .array_mul => return zirArrayMul(mod, scope, old_inst.castTag(.array_mul).?), + .bit_and => return zirBitwise(mod, scope, old_inst.castTag(.bit_and).?), + .bit_not => return zirBitNot(mod, scope, old_inst.castTag(.bit_not).?), + .bit_or => return zirBitwise(mod, scope, old_inst.castTag(.bit_or).?), + .xor => return zirBitwise(mod, scope, old_inst.castTag(.xor).?), + .shl => return zirShl(mod, scope, old_inst.castTag(.shl).?), + .shr => return zirShr(mod, scope, old_inst.castTag(.shr).?), + .cmp_lt => return zirCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt), + .cmp_lte => return zirCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte), + .cmp_eq => return zirCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq), + .cmp_gte => return zirCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte), + .cmp_gt => return zirCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt), + .cmp_neq => return zirCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq), + .condbr => return zirCondbr(mod, scope, old_inst.castTag(.condbr).?), + .is_null => return zirIsNull(mod, scope, old_inst.castTag(.is_null).?, false), + .is_non_null => return zirIsNull(mod, scope, old_inst.castTag(.is_non_null).?, true), + .is_null_ptr => return zirIsNullPtr(mod, scope, old_inst.castTag(.is_null_ptr).?, false), + .is_non_null_ptr => return zirIsNullPtr(mod, scope, old_inst.castTag(.is_non_null_ptr).?, true), + .is_err => return zirIsErr(mod, scope, old_inst.castTag(.is_err).?), + .is_err_ptr => return zirIsErrPtr(mod, scope, old_inst.castTag(.is_err_ptr).?), + .bool_not => return zirBoolNot(mod, scope, old_inst.castTag(.bool_not).?), + .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), + .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), + .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?), + .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), + .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), + .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true), + .optional_payload_unsafe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_unsafe_ptr).?, false), + .err_union_payload_safe => return zirErrUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_safe).?, true), + .err_union_payload_unsafe => return zirErrUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_unsafe).?, false), + .err_union_payload_safe_ptr => return zirErrUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_safe_ptr).?, true), + .err_union_payload_unsafe_ptr => return zirErrUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_unsafe_ptr).?, false), + .err_union_code => return zirErrUnionCode(mod, scope, old_inst.castTag(.err_union_code).?), + .err_union_code_ptr => return zirErrUnionCodePtr(mod, scope, old_inst.castTag(.err_union_code_ptr).?), + .ensure_err_payload_void => return zirEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), + .array_type => return zirArrayType(mod, scope, old_inst.castTag(.array_type).?), + .array_type_sentinel => return zirArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), + .enum_literal => return zirEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), + .merge_error_sets => return zirMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?), + .error_union_type => return zirErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?), + .anyframe_type => return zirAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?), + .error_set => return zirErrorSet(mod, scope, old_inst.castTag(.error_set).?), + .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?), + .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?), + .import => return zirImport(mod, scope, old_inst.castTag(.import).?), + .switchbr => return zirSwitchbr(mod, scope, old_inst.castTag(.switchbr).?), + .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), + .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), + .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .container_field_named, .container_field_typed, @@ -258,7 +248,7 @@ pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE }; } -fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { +fn zirConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions @@ -275,44 +265,35 @@ fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError }; } -fn analyzeInstCoerceResultBlockPtr( +fn zirCoerceResultBlockPtr( mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceResultBlockPtr, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirCoerceResultBlockPtr", .{}); } -fn bitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirBitcastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement zir_sema.bitCastRef", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zir_sema.zirBitcastRef", .{}); } -fn bitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirBitcastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement zir_sema.bitCastResultPtr", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zir_sema.zirBitcastResultPtr", .{}); } -fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirCoerceResultPtr", .{}); } -/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. -fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const ptr = try resolveInst(mod, scope, inst.positionals.ptr); - const operand = try resolveInst(mod, scope, inst.positionals.value); - return mod.coerce(scope, ptr.ty.elemType(), operand); -} - -fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { +fn zirRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); @@ -322,7 +303,7 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr return mod.addNoOp(b, inst.base.src, ptr_type, .alloc); } -fn ref(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -330,7 +311,7 @@ fn ref(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { return mod.analyzeRef(scope, inst.base.src, operand); } -fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { +fn zirRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); @@ -339,7 +320,7 @@ fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr return mod.constType(scope, inst.base.src, ret_type); } -fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); @@ -349,7 +330,7 @@ fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp } } -fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); @@ -359,7 +340,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. } } -fn indexablePtrLen(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirIndexablePtrLen(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -389,7 +370,7 @@ fn indexablePtrLen(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src); } -fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); @@ -398,7 +379,7 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro return mod.addNoOp(b, inst.base.src, ptr_type, .alloc); } -fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); @@ -408,7 +389,7 @@ fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerE return mod.addNoOp(b, inst.base.src, ptr_type, .alloc); } -fn analyzeInstAllocInferred( +fn zirAllocInferred( mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp, @@ -437,7 +418,7 @@ fn analyzeInstAllocInferred( return result; } -fn analyzeInstResolveInferredAlloc( +fn zirResolveInferredAlloc( mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, @@ -466,28 +447,44 @@ fn analyzeInstResolveInferredAlloc( return mod.constVoid(scope, inst.base.src); } -fn analyzeInstStoreToInferredPtr( +fn zirStoreToBlockPtr( + mod: *Module, + scope: *Scope, + inst: *zir.Inst.BinOp, +) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + const ptr = try resolveInst(mod, scope, inst.positionals.lhs); + const value = try resolveInst(mod, scope, inst.positionals.rhs); + const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One); + const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr); + return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); +} + +fn zirStoreToInferredPtr( mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; // Add the stored instruction to the set we will use to resolve peer types // for the inferred allocation. try inferred_alloc.data.stored_inst_list.append(scope.arena(), value); - // Create a new alloc with exactly the type the pointer wants. - // Later it gets cleaned up by aliasing the alloc we are supposed to be storing to. + // Create a runtime bitcast instruction with exactly the type the pointer wants. const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One); const b = try mod.requireRuntimeBlock(scope, inst.base.src); const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr); return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); } -fn analyzeInstSetEvalBranchQuota( +fn zirSetEvalBranchQuota( mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, @@ -499,15 +496,16 @@ fn analyzeInstSetEvalBranchQuota( return mod.constVoid(scope, inst.base.src); } -fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); return mod.storePtr(scope, inst.base.src, ptr, value); } -fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { +fn zirParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const fn_inst = try resolveInst(mod, scope, inst.positionals.func); @@ -516,7 +514,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { .Fn => fn_inst.ty, .BoundFn => { - return mod.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{}); + return mod.fail(scope, fn_inst.src, "TODO implement zirParamType for method call syntax", .{}); }, else => { return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); @@ -538,7 +536,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) return mod.constType(scope, inst.base.src, param_type); } -fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { +fn zirStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // The bytes references memory inside the ZIR module, which can get deallocated @@ -557,14 +555,14 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl); } -fn analyzeInstInt(mod: *Module, scope: *Scope, inst: *zir.Inst.Int) InnerError!*Inst { +fn zirInt(mod: *Module, scope: *Scope, inst: *zir.Inst.Int) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int); } -fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { +fn zirExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name); @@ -574,14 +572,14 @@ fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) return mod.constVoid(scope, export_inst.base.src); } -fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const msg = try resolveConstString(mod, scope, inst.positionals.operand); return mod.fail(scope, inst.base.src, "{s}", .{msg}); } -fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst { +fn zirCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst { var managed = mod.compile_log_text.toManaged(mod.gpa); defer mod.compile_log_text = managed.moveToUnmanaged(); const writer = managed.writer(); @@ -608,7 +606,7 @@ fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog return mod.constVoid(scope, inst.base.src); } -fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { +fn zirArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); @@ -631,7 +629,7 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!* return mod.addArg(b, inst.base.src, param_type, name); } -fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst { +fn zirLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; @@ -672,7 +670,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError return &loop_inst.base; } -fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { +fn zirBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; @@ -704,9 +702,15 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c return resolveInst(mod, scope, last_zir_inst); } -fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { +fn zirBlock( + mod: *Module, + scope: *Scope, + inst: *zir.Inst.Block, + is_comptime: bool, +) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const parent_block = scope.cast(Scope.Block).?; // Reserve space for a Block instruction so that generated Break instructions can @@ -798,30 +802,52 @@ fn analyzeBlockBody( return &merges.block_inst.base; } -fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { +fn zirBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const b = try mod.requireRuntimeBlock(scope, inst.base.src); return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint); } -fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { +fn zirBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const operand = try resolveInst(mod, scope, inst.positionals.operand); const block = inst.positionals.block; return analyzeBreak(mod, scope, inst.base.src, block, operand); } -fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { +fn zirBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); + const block = inst.positionals.block; const void_inst = try mod.constVoid(scope, inst.base.src); return analyzeBreak(mod, scope, inst.base.src, block, void_inst); } -fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { +fn analyzeBreak( + mod: *Module, + scope: *Scope, + src: usize, + zir_block: *zir.Inst.Block, + operand: *Inst, +) InnerError!*Inst { + var opt_block = scope.cast(Scope.Block); + while (opt_block) |block| { + if (block.label) |*label| { + if (label.zir_block == zir_block) { + try label.merges.results.append(mod.gpa, operand); + const b = try mod.requireFunctionBlock(scope, src); + return mod.addBr(b, src, label.merges.block_inst, operand); + } + } + opt_block = block.parent; + } else unreachable; +} + +fn zirDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); if (scope.cast(Scope.Block)) |b| { @@ -832,26 +858,26 @@ fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr return mod.constVoid(scope, inst.base.src); } -fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { +fn zirDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const decl_name = try resolveConstString(mod, scope, inst.positionals.name); return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name); } -fn declRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { +fn zirDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl); } -fn declVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { +fn zirDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.analyzeDeclVal(scope, inst.base.src, inst.positionals.decl); } -fn call(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { +fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1002,7 +1028,7 @@ fn call(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { return mod.addCall(b, inst.base.src, ret_type, func, casted_args); } -fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { +fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); @@ -1019,13 +1045,13 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! }); } -fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { +fn zirIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); } -fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { +fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const child_type = try resolveType(mod, scope, optional.positionals.operand); @@ -1033,7 +1059,7 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); } -fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { +fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // TODO these should be lazily evaluated @@ -1043,7 +1069,7 @@ fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) Inn return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type)); } -fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst { +fn zirArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // TODO these should be lazily evaluated @@ -1054,7 +1080,7 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); } -fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const error_union = try resolveType(mod, scope, inst.positionals.lhs); @@ -1067,7 +1093,7 @@ fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload)); } -fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const return_type = try resolveType(mod, scope, inst.positionals.operand); @@ -1075,7 +1101,7 @@ fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); } -fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { +fn zirErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // The declarations arena will store the hashmap. @@ -1107,13 +1133,13 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In return mod.analyzeDeclVal(scope, inst.base.src, new_decl); } -fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{}); } -fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { +fn zirEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const duped_name = try scope.arena().dupe(u8, inst.positionals.name); @@ -1124,7 +1150,7 @@ fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiter } /// Pointer in, pointer out. -fn optionalPayloadPtr( +fn zirOptionalPayloadPtr( mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, @@ -1165,7 +1191,7 @@ fn optionalPayloadPtr( } /// Value in, value out. -fn optionalPayload( +fn zirOptionalPayload( mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, @@ -1201,40 +1227,40 @@ fn optionalPayload( } /// Value in, value out -fn errorUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { +fn zirErrUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionPayload", .{}); + return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionPayload", .{}); } /// Pointer in, pointer out -fn errorUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { +fn zirErrUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionPayloadPtr", .{}); + return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionPayloadPtr", .{}); } /// Value in, value out -fn errorUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { +fn zirErrUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionCode", .{}); + return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionCode", .{}); } /// Pointer in, value out -fn errorUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { +fn zirErrUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionCodePtr", .{}); + return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionCodePtr", .{}); } -fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { +fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); + return mod.fail(scope, unwrap.base.src, "TODO implement zirEnsureErrPayloadVoid", .{}); } -fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { +fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const return_type = try resolveType(mod, scope, fntype.positionals.return_type); @@ -1277,13 +1303,13 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne return mod.constType(scope, fntype.base.src, fn_ty); } -fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { +fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); } -fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { +fn zirAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const dest_type = try resolveType(mod, scope, as.positionals.lhs); @@ -1291,7 +1317,7 @@ fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*I return mod.coerce(scope, dest_type, new_inst); } -fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { +fn zirPtrtoint(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand); @@ -1304,7 +1330,7 @@ fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) In return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr); } -fn fieldVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst { +fn zirFieldVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1315,7 +1341,7 @@ fn fieldVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src); } -fn fieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst { +fn zirFieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1324,7 +1350,7 @@ fn fieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, inst.base.src); } -fn fieldValNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst { +fn zirFieldValNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1336,7 +1362,7 @@ fn fieldValNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerE return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src); } -fn fieldPtrNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst { +fn zirFieldPtrNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1346,7 +1372,7 @@ fn fieldPtrNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerE return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, fsrc); } -fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirIntcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); @@ -1384,7 +1410,7 @@ fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); } -fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirBitcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); @@ -1392,7 +1418,7 @@ fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE return mod.bitcast(scope, dest_type, operand); } -fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirFloatcast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); @@ -1430,7 +1456,7 @@ fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inne return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{}); } -fn elemVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { +fn zirElemVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1441,7 +1467,7 @@ fn elemVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src); } -fn elemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { +fn zirElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1450,7 +1476,7 @@ fn elemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { return mod.elemPtr(scope, inst.base.src, array_ptr, elem_index); } -fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst { +fn zirSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); @@ -1461,7 +1487,7 @@ fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerErr return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel); } -fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs); @@ -1470,7 +1496,7 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); } -fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const start = try resolveInst(mod, scope, inst.positionals.lhs); @@ -1494,7 +1520,7 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In return mod.constVoid(scope, inst.base.src); } -fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { +fn zirSwitchbr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); @@ -1698,7 +1724,7 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw } } -fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveConstString(mod, scope, inst.positionals.operand); @@ -1718,19 +1744,19 @@ fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErr return mod.constType(scope, inst.base.src, file_scope.root_container.ty); } -fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirShl", .{}); } -fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirShr", .{}); } -fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1784,8 +1810,8 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE const b = try mod.requireRuntimeBlock(scope, inst.base.src); const ir_tag = switch (inst.base.tag) { - .bitand => Inst.Tag.bitand, - .bitor => Inst.Tag.bitor, + .bit_and => Inst.Tag.bit_and, + .bit_or => Inst.Tag.bit_or, .xor => Inst.Tag.xor, else => unreachable, }; @@ -1793,25 +1819,25 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); } -fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirBitNot", .{}); } -fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirArrayCat", .{}); } -fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{}); + return mod.fail(scope, inst.base.src, "TODO implement zirArrayMul", .{}); } -fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1912,14 +1938,14 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir }); } -fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { +fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const ptr = try resolveInst(mod, scope, deref.positionals.operand); return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src); } -fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { +fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const return_type = try resolveType(mod, scope, assembly.positionals.return_type); @@ -1960,7 +1986,7 @@ fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerErr return &inst.base; } -fn analyzeInstCmp( +fn zirCmp( mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp, @@ -2018,14 +2044,14 @@ fn analyzeInstCmp( return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); } -fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirTypeof(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.constType(scope, inst.base.src, operand.ty); } -fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer) InnerError!*Inst { +fn zirTypeofPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); var insts_to_res = try mod.gpa.alloc(*ir.Inst, inst.positionals.items.len); @@ -2037,7 +2063,7 @@ fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer return mod.constType(scope, inst.base.src, pt_res); } -fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand); @@ -2050,7 +2076,7 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr return mod.addUnOp(b, inst.base.src, bool_type, .not, operand); } -fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { +fn zirBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const bool_type = Type.initTag(.bool); @@ -2059,7 +2085,7 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr const uncasted_rhs = try resolveInst(mod, scope, inst.positionals.rhs); const rhs = try mod.coerce(scope, bool_type, uncasted_rhs); - const is_bool_or = inst.base.tag == .boolor; + const is_bool_or = inst.base.tag == .bool_or; if (lhs.value()) |lhs_val| { if (rhs.value()) |rhs_val| { @@ -2071,17 +2097,17 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr } } const b = try mod.requireRuntimeBlock(scope, inst.base.src); - return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs); + return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .bool_or else .bool_and, lhs, rhs); } -fn isNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { +fn zirIsNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); } -fn isNullPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { +fn zirIsNullPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.operand); @@ -2089,14 +2115,14 @@ fn isNullPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bo return mod.analyzeIsNull(scope, inst.base.src, loaded, invert_logic); } -fn isErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsErr(scope, inst.base.src, operand); } -fn isErrPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirIsErrPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.operand); @@ -2104,7 +2130,7 @@ fn isErrPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst return mod.analyzeIsErr(scope, inst.base.src, loaded); } -fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { +fn zirCondbr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition); @@ -2153,7 +2179,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); } -fn analyzeInstUnreachable( +fn zirUnreachable( mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp, @@ -2170,7 +2196,7 @@ fn analyzeInstUnreachable( } } -fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { +fn zirReturn(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); @@ -2185,7 +2211,7 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError! return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); } -fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { +fn zirReturnVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); @@ -2216,27 +2242,7 @@ fn floatOpAllowed(tag: zir.Inst.Tag) bool { }; } -fn analyzeBreak( - mod: *Module, - scope: *Scope, - src: usize, - zir_block: *zir.Inst.Block, - operand: *Inst, -) InnerError!*Inst { - var opt_block = scope.cast(Scope.Block); - while (opt_block) |block| { - if (block.label) |*label| { - if (label.zir_block == zir_block) { - try label.merges.results.append(mod.gpa, operand); - const b = try mod.requireFunctionBlock(scope, src); - return mod.addBr(b, src, label.merges.block_inst, operand); - } - } - opt_block = block.parent; - } else unreachable; -} - -fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst { +fn zirSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const elem_type = try resolveType(mod, scope, inst.positionals.operand); @@ -2244,7 +2250,7 @@ fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, m return mod.constType(scope, inst.base.src, ty); } -fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst { +fn zirPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); // TODO lazy values -- cgit v1.2.3 From 588171c30b34426fbb07645aa2625e989f369eec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Jan 2021 16:45:09 -0700 Subject: sema: after block gets peer type resolved, insert type coercions on the break instruction operands. This involves a new TZIR instruction, br_block_flat, which represents a break instruction where the operand is the result of a flat block. See the doc comments on the instructions for more details. How it works: when adding break instructions in semantic analysis, the underlying allocation is slightly padded so that it is the size of a br_block_flat instruction, which allows the break instruction to later be converted without removing instructions inside the parent body. The extra type coercion instructions go into the body of the br_block_flat, and backends are responsible for dispatching the instruction correctly (it should map to the same function calls for related instructions). --- src/Module.zig | 28 +++++++++++++++-- src/codegen.zig | 31 ++++++++++++------- src/ir.zig | 32 +++++++++++++++++++- src/zir.zig | 28 +++++++++++++++-- src/zir_sema.zig | 91 ++++++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 171 insertions(+), 39 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 2dc84a93a9..b7967aacc5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -671,14 +671,36 @@ pub const Scope = struct { }; pub const Merges = struct { - results: ArrayListUnmanaged(*Inst), block_inst: *Inst.Block, + /// Separate array list from break_inst_list so that it can be passed directly + /// to resolvePeerTypes. + results: ArrayListUnmanaged(*Inst), + /// Keeps track of the break instructions so that the operand can be replaced + /// if we need to add type coercion at the end of block analysis. + /// Same indexes, capacity, length as `results`. + br_list: ArrayListUnmanaged(*Inst.Br), }; /// For debugging purposes. pub fn dump(self: *Block, mod: Module) void { zir.dumpBlock(mod, self); } + + pub fn makeSubBlock(parent: *Block) Block { + return .{ + .parent = parent, + .inst_table = parent.inst_table, + .func = parent.func, + .owner_decl = parent.owner_decl, + .src_decl = parent.src_decl, + .instructions = .{}, + .arena = parent.arena, + .label = null, + .inlining = parent.inlining, + .is_comptime = parent.is_comptime, + .branch_quota = parent.branch_quota, + }; + } }; /// This is a temporary structure, references to it are valid only @@ -2107,7 +2129,7 @@ pub fn addBr( src: usize, target_block: *Inst.Block, operand: *Inst, -) !*Inst { +) !*Inst.Br { const inst = try scope_block.arena.create(Inst.Br); inst.* = .{ .base = .{ @@ -2119,7 +2141,7 @@ pub fn addBr( .block = target_block, }; try scope_block.instructions.append(self.gpa, &inst.base); - return &inst.base; + return inst; } pub fn addCondBr( diff --git a/src/codegen.zig b/src/codegen.zig index a7b067f7e1..1f5aad8ab8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -844,6 +844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .bit_or => return self.genBitOr(inst.castTag(.bit_or).?), .block => return self.genBlock(inst.castTag(.block).?), .br => return self.genBr(inst.castTag(.br).?), + .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?), .breakpoint => return self.genBreakpoint(inst.src), .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?), @@ -2441,17 +2442,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genBrBlockFlat(self: *Self, parent_inst: *ir.Inst.BrBlockFlat) !MCValue { + try self.genBody(parent_inst.body); + const last = parent_inst.body.instructions[parent_inst.body.instructions.len - 1]; + return self.br(parent_inst.base.src, parent_inst.block, last); + } + fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue { - if (inst.operand.ty.hasCodeGenBits()) { - const operand = try self.resolveInst(inst.operand); - const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv); - if (block_mcv == .none) { - inst.block.codegen.mcv = @bitCast(AnyMCValue, operand); - } else { - try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand); - } - } - return self.brVoid(inst.base.src, inst.block); + return self.br(inst.base.src, inst.block, inst.operand); } fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue { @@ -2478,6 +2476,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue { + if (operand.ty.hasCodeGenBits()) { + const operand_mcv = try self.resolveInst(operand); + const block_mcv = @bitCast(MCValue, block.codegen.mcv); + if (block_mcv == .none) { + block.codegen.mcv = @bitCast(AnyMCValue, operand_mcv); + } else { + try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv); + } + } + return self.brVoid(src, block); + } + fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue { // Emit a jump with a relocation. It will be patched up after the block ends. try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); diff --git a/src/ir.zig b/src/ir.zig index b1147871f4..4d421dda4c 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -61,6 +61,13 @@ pub const Inst = struct { bit_or, block, br, + /// Same as `br` except the operand is a list of instructions to be treated as + /// a flat block; that is there is only 1 break instruction from the block, and + /// it is implied to be after the last instruction, and the last instruction is + /// the break operand. + /// This instruction exists for late-stage semantic analysis patch ups, to + /// replace one br operand with multiple instructions, without moving anything else around. + br_block_flat, breakpoint, brvoid, call, @@ -158,6 +165,7 @@ pub const Inst = struct { .assembly => Assembly, .block => Block, .br => Br, + .br_block_flat => BrBlockFlat, .brvoid => BrVoid, .call => Call, .condbr => CondBr, @@ -252,6 +260,7 @@ pub const Inst = struct { return switch (base.tag) { .br => base.castTag(.br).?.block, .brvoid => base.castTag(.brvoid).?.block, + .br_block_flat => base.castTag(.br_block_flat).?.block, else => null, }; } @@ -355,6 +364,27 @@ pub const Inst = struct { } }; + pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); + pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); + comptime { + assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base")); + } + + pub const BrBlockFlat = struct { + pub const base_tag = Tag.br_block_flat; + + base: Inst, + block: *Block, + body: Body, + + pub fn operandCount(self: *const BrBlockFlat) usize { + return 0; + } + pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { + return null; + } + }; + pub const Br = struct { pub const base_tag = Tag.br; @@ -363,7 +393,7 @@ pub const Inst = struct { operand: *Inst, pub fn operandCount(self: *const Br) usize { - return 0; + return 1; } pub fn getOperand(self: *const Br, index: usize) ?*Inst { if (index == 0) diff --git a/src/zir.zig b/src/zir.zig index 651e5ee3dc..301b52efc0 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -1634,6 +1634,12 @@ const DumpTzir = struct { try dtz.findConst(br.operand); }, + .br_block_flat => { + const br_block_flat = inst.castTag(.br_block_flat).?; + try dtz.findConst(&br_block_flat.block.base); + try dtz.fetchInstsAndResolveConsts(br_block_flat.body); + }, + .brvoid => { const brvoid = inst.castTag(.brvoid).?; try dtz.findConst(&brvoid.block.base); @@ -1779,6 +1785,24 @@ const DumpTzir = struct { } }, + .br_block_flat => { + const br_block_flat = inst.castTag(.br_block_flat).?; + const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base); + if (block_kinky != null) { + try writer.writeAll(", { // Instruction does not dominate all uses!\n"); + } else { + try writer.writeAll(", {\n"); + } + + const old_indent = dtz.indent; + dtz.indent += 2; + try dtz.dumpBody(br_block_flat.body, writer); + dtz.indent = old_indent; + + try writer.writeByteNTimes(' ', dtz.indent); + try writer.writeAll("})\n"); + }, + .brvoid => { const brvoid = inst.castTag(.brvoid).?; const kinky = try dtz.writeInst(writer, &brvoid.block.base); @@ -1792,7 +1816,7 @@ const DumpTzir = struct { .block => { const block = inst.castTag(.block).?; - try writer.writeAll("\n"); + try writer.writeAll("{\n"); const old_indent = dtz.indent; dtz.indent += 2; @@ -1800,7 +1824,7 @@ const DumpTzir = struct { dtz.indent = old_indent; try writer.writeByteNTimes(' ', dtz.indent); - try writer.writeAll(")\n"); + try writer.writeAll("})\n"); }, .condbr => { diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 40ea563bb6..0297b9873a 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -664,20 +664,9 @@ fn zirBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; - var child_block: Scope.Block = .{ - .parent = parent_block, - .inst_table = parent_block.inst_table, - .func = parent_block.func, - .owner_decl = parent_block.owner_decl, - .src_decl = parent_block.src_decl, - .instructions = .{}, - .arena = parent_block.arena, - .label = null, - .inlining = parent_block.inlining, - .is_comptime = parent_block.is_comptime or is_comptime, - .branch_quota = parent_block.branch_quota, - }; + var child_block = parent_block.makeSubBlock(); defer child_block.instructions.deinit(mod.gpa); + child_block.is_comptime = child_block.is_comptime or is_comptime; try analyzeBody(mod, &child_block, inst.positionals.body); @@ -728,6 +717,7 @@ fn zirBlock( .zir_block = inst, .merges = .{ .results = .{}, + .br_list = .{}, .block_inst = block_inst, }, }), @@ -739,6 +729,7 @@ fn zirBlock( defer child_block.instructions.deinit(mod.gpa); defer merges.results.deinit(mod.gpa); + defer merges.br_list.deinit(mod.gpa); try analyzeBody(mod, &child_block, inst.positionals.body); @@ -772,22 +763,53 @@ fn analyzeBlockBody( const last_inst = child_block.instructions.items[last_inst_index]; if (last_inst.breakBlock()) |br_block| { if (br_block == merges.block_inst) { - // No need for a block instruction. We can put the new instructions directly into the parent block. - // Here we omit the break instruction. + // No need for a block instruction. We can put the new instructions directly + // into the parent block. Here we omit the break instruction. const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); return merges.results.items[0]; } } } - // It should be impossible to have the number of results be > 1 in a comptime scope. - assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition. + // It is impossible to have the number of results be > 1 in a comptime scope. + assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. // Need to set the type and emit the Block instruction. This allows machine code generation // to emit a jump instruction to after the block when it encounters the break. try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); - merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items); - merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; + const resolved_ty = try mod.resolvePeerTypes(scope, merges.results.items); + merges.block_inst.base.ty = resolved_ty; + merges.block_inst.body = .{ + .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items), + }; + // Now that the block has its type resolved, we need to go back into all the break + // instructions, and insert type coercion on the operands. + for (merges.br_list.items) |br| { + if (br.operand.ty.eql(resolved_ty)) { + // No type coercion needed. + continue; + } + var coerce_block = parent_block.makeSubBlock(); + defer coerce_block.instructions.deinit(mod.gpa); + const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand); + assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); + // Here we depend on the br instruction having been over-allocated (if necessary) + // inide analyzeBreak so that it can be converted into a br_block_flat instruction. + const br_src = br.base.src; + const br_ty = br.base.ty; + const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br); + br_block_flat.* = .{ + .base = .{ + .src = br_src, + .ty = br_ty, + .tag = .br_block_flat, + }, + .block = merges.block_inst, + .body = .{ + .instructions = try parent_block.arena.dupe(*Inst, coerce_block.instructions.items), + }, + }; + } return &merges.block_inst.base; } @@ -827,9 +849,28 @@ fn analyzeBreak( while (opt_block) |block| { if (block.label) |*label| { if (label.zir_block == zir_block) { - try label.merges.results.append(mod.gpa, operand); const b = try mod.requireFunctionBlock(scope, src); - return mod.addBr(b, src, label.merges.block_inst, operand); + // Here we add a br instruction, but we over-allocate a little bit + // (if necessary) to make it possible to convert the instruction into + // a br_block_flat instruction later. + const br = @ptrCast(*Inst.Br, try b.arena.alignedAlloc( + u8, + Inst.convertable_br_align, + Inst.convertable_br_size, + )); + br.* = .{ + .base = .{ + .tag = .br, + .ty = Type.initTag(.noreturn), + .src = src, + }, + .operand = operand, + .block = label.merges.block_inst, + }; + try b.instructions.append(mod.gpa, &br.base); + try label.merges.results.append(mod.gpa, operand); + try label.merges.br_list.append(mod.gpa, br); + return &br.base; } } opt_block = block.parent; @@ -980,6 +1021,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { .casted_args = casted_args, .merges = .{ .results = .{}, + .br_list = .{}, .block_inst = block_inst, }, }; @@ -1004,6 +1046,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { defer child_block.instructions.deinit(mod.gpa); defer merges.results.deinit(mod.gpa); + defer merges.br_list.deinit(mod.gpa); try mod.emitBackwardBranch(&child_block, inst.base.src); @@ -2194,7 +2237,8 @@ fn zirReturn(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst if (b.inlining) |inlining| { // We are inlining a function call; rewrite the `ret` as a `break`. try inlining.merges.results.append(mod.gpa, operand); - return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); + const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); + return &br.base; } return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); @@ -2208,7 +2252,8 @@ fn zirReturnVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!* // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. const void_inst = try mod.constVoid(scope, inst.base.src); try inlining.merges.results.append(mod.gpa, void_inst); - return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); + const br = try mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); + return &br.base; } if (b.func) |func| { -- cgit v1.2.3 From 6c8985fceeeb6314143691570cf0c7a42521e590 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Jan 2021 20:23:37 -0700 Subject: astgen: rework labeled blocks --- src/Module.zig | 23 +++-- src/astgen.zig | 249 +++++++++++++++++++++++++++++++++++-------------------- src/codegen.zig | 10 +-- src/ir.zig | 8 +- src/zir.zig | 19 +++-- src/zir_sema.zig | 3 + 6 files changed, 200 insertions(+), 112 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index b7967aacc5..0bafc72e6b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -717,7 +717,7 @@ pub const Scope = struct { label: ?Label = null, break_block: ?*zir.Inst.Block = null, continue_block: ?*zir.Inst.Block = null, - /// only valid if label != null or (continue_block and break_block) != null + /// Only valid when setBlockResultLoc is called. break_result_loc: astgen.ResultLoc = undefined, /// When a block has a pointer result location, here it is. rl_ptr: ?*zir.Inst = null, @@ -726,6 +726,17 @@ pub const Scope = struct { /// whether to rely on break instructions or writing to the result /// pointer for the result instruction. rvalue_rl_count: usize = 0, + /// Keeps track of how many break instructions there are. When astgen is finished + /// with a block, it can check this against rvalue_rl_count to find out whether + /// the break instructions should be downgraded to break_void. + break_count: usize = 0, + /// Tracks `break :foo bar` instructions so they can possibly be elided later if + /// the labeled block ends up not needing a result location pointer. + labeled_breaks: std.ArrayListUnmanaged(*zir.Inst.Break) = .{}, + /// Tracks `store_to_block_ptr` instructions that correspond to break instructions + /// so they can possibly be elided later if the labeled block ends up not needing + /// a result location pointer. + labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{}, pub const Label = struct { token: ast.TokenIndex, @@ -3495,18 +3506,18 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic }; const ok_body: ir.Body = .{ - .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid. + .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the br_void. }; - const brvoid = try parent_block.arena.create(Inst.BrVoid); - brvoid.* = .{ + const br_void = try parent_block.arena.create(Inst.BrVoid); + br_void.* = .{ .base = .{ - .tag = .brvoid, + .tag = .br_void, .ty = Type.initTag(.noreturn), .src = ok.src, }, .block = block_inst, }; - ok_body.instructions[0] = &brvoid.base; + ok_body.instructions[0] = &br_void.base; var fail_block: Scope.Block = .{ .parent = parent_block, diff --git a/src/astgen.zig b/src/astgen.zig index 49f60aa6ba..994205c3a7 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -38,6 +38,21 @@ pub const ResultLoc = union(enum) { /// is inferred based on peer type resolution for a `zir.Inst.Block`. /// The result instruction from the expression must be ignored. block_ptr: *Module.Scope.GenZIR, + + pub const Strategy = struct { + elide_store_to_block_ptr_instructions: bool, + tag: Tag, + + pub const Tag = enum { + /// Both branches will use break_void; result location is used to communicate the + /// result instruction. + break_void, + /// Use break statements to pass the block result value, and call rvalue() at + /// the end depending on rl. Also elide the store_to_block_ptr instructions + /// depending on rl. + break_operand, + }; + }; }; pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { @@ -348,10 +363,11 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as return &block.base; } -fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { - if (true) { - @panic("TODO reimplement this"); - } +fn breakExpr( + mod: *Module, + parent_scope: *Scope, + node: *ast.Node.ControlFlowExpression, +) InnerError!*zir.Inst { const tree = parent_scope.tree(); const src = tree.token_locs[node.ltoken].start; @@ -377,25 +393,31 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr continue; }; - if (node.getRHS()) |rhs| { - // Most result location types can be forwarded directly; however - // if we need to write to a pointer which has an inferred type, - // proper type inference requires peer type resolution on the block's - // break operand expressions. - const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) { - .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc, - .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst }, - }; - const operand = try expr(mod, parent_scope, branch_rl, rhs); - return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{ + const rhs = node.getRHS() orelse { + return addZirInstTag(mod, parent_scope, src, .break_void, .{ .block = block_inst, - .operand = operand, - }, .{}); - } else { - return try addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{ - .block = block_inst, - }, .{}); + }); + }; + gen_zir.break_count += 1; + const prev_rvalue_rl_count = gen_zir.rvalue_rl_count; + const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs); + const have_store_to_block = gen_zir.rvalue_rl_count != prev_rvalue_rl_count; + const br = try addZirInstTag(mod, parent_scope, src, .@"break", .{ + .block = block_inst, + .operand = operand, + }); + if (gen_zir.break_result_loc == .block_ptr) { + try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?); + + if (have_store_to_block) { + const inst_list = parent_scope.cast(Scope.GenZIR).?.instructions.items; + const last_inst = inst_list[inst_list.len - 2]; + const store_inst = last_inst.castTag(.store_to_block_ptr).?; + assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?); + try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst); + } } + return br; }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, @@ -538,7 +560,6 @@ fn labeledBlockExpr( .decl = parent_scope.ownerDecl().?, .arena = gen_zir.arena, .instructions = .{}, - .break_result_loc = rl, // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ .token = block_node.label, @@ -546,19 +567,57 @@ fn labeledBlockExpr( }), }; defer block_scope.instructions.deinit(mod.gpa); + defer block_scope.labeled_breaks.deinit(mod.gpa); + defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); + + setBlockResultLoc(&block_scope, rl); try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); + if (!block_scope.label.?.used) { return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{}); } - block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); try gen_zir.instructions.append(mod.gpa, &block_inst.base); - return &block_inst.base; + const strat = rlStrategy(rl, &block_scope); + switch (strat.tag) { + .break_void => { + // The code took advantage of the result location as a pointer. + // Turn the break instructions into break_void instructions. + for (block_scope.labeled_breaks.items) |br| { + br.base.tag = .break_void; + } + // 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.positionals.body, block_scope); + + return &block_inst.base; + }, + .break_operand => { + // All break operands are values that did not use the result location pointer. + if (strat.elide_store_to_block_ptr_instructions) { + for (block_scope.labeled_store_to_block_ptr_list.items) |inst| { + inst.base.tag = .void_value; + } + // TODO technically not needed since we changed the tag to void_value but + // would be better still to elide the ones that are in this list. + } + try copyBodyNoEliding(&block_inst.positionals.body, block_scope); + switch (rl) { + .ref => return &block_inst.base, + else => return rvalue(mod, parent_scope, rl, &block_inst.base), + } + }, + } } -fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { +fn blockExprStmts( + mod: *Module, + parent_scope: *Scope, + node: *ast.Node, + statements: []*ast.Node, +) !void { const tree = parent_scope.tree(); var block_arena = std.heap.ArenaAllocator.init(mod.gpa); @@ -1659,7 +1718,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn cond_kind = .{ .err_union = null }; } } - const block_branch_count = 2; // then and else var block_scope: Scope.GenZIR = .{ .parent = scope, .decl = scope.ownerDecl().?, @@ -1668,6 +1726,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn }; defer block_scope.instructions.deinit(mod.gpa); + setBlockResultLoc(&block_scope, rl); + const tree = scope.tree(); const if_src = tree.token_locs[if_node.if_token].start; const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition); @@ -1682,33 +1742,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); - // Depending on whether the result location is a pointer or value, different - // ZIR needs to be generated. In the former case we rely on storing to the - // pointer to communicate the result, and use breakvoid; in the latter case - // the block break instructions will have the result values. - // One more complication: when the result location is a pointer, we detect - // the scenario where the result location is not consumed. In this case - // we emit ZIR for the block break instructions to have the result values, - // and then rvalue() on that to pass the value to the result location. - const branch_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .ref => rl, - - .inferred_ptr => |ptr| blk: { - block_scope.rl_ptr = &ptr.base; - break :blk .{ .block_ptr = &block_scope }; - }, - - .bitcasted_ptr => |ptr| blk: { - block_scope.rl_ptr = &ptr.base; - break :blk .{ .block_ptr = &block_scope }; - }, - - .block_ptr => |parent_block_scope| blk: { - block_scope.rl_ptr = parent_block_scope.rl_ptr.?; - break :blk .{ .block_ptr = &block_scope }; - }, - }; - const then_src = tree.token_locs[if_node.body.lastToken()].start; var then_scope: Scope.GenZIR = .{ .parent = scope, @@ -1721,7 +1754,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn // declare payload to the then_scope const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); - const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body); + block_scope.break_count += 1; + const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_node.body); // We hold off on the break instructions as well as copying the then/else // instructions into place until we know whether to keep store_to_block_ptr // instructions or not. @@ -1741,47 +1775,18 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn // declare payload to the then_scope else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); - break :blk try expr(mod, else_sub_scope, branch_rl, else_node.body); + block_scope.break_count += 1; + break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body); } else blk: { else_src = tree.token_locs[if_node.lastToken()].start; else_sub_scope = &else_scope.base; - block_scope.rvalue_rl_count += 1; break :blk null; }; // We now have enough information to decide whether the result instruction should // be communicated via result location pointer or break instructions. - const Strategy = enum { - /// Both branches will use break_void; result location is used to communicate the - /// result instruction. - break_void, - /// Use break statements to pass the block result value, and call rvalue() at - /// the end depending on rl. Also elide the store_to_block_ptr instructions - /// depending on rl. - break_operand, - }; - var elide_store_to_block_ptr_instructions = false; - const strategy: Strategy = switch (rl) { - // In this branch there will not be any store_to_block_ptr instructions. - .discard, .none, .ty, .ref => .break_operand, - // The pointer got passed through to the sub-expressions, so we will use - // break_void here. - // In this branch there will not be any store_to_block_ptr instructions. - .ptr => .break_void, - .inferred_ptr, .bitcasted_ptr, .block_ptr => blk: { - if (block_scope.rvalue_rl_count == 2) { - // Neither prong of the if consumed the result location, so we can - // use break instructions to create an rvalue. - elide_store_to_block_ptr_instructions = true; - break :blk Strategy.break_operand; - } else { - // Allow the store_to_block_ptr instructions to remain so that - // semantic analysis can turn them into bitcasts. - break :blk Strategy.break_void; - } - }, - }; - switch (strategy) { + const strat = rlStrategy(rl, &block_scope); + switch (strat.tag) { .break_void => { if (!then_result.tag.isNoReturn()) { _ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{ @@ -1799,7 +1804,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .block = block, }); } - assert(!elide_store_to_block_ptr_instructions); + assert(!strat.elide_store_to_block_ptr_instructions); try copyBodyNoEliding(&condbr.positionals.then_body, then_scope); try copyBodyNoEliding(&condbr.positionals.else_body, else_scope); return &block.base; @@ -1823,7 +1828,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .block = block, }); } - if (elide_store_to_block_ptr_instructions) { + if (strat.elide_store_to_block_ptr_instructions) { try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope); try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope); } else { @@ -3376,6 +3381,72 @@ fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul return rvalue(mod, scope, rl, void_inst); } +fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { + var elide_store_to_block_ptr_instructions = false; + switch (rl) { + // In this branch there will not be any store_to_block_ptr instructions. + .discard, .none, .ty, .ref => return .{ + .tag = .break_operand, + .elide_store_to_block_ptr_instructions = false, + }, + // The pointer got passed through to the sub-expressions, so we will use + // break_void here. + // In this branch there will not be any store_to_block_ptr instructions. + .ptr => return .{ + .tag = .break_void, + .elide_store_to_block_ptr_instructions = false, + }, + .inferred_ptr, .bitcasted_ptr, .block_ptr => { + if (block_scope.rvalue_rl_count == block_scope.break_count) { + // Neither prong of the if consumed the result location, so we can + // use break instructions to create an rvalue. + return .{ + .tag = .break_operand, + .elide_store_to_block_ptr_instructions = true, + }; + } else { + // Allow the store_to_block_ptr instructions to remain so that + // semantic analysis can turn them into bitcasts. + return .{ + .tag = .break_void, + .elide_store_to_block_ptr_instructions = false, + }; + } + }, + } +} + +fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { + // Depending on whether the result location is a pointer or value, different + // ZIR needs to be generated. In the former case we rely on storing to the + // pointer to communicate the result, and use breakvoid; in the latter case + // the block break instructions will have the result values. + // One more complication: when the result location is a pointer, we detect + // the scenario where the result location is not consumed. In this case + // we emit ZIR for the block break instructions to have the result values, + // and then rvalue() on that to pass the value to the result location. + switch (parent_rl) { + .discard, .none, .ty, .ptr, .ref => { + block_scope.break_result_loc = parent_rl; + }, + + .inferred_ptr => |ptr| { + block_scope.rl_ptr = &ptr.base; + block_scope.break_result_loc = .{ .block_ptr = block_scope }; + }, + + .bitcasted_ptr => |ptr| { + block_scope.rl_ptr = &ptr.base; + block_scope.break_result_loc = .{ .block_ptr = block_scope }; + }, + + .block_ptr => |parent_block_scope| { + block_scope.rl_ptr = parent_block_scope.rl_ptr.?; + block_scope.break_result_loc = .{ .block_ptr = block_scope }; + }, + } +} + pub fn addZirInstTag( mod: *Module, scope: *Scope, diff --git a/src/codegen.zig b/src/codegen.zig index 1f5aad8ab8..362b04ab26 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -846,7 +846,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .br => return self.genBr(inst.castTag(.br).?), .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?), .breakpoint => return self.genBreakpoint(inst.src), - .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), + .br_void => return self.genBrVoid(inst.castTag(.br_void).?), .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?), .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?), .call => return self.genCall(inst.castTag(.call).?), @@ -2442,10 +2442,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } - fn genBrBlockFlat(self: *Self, parent_inst: *ir.Inst.BrBlockFlat) !MCValue { - try self.genBody(parent_inst.body); - const last = parent_inst.body.instructions[parent_inst.body.instructions.len - 1]; - return self.br(parent_inst.base.src, parent_inst.block, last); + fn genBrBlockFlat(self: *Self, inst: *ir.Inst.BrBlockFlat) !MCValue { + try self.genBody(inst.body); + const last = inst.body.instructions[inst.body.instructions.len - 1]; + return self.br(inst.base.src, inst.block, last); } fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue { diff --git a/src/ir.zig b/src/ir.zig index 4d421dda4c..408efc3bba 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -69,7 +69,7 @@ pub const Inst = struct { /// replace one br operand with multiple instructions, without moving anything else around. br_block_flat, breakpoint, - brvoid, + br_void, call, cmp_lt, cmp_lte, @@ -166,7 +166,7 @@ pub const Inst = struct { .block => Block, .br => Br, .br_block_flat => BrBlockFlat, - .brvoid => BrVoid, + .br_void => BrVoid, .call => Call, .condbr => CondBr, .constant => Constant, @@ -259,7 +259,7 @@ pub const Inst = struct { pub fn breakBlock(base: *Inst) ?*Block { return switch (base.tag) { .br => base.castTag(.br).?.block, - .brvoid => base.castTag(.brvoid).?.block, + .br_void => base.castTag(.br_void).?.block, .br_block_flat => base.castTag(.br_block_flat).?.block, else => null, }; @@ -403,7 +403,7 @@ pub const Inst = struct { }; pub const BrVoid = struct { - pub const base_tag = Tag.brvoid; + pub const base_tag = Tag.br_void; base: Inst, block: *Block, diff --git a/src/zir.zig b/src/zir.zig index 301b52efc0..d372cfbf00 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -264,8 +264,7 @@ pub const Inst = struct { /// Write a value to a pointer. For loading, see `deref`. store, /// Same as `store` but the type of the value being stored will be used to infer - /// the block type. The LHS is a block instruction, whose result location is - /// being stored to. + /// the block type. The LHS is the pointer to store to. store_to_block_ptr, /// Same as `store` but the type of the value being stored will be used to infer /// the pointer type. @@ -343,6 +342,8 @@ pub const Inst = struct { /// Only checks that `lhs >= rhs` if they are ints, everything else is /// validated by the .switch instruction. switch_range, + /// Does nothing; returns a void value. + void_value, pub fn Type(tag: Tag) type { return switch (tag) { @@ -355,6 +356,7 @@ pub const Inst = struct { .ret_type, .unreachable_unsafe, .unreachable_safe, + .void_value, => NoOp, .alloc, @@ -611,6 +613,7 @@ pub const Inst = struct { .enum_type, .union_type, .struct_type, + .void_value, => false, .@"break", @@ -1640,9 +1643,9 @@ const DumpTzir = struct { try dtz.fetchInstsAndResolveConsts(br_block_flat.body); }, - .brvoid => { - const brvoid = inst.castTag(.brvoid).?; - try dtz.findConst(&brvoid.block.base); + .br_void => { + const br_void = inst.castTag(.br_void).?; + try dtz.findConst(&br_void.block.base); }, .block => { @@ -1803,9 +1806,9 @@ const DumpTzir = struct { try writer.writeAll("})\n"); }, - .brvoid => { - const brvoid = inst.castTag(.brvoid).?; - const kinky = try dtz.writeInst(writer, &brvoid.block.base); + .br_void => { + const br_void = inst.castTag(.br_void).?; + const kinky = try dtz.writeInst(writer, &br_void.block.base); if (kinky) |_| { try writer.writeAll(") // Instruction does not dominate all uses!\n"); } else { diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 0297b9873a..773c782746 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -155,6 +155,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), + .void_value => return mod.constVoid(scope, old_inst.src), .container_field_named, .container_field_typed, @@ -447,6 +448,8 @@ fn zirStoreToBlockPtr( const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One); + // TODO detect when this store should be done at compile-time. For example, + // if expressions should force it when the condition is compile-time known. const b = try mod.requireRuntimeBlock(scope, inst.base.src); const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr); return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); -- cgit v1.2.3 From 0f5eda973e0c17b3f792cdb06674bf8d2863c8fb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 31 Jan 2021 20:58:11 -0700 Subject: stage2: delete astgen for switch expressions The astgen for switch expressions did not respect the ZIR rules of only referencing instructions that are in scope: %14 = block_comptime_flat({ %15 = block_comptime_flat({ %16 = const(TypedValue{ .ty = comptime_int, .val = 1}) }) %17 = block_comptime_flat({ %18 = const(TypedValue{ .ty = comptime_int, .val = 2}) }) }) %19 = block({ %20 = ref(%5) %21 = deref(%20) %22 = switchbr(%20, [%15, %17], { %15 => { %23 = const(TypedValue{ .ty = comptime_int, .val = 1}) %24 = store(%10, %23) %25 = const(TypedValue{ .ty = void, .val = {}}) %26 = break("label_19", %25) }, %17 => { %27 = const(TypedValue{ .ty = comptime_int, .val = 2}) %28 = store(%10, %27) %29 = const(TypedValue{ .ty = void, .val = {}}) %30 = break("label_19", %29) } }, { %31 = unreachable_safe() }, special_prong=else) }) In this snippet you can see that the comptime expr referenced %15 and %17 which are not in scope. There also was no test coverage for runtime switch expressions. Switch expressions will have to be re-introduced to follow these rules and with some test coverage. There is some usable code being deleted in this commit; it will be useful to reference when re-implementing switch later. A few more improvements to do while we're at it: * only use .ref result loc on switch target if any prongs obtain the payload with |*syntax| - this improvement should be done to if, while, and for as well. - this will remove the needless ref/deref instructions above * remove switchbr and add switch_block, which is both a block and a switch branch. - similarly we should remove loop and add loop_block. This commit introduces a "force_comptime" flag into the GenZIR scope. The main purpose of this will be to choose the "comptime" variants of certain key zir instructions, such as function calls and branches. We will be moving away from using the block_comptime_flat ZIR instruction, and eventually deleting it. This commit also contains miscellaneous fixes to this branch that bring it to the state of passing all the tests. --- src/Module.zig | 13 +- src/astgen.zig | 342 ++++++++------------------------------------------- src/zir.zig | 56 --------- src/zir_sema.zig | 236 +---------------------------------- test/stage2/test.zig | 37 ------ 5 files changed, 72 insertions(+), 612 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 0bafc72e6b..b495afb336 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -375,6 +375,10 @@ pub const Scope = struct { } } + pub fn isComptime(self: *Scope) bool { + return self.getGenZIR().force_comptime; + } + pub fn ownerDecl(self: *Scope) ?*Decl { return switch (self.tag) { .block => self.cast(Block).?.owner_decl, @@ -712,6 +716,7 @@ pub const Scope = struct { parent: *Scope, decl: *Decl, arena: *Allocator, + force_comptime: bool, /// The first N instructions in a function body ZIR are arg instructions. instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, label: ?Label = null, @@ -1008,6 +1013,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &fn_type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer fn_type_scope.instructions.deinit(self.gpa); @@ -1171,6 +1177,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &decl_arena.allocator, .parent = &decl.container.base, + .force_comptime = false, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1369,6 +1376,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &gen_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = false, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1428,6 +1436,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer type_scope.instructions.deinit(self.gpa); @@ -1497,13 +1506,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { decl.analysis = .in_progress; - // A comptime decl does not store any value so we can just deinit this arena after analysis is done. + // A comptime decl does not store any value so we can just deinit + // this arena after analysis is done. var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); defer analysis_arena.deinit(); var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &analysis_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer gen_scope.instructions.deinit(self.gpa); diff --git a/src/astgen.zig b/src/astgen.zig index 446ee98eb4..dfc5f06ddc 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -309,7 +309,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), - .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), + .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}), .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), @@ -334,11 +334,19 @@ fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.C return comptimeExpr(mod, scope, rl, node.expr); } -pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { - const tree = parent_scope.tree(); - const src = tree.token_locs[node.firstToken()].start; +pub fn comptimeExpr( + mod: *Module, + parent_scope: *Scope, + rl: ResultLoc, + node: *ast.Node, +) InnerError!*zir.Inst { + // If we are already in a comptime scope, no need to make another one. + if (parent_scope.isComptime()) { + return expr(mod, parent_scope, rl, node); + } - // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one. + // Optimization for labeled blocks: don't need to have 2 layers of blocks, + // we can reuse the existing one. if (node.castTag(.LabeledBlock)) |block_node| { return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime); } @@ -348,6 +356,7 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as .parent = parent_scope, .decl = parent_scope.ownerDecl().?, .arena = parent_scope.arena(), + .force_comptime = true, .instructions = .{}, }; defer block_scope.instructions.deinit(mod.gpa); @@ -356,6 +365,9 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as // instruction is the block's result value. _ = try expr(mod, &block_scope.base, rl, node); + const tree = parent_scope.tree(); + const src = tree.token_locs[node.firstToken()].start; + const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); @@ -410,7 +422,7 @@ fn breakExpr( try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?); if (have_store_to_block) { - const inst_list = parent_scope.cast(Scope.GenZIR).?.instructions.items; + const inst_list = parent_scope.getGenZIR().instructions.items; const last_inst = inst_list[inst_list.len - 2]; const store_inst = last_inst.castTag(.store_to_block_ptr).?; assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?); @@ -559,6 +571,7 @@ fn labeledBlockExpr( .parent = parent_scope, .decl = parent_scope.ownerDecl().?, .arena = gen_zir.arena, + .force_comptime = parent_scope.isComptime(), .instructions = .{}, // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ @@ -746,6 +759,7 @@ fn varDecl( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer init_scope.instructions.deinit(mod.gpa); @@ -1107,6 +1121,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -1343,6 +1358,7 @@ fn orelseCatchExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&block_scope, rl); @@ -1367,6 +1383,7 @@ fn orelseCatchExpr( .parent = &block_scope.base, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1395,6 +1412,7 @@ fn orelseCatchExpr( .parent = &block_scope.base, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -1416,6 +1434,7 @@ fn orelseCatchExpr( then_result, unwrapped_payload, block, + block, ); } @@ -1432,7 +1451,8 @@ fn finishThenElseBlock( else_src: usize, then_result: *zir.Inst, else_result: ?*zir.Inst, - block: *zir.Inst.Block, + main_block: *zir.Inst.Block, + then_break_block: *zir.Inst.Block, ) InnerError!*zir.Inst { // We now have enough information to decide whether the result instruction should // be communicated via result location pointer or break instructions. @@ -1441,42 +1461,42 @@ fn finishThenElseBlock( .break_void => { if (!then_result.tag.isNoReturn()) { _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{ - .block = block, + .block = then_break_block, }); } if (else_result) |inst| { if (!inst.tag.isNoReturn()) { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } } else { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } assert(!strat.elide_store_to_block_ptr_instructions); try copyBodyNoEliding(then_body, then_scope.*); try copyBodyNoEliding(else_body, else_scope.*); - return &block.base; + return &main_block.base; }, .break_operand => { if (!then_result.tag.isNoReturn()) { _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{ - .block = block, + .block = then_break_block, .operand = then_result, }); } if (else_result) |inst| { if (!inst.tag.isNoReturn()) { _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{ - .block = block, + .block = main_block, .operand = inst, }); } } else { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } if (strat.elide_store_to_block_ptr_instructions) { @@ -1487,8 +1507,8 @@ fn finishThenElseBlock( try copyBodyNoEliding(else_body, else_scope.*); } switch (rl) { - .ref => return &block.base, - else => return rvalue(mod, parent_scope, rl, &block.base), + .ref => return &main_block.base, + else => return rvalue(mod, parent_scope, rl, &main_block.base), } }, } @@ -1643,6 +1663,7 @@ fn boolBinOp( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer block_scope.instructions.deinit(mod.gpa); @@ -1662,6 +1683,7 @@ fn boolBinOp( .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer rhs_scope.instructions.deinit(mod.gpa); @@ -1676,6 +1698,7 @@ fn boolBinOp( .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer const_scope.instructions.deinit(mod.gpa); @@ -1789,6 +1812,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&block_scope, rl); @@ -1813,6 +1837,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1830,6 +1855,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -1863,6 +1889,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn then_result, else_result, block, + block, ); } @@ -1912,6 +1939,7 @@ fn whileExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&loop_scope, rl); @@ -1921,6 +1949,7 @@ fn whileExpr( .parent = &loop_scope.base, .decl = loop_scope.decl, .arena = loop_scope.arena, + .force_comptime = loop_scope.force_comptime, .instructions = .{}, }; defer continue_scope.instructions.deinit(mod.gpa); @@ -1978,6 +2007,7 @@ fn whileExpr( .parent = &continue_scope.base, .decl = continue_scope.decl, .arena = continue_scope.arena, + .force_comptime = continue_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1992,6 +2022,7 @@ fn whileExpr( .parent = &continue_scope.base, .decl = continue_scope.decl, .arena = continue_scope.arena, + .force_comptime = continue_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -2027,6 +2058,7 @@ fn whileExpr( then_result, else_result, while_block, + cond_block, ); } @@ -2068,6 +2100,7 @@ fn forExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&loop_scope, rl); @@ -2077,6 +2110,7 @@ fn forExpr( .parent = &loop_scope.base, .decl = loop_scope.decl, .arena = loop_scope.arena, + .force_comptime = loop_scope.force_comptime, .instructions = .{}, }; defer cond_scope.instructions.deinit(mod.gpa); @@ -2134,6 +2168,7 @@ fn forExpr( .parent = &cond_scope.base, .decl = cond_scope.decl, .arena = cond_scope.arena, + .force_comptime = cond_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -2174,6 +2209,7 @@ fn forExpr( .parent = &cond_scope.base, .decl = cond_scope.decl, .arena = cond_scope.arena, + .force_comptime = cond_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -2206,281 +2242,10 @@ fn forExpr( then_result, else_result, for_block, + cond_block, ); } -fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { - var cur = node; - while (true) { - switch (cur.tag) { - .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), - .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr, - else => return null, - } - } -} - -fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { - if (true) { - @panic("TODO reimplement this"); - } - var block_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = scope.ownerDecl().?, - .arena = scope.arena(), - .instructions = .{}, - }; - defer block_scope.instructions.deinit(mod.gpa); - - const tree = scope.tree(); - const switch_src = tree.token_locs[switch_node.switch_token].start; - const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr); - const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr); - // Add the switch instruction here so that it comes before any range checks. - const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ - .target_ptr = target_ptr, - .cases = undefined, // populated below - .items = &[_]*zir.Inst{}, // populated below - .else_body = undefined, // populated below - }, .{})).castTag(.switchbr).?; - - var items = std.ArrayList(*zir.Inst).init(mod.gpa); - defer items.deinit(); - var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa); - defer cases.deinit(); - - // Add comptime block containing all prong items first, - const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{ - .instructions = undefined, // populated below - }); - // then add block containing the switch. - const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), - }); - - // Most result location types can be forwarded directly; however - // if we need to write to a pointer which has an inferred type, - // proper type inference requires peer type resolution on the switch case. - const case_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .ref => rl, - .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, - }; - - var item_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = scope.ownerDecl().?, - .arena = scope.arena(), - .instructions = .{}, - }; - defer item_scope.instructions.deinit(mod.gpa); - - var case_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = block_scope.decl, - .arena = block_scope.arena, - .instructions = .{}, - }; - defer case_scope.instructions.deinit(mod.gpa); - - var else_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = block_scope.decl, - .arena = block_scope.arena, - .instructions = .{}, - }; - defer else_scope.instructions.deinit(mod.gpa); - - // first we gather all the switch items and check else/'_' prongs - var else_src: ?usize = null; - var underscore_src: ?usize = null; - var first_range: ?*zir.Inst = null; - var special_case: ?*ast.Node.SwitchCase = null; - for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.SwitchCase).?; - const case_src = tree.token_locs[case.firstToken()].start; - // reset without freeing to reduce allocations. - case_scope.instructions.items.len = 0; - assert(case.items_len != 0); - - // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { - if (else_src) |src| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - case_src, - "multiple else prongs in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, src, msg, "previous else prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - else_src = case_src; - special_case = case; - continue; - } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and - mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) - { - if (underscore_src) |src| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - case_src, - "multiple '_' prongs in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - underscore_src = case_src; - special_case = case; - continue; - } - - if (else_src) |some_else| { - if (underscore_src) |some_underscore| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - switch_src, - "else and '_' prong in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, some_else, msg, "else prong is here", .{}); - try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - } - - // If this is a simple one item prong then it is handled by the switchbr. - if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) { - const item = try expr(mod, &item_scope.base, .none, case.items()[0]); - try items.append(item); - try switchCaseExpr(mod, &case_scope.base, case_rl, block, case); - - try cases.append(.{ - .item = item, - .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) }, - }); - continue; - } - - // TODO if the case has few items and no ranges it might be better - // to just handle them as switch prongs. - - // Check if the target matches any of the items. - // 1, 2, 3..6 will result in - // target == 1 or target == 2 or (target >= 3 and target <= 6) - var any_ok: ?*zir.Inst = null; - for (case.items()) |item| { - if (getRangeNode(item)) |range| { - const start = try expr(mod, &item_scope.base, .none, range.lhs); - const end = try expr(mod, &item_scope.base, .none, range.rhs); - const range_src = tree.token_locs[range.op_token].start; - const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end); - try items.append(range_inst); - if (first_range == null) first_range = range_inst; - - // target >= start and target <= end - const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start); - const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end); - const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); - - if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); - } else { - any_ok = range_ok; - } - continue; - } - - const item_inst = try expr(mod, &item_scope.base, .none, item); - try items.append(item_inst); - const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); - - if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); - } else { - any_ok = cpm_ok; - } - } - - const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{ - .condition = any_ok.?, - .then_body = undefined, // populated below - .else_body = undefined, // populated below - }, .{}); - const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }); - - // reset cond_scope for then_body - case_scope.instructions.items.len = 0; - try switchCaseExpr(mod, &case_scope.base, case_rl, block, case); - condbr.positionals.then_body = .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }; - - // reset cond_scope for else_body - case_scope.instructions.items.len = 0; - _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{ - .block = cond_block, - }, .{}); - condbr.positionals.else_body = .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }; - } - - // Generate else block or a break last to finish the block. - if (special_case) |case| { - try switchCaseExpr(mod, &else_scope.base, case_rl, block, case); - } else { - // Not handling all possible cases is a compile error. - _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); - } - - // All items have been generated, add the instructions to the comptime block. - item_block.positionals.body = .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items), - }; - - // Actually populate switch instruction values. - if (else_src != null) switch_inst.kw_args.special_prong = .@"else"; - if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore; - switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items); - switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items); - switch_inst.kw_args.range = first_range; - switch_inst.positionals.else_body = .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), - }; - return &block.base; -} - -fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void { - const tree = scope.tree(); - const case_src = tree.token_locs[case.firstToken()].start; - if (case.payload != null) { - return mod.fail(scope, case_src, "TODO switch case payload capture", .{}); - } - - const case_body = try expr(mod, scope, rl, case.expr); - if (!case_body.tag.isNoReturn()) { - _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{ - .block = block, - .operand = case_body, - }, .{}); - } -} - fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[cfe.ltoken].start; @@ -2859,6 +2624,7 @@ fn asRlPtr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer as_scope.instructions.deinit(mod.gpa); diff --git a/src/zir.zig b/src/zir.zig index 83eee71f87..2559fcdc8e 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -336,12 +336,6 @@ pub const Inst = struct { enum_literal, /// Create an enum type. enum_type, - /// A switch expression. - switchbr, - /// A range in a switch case, `lhs...rhs`. - /// Only checks that `lhs >= rhs` if they are ints, everything else is - /// validated by the .switch instruction. - switch_range, /// Does nothing; returns a void value. void_value, @@ -441,7 +435,6 @@ pub const Inst = struct { .error_union_type, .merge_error_sets, .slice_start, - .switch_range, => BinOp, .block, @@ -478,7 +471,6 @@ pub const Inst = struct { .enum_literal => EnumLiteral, .error_set => ErrorSet, .slice => Slice, - .switchbr => SwitchBr, .typeof_peer => TypeOfPeer, .container_field_named => ContainerFieldNamed, .container_field_typed => ContainerFieldTyped, @@ -605,7 +597,6 @@ pub const Inst = struct { .slice, .slice_start, .import, - .switch_range, .typeof_peer, .resolve_inferred_alloc, .set_eval_branch_quota, @@ -625,7 +616,6 @@ pub const Inst = struct { .unreachable_unsafe, .unreachable_safe, .loop, - .switchbr, .container_field_named, .container_field_typed, .container_field, @@ -1091,32 +1081,6 @@ pub const Inst = struct { }, }; - pub const SwitchBr = struct { - pub const base_tag = Tag.switchbr; - base: Inst, - - positionals: struct { - target_ptr: *Inst, - /// List of all individual items and ranges - items: []*Inst, - cases: []Case, - else_body: Body, - }, - kw_args: struct { - /// Pointer to first range if such exists. - range: ?*Inst = null, - special_prong: enum { - none, - @"else", - underscore, - } = .none, - }, - - pub const Case = struct { - item: *Inst, - body: Body, - }; - }; pub const TypeOfPeer = struct { pub const base_tag = .typeof_peer; base: Inst, @@ -1467,26 +1431,6 @@ const Writer = struct { } try stream.writeByte(']'); }, - []Inst.SwitchBr.Case => { - if (param.len == 0) { - return stream.writeAll("{}"); - } - try stream.writeAll("{\n"); - for (param) |*case, i| { - if (i != 0) { - try stream.writeAll(",\n"); - } - try stream.writeByteNTimes(' ', self.indent); - self.indent += 2; - try self.writeParamToStream(stream, &case.item); - try stream.writeAll(" => "); - try self.writeParamToStream(stream, &case.body); - self.indent -= 2; - } - try stream.writeByte('\n'); - try stream.writeByteNTimes(' ', self.indent - 2); - try stream.writeByte('}'); - }, else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), } } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 773c782746..301b95ad97 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -151,8 +151,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?), .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?), .import => return zirImport(mod, scope, old_inst.castTag(.import).?), - .switchbr => return zirSwitchbr(mod, scope, old_inst.castTag(.switchbr).?), - .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .void_value => return mod.constVoid(scope, old_inst.src), @@ -795,6 +793,12 @@ fn analyzeBlockBody( var coerce_block = parent_block.makeSubBlock(); defer coerce_block.instructions.deinit(mod.gpa); const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand); + // If no instructions were produced, such as in the case of a coercion of a + // constant value to a new type, we can simply point the br operand to it. + if (coerce_block.instructions.items.len == 0) { + br.operand = coerced_operand; + continue; + } assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); // Here we depend on the br instruction having been over-allocated (if necessary) // inide analyzeBreak so that it can be converted into a br_block_flat instruction. @@ -1531,234 +1535,6 @@ fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); } -fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const start = try resolveInst(mod, scope, inst.positionals.lhs); - const end = try resolveInst(mod, scope, inst.positionals.rhs); - - switch (start.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => return mod.constVoid(scope, inst.base.src), - } - switch (end.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => return mod.constVoid(scope, inst.base.src), - } - if (start.value()) |start_val| { - if (end.value()) |end_val| { - if (start_val.compare(.gte, end_val)) { - return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{}); - } - } - } - return mod.constVoid(scope, inst.base.src); -} - -fn zirSwitchbr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); - const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); - try validateSwitch(mod, scope, target, inst); - - if (try mod.resolveDefinedValue(scope, target)) |target_val| { - for (inst.positionals.cases) |case| { - const resolved = try resolveInst(mod, scope, case.item); - const casted = try mod.coerce(scope, target.ty, resolved); - const item = try mod.resolveConstValue(scope, casted); - - if (target_val.eql(item)) { - try analyzeBody(mod, scope.cast(Scope.Block).?, case.body); - return mod.constNoReturn(scope, inst.base.src); - } - } - try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); - return mod.constNoReturn(scope, inst.base.src); - } - - if (inst.positionals.cases.len == 0) { - // no cases just analyze else_branch - try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); - return mod.constNoReturn(scope, inst.base.src); - } - - const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); - const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); - - var case_block: Scope.Block = .{ - .parent = parent_block, - .inst_table = parent_block.inst_table, - .func = parent_block.func, - .owner_decl = parent_block.owner_decl, - .src_decl = parent_block.src_decl, - .instructions = .{}, - .arena = parent_block.arena, - .inlining = parent_block.inlining, - .is_comptime = parent_block.is_comptime, - .branch_quota = parent_block.branch_quota, - }; - defer case_block.instructions.deinit(mod.gpa); - - for (inst.positionals.cases) |case, i| { - // Reset without freeing. - case_block.instructions.items.len = 0; - - const resolved = try resolveInst(mod, scope, case.item); - const casted = try mod.coerce(scope, target.ty, resolved); - const item = try mod.resolveConstValue(scope, casted); - - try analyzeBody(mod, &case_block, case.body); - - cases[i] = .{ - .item = item, - .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, - }; - } - - case_block.instructions.items.len = 0; - try analyzeBody(mod, &case_block, inst.positionals.else_body); - - const else_body: ir.Body = .{ - .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), - }; - - return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body); -} - -fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { - // validate usage of '_' prongs - if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { - return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); - // TODO notes "'_' prong here" inst.positionals.cases[last].src - } - - // check that target type supports ranges - if (inst.kw_args.range) |range_inst| { - switch (target.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => { - return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); - // TODO notes "range used here" range_inst.src - }, - } - } - - // validate for duplicate items/missing else prong - switch (target.ty.zigTypeTag()) { - .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), - .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), - .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), - .Int, .ComptimeInt => { - var range_set = @import("RangeSet.zig").init(mod.gpa); - defer range_set.deinit(); - - for (inst.positionals.items) |item| { - const maybe_src = if (item.castTag(.switch_range)) |range| blk: { - const start_resolved = try resolveInst(mod, scope, range.positionals.lhs); - const start_casted = try mod.coerce(scope, target.ty, start_resolved); - const end_resolved = try resolveInst(mod, scope, range.positionals.rhs); - const end_casted = try mod.coerce(scope, target.ty, end_resolved); - - break :blk try range_set.add( - try mod.resolveConstValue(scope, start_casted), - try mod.resolveConstValue(scope, end_casted), - item.src, - ); - } else blk: { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, target.ty, resolved); - const value = try mod.resolveConstValue(scope, casted); - break :blk try range_set.add(value, value, item.src); - }; - - if (maybe_src) |previous_src| { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - // TODO notes "previous value is here" previous_src - } - } - - if (target.ty.zigTypeTag() == .Int) { - var arena = std.heap.ArenaAllocator.init(mod.gpa); - defer arena.deinit(); - - const start = try target.ty.minInt(&arena, mod.getTarget()); - const end = try target.ty.maxInt(&arena, mod.getTarget()); - if (try range_set.spans(start, end)) { - if (inst.kw_args.special_prong == .@"else") { - return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); - } - return; - } - } - - if (inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); - } - }, - .Bool => { - var true_count: u8 = 0; - var false_count: u8 = 0; - for (inst.positionals.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); - if ((try mod.resolveConstValue(scope, casted)).toBool()) { - true_count += 1; - } else { - false_count += 1; - } - - if (true_count + false_count > 2) { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - } - } - if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); - } - if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { - return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); - } - }, - .EnumLiteral, .Void, .Fn, .Pointer, .Type => { - if (inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); - } - - var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); - defer seen_values.deinit(); - - for (inst.positionals.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, target.ty, resolved); - const val = try mod.resolveConstValue(scope, casted); - - if (try seen_values.fetchPut(val, item.src)) |prev| { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - // TODO notes "previous value here" prev.value - } - } - }, - - .ErrorUnion, - .NoReturn, - .Array, - .Struct, - .Undefined, - .Null, - .Optional, - .BoundFn, - .Opaque, - .Vector, - .Frame, - .AnyFrame, - .ComptimeFloat, - .Float, - => { - return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty}); - }, - } -} - fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 0d5a52980b..78d7eba262 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -962,43 +962,6 @@ pub fn addCases(ctx: *TestContext) !void { , "hello\nhello\nhello\nhello\nhello\n", ); - - // comptime switch - - // Basic for loop - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ assert(foo() == 1); - \\ exit(); - \\} - \\ - \\fn foo() u32 { - \\ const a: comptime_int = 1; - \\ var b: u32 = 0; - \\ switch (a) { - \\ 1 => b = 1, - \\ 2 => b = 2, - \\ else => unreachable, - \\ } - \\ return b; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); } { -- cgit v1.2.3 From 106520329e5adc6cf5ef83595da6c9d5dd3c4b35 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 11:40:34 +0200 Subject: stage2 cbe: implement switchbr --- src/Module.zig | 4 +-- src/codegen/c.zig | 35 ++++++++++++++++++++++++ src/ir.zig | 4 +-- test/stage2/cbe.zig | 78 ++++++++++++++++++++++++++--------------------------- 4 files changed, 78 insertions(+), 43 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index b495afb336..46c3d513f1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2215,7 +2215,7 @@ pub fn addSwitchBr( self: *Module, block: *Scope.Block, src: usize, - target_ptr: *Inst, + target: *Inst, cases: []Inst.SwitchBr.Case, else_body: ir.Body, ) !*Inst { @@ -2226,7 +2226,7 @@ pub fn addSwitchBr( .ty = Type.initTag(.noreturn), .src = src, }, - .target_ptr = target_ptr, + .target = target, .cases = cases, .else_body = else_body, }; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e33f812f0b..7fcbe44205 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -129,6 +129,9 @@ pub const DeclGen = struct { t: Type, val: Value, ) error{ OutOfMemory, AnalysisFail }!void { + if (val.isUndef()) { + return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{}); + } switch (t.zigTypeTag()) { .Int => { if (t.isSignedInt()) @@ -196,6 +199,7 @@ pub const DeclGen = struct { }, } }, + .Bool => return writer.print("{}", .{val.toBool()}), else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -409,6 +413,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .condbr => try genCondBr(o, inst.castTag(.condbr).?), .br => try genBr(o, inst.castTag(.br).?), .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), + .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), + // booland and boolor are non-short-circuit operations + .booland => try genBinOp(o, inst.castTag(.booland).?, " & "), + .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -688,6 +696,33 @@ fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { return CValue.none; } +fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue { + const target = try o.resolveInst(inst.target); + const writer = o.writer(); + + try writer.writeAll("switch ("); + try o.writeCValue(writer, target); + try writer.writeAll(") {\n"); + o.indent_writer.pushIndent(); + + for (inst.cases) |case| { + try writer.writeAll("case "); + try o.dg.renderValue(writer, inst.target.ty, case.item); + try writer.writeAll(": "); + // the case body must be noreturn so we don't need to insert a break + try genBody(o, case.body); + try o.indent_writer.insertNewline(); + } + + try writer.writeAll("default: "); + try genBody(o, inst.else_body); + try o.indent_writer.insertNewline(); + + o.indent_writer.popIndent(); + try writer.writeAll("}\n"); + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; diff --git a/src/ir.zig b/src/ir.zig index 408efc3bba..0e83dbfd56 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -521,7 +521,7 @@ pub const Inst = struct { pub const base_tag = Tag.switchbr; base: Inst, - target_ptr: *Inst, + target: *Inst, cases: []Case, /// Set of instructions whose lifetimes end at the start of one of the cases. /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. @@ -544,7 +544,7 @@ pub const Inst = struct { var i = index; if (i < 1) - return self.target_ptr; + return self.target; i -= 1; return null; diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 8a264f5ca6..aacb2b7077 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -133,45 +133,6 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ , ""); - - // Simple while loop - case.addCompareOutput( - \\export fn main() c_int { - \\ var a: c_int = 0; - \\ while (a < 5) : (a+=1) {} - \\ exit(a - 5); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); - - // If expression - case.addCompareOutput( - \\export fn main() c_int { - \\ var cond: c_int = 0; - \\ var a: c_int = @as(c_int, if (cond == 0) - \\ 2 - \\ else - \\ 3) + 9; - \\ exit(a - 11); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); } { @@ -224,6 +185,45 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("control flow", .{}); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var a: c_int = 0; + \\ while (a < 5) : (a+=1) {} + \\ return a - 5; + \\} + , ""); + + // If expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = @as(c_int, if (cond == 0) + \\ 2 + \\ else + \\ 3) + 9; + \\ return a - 11; + \\} + , ""); + + // Switch expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = switch (cond) { + \\ 1 => 1, + \\ 2 => 2, + \\ 99...300, 12 => 3, + \\ 0 => 4, + \\ else => 5, + \\ }; + \\ return a - 4; + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; -- cgit v1.2.3 From 1480c428065c01c6feff22ce84021c2e0e30aa9b Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 3 Jan 2021 02:20:37 -0700 Subject: require specifier for arrayish types --- lib/std/build.zig | 2 +- lib/std/fmt.zig | 85 ++++++++++++++++++++++++++++---------------- lib/std/testing.zig | 14 ++++---- src/Module.zig | 2 +- src/codegen.zig | 2 +- src/zir_sema.zig | 2 +- test/cli.zig | 8 ++--- test/standalone/cat/main.zig | 2 +- 8 files changed, 71 insertions(+), 46 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/build.zig b/lib/std/build.zig index 0db9d4c24e..77ca854f15 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -739,7 +739,7 @@ pub const Builder = struct { return args.default_target; }, else => |e| { - warn("Unable to parse target '{}': {s}\n\n", .{ triple, @errorName(e) }); + warn("Unable to parse target '{s}': {s}\n\n", .{ triple, @errorName(e) }); self.markInvalidUserInput(); return args.default_target; }, diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 27e68ee9c1..c1b24cc6da 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -69,6 +69,7 @@ pub const FormatOptions = struct { /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. /// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. /// - `*`: output the address of the value instead of the value itself. +/// - `any`: output a value of any type using its default format /// /// If a formatted user type contains a function of the type /// ``` @@ -387,17 +388,32 @@ pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @T return; } }, - .Array => |info| { - try writer.writeAll(@typeName(info.child) ++ "@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); - return; - }, else => {}, } @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); } +// This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 +const ANY = "any"; + +fn defaultSpec(comptime T: type) [:0]const u8 { + switch (@typeInfo(T)) { + .Array => |_| return ANY, + .Pointer => |ptr_info| switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |_| return "*", + else => {}, + }, + .Many, .C => return "*", + .Slice => return ANY, + }, + .Optional => |info| return defaultSpec(info.child), + else => {}, + } + return ""; +} + pub fn formatType( value: anytype, comptime fmt: []const u8, @@ -405,18 +421,19 @@ pub fn formatType( writer: anytype, max_depth: usize, ) @TypeOf(writer).Error!void { - if (comptime std.mem.eql(u8, fmt, "*")) { + const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) defaultSpec(@TypeOf(value)) else fmt; + if (comptime std.mem.eql(u8, actual_fmt, "*")) { return formatAddress(value, options, writer); } const T = @TypeOf(value); if (comptime std.meta.trait.hasFn("format")(T)) { - return try value.format(fmt, options, writer); + return try value.format(actual_fmt, options, writer); } switch (@typeInfo(T)) { .ComptimeInt, .Int, .ComptimeFloat, .Float => { - return formatValue(value, fmt, options, writer); + return formatValue(value, actual_fmt, options, writer); }, .Void => { return formatBuf("void", options, writer); @@ -426,16 +443,16 @@ pub fn formatType( }, .Optional => { if (value) |payload| { - return formatType(payload, fmt, options, writer, max_depth); + return formatType(payload, actual_fmt, options, writer, max_depth); } else { return formatBuf("null", options, writer); } }, .ErrorUnion => { if (value) |payload| { - return formatType(payload, fmt, options, writer, max_depth); + return formatType(payload, actual_fmt, options, writer, max_depth); } else |err| { - return formatType(err, fmt, options, writer, max_depth); + return formatType(err, actual_fmt, options, writer, max_depth); } }, .ErrorSet => { @@ -461,7 +478,7 @@ pub fn formatType( } try writer.writeAll("("); - try formatType(@enumToInt(value), fmt, options, writer, max_depth); + try formatType(@enumToInt(value), actual_fmt, options, writer, max_depth); try writer.writeAll(")"); }, .Union => |info| { @@ -475,7 +492,7 @@ pub fn formatType( try writer.writeAll(" = "); inline for (info.fields) |u_field| { if (value == @field(UnionTagType, u_field.name)) { - try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1); + try formatType(@field(value, u_field.name), ANY, options, writer, max_depth - 1); } } try writer.writeAll(" }"); @@ -497,48 +514,54 @@ pub fn formatType( } try writer.writeAll(f.name); try writer.writeAll(" = "); - try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1); + try formatType(@field(value, f.name), ANY, options, writer, max_depth - 1); } try writer.writeAll(" }"); }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { + if (actual_fmt.len == 0) + @compileError("cannot format array ref without a specifier (i.e. {s} or {*})"); if (info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(value, actual_fmt, options, writer); } } - return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); + @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); }, .Enum, .Union, .Struct => { - return formatType(value.*, fmt, options, writer, max_depth); + return formatType(value.*, actual_fmt, options, writer, max_depth); }, else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }), }, .Many, .C => { + if (actual_fmt.len == 0) + @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); if (ptr_info.sentinel) |sentinel| { - return formatType(mem.span(value), fmt, options, writer, max_depth); + return formatType(mem.span(value), actual_fmt, options, writer, max_depth); } if (ptr_info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(mem.span(value), fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(mem.span(value), actual_fmt, options, writer); } } - return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); + @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); }, .Slice => { + if (actual_fmt.len == 0) + @compileError("cannot format slice without a specifier (i.e. {s} or {any})"); if (max_depth == 0) { return writer.writeAll("{ ... }"); } if (ptr_info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(value, actual_fmt, options, writer); } } try writer.writeAll("{ "); for (value) |elem, i| { - try formatType(elem, fmt, options, writer, max_depth - 1); + try formatType(elem, actual_fmt, options, writer, max_depth - 1); if (i != value.len - 1) { try writer.writeAll(", "); } @@ -547,17 +570,19 @@ pub fn formatType( }, }, .Array => |info| { + if (actual_fmt.len == 0) + @compileError("cannot format array without a specifier (i.e. {s} or {any})"); if (max_depth == 0) { return writer.writeAll("{ ... }"); } if (info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(&value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(&value, actual_fmt, options, writer); } } try writer.writeAll("{ "); for (value) |elem, i| { - try formatType(elem, fmt, options, writer, max_depth - 1); + try formatType(elem, actual_fmt, options, writer, max_depth - 1); if (i < value.len - 1) { try writer.writeAll(", "); } @@ -568,7 +593,7 @@ pub fn formatType( try writer.writeAll("{ "); var i: usize = 0; while (i < info.len) : (i += 1) { - try formatValue(value[i], fmt, options, writer); + try formatValue(value[i], actual_fmt, options, writer); if (i < info.len - 1) { try writer.writeAll(", "); } @@ -1668,7 +1693,7 @@ test "slice" { { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; var runtime_zero: usize = 0; - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 8df05ba7fe..1d89155a58 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -29,7 +29,7 @@ pub var zig_exe_path: []const u8 = undefined; /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void { if (actual_error_union) |actual_payload| { - std.debug.panic("expected error.{s}, found {}", .{ @errorName(expected_error), actual_payload }); + std.debug.panic("expected error.{s}, found {any}", .{ @errorName(expected_error), actual_payload }); } else |actual_error| { if (expected_error != actual_error) { std.debug.panic("expected error.{s}, found error.{s}", .{ @@ -88,7 +88,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { }, .Slice => { if (actual.ptr != expected.ptr) { - std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); + std.debug.panic("expected slice ptr {*}, found {*}", .{ expected.ptr, actual.ptr }); } if (actual.len != expected.len) { std.debug.panic("expected slice len {}, found {}", .{ expected.len, actual.len }); @@ -145,11 +145,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else { - std.debug.panic("expected {}, found null", .{expected_payload}); + std.debug.panic("expected {any}, found null", .{expected_payload}); } } else { if (actual) |actual_payload| { - std.debug.panic("expected null, found {}", .{actual_payload}); + std.debug.panic("expected null, found {any}", .{actual_payload}); } } }, @@ -159,11 +159,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else |actual_err| { - std.debug.panic("expected {}, found {}", .{ expected_payload, actual_err }); + std.debug.panic("expected {any}, found {}", .{ expected_payload, actual_err }); } } else |expected_err| { if (actual) |actual_payload| { - std.debug.panic("expected {}, found {}", .{ expected_err, actual_payload }); + std.debug.panic("expected {}, found {any}", .{ expected_err, actual_payload }); } else |actual_err| { expectEqual(expected_err, actual_err); } @@ -279,7 +279,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const var i: usize = 0; while (i < expected.len) : (i += 1) { if (!std.meta.eql(expected[i], actual[i])) { - std.debug.panic("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] }); + std.debug.panic("index {} incorrect. expected {any}, found {any}", .{ i, expected[i], actual[i] }); } } } diff --git a/src/Module.zig b/src/Module.zig index 8de03b54ab..a90998a386 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2400,7 +2400,7 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn else => unreachable, }; const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]); - return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column }); + return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column }); } fn getNextAnonNameIndex(self: *Module) usize { diff --git a/src/codegen.zig b/src/codegen.zig index 63dbe3268e..9771386403 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2223,7 +2223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); break :blk .ne; }, - else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), + else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), }; const reloc = Reloc{ diff --git a/src/zir_sema.zig b/src/zir_sema.zig index f373d7174d..a8120108a4 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -1832,7 +1832,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; if (!is_int) { - return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); + return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } if (casted_lhs.value()) |lhs_val| { diff --git a/test/cli.zig b/test/cli.zig index 33dbc2d62b..8dbef06887 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -51,9 +51,9 @@ fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { } fn printCmd(cwd: []const u8, argv: []const []const u8) void { - std.debug.warn("cd {} && ", .{cwd}); + std.debug.warn("cd {s} && ", .{cwd}); for (argv) |arg| { - std.debug.warn("{} ", .{arg}); + std.debug.warn("{s} ", .{arg}); } std.debug.warn("\n", .{}); } @@ -75,14 +75,14 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess if ((code != 0) == expect_0) { std.debug.warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", .{result.stderr}); + std.debug.warn("stderr:\n{s}\n", .{result.stderr}); return error.CommandFailed; } }, else => { std.debug.warn("The following command terminated unexpectedly:\n", .{}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", .{result.stderr}); + std.debug.warn("stderr:\n{s}\n", .{result.stderr}); return error.CommandFailed; }, } diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig index 89e5fde3cd..80ec97877a 100644 --- a/test/standalone/cat/main.zig +++ b/test/standalone/cat/main.zig @@ -41,6 +41,6 @@ pub fn main() !void { } fn usage(exe: []const u8) !void { - warn("Usage: {} [FILE]...\n", .{exe}); + warn("Usage: {s} [FILE]...\n", .{exe}); return error.Invalid; } -- cgit v1.2.3 From 7644e9a752ebe50c4372fae8ea0e8eaecb211508 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Mon, 11 Jan 2021 10:30:15 -0700 Subject: stage2: switch from inline fn to callconv(.Inline) --- src/Module.zig | 35 +++++++++++++++++++---------------- src/type.zig | 4 +++- src/zir.zig | 9 +++------ src/zir_sema.zig | 32 +++++++++++++------------------- 4 files changed, 38 insertions(+), 42 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index a90998a386..322f190673 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1087,14 +1087,23 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { if (fn_proto.getSectionExpr()) |sect_expr| { return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); } - if (fn_proto.getCallconvExpr()) |callconv_expr| { - return self.failNode( - &fn_type_scope.base, - callconv_expr, - "TODO implement function calling convention expression", - .{}, - ); - } + + const enum_literal_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.enum_literal_type), + }); + const enum_literal_type_rl: astgen.ResultLoc = .{ .ty = enum_literal_type }; + const cc = if (fn_proto.getCallconvExpr()) |callconv_expr| + try astgen.expr(self, &fn_type_scope.base, enum_literal_type_rl, callconv_expr) + else + try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ + .ty = Type.initTag(.enum_literal), + .val = try Value.Tag.enum_literal.create( + &fn_type_scope_arena.allocator, + try fn_type_scope_arena.allocator.dupe(u8, "Unspecified"), + ), + }); + const return_type_expr = switch (fn_proto.return_type) { .Explicit => |node| node, .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), @@ -1105,6 +1114,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ .return_type = return_type_inst, .param_types = param_types, + .cc = cc, }, .{}); if (std.builtin.mode == .Debug and self.comp.verbose_ir) { @@ -1230,14 +1240,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { }; }; - const is_inline = blk: { - if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { - if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { - break :blk true; - } - } - break :blk false; - }; + const is_inline = fn_type.fnCallingConvention() == .Inline; const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; new_func.* = .{ diff --git a/src/type.zig b/src/type.zig index 837e1ad29a..e1006e554c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -552,7 +552,9 @@ pub const Type = extern union { if (i != 0) try out_stream.writeAll(", "); try param_type.format("", .{}, out_stream); } - try out_stream.writeAll(") "); + try out_stream.writeAll(") callconv(."); + try out_stream.writeAll(@tagName(payload.cc)); + try out_stream.writeAll(")"); ty = payload.return_type; continue; }, diff --git a/src/zir.zig b/src/zir.zig index 30bfeead9b..eefded0c6f 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -863,9 +863,7 @@ pub const Inst = struct { fn_type: *Inst, body: Body, }, - kw_args: struct { - is_inline: bool = false, - }, + kw_args: struct {}, }; pub const FnType = struct { @@ -875,10 +873,9 @@ pub const Inst = struct { positionals: struct { param_types: []*Inst, return_type: *Inst, + cc: *Inst, }, - kw_args: struct { - cc: std.builtin.CallingConvention = .Unspecified, - }, + kw_args: struct {}, }; pub const IntType = struct { diff --git a/src/zir_sema.zig b/src/zir_sema.zig index a8120108a4..480e0b4c33 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -980,18 +980,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { const b = try mod.requireFunctionBlock(scope, inst.base.src); const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; - const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: { - // This logic will get simplified by - // https://github.com/ziglang/zig/issues/6429 - if (try mod.resolveDefinedValue(scope, func)) |func_val| { - const module_fn = switch (func_val.tag()) { - .function => func_val.castTag(.function).?.data, - else => break :blk false, - }; - break :blk module_fn.state == .inline_only; - } - break :blk false; - }; + const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or + func.ty.fnCallingConvention() == .Inline; if (is_inline_call) { const func_val = try mod.resolveConstValue(scope, func); const module_fn = switch (func_val.tag()) { @@ -1075,7 +1065,7 @@ fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); const new_func = try scope.arena().create(Module.Fn); new_func.* = .{ - .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued, + .state = if (fn_type.fnCallingConvention() == .Inline) .inline_only else .queued, .zir = fn_inst.positionals.body, .body = undefined, .owner_decl = scope.ownerDecl().?, @@ -1305,22 +1295,26 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* const tracy = trace(@src()); defer tracy.end(); const return_type = try resolveType(mod, scope, fntype.positionals.return_type); + const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); + const cc_str = cc_tv.val.castTag(.enum_literal).?.data; + const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse + return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); // Hot path for some common function types. if (fntype.positionals.param_types.len == 0) { - if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) { + if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); } - if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) { + if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); } - if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) { + if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); } - if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) { + if (return_type.zigTypeTag() == .Void and cc == .C) { return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); } } @@ -1337,9 +1331,9 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* } const fn_ty = try Type.Tag.function.create(arena, .{ - .cc = fntype.kw_args.cc, - .return_type = return_type, .param_types = param_types, + .return_type = return_type, + .cc = cc, }); return mod.constType(scope, fntype.base.src, fn_ty); } -- cgit v1.2.3