Commit 724d753638

Cody Tapscott <topolarity@tapscott.me>
2022-09-14 22:49:54
stage2: Add `.save_err_return_trace_index` AIR op
This is encoded as a primitive AIR instruction to resolve one corner case: A function may include a `catch { ... }` or `else |err| { ... }` block but not call any errorable fn. In that case, there is no error return trace to save the index of and codegen needs to avoid interacting with the non-existing error trace. By using a primitive AIR op, we can depend on Liveness to mark this unused in this corner case.
1 parent 3007fdd
src/arch/aarch64/CodeGen.zig
@@ -702,6 +702,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
             .err_return_trace           => try self.airErrReturnTrace(inst),
             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
+            .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
 
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -2867,6 +2868,11 @@ fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
 }
 
+fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
+}
+
 fn airWrapOptional(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: {
src/arch/arm/CodeGen.zig
@@ -751,6 +751,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
             .err_return_trace           => try self.airErrReturnTrace(inst),
             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
+            .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
 
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -2116,6 +2117,11 @@ fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
 }
 
+fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
+}
+
 /// T to E!T
 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
src/arch/riscv64/CodeGen.zig
@@ -665,6 +665,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
             .err_return_trace           => try self.airErrReturnTrace(inst),
             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
+            .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
 
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -1329,6 +1330,11 @@ fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
 }
 
+fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
+}
+
 fn airWrapOptional(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: {
src/arch/sparc64/CodeGen.zig
@@ -679,6 +679,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
             .err_return_trace           => @panic("TODO try self.airErrReturnTrace(inst)"),
             .set_err_return_trace       => @panic("TODO try self.airSetErrReturnTrace(inst)"),
+            .save_err_return_trace_index=> @panic("TODO try self.airSaveErrReturnTraceIndex(inst)"),
 
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
src/arch/wasm/CodeGen.zig
@@ -1857,6 +1857,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .tag_name,
         .err_return_trace,
         .set_err_return_trace,
+        .save_err_return_trace_index,
         .is_named_enum_value,
         .error_set_has_value,
         .addrspace_cast,
src/arch/x86_64/CodeGen.zig
@@ -756,6 +756,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .errunion_payload_ptr_set   => try self.airErrUnionPayloadPtrSet(inst),
             .err_return_trace           => try self.airErrReturnTrace(inst),
             .set_err_return_trace       => try self.airSetErrReturnTrace(inst),
+            .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst),
 
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -1973,6 +1974,11 @@ fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
 }
 
+fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
+}
+
 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     if (self.liveness.isUnused(inst)) {
src/codegen/c.zig
@@ -1935,6 +1935,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .errunion_payload_ptr_set    => try airErrUnionPayloadPtrSet(f, inst),
             .err_return_trace            => try airErrReturnTrace(f, inst),
             .set_err_return_trace        => try airSetErrReturnTrace(f, inst),
+            .save_err_return_trace_index => try airSaveErrReturnTraceIndex(f, inst),
 
             .wasm_memory_size => try airWasmMemorySize(f, inst),
             .wasm_memory_grow => try airWasmMemoryGrow(f, inst),
@@ -3625,6 +3626,11 @@ fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
     return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
 }
 
+fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
+    _ = inst;
+    return f.fail("TODO: C backend: implement airSaveErrReturnTraceIndex", .{});
+}
+
 fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
src/codegen/llvm.zig
@@ -4592,6 +4592,7 @@ pub const FuncGen = struct {
                 .errunion_payload_ptr_set    => try self.airErrUnionPayloadPtrSet(inst),
                 .err_return_trace            => try self.airErrReturnTrace(inst),
                 .set_err_return_trace        => try self.airSetErrReturnTrace(inst),
+                .save_err_return_trace_index => try self.airSaveErrReturnTraceIndex(inst),
 
                 .wrap_optional         => try self.airWrapOptional(inst),
                 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -6543,6 +6544,24 @@ pub const FuncGen = struct {
         return null;
     }
 
+    fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+        if (self.liveness.isUnused(inst)) return null;
+
+        const target = self.dg.module.getTarget();
+
+        const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+        //const struct_ty = try self.resolveInst(ty_pl.ty);
+        const struct_ty = self.air.getRefType(ty_pl.ty);
+        const field_index = ty_pl.payload;
+
+        var ptr_ty_buf: Type.Payload.Pointer = undefined;
+        const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
+        const struct_llvm_ty = try self.dg.lowerType(struct_ty);
+        const field_ptr = self.builder.buildStructGEP(struct_llvm_ty, self.err_ret_trace.?, llvm_field_index, "");
+        const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base);
+        return self.load(field_ptr, field_ptr_ty);
+    }
+
     fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
         if (self.liveness.isUnused(inst)) return null;
 
src/Air.zig
@@ -733,6 +733,10 @@ pub const Inst = struct {
         /// Uses the `ty_op` field.
         addrspace_cast,
 
+        /// Saves the error return trace index, if any. Otherwise, returns 0.
+        /// Uses the `ty_op` field.
+        save_err_return_trace_index,
+
         pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag {
             switch (op) {
                 .lt => return if (optimized) .cmp_lt_optimized else .cmp_lt,
@@ -1179,6 +1183,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         .slice_len,
         .ret_addr,
         .frame_addr,
+        .save_err_return_trace_index,
         => return Type.usize,
 
         .wasm_memory_grow => return Type.i32,
src/Liveness.zig
@@ -228,6 +228,7 @@ pub fn categorizeOperand(
         .frame_addr,
         .wasm_memory_size,
         .err_return_trace,
+        .save_err_return_trace_index,
         => return .none,
 
         .fence => return .write,
@@ -805,6 +806,7 @@ fn analyzeInst(
         .frame_addr,
         .wasm_memory_size,
         .err_return_trace,
+        .save_err_return_trace_index,
         => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
 
         .not,
src/print_air.zig
@@ -197,6 +197,7 @@ const Writer = struct {
             .unreach,
             .ret_addr,
             .frame_addr,
+            .save_err_return_trace_index,
             => try w.writeNoOp(s, inst),
 
             .const_ty,
src/Sema.zig
@@ -16228,22 +16228,28 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     // This is only relevant at runtime.
     if (block.is_comptime) return Air.Inst.Ref.zero_usize;
 
-    // In the corner case that `catch { ... }` or `else |err| { ... }` is used in a function
-    // that does *not* make any errorable calls, we still need an error trace to interact with
-    // the AIR instructions we've already emitted.
-    if (sema.owner_func != null)
-        sema.owner_func.?.calls_or_awaits_errorable_fn = true;
-
     const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
     const ok = sema.mod.comp.bin_file.options.error_return_tracing and
         backend_supports_error_return_tracing;
     if (!ok) return Air.Inst.Ref.zero_usize;
 
+    // This is encoded as a primitive AIR instruction to resolve one corner case: A function
+    // may include a `catch { ... }` or `else |err| { ... }` block but not call any errorable
+    // fn. In that case, there is no error return trace to save the index of and codegen needs
+    // to avoid interacting with the non-existing error trace.
+    //
+    // By using a primitive AIR op, we can depend on Liveness to mark this unused in this corner case.
+
     const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
     const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
-    const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
-    const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
-    return sema.fieldVal(block, src, err_return_trace, "index", src);
+    const field_index = try sema.structFieldIndex(block, stack_trace_ty, "index", src);
+    return block.addInst(.{
+        .tag = .save_err_return_trace_index,
+        .data = .{ .ty_pl = .{
+            .ty = try sema.addType(stack_trace_ty),
+            .payload = @intCast(u32, field_index),
+        } },
+    });
 }
 
 fn zirRestoreErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -16254,7 +16260,8 @@ fn zirRestoreErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
     if (block.is_comptime) return;
 
     const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
-    const ok = sema.mod.comp.bin_file.options.error_return_tracing and
+    const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and
+        sema.mod.comp.bin_file.options.error_return_tracing and
         backend_supports_error_return_tracing;
     if (!ok) return;