From 0224ad19b82bb307a2c246f2e30826af599aa895 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 16:50:33 -0700 Subject: [PATCH 01/11] AstGen: introduce `try` instruction This introduces two ZIR instructions: * `try` * `try_inline` This is part of an effort to implement #11772. --- src/AstGen.zig | 86 ++++++++++++++++++----------------------------- src/Sema.zig | 71 +++++++++++++++++++++++++++++++++----- src/Zir.zig | 33 ++++++++++++++++++ src/print_zir.zig | 27 +++++++++++---- 4 files changed, 150 insertions(+), 67 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ab5befa4ba40..86ac6633ba49 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2425,6 +2425,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .param_type, .ret_ptr, .ret_type, + .@"try", + .try_inline, => break :b false, .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { @@ -4871,68 +4873,30 @@ fn tryExpr( if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); - var block_scope = parent_gz.makeSubBlock(scope); - block_scope.setBreakResultLoc(rl); - defer block_scope.unstack(); - - const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { + const operand_rl: ResultLoc = switch (rl) { .ref => .ref, else => .none, }; - const err_ops = switch (operand_rl) { - // zig fmt: off - .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, - else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, - // zig fmt: on - }; - // This could be a pointer or value depending on the `operand_rl` parameter. - // We cannot use `block_scope.break_result_loc` because that has the bare - // type, whereas this expression has the optional type. Later we make - // up for this fact by calling rvalue on the else branch. - const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node); - const cond = try block_scope.addUnNode(err_ops[0], operand, node); - const condbr = try block_scope.addCondBr(.condbr, node); + // This could be a pointer or value depending on the `rl` parameter. + const operand = try expr(parent_gz, scope, operand_rl, operand_node); + const is_inline = parent_gz.force_comptime; + const block_tag: Zir.Inst.Tag = if (is_inline) .try_inline else .@"try"; + const try_inst = try parent_gz.makeBlockInst(block_tag, node); + try parent_gz.instructions.append(astgen.gpa, try_inst); - const block = try parent_gz.makeBlockInst(.block, node); - try block_scope.setBlockBody(block); - // block_scope unstacked now, can add new instructions to parent_gz - try parent_gz.instructions.append(astgen.gpa, block); - - var then_scope = parent_gz.makeSubBlock(scope); - defer then_scope.unstack(); - - block_scope.break_count += 1; - // This could be a pointer or value depending on `err_ops[2]`. - const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); - const then_result = switch (rl) { - .ref => unwrapped_payload, - else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), - }; - - // else_scope will be stacked on then_scope as both are stacked on parent_gz var else_scope = parent_gz.makeSubBlock(scope); defer else_scope.unstack(); - const err_code = try else_scope.addUnNode(err_ops[1], operand, node); + const err_tag = switch (rl) { + .ref => Zir.Inst.Tag.err_union_code_ptr, + else => Zir.Inst.Tag.err_union_code, + }; + const err_code = try else_scope.addUnNode(err_tag, operand, node); try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); - const else_result = try else_scope.addUnNode(.ret_node, err_code, node); + _ = try else_scope.addUnNode(.ret_node, err_code, node); - const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; - return finishThenElseBlock( - parent_gz, - rl, - node, - &block_scope, - &then_scope, - &else_scope, - condbr, - cond, - then_result, - else_result, - block, - block, - break_tag, - ); + try else_scope.setTryBody(try_inst, operand); + return indexToRef(try_inst); } fn orelseCatchExpr( @@ -10011,6 +9975,22 @@ const GenZir = struct { gz.unstack(); } + /// Assumes nothing stacked on `gz`. Unstacks `gz`. + fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { + const gpa = gz.astgen.gpa; + const body = gz.instructionsSlice(); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len); + const zir_datas = gz.astgen.instructions.items(.data); + zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( + Zir.Inst.Try{ + .operand = operand, + .body_len = @intCast(u32, body.len), + }, + ); + gz.astgen.extra.appendSliceAssumeCapacity(body); + gz.unstack(); + } + /// Must be called with the following stack set up: /// * gz (bottom) /// * align_gz diff --git a/src/Sema.zig b/src/Sema.zig index 593b299833d8..8331aed409bc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1322,6 +1322,13 @@ fn analyzeBodyInner( break break_data.inst; } }, + .@"try" => blk: { + if (!block.is_comptime) break :blk try sema.zirTry(block, inst); + @panic("TODO"); + }, + .try_inline => { + @panic("TODO"); + }, }; if (sema.typeOf(air_inst).isNoReturn()) break always_noreturn; @@ -6415,32 +6422,43 @@ fn zirErrUnionPayload( const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_src = src; - const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) { + const err_union_ty = sema.typeOf(operand); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ - operand_ty.fmt(sema.mod), + err_union_ty.fmt(sema.mod), }); } + return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check); +} - const result_ty = operand_ty.errorUnionPayload(); - if (try sema.resolveDefinedValue(block, src, operand)) |val| { +fn analyzeErrUnionPayload( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + err_union_ty: Type, + operand: Zir.Inst.Ref, + operand_src: LazySrcLoc, + safety_check: bool, +) CompileError!Air.Inst.Ref { + const payload_ty = err_union_ty.errorUnionPayload(); + if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { if (val.getError()) |name| { return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } const data = val.castTag(.eu_payload).?.data; - return sema.addConstant(result_ty, data); + return sema.addConstant(payload_ty, data); } try sema.requireRuntimeBlock(block, src); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - operand_ty.errorUnionSet().errorSetCardinality() != .zero) + err_union_ty.errorUnionSet().errorSetCardinality() != .zero) { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } - return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); + return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); } /// Pointer in, pointer out. @@ -12958,6 +12976,43 @@ fn zirCondbr( return always_noreturn; } +fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(parent_block, src, operand, operand_src) + else + operand; + const err_union_ty = sema.typeOf(err_union); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ + err_union_ty.fmt(sema.mod), + }); + } + const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); + + if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + if (is_non_err_val.toBool()) { + if (is_ptr) { + return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); + } else { + return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, operand, operand_src, false); + } + } + // We can analyze the body directly in the parent block because we know there are + // no breaks from the body possible, and that the body is noreturn. + return sema.resolveBody(parent_block, body, inst); + } + _ = body; + _ = is_non_err; + @panic("TODO"); +} + // A `break` statement is inside a runtime condition, but trying to // break from an inline loop. In such case we must convert it to // a runtime break. diff --git a/src/Zir.zig b/src/Zir.zig index f09f2015e037..a089c4089eab 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -319,6 +319,19 @@ pub const Inst = struct { /// only the taken branch is analyzed. The then block and else block must /// terminate with an "inline" variant of a noreturn instruction. condbr_inline, + /// Given an operand which is an error union, splits control flow. In + /// case of error, control flow goes into the block that is part of this + /// instruction, which is guaranteed to end with a return instruction + /// and never breaks out of the block. + /// In the case of non-error, control flow proceeds to the next instruction + /// after the `try`, with the result of this instruction being the unwrapped + /// payload value, as if `err_union_payload_unsafe` was executed on the operand. + /// Uses the `pl_node` union field. Payload is `Try`. + @"try", + /// Same as `try` except the operand is coerced to a comptime value, and + /// only the taken branch is analyzed. The block must terminate with an "inline" + /// variant of a noreturn instruction. + try_inline, /// An error set type definition. Contains a list of field names. /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. error_set_decl, @@ -1231,6 +1244,8 @@ pub const Inst = struct { .closure_capture, .ret_ptr, .ret_type, + .@"try", + .try_inline, => false, .@"break", @@ -1509,6 +1524,8 @@ pub const Inst = struct { .repeat, .repeat_inline, .panic, + .@"try", + .try_inline, => false, .extended => switch (data.extended.opcode) { @@ -1569,6 +1586,8 @@ pub const Inst = struct { .coerce_result_ptr = .bin, .condbr = .pl_node, .condbr_inline = .pl_node, + .@"try" = .pl_node, + .try_inline = .pl_node, .error_set_decl = .pl_node, .error_set_decl_anon = .pl_node, .error_set_decl_func = .pl_node, @@ -2803,6 +2822,14 @@ pub const Inst = struct { else_body_len: u32, }; + /// This data is stored inside extra, trailed by: + /// * 0. body: Index // for each `body_len`. + pub const Try = struct { + /// The error union to unwrap. + operand: Ref, + body_len: u32, + }; + /// Stored in extra. Depending on the flags in Data, there will be up to 5 /// trailing Ref fields: /// 0. sentinel: Ref // if `has_sentinel` flag is set @@ -3739,6 +3766,12 @@ fn findDeclsInner( try zir.findDeclsBody(list, then_body); try zir.findDeclsBody(list, else_body); }, + .@"try", .try_inline => { + const inst_data = datas[inst].pl_node; + const extra = zir.extraData(Inst.Try, inst_data.payload_index); + const body = zir.extra[extra.end..][0..extra.data.body_len]; + try zir.findDeclsBody(list, body); + }, .switch_block => return findDeclsSwitch(zir, list, inst), .suspend_block => @panic("TODO iterate suspend block"), diff --git a/src/print_zir.zig b/src/print_zir.zig index 8081a940933b..04289aa95577 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -374,17 +374,21 @@ const Writer = struct { .validate_array_init_comptime, .c_import, .typeof_builtin, - => try self.writePlNodeBlock(stream, inst), + => try self.writeBlock(stream, inst), .condbr, .condbr_inline, - => try self.writePlNodeCondBr(stream, inst), + => try self.writeCondBr(stream, inst), + + .@"try", + .try_inline, + => try self.writeTry(stream, inst), .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), - .switch_block => try self.writePlNodeSwitchBlock(stream, inst), + .switch_block => try self.writeSwitchBlock(stream, inst), .field_ptr, .field_val, @@ -1171,7 +1175,7 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; try self.writePlNodeBlockWithoutSrc(stream, inst); try self.writeSrc(stream, inst_data.src()); @@ -1185,7 +1189,7 @@ const Writer = struct { try stream.writeAll(") "); } - fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; @@ -1199,6 +1203,17 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + try self.writeInstRef(stream, extra.data.operand); + try stream.writeAll(", "); + try self.writeBracedBody(stream, body); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); @@ -1746,7 +1761,7 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); From ef885a78d606693c73641159731274cc57f6ea98 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 18:48:32 -0700 Subject: [PATCH 02/11] stage2: implement the new "try" ZIR/AIR instruction Implements semantic analysis for the new try/try_inline ZIR instruction. Adds the new try/try_ptr AIR instructions and implements them for the LLVM backend. Fixes not calling rvalue() for tryExpr in AstGen. This is part of an effort to implement #11772. --- src/Air.zig | 33 ++++++++++++++++ src/AstGen.zig | 3 +- src/Liveness.zig | 19 +++++++++ src/Sema.zig | 52 ++++++++++++++++++++++-- src/arch/aarch64/CodeGen.zig | 3 ++ src/arch/arm/CodeGen.zig | 3 ++ src/arch/riscv64/CodeGen.zig | 3 ++ src/arch/sparc64/CodeGen.zig | 3 ++ src/arch/wasm/CodeGen.zig | 3 ++ src/arch/x86_64/CodeGen.zig | 3 ++ src/codegen/c.zig | 3 ++ src/codegen/llvm.zig | 77 ++++++++++++++++++++++++++++++++++-- src/print_air.zig | 32 +++++++++++++++ 13 files changed, 229 insertions(+), 8 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 5571fc63597d..efaa7f9b6b0c 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -320,6 +320,20 @@ pub const Inst = struct { /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. switch_br, + /// Given an operand which is an error union, splits control flow. In + /// case of error, control flow goes into the block that is part of this + /// instruction, which is guaranteed to end with a return instruction + /// and never breaks out of the block. + /// In the case of non-error, control flow proceeds to the next instruction + /// after the `try`, with the result of this instruction being the unwrapped + /// payload value, as if `unwrap_errunion_payload` was executed on the operand. + /// Uses the `pl_op` field. Payload is `Try`. + @"try", + /// Same as `try` except the operand is a pointer to an error union, and the + /// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr` + /// was executed on the operand. + /// Uses the `ty_pl` field. Payload is `TryPtr`. + try_ptr, /// A comptime-known value. Uses the `ty_pl` field, payload is index of /// `values` array. constant, @@ -780,6 +794,19 @@ pub const SwitchBr = struct { }; }; +/// This data is stored inside extra. Trailing: +/// 0. body: Inst.Index // for each body_len +pub const Try = struct { + body_len: u32, +}; + +/// This data is stored inside extra. Trailing: +/// 0. body: Inst.Index // for each body_len +pub const TryPtr = struct { + ptr: Inst.Ref, + body_len: u32, +}; + pub const StructField = struct { /// Whether this is a pointer or byval is determined by the AIR tag. struct_operand: Inst.Ref, @@ -1028,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .popcount, .byte_swap, .bit_reverse, + .try_ptr, => return air.getRefType(datas[inst].ty_op.ty), .loop, @@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; return air.typeOf(extra.lhs); }, + + .@"try" => { + const err_union_ty = air.typeOf(datas[inst].pl_op.operand); + return err_union_ty.errorUnionPayload(); + }, } } diff --git a/src/AstGen.zig b/src/AstGen.zig index 86ac6633ba49..7874ed821882 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4896,7 +4896,8 @@ fn tryExpr( _ = try else_scope.addUnNode(.ret_node, err_code, node); try else_scope.setTryBody(try_inst, operand); - return indexToRef(try_inst); + const result = indexToRef(try_inst); + return rvalue(parent_gz, rl, result, node); } fn orelseCatchExpr( diff --git a/src/Liveness.zig b/src/Liveness.zig index b4576c0f1801..ecb755ae0a20 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -478,6 +478,12 @@ pub fn categorizeOperand( .block => { return .complex; }, + .@"try" => { + return .complex; + }, + .try_ptr => { + return .complex; + }, .loop => { return .complex; }, @@ -1019,6 +1025,19 @@ fn analyzeInst( try analyzeWithContext(a, new_set, body); return; // Loop has no operands and it is always unreferenced. }, + .@"try" => { + const pl_op = inst_datas[inst].pl_op; + const extra = a.air.extraData(Air.Try, pl_op.payload); + const body = a.air.extra[extra.end..][0..extra.data.body_len]; + try analyzeWithContext(a, new_set, body); + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none }); + }, + .try_ptr => { + const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload); + const body = a.air.extra[extra.end..][0..extra.data.body_len]; + try analyzeWithContext(a, new_set, body); + return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none }); + }, .cond_br => { // Each death that occurs inside one branch, but not the other, needs // to be added as a death immediately upon entering the other branch. diff --git a/src/Sema.zig b/src/Sema.zig index 8331aed409bc..de4b6c423671 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12983,7 +12983,8 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer; + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; const err_union = if (is_ptr) try sema.analyzeLoad(parent_block, src, operand, operand_src) else @@ -13008,9 +13009,52 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! // no breaks from the body possible, and that the body is noreturn. return sema.resolveBody(parent_block, body, inst); } - _ = body; - _ = is_non_err; - @panic("TODO"); + + var sub_block = parent_block.makeSubBlock(); + defer sub_block.instructions.deinit(sema.gpa); + + // This body is guaranteed to end with noreturn and has no breaks. + _ = try sema.analyzeBodyInner(&sub_block, body); + + if (is_ptr) { + const ptr_info = operand_ty.ptrInfo().data; + const res_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = err_union_ty.errorUnionPayload(), + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + }); + const res_ty_ref = try sema.addType(res_ty); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .try_ptr, + .data = .{ .ty_pl = .{ + .ty = res_ty_ref, + .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ + .ptr = operand, + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; + } + + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .@"try", + .data = .{ .pl_op = .{ + .operand = operand, + .payload = sema.addExtraAssumeCapacity(Air.Try{ + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; } // A `break` statement is inside a runtime condition, but trying to diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index c85c280fddcd..4574469920e9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index a0f9c12c345a..16e4b6e07b51 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c94b4de378df..5d6d50fd09cd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index eb9d9a4ad9c2..780f01cdd447 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => @panic("TODO try self.airPrefetch(inst)"), .mul_add => @panic("TODO try self.airMulAdd(inst)"), + .@"try" => @panic("TODO try self.airTry(inst)"), + .try_ptr => @panic("TODO try self.airTryPtr(inst)"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 944ef162940b..d889babb58a2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .int_to_float => self.airIntToFloat(inst), .get_union_tag => self.airGetUnionTag(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + // TODO .dbg_inline_begin, .dbg_inline_end, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 33734eda308a..7d2babed3e50 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1e4509064817..597b3b0f6ba9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try airDbgVar(f, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aab2dfe51ad2..9e9dd2cb0c2d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4040,6 +4040,8 @@ pub const FuncGen = struct { .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), .cond_br => try self.airCondBr(inst), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .intcast => try self.airIntCast(inst), .trunc => try self.airTrunc(inst), .fptrunc => try self.airFptrunc(inst), @@ -4731,6 +4733,75 @@ pub const FuncGen = struct { return null; } + fn airTry(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const err_union = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + const result_ty = self.air.typeOfIndex(inst); + return lowerTry(self, err_union, body, err_union_ty, false, result_ty); + } + + fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + const result_ty = self.air.typeOfIndex(inst); + return lowerTry(self, err_union_ptr, body, err_union_ty, true, result_ty); + } + + fn lowerTry(fg: *FuncGen, err_union: *const llvm.Value, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, result_ty: Type) !?*const llvm.Value { + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. + return err_union; + } + + const payload_ty = err_union_ty.errorUnionPayload(); + const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); + const target = fg.dg.module.getTarget(); + const is_err = err: { + const err_set_ty = try fg.dg.lowerType(Type.anyerror); + const zero = err_set_ty.constNull(); + if (!payload_has_bits) { + const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union; + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const err_field_index = errUnionErrorOffset(payload_ty, target); + if (operand_is_ptr or isByRef(err_union_ty)) { + const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, ""); + const loaded = fg.builder.buildLoad(err_field_ptr, ""); + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const loaded = fg.builder.buildExtractValue(err_union, err_field_index, ""); + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + }; + + const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet"); + const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont"); + _ = fg.builder.buildCondBr(is_err, return_block, continue_block); + + fg.builder.positionBuilderAtEnd(return_block); + try fg.genBody(body); + + fg.builder.positionBuilderAtEnd(continue_block); + if (!payload_has_bits) { + if (!operand_is_ptr) return null; + + // TODO once we update to LLVM 14 this bitcast won't be necessary. + const res_ptr_ty = try fg.dg.lowerType(result_ty); + return fg.builder.buildBitCast(err_union, res_ptr_ty, ""); + } + const offset = errUnionPayloadOffset(payload_ty, target); + if (operand_is_ptr or isByRef(payload_ty)) { + return fg.builder.buildStructGEP(err_union, offset, ""); + } + return fg.builder.buildExtractValue(err_union, offset, ""); + } + fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const cond = try self.resolveInst(pl_op.operand); @@ -5673,15 +5744,14 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - // If the error set has no fields, then the payload and the error - // union are the same value. if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. return operand; } const result_ty = self.air.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; const target = self.dg.module.getTarget(); - const offset = errUnionPayloadOffset(payload_ty, target); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { if (!operand_is_ptr) return null; @@ -5690,6 +5760,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.lowerType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } + const offset = errUnionPayloadOffset(payload_ty, target); if (operand_is_ptr or isByRef(payload_ty)) { return self.builder.buildStructGEP(operand, offset, ""); } diff --git a/src/print_air.zig b/src/print_air.zig index e62ca806b717..af1bcb8cfbfa 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -258,6 +258,8 @@ const Writer = struct { .union_init => try w.writeUnionInit(s, inst), .br => try w.writeBr(s, inst), .cond_br => try w.writeCondBr(s, inst), + .@"try" => try w.writeTry(s, inst), + .try_ptr => try w.writeTryPtr(s, inst), .switch_br => try w.writeSwitchBr(s, inst), .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), .fence => try w.writeFence(s, inst), @@ -624,6 +626,36 @@ const Writer = struct { try w.writeOperand(s, inst, 0, br.operand); } + fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[inst].pl_op; + const extra = w.air.extraData(Air.Try, pl_op.payload); + const body = w.air.extra[extra.end..][0..extra.data.body_len]; + + try w.writeOperand(s, inst, 0, pl_op.operand); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + + fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.TryPtr, ty_pl.payload); + const body = w.air.extra[extra.end..][0..extra.data.body_len]; + + try w.writeOperand(s, inst, 0, extra.data.ptr); + try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.CondBr, pl_op.payload); From 00720c52f6333c314aee68de31ef505b2665e44e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:05:53 -0700 Subject: [PATCH 03/11] Sema: implement try_inline --- src/Sema.zig | 62 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index de4b6c423671..d2186b75db68 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1324,10 +1324,66 @@ fn analyzeBodyInner( }, .@"try" => blk: { if (!block.is_comptime) break :blk try sema.zirTry(block, inst); - @panic("TODO"); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(block, src, operand, operand_src) + else + operand; + const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + if (is_non_err_tv.val.toBool()) { + if (is_ptr) { + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + } else { + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + } + } + const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + break always_noreturn; + if (inst == break_data.block_inst) { + break :blk try sema.resolveInst(break_data.operand); + } else { + break break_data.inst; + } }, - .try_inline => { - @panic("TODO"); + .try_inline => blk: { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(block, src, operand, operand_src) + else + operand; + const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + if (is_non_err_tv.val.toBool()) { + if (is_ptr) { + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + } else { + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + } + } + const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + break always_noreturn; + if (inst == break_data.block_inst) { + break :blk try sema.resolveInst(break_data.operand); + } else { + break break_data.inst; + } }, }; if (sema.typeOf(air_inst).isNoReturn()) From 6d3586e0edf3278862a3a969debaa842014f8110 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:40:18 -0700 Subject: [PATCH 04/11] explicit "_ptr" variants of ZIR try instruction * Introduce "_ptr" variants of ZIR try instruction to disallow constructs such as `try` on a pointer value instead of an error union value. * Disable the "_inline" variants of the ZIR try instruction for now because we are out of ZIR tags. I will free up some space in an independent commit. * AstGen: fix tryExpr calling rvalue() on ResultLoc.ref --- src/Air.zig | 2 +- src/AstGen.zig | 20 ++++++++-- src/Sema.zig | 97 ++++++++++++++++++++++++++++++++--------------- src/Zir.zig | 26 +++++++++---- src/print_zir.zig | 2 +- 5 files changed, 104 insertions(+), 43 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index efaa7f9b6b0c..53421b647534 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1018,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .shl_with_overflow, .ptr_add, .ptr_sub, + .try_ptr, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -1055,7 +1056,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .popcount, .byte_swap, .bit_reverse, - .try_ptr, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/AstGen.zig b/src/AstGen.zig index 7874ed821882..adb1223b7177 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2426,7 +2426,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .ret_ptr, .ret_type, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => break :b false, .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { @@ -4880,7 +4882,16 @@ fn tryExpr( // This could be a pointer or value depending on the `rl` parameter. const operand = try expr(parent_gz, scope, operand_rl, operand_node); const is_inline = parent_gz.force_comptime; - const block_tag: Zir.Inst.Tag = if (is_inline) .try_inline else .@"try"; + const is_inline_bit = @as(u2, @boolToInt(is_inline)); + const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1; + const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) { + 0b00 => .@"try", + 0b01 => .@"try", + //0b01 => .try_inline, + 0b10 => .try_ptr, + 0b11 => .try_ptr, + //0b11 => .try_ptr_inline, + }; const try_inst = try parent_gz.makeBlockInst(block_tag, node); try parent_gz.instructions.append(astgen.gpa, try_inst); @@ -4897,7 +4908,10 @@ fn tryExpr( try else_scope.setTryBody(try_inst, operand); const result = indexToRef(try_inst); - return rvalue(parent_gz, rl, result, node); + switch (rl) { + .ref => return result, + else => return rvalue(parent_gz, rl, result, node), + } } fn orelseCatchExpr( diff --git a/src/Sema.zig b/src/Sema.zig index d2186b75db68..12ff5c5dbc6e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1323,28 +1323,18 @@ fn analyzeBodyInner( } }, .@"try" => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst); + if (!block.is_comptime) break :blk try sema.zirTry(block, inst, false); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; - const err_union = if (is_ptr) - try sema.analyzeLoad(block, src, operand, operand_src) - else - operand; + const err_union = try sema.resolveInst(extra.data.operand); const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { - if (is_ptr) { - break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); - } else { - const err_union_ty = sema.typeOf(err_union); - break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); - } + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1354,28 +1344,50 @@ fn analyzeBodyInner( break break_data.inst; } }, - .try_inline => blk: { + //.try_inline => blk: { + // const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + // const src = inst_data.src(); + // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + // const operand = try sema.resolveInst(extra.data.operand); + // const operand_ty = sema.typeOf(operand); + // const is_ptr = operand_ty.zigTypeTag() == .Pointer; + // const err_union = if (is_ptr) + // try sema.analyzeLoad(block, src, operand, operand_src) + // else + // operand; + // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + // if (is_non_err_tv.val.toBool()) { + // if (is_ptr) { + // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + // } else { + // const err_union_ty = sema.typeOf(err_union); + // break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + // } + // } + // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + // break always_noreturn; + // if (inst == break_data.block_inst) { + // break :blk try sema.resolveInst(break_data.operand); + // } else { + // break break_data.inst; + // } + //}, + .try_ptr => blk: { + if (!block.is_comptime) break :blk try sema.zirTry(block, inst, true); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; - const err_union = if (is_ptr) - try sema.analyzeLoad(block, src, operand, operand_src) - else - operand; + const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { - if (is_ptr) { - break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); - } else { - const err_union_ty = sema.typeOf(err_union); - break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); - } + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1385,6 +1397,27 @@ fn analyzeBodyInner( break break_data.inst; } }, + //.try_ptr_inline => blk: { + // const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + // const src = inst_data.src(); + // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + // const operand = try sema.resolveInst(extra.data.operand); + // const err_union = try sema.analyzeLoad(block, src, operand, operand_src); + // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + // if (is_non_err_tv.val.toBool()) { + // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + // } + // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + // break always_noreturn; + // if (inst == break_data.block_inst) { + // break :blk try sema.resolveInst(break_data.operand); + // } else { + // break break_data.inst; + // } + //}, }; if (sema.typeOf(air_inst).isNoReturn()) break always_noreturn; @@ -13032,15 +13065,18 @@ fn zirCondbr( return always_noreturn; } -fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { +fn zirTry( + sema: *Sema, + parent_block: *Block, + inst: Zir.Inst.Index, + is_ptr: bool, +) CompileError!Zir.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; const err_union = if (is_ptr) try sema.analyzeLoad(parent_block, src, operand, operand_src) else @@ -13073,6 +13109,7 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! _ = try sema.analyzeBodyInner(&sub_block, body); if (is_ptr) { + const operand_ty = sema.typeOf(operand); const ptr_info = operand_ty.ptrInfo().data; const res_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = err_union_ty.errorUnionPayload(), diff --git a/src/Zir.zig b/src/Zir.zig index a089c4089eab..02f9a9715516 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -328,10 +328,14 @@ pub const Inst = struct { /// payload value, as if `err_union_payload_unsafe` was executed on the operand. /// Uses the `pl_node` union field. Payload is `Try`. @"try", - /// Same as `try` except the operand is coerced to a comptime value, and - /// only the taken branch is analyzed. The block must terminate with an "inline" - /// variant of a noreturn instruction. - try_inline, + ///// Same as `try` except the operand is coerced to a comptime value, and + ///// only the taken branch is analyzed. The block must terminate with an "inline" + ///// variant of a noreturn instruction. + //try_inline, + /// Same as `try` except the operand is a pointer and the result is a pointer. + try_ptr, + ///// Same as `try_inline` except the operand is a pointer and the result is a pointer. + //try_ptr_inline, /// An error set type definition. Contains a list of field names. /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. error_set_decl, @@ -1245,7 +1249,9 @@ pub const Inst = struct { .ret_ptr, .ret_type, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => false, .@"break", @@ -1525,7 +1531,9 @@ pub const Inst = struct { .repeat_inline, .panic, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => false, .extended => switch (data.extended.opcode) { @@ -1587,7 +1595,9 @@ pub const Inst = struct { .condbr = .pl_node, .condbr_inline = .pl_node, .@"try" = .pl_node, - .try_inline = .pl_node, + .try_ptr = .pl_node, + //.try_inline = .pl_node, + //.try_ptr_inline = .pl_node, .error_set_decl = .pl_node, .error_set_decl_anon = .pl_node, .error_set_decl_func = .pl_node, @@ -3766,7 +3776,7 @@ fn findDeclsInner( try zir.findDeclsBody(list, then_body); try zir.findDeclsBody(list, else_body); }, - .@"try", .try_inline => { + .@"try", .try_ptr => { const inst_data = datas[inst].pl_node; const extra = zir.extraData(Inst.Try, inst_data.payload_index); const body = zir.extra[extra.end..][0..extra.data.body_len]; diff --git a/src/print_zir.zig b/src/print_zir.zig index 04289aa95577..3257a3cb58bd 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -381,7 +381,7 @@ const Writer = struct { => try self.writeCondBr(stream, inst), .@"try", - .try_inline, + .try_ptr, => try self.writeTry(stream, inst), .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), From f4ac37f55da7258c25194e66e1cfe3270ca9d419 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:49:34 -0700 Subject: [PATCH 05/11] Sema: extract out zirTryPtr from zirTry This function took is_ptr: bool and then branched on it three times. Now, instead, each implementation does no branching and the logic is easier to follow, both for maintainers and compilers. I also fixed a bug with TryPtr not ensuring enough capacity in the extra array. --- src/Sema.zig | 109 +++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 12ff5c5dbc6e..6d618fee2c87 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1323,7 +1323,7 @@ fn analyzeBodyInner( } }, .@"try" => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst, false); + if (!block.is_comptime) break :blk try sema.zirTry(block, inst); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -1376,7 +1376,7 @@ fn analyzeBodyInner( // } //}, .try_ptr => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst, true); + if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -13065,22 +13065,13 @@ fn zirCondbr( return always_noreturn; } -fn zirTry( - sema: *Sema, - parent_block: *Block, - inst: Zir.Inst.Index, - is_ptr: bool, -) CompileError!Zir.Inst.Ref { +fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const operand = try sema.resolveInst(extra.data.operand); - const err_union = if (is_ptr) - try sema.analyzeLoad(parent_block, src, operand, operand_src) - else - operand; + const err_union = try sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); if (err_union_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ @@ -13091,11 +13082,7 @@ fn zirTry( if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { if (is_non_err_val.toBool()) { - if (is_ptr) { - return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); - } else { - return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, operand, operand_src, false); - } + return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); } // We can analyze the body directly in the parent block because we know there are // no breaks from the body possible, and that the body is noreturn. @@ -13108,39 +13095,12 @@ fn zirTry( // This body is guaranteed to end with noreturn and has no breaks. _ = try sema.analyzeBodyInner(&sub_block, body); - if (is_ptr) { - const operand_ty = sema.typeOf(operand); - const ptr_info = operand_ty.ptrInfo().data; - const res_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = err_union_ty.errorUnionPayload(), - .@"addrspace" = ptr_info.@"addrspace", - .mutable = ptr_info.mutable, - .@"allowzero" = ptr_info.@"allowzero", - .@"volatile" = ptr_info.@"volatile", - }); - const res_ty_ref = try sema.addType(res_ty); - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + - sub_block.instructions.items.len); - const try_inst = try parent_block.addInst(.{ - .tag = .try_ptr, - .data = .{ .ty_pl = .{ - .ty = res_ty_ref, - .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ - .ptr = operand, - .body_len = @intCast(u32, sub_block.instructions.items.len), - }), - } }, - }); - sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); - return try_inst; - } - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + sub_block.instructions.items.len); const try_inst = try parent_block.addInst(.{ .tag = .@"try", .data = .{ .pl_op = .{ - .operand = operand, + .operand = err_union, .payload = sema.addExtraAssumeCapacity(Air.Try{ .body_len = @intCast(u32, sub_block.instructions.items.len), }), @@ -13150,6 +13110,63 @@ fn zirTry( return try_inst; } +fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); + const err_union_ty = sema.typeOf(err_union); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ + err_union_ty.fmt(sema.mod), + }); + } + const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); + + if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + if (is_non_err_val.toBool()) { + return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); + } + // We can analyze the body directly in the parent block because we know there are + // no breaks from the body possible, and that the body is noreturn. + return sema.resolveBody(parent_block, body, inst); + } + + var sub_block = parent_block.makeSubBlock(); + defer sub_block.instructions.deinit(sema.gpa); + + // This body is guaranteed to end with noreturn and has no breaks. + _ = try sema.analyzeBodyInner(&sub_block, body); + + const operand_ty = sema.typeOf(operand); + const ptr_info = operand_ty.ptrInfo().data; + const res_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = err_union_ty.errorUnionPayload(), + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + }); + const res_ty_ref = try sema.addType(res_ty); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .try_ptr, + .data = .{ .ty_pl = .{ + .ty = res_ty_ref, + .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ + .ptr = operand, + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; +} + // A `break` statement is inside a runtime condition, but trying to // break from an inline loop. In such case we must convert it to // a runtime break. From 779770cff58923e5113b5419accf910059fddea7 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 3 Jun 2022 20:46:07 +0200 Subject: [PATCH 06/11] wasm: Implement `try` instruction --- src/arch/wasm/CodeGen.zig | 69 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d889babb58a2..039d62b05ca6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1490,8 +1490,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .int_to_float => self.airIntToFloat(inst), .get_union_tag => self.airGetUnionTag(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => self.airTry(inst), + .try_ptr => self.airTryPtr(inst), // TODO .dbg_inline_begin, @@ -4626,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { } }); return WValue{ .none = {} }; } + +fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const err_union = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + return lowerTry(self, err_union, body, err_union_ty, false); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + return lowerTry(self, err_union_ptr, body, err_union_ty, true); +} + +fn lowerTry( + self: *Self, + err_union: WValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, +) InnerError!WValue { + if (operand_is_ptr) { + return self.fail("TODO: lowerTry for pointers", .{}); + } + + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + return err_union; + } + + const pl_ty = err_union_ty.errorUnionPayload(); + const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(); + + // Block we can jump out of when error is not set + try self.startBlock(.block, wasm.block_empty); + + // check if the error tag is set for the error union. + try self.emitWValue(err_union); + if (pl_has_bits) { + const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target)); + try self.addMemArg(.i32_load16_u, .{ + .offset = err_union.offset() + err_offset, + .alignment = Type.anyerror.abiAlignment(self.target), + }); + } + try self.addTag(.i32_eqz); + try self.addLabel(.br_if, 0); // jump out of block when error is '0' + try self.genBody(body); + try self.endBlock(); + + // if we reach here it means error was not set, and we want the payload + if (!pl_has_bits) { + return WValue{ .none = {} }; + } + + const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)); + if (isByRef(pl_ty, self.target)) { + return buildPointerOffset(self, err_union, pl_offset, .new); + } + return self.load(err_union, pl_ty, pl_offset); +} From d2f31d315e47c3918cd711d1008862df49df38f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Jun 2022 16:10:41 -0700 Subject: [PATCH 07/11] C backend: implement `try` instruction --- src/codegen/c.zig | 94 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 597b3b0f6ba9..4c2239b3062d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1875,8 +1875,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try airTry(f, inst), + .try_ptr => try airTryPtr(f, inst), .dbg_var_ptr, .dbg_var_val, @@ -2864,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { return result; } +fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const err_union = try f.resolveInst(pl_op.operand); + const extra = f.air.extraData(Air.Try, pl_op.payload); + const body = f.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = f.air.typeOf(pl_op.operand); + const result_ty = f.air.typeOfIndex(inst); + return lowerTry(f, err_union, body, err_union_ty, false, result_ty); +} + +fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try f.resolveInst(extra.data.ptr); + const body = f.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = f.air.typeOf(extra.data.ptr).childType(); + const result_ty = f.air.typeOfIndex(inst); + return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty); +} + +fn lowerTry( + f: *Function, + err_union: CValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, + result_ty: Type, +) !CValue { + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. + return err_union; + } + + const payload_ty = err_union_ty.errorUnionPayload(); + const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); + + const writer = f.object.writer(); + + err: { + if (!payload_has_bits) { + if (operand_is_ptr) { + try writer.writeAll("if(*"); + } else { + try writer.writeAll("if("); + } + try f.writeCValue(writer, err_union); + try writer.writeAll(")"); + break :err; + } + if (operand_is_ptr or isByRef(err_union_ty)) { + try writer.writeAll("if("); + try f.writeCValue(writer, err_union); + try writer.writeAll("->error)"); + break :err; + } + try writer.writeAll("if("); + try f.writeCValue(writer, err_union); + try writer.writeAll(".error)"); + } + + try genBody(f, body); + try f.object.indent_writer.insertNewline(); + + if (!payload_has_bits) { + if (!operand_is_ptr) { + return CValue.none; + } else { + return err_union; + } + } + + const local = try f.allocLocal(result_ty, .Const); + if (operand_is_ptr or isByRef(payload_ty)) { + try writer.writeAll(" = &"); + try f.writeCValue(writer, err_union); + try writer.writeAll("->payload;\n"); + } else { + try writer.writeAll(" = "); + try f.writeCValue(writer, err_union); + try writer.writeAll(".payload;\n"); + } + return local; +} + fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { const branch = f.air.instructions.items(.data)[inst].br; const block = f.blocks.get(branch.block_inst).?; @@ -4224,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool { } return false; } + +fn isByRef(ty: Type) bool { + _ = ty; + return false; +} From 6da420419d79a735e88416056587c154b3cb3acd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Jun 2022 17:55:10 -0700 Subject: [PATCH 08/11] Sema: avoid emitting unused is_non_err AIR instruction --- src/Sema.zig | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 6d618fee2c87..78d025d54894 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1330,7 +1330,8 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const err_union = try sema.resolveInst(extra.data.operand); - const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + assert(is_non_err != .none); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { const err_union_ty = sema.typeOf(err_union); @@ -1357,7 +1358,8 @@ fn analyzeBodyInner( // try sema.analyzeLoad(block, src, operand, operand_src) // else // operand; - // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + // assert(is_non_err != .none); // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); // if (is_non_err_tv.val.toBool()) { // if (is_ptr) { @@ -1384,7 +1386,8 @@ fn analyzeBodyInner( const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); const err_union = try sema.analyzeLoad(block, src, operand, operand_src); - const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + assert(is_non_err != .none); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); @@ -1405,7 +1408,8 @@ fn analyzeBodyInner( // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; // const operand = try sema.resolveInst(extra.data.operand); // const err_union = try sema.analyzeLoad(block, src, operand, operand_src); - // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + // assert(is_non_err != .none); // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); // if (is_non_err_tv.val.toBool()) { // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); @@ -13078,9 +13082,9 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! err_union_ty.fmt(sema.mod), }); } - const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); - - if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); + if (is_non_err != .none) { + const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; if (is_non_err_val.toBool()) { return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); } @@ -13124,9 +13128,9 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr err_union_ty.fmt(sema.mod), }); } - const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); - - if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); + if (is_non_err != .none) { + const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; if (is_non_err_val.toBool()) { return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); } @@ -21795,7 +21799,7 @@ fn analyzeIsNull( return block.addUnOp(air_tag, operand); } -fn analyzeIsNonErr( +fn analyzeIsNonErrComptimeOnly( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -21847,8 +21851,22 @@ fn analyzeIsNonErr( return Air.Inst.Ref.bool_false; } } - try sema.requireRuntimeBlock(block, src); - return block.addUnOp(.is_non_err, operand); + return Air.Inst.Ref.none; +} + +fn analyzeIsNonErr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); + if (result == .none) { + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(.is_non_err, operand); + } else { + return result; + } } fn analyzeSlice( From ff00bbf4de0c6a91a1e98076ceb68d4cec127aee Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Jun 2022 19:54:00 +0200 Subject: [PATCH 09/11] x64: lower try and try_ptr --- src/arch/x86_64/CodeGen.zig | 81 +++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7d2babed3e50..e3ca7c318419 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -681,8 +681,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -1807,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); } const err_union_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn genUnwrapErrorUnionPayloadMir( + self: *Self, + maybe_inst: ?Air.Inst.Index, + err_union_ty: Type, + err_union: MCValue, +) !MCValue { const payload_ty = err_union_ty.errorUnionPayload(); const err_ty = err_union_ty.errorUnionSet(); - const operand = try self.resolveInst(ty_op.operand); const result: MCValue = result: { if (err_ty.errorSetCardinality() == .zero) { // TODO check if we can reuse - break :result operand; + break :result err_union; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -1822,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { } const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); - switch (operand) { + switch (err_union) { .stack_offset => |off| { const offset = off - @intCast(i32, payload_off); break :result MCValue{ .stack_offset = offset }; @@ -1831,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse operand const lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(lock); - const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); + const result_reg: Register = if (maybe_inst) |inst| + (try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register + else + try self.copyToTmpRegister(err_union_ty, err_union); if (payload_off > 0) { const shift = @intCast(u6, payload_off * 8); - try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift }); + try self.genShiftBinOpMir(.shr, err_union_ty, result_reg, .{ .immediate = shift }); } else { - try self.truncateRegister(payload_ty, result.register); + try self.truncateRegister(payload_ty, result_reg); } - break :result result; + break :result MCValue{ .register = result_reg }; }, - else => return self.fail("TODO implement unwrap_err_payload for {}", .{operand}), + else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}), } }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + + return result; } // *(E!T) -> E @@ -4231,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + const err_union = try self.resolveInst(pl_op.operand); + const result = try self.genTry(inst, err_union, body, err_union_ty, false); + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true); + return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); +} + +fn genTry( + self: *Self, + inst: Air.Inst.Index, + err_union: MCValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, +) !MCValue { + if (operand_is_ptr) { + return self.fail("TODO genTry for pointers", .{}); + } + const is_err_mcv = try self.isErr(null, err_union_ty, err_union); + const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv); + try self.genBody(body); + try self.performReloc(reloc); + const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union); + return result; +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; const payload = try self.addExtra(Mir.DbgLineColumn{ @@ -4596,7 +4649,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV return MCValue{ .eflags = is_null_res.eflags.negate() }; } -fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { +fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const err_type = ty.errorUnionSet(); if (err_type.errorSetCardinality() == .zero) { @@ -4604,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue } try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; + if (maybe_inst) |inst| { + self.eflags_inst = inst; + } const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); switch (operand) { From a34f3ff04a9a6d1c3bf7b818a8de28c453cc53d6 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 6 Jun 2022 12:34:19 +0200 Subject: [PATCH 10/11] stage2 ARM: implement `try` AIR instruction --- src/arch/arm/CodeGen.zig | 114 ++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 16e4b6e07b51..01f3f1ff6ac6 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -677,8 +677,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -1837,8 +1837,8 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const mcv = try self.resolveInst(ty_op.operand); - break :result try self.errUnionPayload(mcv, error_union_ty); + const error_union = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(error_union, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3705,6 +3705,42 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ operand, .none, .none }); } +/// Given a boolean condition, emit a jump that is taken when that +/// condition is false. +fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { + const condition_code: Condition = switch (condition) { + .cpsr_flags => |cond| cond.negate(), + else => blk: { + const reg = switch (condition) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.bool, condition), + }; + + try self.spillCompareFlagsIfOccupied(); + + // cmp reg, 1 + // bne ... + _ = try self.addInst(.{ + .tag = .cmp, + .cond = .al, + .data = .{ .rr_op = .{ + .rd = .r0, + .rn = reg, + .op = Instruction.Operand.imm(1, 0), + } }, + }); + + break :blk .ne; + }, + }; + + return try self.addInst(.{ + .tag = .b, + .cond = condition_code, + .data = .{ .inst = undefined }, // populated later through performReloc + }); +} + fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const cond_inst = try self.resolveInst(pl_op.operand); @@ -3713,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; const liveness_condbr = self.liveness.getCondBr(inst); - const reloc: Mir.Inst.Index = reloc: { - const condition: Condition = switch (cond_inst) { - .cpsr_flags => |cond| cond.negate(), - else => blk: { - const reg = switch (cond_inst) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond_inst), - }; - - try self.spillCompareFlagsIfOccupied(); - - // cmp reg, 1 - // bne ... - _ = try self.addInst(.{ - .tag = .cmp, - .cond = .al, - .data = .{ .rr_op = .{ - .rd = .r0, - .rn = reg, - .op = Instruction.Operand.imm(1, 0), - } }, - }); - - break :blk .ne; - }, - }; - - break :reloc try self.addInst(.{ - .tag = .b, - .cond = condition, - .data = .{ .inst = undefined }, // populated later through performReloc - }); - }; + const reloc: Mir.Inst.Index = try self.condBr(cond_inst); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -4157,13 +4161,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { .lhs = condition, .rhs = item, } }; - const cmp_result = try self.cmp(operands, condition_ty, .neq); - - relocs[0] = try self.addInst(.{ - .tag = .b, - .cond = cmp_result.cpsr_flags, - .data = .{ .inst = undefined }, // populated later through performReloc - }); + const cmp_result = try self.cmp(operands, condition_ty, .eq); + relocs[0] = try self.condBr(cmp_result); } else { return self.fail("TODO switch with multiple items", .{}); } @@ -5148,6 +5147,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const result: MCValue = result: { + const error_union_ty = self.air.typeOf(pl_op.operand); + const error_union = try self.resolveInst(pl_op.operand); + const is_err_result = try self.isErr(error_union_ty, error_union); + const reloc = try self.condBr(is_err_result); + + try self.genBody(body); + + try self.performReloc(reloc); + break :result try self.errUnionPayload(error_union, error_union_ty); + }; + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + _ = body; + return self.fail("TODO implement airTryPtr for arm", .{}); + // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); From 8ca6dc33d1523061f198474c84a68330085d6667 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 6 Jun 2022 21:27:36 +0200 Subject: [PATCH 11/11] stage2 AArch64: implement `try` AIR instruction --- src/arch/aarch64/CodeGen.zig | 134 +++++++++++++++++++++++++++-------- 1 file changed, 104 insertions(+), 30 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4574469920e9..2875bda7e979 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -665,8 +665,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -2308,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Given an error union, returns the error +fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return MCValue{ .immediate = 0 }; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return error_union_mcv; + } + + const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionErr for registers", .{}), + .stack_offset => |off| { + return MCValue{ .stack_offset = off - err_offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr + err_offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); const mcv = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) break :result mcv; - - return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + break :result try self.errUnionErr(mcv, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Given an error union, returns the payload +fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return error_union_mcv; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return MCValue.none; + } + + const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionPayload for registers", .{}), + .stack_offset => |off| { + return MCValue{ .stack_offset = off - payload_offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr + payload_offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; - - return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + const error_union = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(error_union, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3389,45 +3432,38 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ operand, .none, .none }); } -fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; - const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const liveness_condbr = self.liveness.getCondBr(inst); - - const reloc: Mir.Inst.Index = switch (cond) { +fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { + switch (condition) { .compare_flags_signed, .compare_flags_unsigned, - => try self.addInst(.{ + => return try self.addInst(.{ .tag = .b_cond, .data = .{ .inst_cond = .{ .inst = undefined, // populated later through performReloc - .cond = switch (cond) { + .cond = switch (condition) { .compare_flags_signed => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op); - break :blk condition.negate(); + const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op); + break :blk condition_code.negate(); }, .compare_flags_unsigned => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); - break :blk condition.negate(); + const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); + break :blk condition_code.negate(); }, else => unreachable, }, }, }, }), - else => blk: { - const reg = switch (cond) { + else => { + const reg = switch (condition) { .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond), + else => try self.copyToTmpRegister(Type.bool, condition), }; - break :blk try self.addInst(.{ + return try self.addInst(.{ .tag = .cbz, .data = .{ .r_inst = .{ @@ -3437,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { }, }); }, - }; + } +} + +fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const cond = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.CondBr, pl_op.payload); + const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + const liveness_condbr = self.liveness.getCondBr(inst); + + const reloc = try self.condBr(cond); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -4469,6 +4516,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const result: MCValue = result: { + const error_union_ty = self.air.typeOf(pl_op.operand); + const error_union = try self.resolveInst(pl_op.operand); + const is_err_result = try self.isErr(error_union_ty, error_union); + const reloc = try self.condBr(is_err_result); + + try self.genBody(body); + + try self.performReloc(reloc); + break :result try self.errUnionPayload(error_union, error_union_ty); + }; + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + _ = body; + return self.fail("TODO implement airTryPtr for arm", .{}); + // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst);