Skip to content

Commit 4fe97dc

Browse files
andrewrkkubkon
authored andcommitted
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.
1 parent 6a158ef commit 4fe97dc

File tree

13 files changed

+229
-8
lines changed

13 files changed

+229
-8
lines changed

src/Air.zig

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ pub const Inst = struct {
320320
/// Result type is always noreturn; no instructions in a block follow this one.
321321
/// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
322322
switch_br,
323+
/// Given an operand which is an error union, splits control flow. In
324+
/// case of error, control flow goes into the block that is part of this
325+
/// instruction, which is guaranteed to end with a return instruction
326+
/// and never breaks out of the block.
327+
/// In the case of non-error, control flow proceeds to the next instruction
328+
/// after the `try`, with the result of this instruction being the unwrapped
329+
/// payload value, as if `unwrap_errunion_payload` was executed on the operand.
330+
/// Uses the `pl_op` field. Payload is `Try`.
331+
@"try",
332+
/// Same as `try` except the operand is a pointer to an error union, and the
333+
/// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr`
334+
/// was executed on the operand.
335+
/// Uses the `ty_pl` field. Payload is `TryPtr`.
336+
try_ptr,
323337
/// A comptime-known value. Uses the `ty_pl` field, payload is index of
324338
/// `values` array.
325339
constant,
@@ -780,6 +794,19 @@ pub const SwitchBr = struct {
780794
};
781795
};
782796

797+
/// This data is stored inside extra. Trailing:
798+
/// 0. body: Inst.Index // for each body_len
799+
pub const Try = struct {
800+
body_len: u32,
801+
};
802+
803+
/// This data is stored inside extra. Trailing:
804+
/// 0. body: Inst.Index // for each body_len
805+
pub const TryPtr = struct {
806+
ptr: Inst.Ref,
807+
body_len: u32,
808+
};
809+
783810
pub const StructField = struct {
784811
/// Whether this is a pointer or byval is determined by the AIR tag.
785812
struct_operand: Inst.Ref,
@@ -1028,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
10281055
.popcount,
10291056
.byte_swap,
10301057
.bit_reverse,
1058+
.try_ptr,
10311059
=> return air.getRefType(datas[inst].ty_op.ty),
10321060

10331061
.loop,
@@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
11021130
const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data;
11031131
return air.typeOf(extra.lhs);
11041132
},
1133+
1134+
.@"try" => {
1135+
const err_union_ty = air.typeOf(datas[inst].pl_op.operand);
1136+
return err_union_ty.errorUnionPayload();
1137+
},
11051138
}
11061139
}
11071140

src/AstGen.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4896,7 +4896,8 @@ fn tryExpr(
48964896
_ = try else_scope.addUnNode(.ret_node, err_code, node);
48974897

48984898
try else_scope.setTryBody(try_inst, operand);
4899-
return indexToRef(try_inst);
4899+
const result = indexToRef(try_inst);
4900+
return rvalue(parent_gz, rl, result, node);
49004901
}
49014902

49024903
fn orelseCatchExpr(

src/Liveness.zig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,12 @@ pub fn categorizeOperand(
478478
.block => {
479479
return .complex;
480480
},
481+
.@"try" => {
482+
return .complex;
483+
},
484+
.try_ptr => {
485+
return .complex;
486+
},
481487
.loop => {
482488
return .complex;
483489
},
@@ -1019,6 +1025,19 @@ fn analyzeInst(
10191025
try analyzeWithContext(a, new_set, body);
10201026
return; // Loop has no operands and it is always unreferenced.
10211027
},
1028+
.@"try" => {
1029+
const pl_op = inst_datas[inst].pl_op;
1030+
const extra = a.air.extraData(Air.Try, pl_op.payload);
1031+
const body = a.air.extra[extra.end..][0..extra.data.body_len];
1032+
try analyzeWithContext(a, new_set, body);
1033+
return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });
1034+
},
1035+
.try_ptr => {
1036+
const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload);
1037+
const body = a.air.extra[extra.end..][0..extra.data.body_len];
1038+
try analyzeWithContext(a, new_set, body);
1039+
return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none });
1040+
},
10221041
.cond_br => {
10231042
// Each death that occurs inside one branch, but not the other, needs
10241043
// to be added as a death immediately upon entering the other branch.

src/Sema.zig

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12983,7 +12983,8 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1298312983
const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1298412984
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
1298512985
const operand = try sema.resolveInst(extra.data.operand);
12986-
const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer;
12986+
const operand_ty = sema.typeOf(operand);
12987+
const is_ptr = operand_ty.zigTypeTag() == .Pointer;
1298712988
const err_union = if (is_ptr)
1298812989
try sema.analyzeLoad(parent_block, src, operand, operand_src)
1298912990
else
@@ -13008,9 +13009,52 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1300813009
// no breaks from the body possible, and that the body is noreturn.
1300913010
return sema.resolveBody(parent_block, body, inst);
1301013011
}
13011-
_ = body;
13012-
_ = is_non_err;
13013-
@panic("TODO");
13012+
13013+
var sub_block = parent_block.makeSubBlock();
13014+
defer sub_block.instructions.deinit(sema.gpa);
13015+
13016+
// This body is guaranteed to end with noreturn and has no breaks.
13017+
_ = try sema.analyzeBodyInner(&sub_block, body);
13018+
13019+
if (is_ptr) {
13020+
const ptr_info = operand_ty.ptrInfo().data;
13021+
const res_ty = try Type.ptr(sema.arena, sema.mod, .{
13022+
.pointee_type = err_union_ty.errorUnionPayload(),
13023+
.@"addrspace" = ptr_info.@"addrspace",
13024+
.mutable = ptr_info.mutable,
13025+
.@"allowzero" = ptr_info.@"allowzero",
13026+
.@"volatile" = ptr_info.@"volatile",
13027+
});
13028+
const res_ty_ref = try sema.addType(res_ty);
13029+
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
13030+
sub_block.instructions.items.len);
13031+
const try_inst = try parent_block.addInst(.{
13032+
.tag = .try_ptr,
13033+
.data = .{ .ty_pl = .{
13034+
.ty = res_ty_ref,
13035+
.payload = sema.addExtraAssumeCapacity(Air.TryPtr{
13036+
.ptr = operand,
13037+
.body_len = @intCast(u32, sub_block.instructions.items.len),
13038+
}),
13039+
} },
13040+
});
13041+
sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
13042+
return try_inst;
13043+
}
13044+
13045+
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
13046+
sub_block.instructions.items.len);
13047+
const try_inst = try parent_block.addInst(.{
13048+
.tag = .@"try",
13049+
.data = .{ .pl_op = .{
13050+
.operand = operand,
13051+
.payload = sema.addExtraAssumeCapacity(Air.Try{
13052+
.body_len = @intCast(u32, sub_block.instructions.items.len),
13053+
}),
13054+
} },
13055+
});
13056+
sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
13057+
return try_inst;
1301413058
}
1301513059

1301613060
// A `break` statement is inside a runtime condition, but trying to

src/arch/aarch64/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
665665
.prefetch => try self.airPrefetch(inst),
666666
.mul_add => try self.airMulAdd(inst),
667667

668+
.@"try" => @panic("TODO"),
669+
.try_ptr => @panic("TODO"),
670+
668671
.dbg_var_ptr,
669672
.dbg_var_val,
670673
=> try self.airDbgVar(inst),

src/arch/arm/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
677677
.prefetch => try self.airPrefetch(inst),
678678
.mul_add => try self.airMulAdd(inst),
679679

680+
.@"try" => @panic("TODO"),
681+
.try_ptr => @panic("TODO"),
682+
680683
.dbg_var_ptr,
681684
.dbg_var_val,
682685
=> try self.airDbgVar(inst),

src/arch/riscv64/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
604604
.prefetch => try self.airPrefetch(inst),
605605
.mul_add => try self.airMulAdd(inst),
606606

607+
.@"try" => @panic("TODO"),
608+
.try_ptr => @panic("TODO"),
609+
607610
.dbg_var_ptr,
608611
.dbg_var_val,
609612
=> try self.airDbgVar(inst),

src/arch/sparc64/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
604604
.prefetch => @panic("TODO try self.airPrefetch(inst)"),
605605
.mul_add => @panic("TODO try self.airMulAdd(inst)"),
606606

607+
.@"try" => @panic("TODO try self.airTry(inst)"),
608+
.try_ptr => @panic("TODO try self.airTryPtr(inst)"),
609+
607610
.dbg_var_ptr,
608611
.dbg_var_val,
609612
=> try self.airDbgVar(inst),

src/arch/wasm/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
14901490
.int_to_float => self.airIntToFloat(inst),
14911491
.get_union_tag => self.airGetUnionTag(inst),
14921492

1493+
.@"try" => @panic("TODO"),
1494+
.try_ptr => @panic("TODO"),
1495+
14931496
// TODO
14941497
.dbg_inline_begin,
14951498
.dbg_inline_end,

src/arch/x86_64/CodeGen.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
681681
.prefetch => try self.airPrefetch(inst),
682682
.mul_add => try self.airMulAdd(inst),
683683

684+
.@"try" => @panic("TODO"),
685+
.try_ptr => @panic("TODO"),
686+
684687
.dbg_var_ptr,
685688
.dbg_var_val,
686689
=> try self.airDbgVar(inst),

src/codegen/c.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
18751875
.union_init => try airUnionInit(f, inst),
18761876
.prefetch => try airPrefetch(f, inst),
18771877

1878+
.@"try" => @panic("TODO"),
1879+
.try_ptr => @panic("TODO"),
1880+
18781881
.dbg_var_ptr,
18791882
.dbg_var_val,
18801883
=> try airDbgVar(f, inst),

src/codegen/llvm.zig

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4040,6 +4040,8 @@ pub const FuncGen = struct {
40404040
.ret_addr => try self.airRetAddr(inst),
40414041
.frame_addr => try self.airFrameAddress(inst),
40424042
.cond_br => try self.airCondBr(inst),
4043+
.@"try" => try self.airTry(inst),
4044+
.try_ptr => try self.airTryPtr(inst),
40434045
.intcast => try self.airIntCast(inst),
40444046
.trunc => try self.airTrunc(inst),
40454047
.fptrunc => try self.airFptrunc(inst),
@@ -4731,6 +4733,75 @@ pub const FuncGen = struct {
47314733
return null;
47324734
}
47334735

4736+
fn airTry(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4737+
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4738+
const err_union = try self.resolveInst(pl_op.operand);
4739+
const extra = self.air.extraData(Air.Try, pl_op.payload);
4740+
const body = self.air.extra[extra.end..][0..extra.data.body_len];
4741+
const err_union_ty = self.air.typeOf(pl_op.operand);
4742+
const result_ty = self.air.typeOfIndex(inst);
4743+
return lowerTry(self, err_union, body, err_union_ty, false, result_ty);
4744+
}
4745+
4746+
fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4747+
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4748+
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4749+
const err_union_ptr = try self.resolveInst(extra.data.ptr);
4750+
const body = self.air.extra[extra.end..][0..extra.data.body_len];
4751+
const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
4752+
const result_ty = self.air.typeOfIndex(inst);
4753+
return lowerTry(self, err_union_ptr, body, err_union_ty, true, result_ty);
4754+
}
4755+
4756+
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 {
4757+
if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
4758+
// If the error set has no fields, then the payload and the error
4759+
// union are the same value.
4760+
return err_union;
4761+
}
4762+
4763+
const payload_ty = err_union_ty.errorUnionPayload();
4764+
const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
4765+
const target = fg.dg.module.getTarget();
4766+
const is_err = err: {
4767+
const err_set_ty = try fg.dg.lowerType(Type.anyerror);
4768+
const zero = err_set_ty.constNull();
4769+
if (!payload_has_bits) {
4770+
const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union;
4771+
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
4772+
}
4773+
const err_field_index = errUnionErrorOffset(payload_ty, target);
4774+
if (operand_is_ptr or isByRef(err_union_ty)) {
4775+
const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, "");
4776+
const loaded = fg.builder.buildLoad(err_field_ptr, "");
4777+
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
4778+
}
4779+
const loaded = fg.builder.buildExtractValue(err_union, err_field_index, "");
4780+
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
4781+
};
4782+
4783+
const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet");
4784+
const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont");
4785+
_ = fg.builder.buildCondBr(is_err, return_block, continue_block);
4786+
4787+
fg.builder.positionBuilderAtEnd(return_block);
4788+
try fg.genBody(body);
4789+
4790+
fg.builder.positionBuilderAtEnd(continue_block);
4791+
if (!payload_has_bits) {
4792+
if (!operand_is_ptr) return null;
4793+
4794+
// TODO once we update to LLVM 14 this bitcast won't be necessary.
4795+
const res_ptr_ty = try fg.dg.lowerType(result_ty);
4796+
return fg.builder.buildBitCast(err_union, res_ptr_ty, "");
4797+
}
4798+
const offset = errUnionPayloadOffset(payload_ty, target);
4799+
if (operand_is_ptr or isByRef(payload_ty)) {
4800+
return fg.builder.buildStructGEP(err_union, offset, "");
4801+
}
4802+
return fg.builder.buildExtractValue(err_union, offset, "");
4803+
}
4804+
47344805
fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
47354806
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
47364807
const cond = try self.resolveInst(pl_op.operand);
@@ -5673,15 +5744,14 @@ pub const FuncGen = struct {
56735744
const operand = try self.resolveInst(ty_op.operand);
56745745
const operand_ty = self.air.typeOf(ty_op.operand);
56755746
const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
5676-
// If the error set has no fields, then the payload and the error
5677-
// union are the same value.
56785747
if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
5748+
// If the error set has no fields, then the payload and the error
5749+
// union are the same value.
56795750
return operand;
56805751
}
56815752
const result_ty = self.air.typeOfIndex(inst);
56825753
const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
56835754
const target = self.dg.module.getTarget();
5684-
const offset = errUnionPayloadOffset(payload_ty, target);
56855755

56865756
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
56875757
if (!operand_is_ptr) return null;
@@ -5690,6 +5760,7 @@ pub const FuncGen = struct {
56905760
const res_ptr_ty = try self.dg.lowerType(result_ty);
56915761
return self.builder.buildBitCast(operand, res_ptr_ty, "");
56925762
}
5763+
const offset = errUnionPayloadOffset(payload_ty, target);
56935764
if (operand_is_ptr or isByRef(payload_ty)) {
56945765
return self.builder.buildStructGEP(operand, offset, "");
56955766
}

src/print_air.zig

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ const Writer = struct {
258258
.union_init => try w.writeUnionInit(s, inst),
259259
.br => try w.writeBr(s, inst),
260260
.cond_br => try w.writeCondBr(s, inst),
261+
.@"try" => try w.writeTry(s, inst),
262+
.try_ptr => try w.writeTryPtr(s, inst),
261263
.switch_br => try w.writeSwitchBr(s, inst),
262264
.cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
263265
.fence => try w.writeFence(s, inst),
@@ -624,6 +626,36 @@ const Writer = struct {
624626
try w.writeOperand(s, inst, 0, br.operand);
625627
}
626628

629+
fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
630+
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
631+
const extra = w.air.extraData(Air.Try, pl_op.payload);
632+
const body = w.air.extra[extra.end..][0..extra.data.body_len];
633+
634+
try w.writeOperand(s, inst, 0, pl_op.operand);
635+
try s.writeAll(", {\n");
636+
const old_indent = w.indent;
637+
w.indent += 2;
638+
try w.writeBody(s, body);
639+
w.indent = old_indent;
640+
try s.writeByteNTimes(' ', w.indent);
641+
try s.writeAll("}");
642+
}
643+
644+
fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
645+
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
646+
const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
647+
const body = w.air.extra[extra.end..][0..extra.data.body_len];
648+
649+
try w.writeOperand(s, inst, 0, extra.data.ptr);
650+
try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()});
651+
const old_indent = w.indent;
652+
w.indent += 2;
653+
try w.writeBody(s, body);
654+
w.indent = old_indent;
655+
try s.writeByteNTimes(' ', w.indent);
656+
try s.writeAll("}");
657+
}
658+
627659
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
628660
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
629661
const extra = w.air.extraData(Air.CondBr, pl_op.payload);

0 commit comments

Comments
 (0)