authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-06 19:01:39-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-06 19:01:39-04:00
logd1bfc83774ffaeeb7646f3003038bc4e4e94f143
treecef9979f6d0034dcc3f99567bf12e0b7f3b643c1
parentbe639eecc209d8039176c9750f28e8c960400fb2
parent8ca6dc33d1523061f198474c84a68330085d6667
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11783 from ziglang/stage2-try

introduce a "try" ZIR and AIR instruction

15 files changed, 919 insertions(+), 153 deletions(-)

src/Air.zig+33
...@@ -320,6 +320,20 @@ pub const Inst = struct {...@@ -320,6 +320,20 @@ pub const Inst = struct {
320 /// Result type is always noreturn; no instructions in a block follow this one.320 /// Result type is always noreturn; no instructions in a block follow this one.
321 /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.321 /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
322 switch_br,322 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,
323 /// A comptime-known value. Uses the `ty_pl` field, payload is index of337 /// A comptime-known value. Uses the `ty_pl` field, payload is index of
324 /// `values` array.338 /// `values` array.
325 constant,339 constant,
...@@ -780,6 +794,19 @@ pub const SwitchBr = struct {...@@ -780,6 +794,19 @@ pub const SwitchBr = struct {
780 };794 };
781};795};
782796
797/// This data is stored inside extra. Trailing:
798/// 0. body: Inst.Index // for each body_len
799pub 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
805pub const TryPtr = struct {
806 ptr: Inst.Ref,
807 body_len: u32,
808};
809
783pub const StructField = struct {810pub const StructField = struct {
784 /// Whether this is a pointer or byval is determined by the AIR tag.811 /// Whether this is a pointer or byval is determined by the AIR tag.
785 struct_operand: Inst.Ref,812 struct_operand: Inst.Ref,
...@@ -991,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -991,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
991 .shl_with_overflow,1018 .shl_with_overflow,
992 .ptr_add,1019 .ptr_add,
993 .ptr_sub,1020 .ptr_sub,
1021 .try_ptr,
994 => return air.getRefType(datas[inst].ty_pl.ty),1022 => return air.getRefType(datas[inst].ty_pl.ty),
9951023
996 .not,1024 .not,
...@@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1102 const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data;1130 const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data;
1103 return air.typeOf(extra.lhs);1131 return air.typeOf(extra.lhs);
1104 },1132 },
1133
1134 .@"try" => {
1135 const err_union_ty = air.typeOf(datas[inst].pl_op.operand);
1136 return err_union_ty.errorUnionPayload();
1137 },
1105 }1138 }
1106}1139}
11071140
src/AstGen.zig+47-52
...@@ -2425,6 +2425,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2425,6 +2425,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2425 .param_type,2425 .param_type,
2426 .ret_ptr,2426 .ret_ptr,
2427 .ret_type,2427 .ret_type,
2428 .@"try",
2429 .try_ptr,
2430 //.try_inline,
2431 //.try_ptr_inline,
2428 => break :b false,2432 => break :b false,
24292433
2430 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {2434 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
...@@ -4871,68 +4875,43 @@ fn tryExpr(...@@ -4871,68 +4875,43 @@ fn tryExpr(
48714875
4872 if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});4876 if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
48734877
4874 var block_scope = parent_gz.makeSubBlock(scope);4878 const operand_rl: ResultLoc = switch (rl) {
4875 block_scope.setBreakResultLoc(rl);
4876 defer block_scope.unstack();
4877
4878 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
4879 .ref => .ref,4879 .ref => .ref,
4880 else => .none,4880 else => .none,
4881 };4881 };
4882 const err_ops = switch (operand_rl) {4882 // This could be a pointer or value depending on the `rl` parameter.
4883 // zig fmt: off4883 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
4884 .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },4884 const is_inline = parent_gz.force_comptime;
4885 else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },4885 const is_inline_bit = @as(u2, @boolToInt(is_inline));
4886 // zig fmt: on4886 const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;
4887 };4887 const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
4888 // This could be a pointer or value depending on the `operand_rl` parameter.4888 0b00 => .@"try",
4889 // We cannot use `block_scope.break_result_loc` because that has the bare4889 0b01 => .@"try",
4890 // type, whereas this expression has the optional type. Later we make4890 //0b01 => .try_inline,
4891 // up for this fact by calling rvalue on the else branch.4891 0b10 => .try_ptr,
4892 const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node);4892 0b11 => .try_ptr,
4893 const cond = try block_scope.addUnNode(err_ops[0], operand, node);4893 //0b11 => .try_ptr_inline,
4894 const condbr = try block_scope.addCondBr(.condbr, node);
4895
4896 const block = try parent_gz.makeBlockInst(.block, node);
4897 try block_scope.setBlockBody(block);
4898 // block_scope unstacked now, can add new instructions to parent_gz
4899 try parent_gz.instructions.append(astgen.gpa, block);
4900
4901 var then_scope = parent_gz.makeSubBlock(scope);
4902 defer then_scope.unstack();
4903
4904 block_scope.break_count += 1;
4905 // This could be a pointer or value depending on `err_ops[2]`.
4906 const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
4907 const then_result = switch (rl) {
4908 .ref => unwrapped_payload,
4909 else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
4910 };4894 };
4895 const try_inst = try parent_gz.makeBlockInst(block_tag, node);
4896 try parent_gz.instructions.append(astgen.gpa, try_inst);
49114897
4912 // else_scope will be stacked on then_scope as both are stacked on parent_gz
4913 var else_scope = parent_gz.makeSubBlock(scope);4898 var else_scope = parent_gz.makeSubBlock(scope);
4914 defer else_scope.unstack();4899 defer else_scope.unstack();
49154900
4916 const err_code = try else_scope.addUnNode(err_ops[1], operand, node);4901 const err_tag = switch (rl) {
4902 .ref => Zir.Inst.Tag.err_union_code_ptr,
4903 else => Zir.Inst.Tag.err_union_code,
4904 };
4905 const err_code = try else_scope.addUnNode(err_tag, operand, node);
4917 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });4906 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
4918 const else_result = try else_scope.addUnNode(.ret_node, err_code, node);4907 _ = try else_scope.addUnNode(.ret_node, err_code, node);
49194908
4920 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";4909 try else_scope.setTryBody(try_inst, operand);
4921 return finishThenElseBlock(4910 const result = indexToRef(try_inst);
4922 parent_gz,4911 switch (rl) {
4923 rl,4912 .ref => return result,
4924 node,4913 else => return rvalue(parent_gz, rl, result, node),
4925 &block_scope,4914 }
4926 &then_scope,
4927 &else_scope,
4928 condbr,
4929 cond,
4930 then_result,
4931 else_result,
4932 block,
4933 block,
4934 break_tag,
4935 );
4936}4915}
49374916
4938fn orelseCatchExpr(4917fn orelseCatchExpr(
...@@ -10018,6 +9997,22 @@ const GenZir = struct {...@@ -10018,6 +9997,22 @@ const GenZir = struct {
10018 gz.unstack();9997 gz.unstack();
10019 }9998 }
100209999
10000 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
10001 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
10002 const gpa = gz.astgen.gpa;
10003 const body = gz.instructionsSlice();
10004 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len);
10005 const zir_datas = gz.astgen.instructions.items(.data);
10006 zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
10007 Zir.Inst.Try{
10008 .operand = operand,
10009 .body_len = @intCast(u32, body.len),
10010 },
10011 );
10012 gz.astgen.extra.appendSliceAssumeCapacity(body);
10013 gz.unstack();
10014 }
10015
10021 /// Must be called with the following stack set up:10016 /// Must be called with the following stack set up:
10022 /// * gz (bottom)10017 /// * gz (bottom)
10023 /// * align_gz10018 /// * align_gz
src/Liveness.zig+19
...@@ -478,6 +478,12 @@ pub fn categorizeOperand(...@@ -478,6 +478,12 @@ pub fn categorizeOperand(
478 .block => {478 .block => {
479 return .complex;479 return .complex;
480 },480 },
481 .@"try" => {
482 return .complex;
483 },
484 .try_ptr => {
485 return .complex;
486 },
481 .loop => {487 .loop => {
482 return .complex;488 return .complex;
483 },489 },
...@@ -1019,6 +1025,19 @@ fn analyzeInst(...@@ -1019,6 +1025,19 @@ fn analyzeInst(
1019 try analyzeWithContext(a, new_set, body);1025 try analyzeWithContext(a, new_set, body);
1020 return; // Loop has no operands and it is always unreferenced.1026 return; // Loop has no operands and it is always unreferenced.
1021 },1027 },
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 },
1022 .cond_br => {1041 .cond_br => {
1023 // Each death that occurs inside one branch, but not the other, needs1042 // Each death that occurs inside one branch, but not the other, needs
1024 // to be added as a death immediately upon entering the other branch.1043 // to be added as a death immediately upon entering the other branch.
src/Sema.zig+238-11
...@@ -1322,6 +1322,106 @@ fn analyzeBodyInner(...@@ -1322,6 +1322,106 @@ fn analyzeBodyInner(
1322 break break_data.inst;1322 break break_data.inst;
1323 }1323 }
1324 },1324 },
1325 .@"try" => blk: {
1326 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
1327 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1328 const src = inst_data.src();
1329 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1330 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1331 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
1332 const err_union = try sema.resolveInst(extra.data.operand);
1333 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1334 assert(is_non_err != .none);
1335 const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
1336 if (is_non_err_tv.val.toBool()) {
1337 const err_union_ty = sema.typeOf(err_union);
1338 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
1339 }
1340 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
1341 break always_noreturn;
1342 if (inst == break_data.block_inst) {
1343 break :blk try sema.resolveInst(break_data.operand);
1344 } else {
1345 break break_data.inst;
1346 }
1347 },
1348 //.try_inline => blk: {
1349 // const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1350 // const src = inst_data.src();
1351 // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1352 // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1353 // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
1354 // const operand = try sema.resolveInst(extra.data.operand);
1355 // const operand_ty = sema.typeOf(operand);
1356 // const is_ptr = operand_ty.zigTypeTag() == .Pointer;
1357 // const err_union = if (is_ptr)
1358 // try sema.analyzeLoad(block, src, operand, operand_src)
1359 // else
1360 // operand;
1361 // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1362 // assert(is_non_err != .none);
1363 // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
1364 // if (is_non_err_tv.val.toBool()) {
1365 // if (is_ptr) {
1366 // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
1367 // } else {
1368 // const err_union_ty = sema.typeOf(err_union);
1369 // break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
1370 // }
1371 // }
1372 // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
1373 // break always_noreturn;
1374 // if (inst == break_data.block_inst) {
1375 // break :blk try sema.resolveInst(break_data.operand);
1376 // } else {
1377 // break break_data.inst;
1378 // }
1379 //},
1380 .try_ptr => blk: {
1381 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
1382 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1383 const src = inst_data.src();
1384 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1385 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1386 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
1387 const operand = try sema.resolveInst(extra.data.operand);
1388 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1389 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1390 assert(is_non_err != .none);
1391 const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
1392 if (is_non_err_tv.val.toBool()) {
1393 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
1394 }
1395 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
1396 break always_noreturn;
1397 if (inst == break_data.block_inst) {
1398 break :blk try sema.resolveInst(break_data.operand);
1399 } else {
1400 break break_data.inst;
1401 }
1402 },
1403 //.try_ptr_inline => blk: {
1404 // const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1405 // const src = inst_data.src();
1406 // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1407 // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1408 // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
1409 // const operand = try sema.resolveInst(extra.data.operand);
1410 // const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1411 // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1412 // assert(is_non_err != .none);
1413 // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
1414 // if (is_non_err_tv.val.toBool()) {
1415 // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
1416 // }
1417 // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
1418 // break always_noreturn;
1419 // if (inst == break_data.block_inst) {
1420 // break :blk try sema.resolveInst(break_data.operand);
1421 // } else {
1422 // break break_data.inst;
1423 // }
1424 //},
1325 };1425 };
1326 if (sema.typeOf(air_inst).isNoReturn())1426 if (sema.typeOf(air_inst).isNoReturn())
1327 break always_noreturn;1427 break always_noreturn;
...@@ -6426,32 +6526,43 @@ fn zirErrUnionPayload(...@@ -6426,32 +6526,43 @@ fn zirErrUnionPayload(
6426 const src = inst_data.src();6526 const src = inst_data.src();
6427 const operand = try sema.resolveInst(inst_data.operand);6527 const operand = try sema.resolveInst(inst_data.operand);
6428 const operand_src = src;6528 const operand_src = src;
6429 const operand_ty = sema.typeOf(operand);6529 const err_union_ty = sema.typeOf(operand);
6430 if (operand_ty.zigTypeTag() != .ErrorUnion) {6530 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
6431 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{6531 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
6432 operand_ty.fmt(sema.mod),6532 err_union_ty.fmt(sema.mod),
6433 });6533 });
6434 }6534 }
6535 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check);
6536}
64356537
6436 const result_ty = operand_ty.errorUnionPayload();6538fn analyzeErrUnionPayload(
6437 if (try sema.resolveDefinedValue(block, src, operand)) |val| {6539 sema: *Sema,
6540 block: *Block,
6541 src: LazySrcLoc,
6542 err_union_ty: Type,
6543 operand: Zir.Inst.Ref,
6544 operand_src: LazySrcLoc,
6545 safety_check: bool,
6546) CompileError!Air.Inst.Ref {
6547 const payload_ty = err_union_ty.errorUnionPayload();
6548 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
6438 if (val.getError()) |name| {6549 if (val.getError()) |name| {
6439 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});6550 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
6440 }6551 }
6441 const data = val.castTag(.eu_payload).?.data;6552 const data = val.castTag(.eu_payload).?.data;
6442 return sema.addConstant(result_ty, data);6553 return sema.addConstant(payload_ty, data);
6443 }6554 }
64446555
6445 try sema.requireRuntimeBlock(block, src);6556 try sema.requireRuntimeBlock(block, src);
64466557
6447 // If the error set has no fields then no safety check is needed.6558 // If the error set has no fields then no safety check is needed.
6448 if (safety_check and block.wantSafety() and6559 if (safety_check and block.wantSafety() and
6449 operand_ty.errorUnionSet().errorSetCardinality() != .zero)6560 err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
6450 {6561 {
6451 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);6562 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
6452 }6563 }
64536564
6454 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);6565 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
6455}6566}
64566567
6457/// Pointer in, pointer out.6568/// Pointer in, pointer out.
...@@ -12969,6 +13080,108 @@ fn zirCondbr(...@@ -12969,6 +13080,108 @@ fn zirCondbr(
12969 return always_noreturn;13080 return always_noreturn;
12970}13081}
1297113082
13083fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
13084 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
13085 const src = inst_data.src();
13086 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
13087 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
13088 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
13089 const err_union = try sema.resolveInst(extra.data.operand);
13090 const err_union_ty = sema.typeOf(err_union);
13091 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
13092 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
13093 err_union_ty.fmt(sema.mod),
13094 });
13095 }
13096 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
13097 if (is_non_err != .none) {
13098 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
13099 if (is_non_err_val.toBool()) {
13100 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
13101 }
13102 // We can analyze the body directly in the parent block because we know there are
13103 // no breaks from the body possible, and that the body is noreturn.
13104 return sema.resolveBody(parent_block, body, inst);
13105 }
13106
13107 var sub_block = parent_block.makeSubBlock();
13108 defer sub_block.instructions.deinit(sema.gpa);
13109
13110 // This body is guaranteed to end with noreturn and has no breaks.
13111 _ = try sema.analyzeBodyInner(&sub_block, body);
13112
13113 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
13114 sub_block.instructions.items.len);
13115 const try_inst = try parent_block.addInst(.{
13116 .tag = .@"try",
13117 .data = .{ .pl_op = .{
13118 .operand = err_union,
13119 .payload = sema.addExtraAssumeCapacity(Air.Try{
13120 .body_len = @intCast(u32, sub_block.instructions.items.len),
13121 }),
13122 } },
13123 });
13124 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
13125 return try_inst;
13126}
13127
13128fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
13129 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
13130 const src = inst_data.src();
13131 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
13132 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
13133 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
13134 const operand = try sema.resolveInst(extra.data.operand);
13135 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
13136 const err_union_ty = sema.typeOf(err_union);
13137 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
13138 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
13139 err_union_ty.fmt(sema.mod),
13140 });
13141 }
13142 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
13143 if (is_non_err != .none) {
13144 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
13145 if (is_non_err_val.toBool()) {
13146 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
13147 }
13148 // We can analyze the body directly in the parent block because we know there are
13149 // no breaks from the body possible, and that the body is noreturn.
13150 return sema.resolveBody(parent_block, body, inst);
13151 }
13152
13153 var sub_block = parent_block.makeSubBlock();
13154 defer sub_block.instructions.deinit(sema.gpa);
13155
13156 // This body is guaranteed to end with noreturn and has no breaks.
13157 _ = try sema.analyzeBodyInner(&sub_block, body);
13158
13159 const operand_ty = sema.typeOf(operand);
13160 const ptr_info = operand_ty.ptrInfo().data;
13161 const res_ty = try Type.ptr(sema.arena, sema.mod, .{
13162 .pointee_type = err_union_ty.errorUnionPayload(),
13163 .@"addrspace" = ptr_info.@"addrspace",
13164 .mutable = ptr_info.mutable,
13165 .@"allowzero" = ptr_info.@"allowzero",
13166 .@"volatile" = ptr_info.@"volatile",
13167 });
13168 const res_ty_ref = try sema.addType(res_ty);
13169 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
13170 sub_block.instructions.items.len);
13171 const try_inst = try parent_block.addInst(.{
13172 .tag = .try_ptr,
13173 .data = .{ .ty_pl = .{
13174 .ty = res_ty_ref,
13175 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
13176 .ptr = operand,
13177 .body_len = @intCast(u32, sub_block.instructions.items.len),
13178 }),
13179 } },
13180 });
13181 sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
13182 return try_inst;
13183}
13184
12972// A `break` statement is inside a runtime condition, but trying to13185// A `break` statement is inside a runtime condition, but trying to
12973// break from an inline loop. In such case we must convert it to13186// break from an inline loop. In such case we must convert it to
12974// a runtime break.13187// a runtime break.
...@@ -21622,7 +21835,7 @@ fn analyzeIsNull(...@@ -21622,7 +21835,7 @@ fn analyzeIsNull(
21622 return block.addUnOp(air_tag, operand);21835 return block.addUnOp(air_tag, operand);
21623}21836}
2162421837
21625fn analyzeIsNonErr(21838fn analyzeIsNonErrComptimeOnly(
21626 sema: *Sema,21839 sema: *Sema,
21627 block: *Block,21840 block: *Block,
21628 src: LazySrcLoc,21841 src: LazySrcLoc,
...@@ -21674,8 +21887,22 @@ fn analyzeIsNonErr(...@@ -21674,8 +21887,22 @@ fn analyzeIsNonErr(
21674 return Air.Inst.Ref.bool_false;21887 return Air.Inst.Ref.bool_false;
21675 }21888 }
21676 }21889 }
21677 try sema.requireRuntimeBlock(block, src);21890 return Air.Inst.Ref.none;
21678 return block.addUnOp(.is_non_err, operand);21891}
21892
21893fn analyzeIsNonErr(
21894 sema: *Sema,
21895 block: *Block,
21896 src: LazySrcLoc,
21897 operand: Air.Inst.Ref,
21898) CompileError!Air.Inst.Ref {
21899 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
21900 if (result == .none) {
21901 try sema.requireRuntimeBlock(block, src);
21902 return block.addUnOp(.is_non_err, operand);
21903 } else {
21904 return result;
21905 }
21679}21906}
2168021907
21681fn analyzeSlice(21908fn analyzeSlice(
src/Zir.zig+43
...@@ -319,6 +319,23 @@ pub const Inst = struct {...@@ -319,6 +319,23 @@ pub const Inst = struct {
319 /// only the taken branch is analyzed. The then block and else block must319 /// only the taken branch is analyzed. The then block and else block must
320 /// terminate with an "inline" variant of a noreturn instruction.320 /// terminate with an "inline" variant of a noreturn instruction.
321 condbr_inline,321 condbr_inline,
322 /// Given an operand which is an error union, splits control flow. In
323 /// case of error, control flow goes into the block that is part of this
324 /// instruction, which is guaranteed to end with a return instruction
325 /// and never breaks out of the block.
326 /// In the case of non-error, control flow proceeds to the next instruction
327 /// after the `try`, with the result of this instruction being the unwrapped
328 /// payload value, as if `err_union_payload_unsafe` was executed on the operand.
329 /// Uses the `pl_node` union field. Payload is `Try`.
330 @"try",
331 ///// Same as `try` except the operand is coerced to a comptime value, and
332 ///// only the taken branch is analyzed. The block must terminate with an "inline"
333 ///// variant of a noreturn instruction.
334 //try_inline,
335 /// Same as `try` except the operand is a pointer and the result is a pointer.
336 try_ptr,
337 ///// Same as `try_inline` except the operand is a pointer and the result is a pointer.
338 //try_ptr_inline,
322 /// An error set type definition. Contains a list of field names.339 /// An error set type definition. Contains a list of field names.
323 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.340 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
324 error_set_decl,341 error_set_decl,
...@@ -1231,6 +1248,10 @@ pub const Inst = struct {...@@ -1231,6 +1248,10 @@ pub const Inst = struct {
1231 .closure_capture,1248 .closure_capture,
1232 .ret_ptr,1249 .ret_ptr,
1233 .ret_type,1250 .ret_type,
1251 .@"try",
1252 .try_ptr,
1253 //.try_inline,
1254 //.try_ptr_inline,
1234 => false,1255 => false,
12351256
1236 .@"break",1257 .@"break",
...@@ -1509,6 +1530,10 @@ pub const Inst = struct {...@@ -1509,6 +1530,10 @@ pub const Inst = struct {
1509 .repeat,1530 .repeat,
1510 .repeat_inline,1531 .repeat_inline,
1511 .panic,1532 .panic,
1533 .@"try",
1534 .try_ptr,
1535 //.try_inline,
1536 //.try_ptr_inline,
1512 => false,1537 => false,
15131538
1514 .extended => switch (data.extended.opcode) {1539 .extended => switch (data.extended.opcode) {
...@@ -1569,6 +1594,10 @@ pub const Inst = struct {...@@ -1569,6 +1594,10 @@ pub const Inst = struct {
1569 .coerce_result_ptr = .bin,1594 .coerce_result_ptr = .bin,
1570 .condbr = .pl_node,1595 .condbr = .pl_node,
1571 .condbr_inline = .pl_node,1596 .condbr_inline = .pl_node,
1597 .@"try" = .pl_node,
1598 .try_ptr = .pl_node,
1599 //.try_inline = .pl_node,
1600 //.try_ptr_inline = .pl_node,
1572 .error_set_decl = .pl_node,1601 .error_set_decl = .pl_node,
1573 .error_set_decl_anon = .pl_node,1602 .error_set_decl_anon = .pl_node,
1574 .error_set_decl_func = .pl_node,1603 .error_set_decl_func = .pl_node,
...@@ -2808,6 +2837,14 @@ pub const Inst = struct {...@@ -2808,6 +2837,14 @@ pub const Inst = struct {
2808 else_body_len: u32,2837 else_body_len: u32,
2809 };2838 };
28102839
2840 /// This data is stored inside extra, trailed by:
2841 /// * 0. body: Index // for each `body_len`.
2842 pub const Try = struct {
2843 /// The error union to unwrap.
2844 operand: Ref,
2845 body_len: u32,
2846 };
2847
2811 /// Stored in extra. Depending on the flags in Data, there will be up to 52848 /// Stored in extra. Depending on the flags in Data, there will be up to 5
2812 /// trailing Ref fields:2849 /// trailing Ref fields:
2813 /// 0. sentinel: Ref // if `has_sentinel` flag is set2850 /// 0. sentinel: Ref // if `has_sentinel` flag is set
...@@ -3744,6 +3781,12 @@ fn findDeclsInner(...@@ -3744,6 +3781,12 @@ fn findDeclsInner(
3744 try zir.findDeclsBody(list, then_body);3781 try zir.findDeclsBody(list, then_body);
3745 try zir.findDeclsBody(list, else_body);3782 try zir.findDeclsBody(list, else_body);
3746 },3783 },
3784 .@"try", .try_ptr => {
3785 const inst_data = datas[inst].pl_node;
3786 const extra = zir.extraData(Inst.Try, inst_data.payload_index);
3787 const body = zir.extra[extra.end..][0..extra.data.body_len];
3788 try zir.findDeclsBody(list, body);
3789 },
3747 .switch_block => return findDeclsSwitch(zir, list, inst),3790 .switch_block => return findDeclsSwitch(zir, list, inst),
37483791
3749 .suspend_block => @panic("TODO iterate suspend block"),3792 .suspend_block => @panic("TODO iterate suspend block"),
src/arch/aarch64/CodeGen.zig+105-28
...@@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
665 .prefetch => try self.airPrefetch(inst),665 .prefetch => try self.airPrefetch(inst),
666 .mul_add => try self.airMulAdd(inst),666 .mul_add => try self.airMulAdd(inst),
667667
668 .@"try" => try self.airTry(inst),
669 .try_ptr => try self.airTryPtr(inst),
670
668 .dbg_var_ptr,671 .dbg_var_ptr,
669 .dbg_var_val,672 .dbg_var_val,
670 => try self.airDbgVar(inst),673 => try self.airDbgVar(inst),
...@@ -2305,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2305,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2305 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2308 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2306}2309}
23072310
2311/// Given an error union, returns the error
2312fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
2313 const err_ty = error_union_ty.errorUnionSet();
2314 const payload_ty = error_union_ty.errorUnionPayload();
2315 if (err_ty.errorSetCardinality() == .zero) {
2316 return MCValue{ .immediate = 0 };
2317 }
2318 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
2319 return error_union_mcv;
2320 }
2321
2322 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
2323 switch (error_union_mcv) {
2324 .register => return self.fail("TODO errUnionErr for registers", .{}),
2325 .stack_offset => |off| {
2326 return MCValue{ .stack_offset = off - err_offset };
2327 },
2328 .memory => |addr| {
2329 return MCValue{ .memory = addr + err_offset };
2330 },
2331 else => unreachable, // invalid MCValue for an error union
2332 }
2333}
2334
2308fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {2335fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
2309 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2336 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2310 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2337 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2311 const error_union_ty = self.air.typeOf(ty_op.operand);2338 const error_union_ty = self.air.typeOf(ty_op.operand);
2312 const payload_ty = error_union_ty.errorUnionPayload();
2313 const mcv = try self.resolveInst(ty_op.operand);2339 const mcv = try self.resolveInst(ty_op.operand);
2314 if (!payload_ty.hasRuntimeBits()) break :result mcv;2340 break :result try self.errUnionErr(mcv, error_union_ty);
2315
2316 return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
2317 };2341 };
2318 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2342 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2319}2343}
23202344
2345/// Given an error union, returns the payload
2346fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
2347 const err_ty = error_union_ty.errorUnionSet();
2348 const payload_ty = error_union_ty.errorUnionPayload();
2349 if (err_ty.errorSetCardinality() == .zero) {
2350 return error_union_mcv;
2351 }
2352 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
2353 return MCValue.none;
2354 }
2355
2356 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
2357 switch (error_union_mcv) {
2358 .register => return self.fail("TODO errUnionPayload for registers", .{}),
2359 .stack_offset => |off| {
2360 return MCValue{ .stack_offset = off - payload_offset };
2361 },
2362 .memory => |addr| {
2363 return MCValue{ .memory = addr + payload_offset };
2364 },
2365 else => unreachable, // invalid MCValue for an error union
2366 }
2367}
2368
2321fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {2369fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
2322 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2370 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2323 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2324 const error_union_ty = self.air.typeOf(ty_op.operand);2372 const error_union_ty = self.air.typeOf(ty_op.operand);
2325 const payload_ty = error_union_ty.errorUnionPayload();2373 const error_union = try self.resolveInst(ty_op.operand);
2326 if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;2374 break :result try self.errUnionPayload(error_union, error_union_ty);
2327
2328 return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
2329 };2375 };
2330 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2376 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2331}2377}
...@@ -3386,45 +3432,38 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {...@@ -3386,45 +3432,38 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
3386 return self.finishAir(inst, .dead, .{ operand, .none, .none });3432 return self.finishAir(inst, .dead, .{ operand, .none, .none });
3387}3433}
33883434
3389fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {3435fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
3390 const pl_op = self.air.instructions.items(.data)[inst].pl_op;3436 switch (condition) {
3391 const cond = try self.resolveInst(pl_op.operand);
3392 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
3393 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
3394 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
3395 const liveness_condbr = self.liveness.getCondBr(inst);
3396
3397 const reloc: Mir.Inst.Index = switch (cond) {
3398 .compare_flags_signed,3437 .compare_flags_signed,
3399 .compare_flags_unsigned,3438 .compare_flags_unsigned,
3400 => try self.addInst(.{3439 => return try self.addInst(.{
3401 .tag = .b_cond,3440 .tag = .b_cond,
3402 .data = .{3441 .data = .{
3403 .inst_cond = .{3442 .inst_cond = .{
3404 .inst = undefined, // populated later through performReloc3443 .inst = undefined, // populated later through performReloc
3405 .cond = switch (cond) {3444 .cond = switch (condition) {
3406 .compare_flags_signed => |cmp_op| blk: {3445 .compare_flags_signed => |cmp_op| blk: {
3407 // Here we map to the opposite condition because the jump is to the false branch.3446 // Here we map to the opposite condition because the jump is to the false branch.
3408 const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op);3447 const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op);
3409 break :blk condition.negate();3448 break :blk condition_code.negate();
3410 },3449 },
3411 .compare_flags_unsigned => |cmp_op| blk: {3450 .compare_flags_unsigned => |cmp_op| blk: {
3412 // Here we map to the opposite condition because the jump is to the false branch.3451 // Here we map to the opposite condition because the jump is to the false branch.
3413 const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op);3452 const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op);
3414 break :blk condition.negate();3453 break :blk condition_code.negate();
3415 },3454 },
3416 else => unreachable,3455 else => unreachable,
3417 },3456 },
3418 },3457 },
3419 },3458 },
3420 }),3459 }),
3421 else => blk: {3460 else => {
3422 const reg = switch (cond) {3461 const reg = switch (condition) {
3423 .register => |r| r,3462 .register => |r| r,
3424 else => try self.copyToTmpRegister(Type.bool, cond),3463 else => try self.copyToTmpRegister(Type.bool, condition),
3425 };3464 };
34263465
3427 break :blk try self.addInst(.{3466 return try self.addInst(.{
3428 .tag = .cbz,3467 .tag = .cbz,
3429 .data = .{3468 .data = .{
3430 .r_inst = .{3469 .r_inst = .{
...@@ -3434,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3434,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
3434 },3473 },
3435 });3474 });
3436 },3475 },
3437 };3476 }
3477}
3478
3479fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
3480 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3481 const cond = try self.resolveInst(pl_op.operand);
3482 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
3483 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
3484 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
3485 const liveness_condbr = self.liveness.getCondBr(inst);
3486
3487 const reloc = try self.condBr(cond);
34383488
3439 // If the condition dies here in this condbr instruction, process3489 // If the condition dies here in this condbr instruction, process
3440 // that death now instead of later as this has an effect on3490 // that death now instead of later as this has an effect on
...@@ -4466,6 +4516,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {...@@ -4466,6 +4516,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
4466 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });4516 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
4467}4517}
44684518
4519fn airTry(self: *Self, inst: Air.Inst.Index) !void {
4520 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4521 const extra = self.air.extraData(Air.Try, pl_op.payload);
4522 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4523 const result: MCValue = result: {
4524 const error_union_ty = self.air.typeOf(pl_op.operand);
4525 const error_union = try self.resolveInst(pl_op.operand);
4526 const is_err_result = try self.isErr(error_union_ty, error_union);
4527 const reloc = try self.condBr(is_err_result);
4528
4529 try self.genBody(body);
4530
4531 try self.performReloc(reloc);
4532 break :result try self.errUnionPayload(error_union, error_union_ty);
4533 };
4534 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
4535}
4536
4537fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
4538 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4539 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4540 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4541 _ = body;
4542 return self.fail("TODO implement airTryPtr for arm", .{});
4543 // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
4544}
4545
4469fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {4546fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4470 // First section of indexes correspond to a set number of constant values.4547 // First section of indexes correspond to a set number of constant values.
4471 const ref_int = @enumToInt(inst);4548 const ref_int = @enumToInt(inst);
src/arch/arm/CodeGen.zig+71-42
...@@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
677 .prefetch => try self.airPrefetch(inst),677 .prefetch => try self.airPrefetch(inst),
678 .mul_add => try self.airMulAdd(inst),678 .mul_add => try self.airMulAdd(inst),
679679
680 .@"try" => try self.airTry(inst),
681 .try_ptr => try self.airTryPtr(inst),
682
680 .dbg_var_ptr,683 .dbg_var_ptr,
681 .dbg_var_val,684 .dbg_var_val,
682 => try self.airDbgVar(inst),685 => try self.airDbgVar(inst),
...@@ -1834,8 +1837,8 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1834,8 +1837,8 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1834 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1837 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1835 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1838 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1836 const error_union_ty = self.air.typeOf(ty_op.operand);1839 const error_union_ty = self.air.typeOf(ty_op.operand);
1837 const mcv = try self.resolveInst(ty_op.operand);1840 const error_union = try self.resolveInst(ty_op.operand);
1838 break :result try self.errUnionPayload(mcv, error_union_ty);1841 break :result try self.errUnionPayload(error_union, error_union_ty);
1839 };1842 };
1840 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1843 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1841}1844}
...@@ -3702,6 +3705,42 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {...@@ -3702,6 +3705,42 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
3702 return self.finishAir(inst, .dead, .{ operand, .none, .none });3705 return self.finishAir(inst, .dead, .{ operand, .none, .none });
3703}3706}
37043707
3708/// Given a boolean condition, emit a jump that is taken when that
3709/// condition is false.
3710fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
3711 const condition_code: Condition = switch (condition) {
3712 .cpsr_flags => |cond| cond.negate(),
3713 else => blk: {
3714 const reg = switch (condition) {
3715 .register => |r| r,
3716 else => try self.copyToTmpRegister(Type.bool, condition),
3717 };
3718
3719 try self.spillCompareFlagsIfOccupied();
3720
3721 // cmp reg, 1
3722 // bne ...
3723 _ = try self.addInst(.{
3724 .tag = .cmp,
3725 .cond = .al,
3726 .data = .{ .rr_op = .{
3727 .rd = .r0,
3728 .rn = reg,
3729 .op = Instruction.Operand.imm(1, 0),
3730 } },
3731 });
3732
3733 break :blk .ne;
3734 },
3735 };
3736
3737 return try self.addInst(.{
3738 .tag = .b,
3739 .cond = condition_code,
3740 .data = .{ .inst = undefined }, // populated later through performReloc
3741 });
3742}
3743
3705fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {3744fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
3706 const pl_op = self.air.instructions.items(.data)[inst].pl_op;3745 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3707 const cond_inst = try self.resolveInst(pl_op.operand);3746 const cond_inst = try self.resolveInst(pl_op.operand);
...@@ -3710,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -3710,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
3710 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];3749 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
3711 const liveness_condbr = self.liveness.getCondBr(inst);3750 const liveness_condbr = self.liveness.getCondBr(inst);
37123751
3713 const reloc: Mir.Inst.Index = reloc: {3752 const reloc: Mir.Inst.Index = try self.condBr(cond_inst);
3714 const condition: Condition = switch (cond_inst) {
3715 .cpsr_flags => |cond| cond.negate(),
3716 else => blk: {
3717 const reg = switch (cond_inst) {
3718 .register => |r| r,
3719 else => try self.copyToTmpRegister(Type.bool, cond_inst),
3720 };
3721
3722 try self.spillCompareFlagsIfOccupied();
3723
3724 // cmp reg, 1
3725 // bne ...
3726 _ = try self.addInst(.{
3727 .tag = .cmp,
3728 .cond = .al,
3729 .data = .{ .rr_op = .{
3730 .rd = .r0,
3731 .rn = reg,
3732 .op = Instruction.Operand.imm(1, 0),
3733 } },
3734 });
3735
3736 break :blk .ne;
3737 },
3738 };
3739
3740 break :reloc try self.addInst(.{
3741 .tag = .b,
3742 .cond = condition,
3743 .data = .{ .inst = undefined }, // populated later through performReloc
3744 });
3745 };
37463753
3747 // If the condition dies here in this condbr instruction, process3754 // If the condition dies here in this condbr instruction, process
3748 // that death now instead of later as this has an effect on3755 // that death now instead of later as this has an effect on
...@@ -4154,13 +4161,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4154,13 +4161,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4154 .lhs = condition,4161 .lhs = condition,
4155 .rhs = item,4162 .rhs = item,
4156 } };4163 } };
4157 const cmp_result = try self.cmp(operands, condition_ty, .neq);4164 const cmp_result = try self.cmp(operands, condition_ty, .eq);
41584165 relocs[0] = try self.condBr(cmp_result);
4159 relocs[0] = try self.addInst(.{
4160 .tag = .b,
4161 .cond = cmp_result.cpsr_flags,
4162 .data = .{ .inst = undefined }, // populated later through performReloc
4163 });
4164 } else {4166 } else {
4165 return self.fail("TODO switch with multiple items", .{});4167 return self.fail("TODO switch with multiple items", .{});
4166 }4168 }
...@@ -5145,6 +5147,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {...@@ -5145,6 +5147,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
5145 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });5147 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
5146}5148}
51475149
5150fn airTry(self: *Self, inst: Air.Inst.Index) !void {
5151 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5152 const extra = self.air.extraData(Air.Try, pl_op.payload);
5153 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5154 const result: MCValue = result: {
5155 const error_union_ty = self.air.typeOf(pl_op.operand);
5156 const error_union = try self.resolveInst(pl_op.operand);
5157 const is_err_result = try self.isErr(error_union_ty, error_union);
5158 const reloc = try self.condBr(is_err_result);
5159
5160 try self.genBody(body);
5161
5162 try self.performReloc(reloc);
5163 break :result try self.errUnionPayload(error_union, error_union_ty);
5164 };
5165 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
5166}
5167
5168fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
5169 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5170 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
5171 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5172 _ = body;
5173 return self.fail("TODO implement airTryPtr for arm", .{});
5174 // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
5175}
5176
5148fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {5177fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
5149 // First section of indexes correspond to a set number of constant values.5178 // First section of indexes correspond to a set number of constant values.
5150 const ref_int = @enumToInt(inst);5179 const ref_int = @enumToInt(inst);
src/arch/riscv64/CodeGen.zig+3
...@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
604 .prefetch => try self.airPrefetch(inst),604 .prefetch => try self.airPrefetch(inst),
605 .mul_add => try self.airMulAdd(inst),605 .mul_add => try self.airMulAdd(inst),
606606
607 .@"try" => @panic("TODO"),
608 .try_ptr => @panic("TODO"),
609
607 .dbg_var_ptr,610 .dbg_var_ptr,
608 .dbg_var_val,611 .dbg_var_val,
609 => try self.airDbgVar(inst),612 => try self.airDbgVar(inst),
src/arch/sparc64/CodeGen.zig+3
...@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
604 .prefetch => @panic("TODO try self.airPrefetch(inst)"),604 .prefetch => @panic("TODO try self.airPrefetch(inst)"),
605 .mul_add => @panic("TODO try self.airMulAdd(inst)"),605 .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
607 .dbg_var_ptr,610 .dbg_var_ptr,
608 .dbg_var_val,611 .dbg_var_val,
609 => try self.airDbgVar(inst),612 => try self.airDbgVar(inst),
src/arch/wasm/CodeGen.zig+68
...@@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1490 .int_to_float => self.airIntToFloat(inst),1490 .int_to_float => self.airIntToFloat(inst),
1491 .get_union_tag => self.airGetUnionTag(inst),1491 .get_union_tag => self.airGetUnionTag(inst),
14921492
1493 .@"try" => self.airTry(inst),
1494 .try_ptr => self.airTryPtr(inst),
1495
1493 // TODO1496 // TODO
1494 .dbg_inline_begin,1497 .dbg_inline_begin,
1495 .dbg_inline_end,1498 .dbg_inline_end,
...@@ -4623,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -4623,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {
4623 } });4626 } });
4624 return WValue{ .none = {} };4627 return WValue{ .none = {} };
4625}4628}
4629
4630fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4631 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4632 const err_union = try self.resolveInst(pl_op.operand);
4633 const extra = self.air.extraData(Air.Try, pl_op.payload);
4634 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4635 const err_union_ty = self.air.typeOf(pl_op.operand);
4636 return lowerTry(self, err_union, body, err_union_ty, false);
4637}
4638
4639fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4640 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4641 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4642 const err_union_ptr = try self.resolveInst(extra.data.ptr);
4643 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4644 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
4645 return lowerTry(self, err_union_ptr, body, err_union_ty, true);
4646}
4647
4648fn lowerTry(
4649 self: *Self,
4650 err_union: WValue,
4651 body: []const Air.Inst.Index,
4652 err_union_ty: Type,
4653 operand_is_ptr: bool,
4654) InnerError!WValue {
4655 if (operand_is_ptr) {
4656 return self.fail("TODO: lowerTry for pointers", .{});
4657 }
4658
4659 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
4660 return err_union;
4661 }
4662
4663 const pl_ty = err_union_ty.errorUnionPayload();
4664 const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime();
4665
4666 // Block we can jump out of when error is not set
4667 try self.startBlock(.block, wasm.block_empty);
4668
4669 // check if the error tag is set for the error union.
4670 try self.emitWValue(err_union);
4671 if (pl_has_bits) {
4672 const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
4673 try self.addMemArg(.i32_load16_u, .{
4674 .offset = err_union.offset() + err_offset,
4675 .alignment = Type.anyerror.abiAlignment(self.target),
4676 });
4677 }
4678 try self.addTag(.i32_eqz);
4679 try self.addLabel(.br_if, 0); // jump out of block when error is '0'
4680 try self.genBody(body);
4681 try self.endBlock();
4682
4683 // if we reach here it means error was not set, and we want the payload
4684 if (!pl_has_bits) {
4685 return WValue{ .none = {} };
4686 }
4687
4688 const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target));
4689 if (isByRef(pl_ty, self.target)) {
4690 return buildPointerOffset(self, err_union, pl_offset, .new);
4691 }
4692 return self.load(err_union, pl_ty, pl_offset);
4693}
src/arch/x86_64/CodeGen.zig+69-11
...@@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
681 .prefetch => try self.airPrefetch(inst),681 .prefetch => try self.airPrefetch(inst),
682 .mul_add => try self.airMulAdd(inst),682 .mul_add => try self.airMulAdd(inst),
683683
684 .@"try" => try self.airTry(inst),
685 .try_ptr => try self.airTryPtr(inst),
686
684 .dbg_var_ptr,687 .dbg_var_ptr,
685 .dbg_var_val,688 .dbg_var_val,
686 => try self.airDbgVar(inst),689 => try self.airDbgVar(inst),
...@@ -1804,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1804,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1804 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });1807 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1805 }1808 }
1806 const err_union_ty = self.air.typeOf(ty_op.operand);1809 const err_union_ty = self.air.typeOf(ty_op.operand);
1810 const operand = try self.resolveInst(ty_op.operand);
1811 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand);
1812 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1813}
1814
1815fn genUnwrapErrorUnionPayloadMir(
1816 self: *Self,
1817 maybe_inst: ?Air.Inst.Index,
1818 err_union_ty: Type,
1819 err_union: MCValue,
1820) !MCValue {
1807 const payload_ty = err_union_ty.errorUnionPayload();1821 const payload_ty = err_union_ty.errorUnionPayload();
1808 const err_ty = err_union_ty.errorUnionSet();1822 const err_ty = err_union_ty.errorUnionSet();
1809 const operand = try self.resolveInst(ty_op.operand);
18101823
1811 const result: MCValue = result: {1824 const result: MCValue = result: {
1812 if (err_ty.errorSetCardinality() == .zero) {1825 if (err_ty.errorSetCardinality() == .zero) {
1813 // TODO check if we can reuse1826 // TODO check if we can reuse
1814 break :result operand;1827 break :result err_union;
1815 }1828 }
18161829
1817 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {1830 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
...@@ -1819,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1819,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1819 }1832 }
18201833
1821 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);1834 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1822 switch (operand) {1835 switch (err_union) {
1823 .stack_offset => |off| {1836 .stack_offset => |off| {
1824 const offset = off - @intCast(i32, payload_off);1837 const offset = off - @intCast(i32, payload_off);
1825 break :result MCValue{ .stack_offset = offset };1838 break :result MCValue{ .stack_offset = offset };
...@@ -1828,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1828,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1828 // TODO reuse operand1841 // TODO reuse operand
1829 const lock = self.register_manager.lockRegAssumeUnused(reg);1842 const lock = self.register_manager.lockRegAssumeUnused(reg);
1830 defer self.register_manager.unlockReg(lock);1843 defer self.register_manager.unlockReg(lock);
1831 const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand);1844 const result_reg: Register = if (maybe_inst) |inst|
1845 (try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register
1846 else
1847 try self.copyToTmpRegister(err_union_ty, err_union);
1832 if (payload_off > 0) {1848 if (payload_off > 0) {
1833 const shift = @intCast(u6, payload_off * 8);1849 const shift = @intCast(u6, payload_off * 8);
1834 try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift });1850 try self.genShiftBinOpMir(.shr, err_union_ty, result_reg, .{ .immediate = shift });
1835 } else {1851 } else {
1836 try self.truncateRegister(payload_ty, result.register);1852 try self.truncateRegister(payload_ty, result_reg);
1837 }1853 }
1838 break :result result;1854 break :result MCValue{ .register = result_reg };
1839 },1855 },
1840 else => return self.fail("TODO implement unwrap_err_payload for {}", .{operand}),1856 else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}),
1841 }1857 }
1842 };1858 };
1843 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1859
1860 return result;
1844}1861}
18451862
1846// *(E!T) -> E1863// *(E!T) -> E
...@@ -4228,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -4228,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
4228 return self.finishAir(inst, result, .{ un_op, .none, .none });4245 return self.finishAir(inst, result, .{ un_op, .none, .none });
4229}4246}
42304247
4248fn airTry(self: *Self, inst: Air.Inst.Index) !void {
4249 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4250 const extra = self.air.extraData(Air.Try, pl_op.payload);
4251 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4252 const err_union_ty = self.air.typeOf(pl_op.operand);
4253 const err_union = try self.resolveInst(pl_op.operand);
4254 const result = try self.genTry(inst, err_union, body, err_union_ty, false);
4255 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
4256}
4257
4258fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
4259 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4260 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
4261 const body = self.air.extra[extra.end..][0..extra.data.body_len];
4262 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
4263 const err_union_ptr = try self.resolveInst(extra.data.ptr);
4264 const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true);
4265 return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
4266}
4267
4268fn genTry(
4269 self: *Self,
4270 inst: Air.Inst.Index,
4271 err_union: MCValue,
4272 body: []const Air.Inst.Index,
4273 err_union_ty: Type,
4274 operand_is_ptr: bool,
4275) !MCValue {
4276 if (operand_is_ptr) {
4277 return self.fail("TODO genTry for pointers", .{});
4278 }
4279 const is_err_mcv = try self.isErr(null, err_union_ty, err_union);
4280 const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
4281 try self.genBody(body);
4282 try self.performReloc(reloc);
4283 const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
4284 return result;
4285}
4286
4231fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {4287fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
4232 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;4288 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
4233 const payload = try self.addExtra(Mir.DbgLineColumn{4289 const payload = try self.addExtra(Mir.DbgLineColumn{
...@@ -4593,7 +4649,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV...@@ -4593,7 +4649,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV
4593 return MCValue{ .eflags = is_null_res.eflags.negate() };4649 return MCValue{ .eflags = is_null_res.eflags.negate() };
4594}4650}
45954651
4596fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {4652fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
4597 const err_type = ty.errorUnionSet();4653 const err_type = ty.errorUnionSet();
45984654
4599 if (err_type.errorSetCardinality() == .zero) {4655 if (err_type.errorSetCardinality() == .zero) {
...@@ -4601,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue...@@ -4601,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
4601 }4657 }
46024658
4603 try self.spillEflagsIfOccupied();4659 try self.spillEflagsIfOccupied();
4604 self.eflags_inst = inst;4660 if (maybe_inst) |inst| {
4661 self.eflags_inst = inst;
4662 }
46054663
4606 const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);4664 const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
4607 switch (operand) {4665 switch (operand) {
src/codegen/c.zig+93
...@@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1875 .union_init => try airUnionInit(f, inst),1875 .union_init => try airUnionInit(f, inst),
1876 .prefetch => try airPrefetch(f, inst),1876 .prefetch => try airPrefetch(f, inst),
18771877
1878 .@"try" => try airTry(f, inst),
1879 .try_ptr => try airTryPtr(f, inst),
1880
1878 .dbg_var_ptr,1881 .dbg_var_ptr,
1879 .dbg_var_val,1882 .dbg_var_val,
1880 => try airDbgVar(f, inst),1883 => try airDbgVar(f, inst),
...@@ -2861,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2861,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
2861 return result;2864 return result;
2862}2865}
28632866
2867fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
2868 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
2869 const err_union = try f.resolveInst(pl_op.operand);
2870 const extra = f.air.extraData(Air.Try, pl_op.payload);
2871 const body = f.air.extra[extra.end..][0..extra.data.body_len];
2872 const err_union_ty = f.air.typeOf(pl_op.operand);
2873 const result_ty = f.air.typeOfIndex(inst);
2874 return lowerTry(f, err_union, body, err_union_ty, false, result_ty);
2875}
2876
2877fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
2878 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2879 const extra = f.air.extraData(Air.TryPtr, ty_pl.payload);
2880 const err_union_ptr = try f.resolveInst(extra.data.ptr);
2881 const body = f.air.extra[extra.end..][0..extra.data.body_len];
2882 const err_union_ty = f.air.typeOf(extra.data.ptr).childType();
2883 const result_ty = f.air.typeOfIndex(inst);
2884 return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty);
2885}
2886
2887fn lowerTry(
2888 f: *Function,
2889 err_union: CValue,
2890 body: []const Air.Inst.Index,
2891 err_union_ty: Type,
2892 operand_is_ptr: bool,
2893 result_ty: Type,
2894) !CValue {
2895 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
2896 // If the error set has no fields, then the payload and the error
2897 // union are the same value.
2898 return err_union;
2899 }
2900
2901 const payload_ty = err_union_ty.errorUnionPayload();
2902 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
2903
2904 const writer = f.object.writer();
2905
2906 err: {
2907 if (!payload_has_bits) {
2908 if (operand_is_ptr) {
2909 try writer.writeAll("if(*");
2910 } else {
2911 try writer.writeAll("if(");
2912 }
2913 try f.writeCValue(writer, err_union);
2914 try writer.writeAll(")");
2915 break :err;
2916 }
2917 if (operand_is_ptr or isByRef(err_union_ty)) {
2918 try writer.writeAll("if(");
2919 try f.writeCValue(writer, err_union);
2920 try writer.writeAll("->error)");
2921 break :err;
2922 }
2923 try writer.writeAll("if(");
2924 try f.writeCValue(writer, err_union);
2925 try writer.writeAll(".error)");
2926 }
2927
2928 try genBody(f, body);
2929 try f.object.indent_writer.insertNewline();
2930
2931 if (!payload_has_bits) {
2932 if (!operand_is_ptr) {
2933 return CValue.none;
2934 } else {
2935 return err_union;
2936 }
2937 }
2938
2939 const local = try f.allocLocal(result_ty, .Const);
2940 if (operand_is_ptr or isByRef(payload_ty)) {
2941 try writer.writeAll(" = &");
2942 try f.writeCValue(writer, err_union);
2943 try writer.writeAll("->payload;\n");
2944 } else {
2945 try writer.writeAll(" = ");
2946 try f.writeCValue(writer, err_union);
2947 try writer.writeAll(".payload;\n");
2948 }
2949 return local;
2950}
2951
2864fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {2952fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
2865 const branch = f.air.instructions.items(.data)[inst].br;2953 const branch = f.air.instructions.items(.data)[inst].br;
2866 const block = f.blocks.get(branch.block_inst).?;2954 const block = f.blocks.get(branch.block_inst).?;
...@@ -4221,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool {...@@ -4221,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool {
4221 }4309 }
4222 return false;4310 return false;
4223}4311}
4312
4313fn isByRef(ty: Type) bool {
4314 _ = ty;
4315 return false;
4316}
src/codegen/llvm.zig+74-3
...@@ -4040,6 +4040,8 @@ pub const FuncGen = struct {...@@ -4040,6 +4040,8 @@ pub const FuncGen = struct {
4040 .ret_addr => try self.airRetAddr(inst),4040 .ret_addr => try self.airRetAddr(inst),
4041 .frame_addr => try self.airFrameAddress(inst),4041 .frame_addr => try self.airFrameAddress(inst),
4042 .cond_br => try self.airCondBr(inst),4042 .cond_br => try self.airCondBr(inst),
4043 .@"try" => try self.airTry(inst),
4044 .try_ptr => try self.airTryPtr(inst),
4043 .intcast => try self.airIntCast(inst),4045 .intcast => try self.airIntCast(inst),
4044 .trunc => try self.airTrunc(inst),4046 .trunc => try self.airTrunc(inst),
4045 .fptrunc => try self.airFptrunc(inst),4047 .fptrunc => try self.airFptrunc(inst),
...@@ -4731,6 +4733,75 @@ pub const FuncGen = struct {...@@ -4731,6 +4733,75 @@ pub const FuncGen = struct {
4731 return null;4733 return null;
4732 }4734 }
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
4734 fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {4805 fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4735 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4806 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4736 const cond = try self.resolveInst(pl_op.operand);4807 const cond = try self.resolveInst(pl_op.operand);
...@@ -5673,15 +5744,14 @@ pub const FuncGen = struct {...@@ -5673,15 +5744,14 @@ pub const FuncGen = struct {
5673 const operand = try self.resolveInst(ty_op.operand);5744 const operand = try self.resolveInst(ty_op.operand);
5674 const operand_ty = self.air.typeOf(ty_op.operand);5745 const operand_ty = self.air.typeOf(ty_op.operand);
5675 const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;5746 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.
5678 if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {5747 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.
5679 return operand;5750 return operand;
5680 }5751 }
5681 const result_ty = self.air.typeOfIndex(inst);5752 const result_ty = self.air.typeOfIndex(inst);
5682 const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;5753 const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
5683 const target = self.dg.module.getTarget();5754 const target = self.dg.module.getTarget();
5684 const offset = errUnionPayloadOffset(payload_ty, target);
56855755
5686 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5756 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5687 if (!operand_is_ptr) return null;5757 if (!operand_is_ptr) return null;
...@@ -5690,6 +5760,7 @@ pub const FuncGen = struct {...@@ -5690,6 +5760,7 @@ pub const FuncGen = struct {
5690 const res_ptr_ty = try self.dg.lowerType(result_ty);5760 const res_ptr_ty = try self.dg.lowerType(result_ty);
5691 return self.builder.buildBitCast(operand, res_ptr_ty, "");5761 return self.builder.buildBitCast(operand, res_ptr_ty, "");
5692 }5762 }
5763 const offset = errUnionPayloadOffset(payload_ty, target);
5693 if (operand_is_ptr or isByRef(payload_ty)) {5764 if (operand_is_ptr or isByRef(payload_ty)) {
5694 return self.builder.buildStructGEP(operand, offset, "");5765 return self.builder.buildStructGEP(operand, offset, "");
5695 }5766 }
src/print_air.zig+32
...@@ -258,6 +258,8 @@ const Writer = struct {...@@ -258,6 +258,8 @@ const Writer = struct {
258 .union_init => try w.writeUnionInit(s, inst),258 .union_init => try w.writeUnionInit(s, inst),
259 .br => try w.writeBr(s, inst),259 .br => try w.writeBr(s, inst),
260 .cond_br => try w.writeCondBr(s, inst),260 .cond_br => try w.writeCondBr(s, inst),
261 .@"try" => try w.writeTry(s, inst),
262 .try_ptr => try w.writeTryPtr(s, inst),
261 .switch_br => try w.writeSwitchBr(s, inst),263 .switch_br => try w.writeSwitchBr(s, inst),
262 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),264 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
263 .fence => try w.writeFence(s, inst),265 .fence => try w.writeFence(s, inst),
...@@ -624,6 +626,36 @@ const Writer = struct {...@@ -624,6 +626,36 @@ const Writer = struct {
624 try w.writeOperand(s, inst, 0, br.operand);626 try w.writeOperand(s, inst, 0, br.operand);
625 }627 }
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
627 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {659 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
628 const pl_op = w.air.instructions.items(.data)[inst].pl_op;660 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
629 const extra = w.air.extraData(Air.CondBr, pl_op.payload);661 const extra = w.air.extraData(Air.CondBr, pl_op.payload);
src/print_zir.zig+21-6
...@@ -374,17 +374,21 @@ const Writer = struct {...@@ -374,17 +374,21 @@ const Writer = struct {
374 .validate_array_init_comptime,374 .validate_array_init_comptime,
375 .c_import,375 .c_import,
376 .typeof_builtin,376 .typeof_builtin,
377 => try self.writePlNodeBlock(stream, inst),377 => try self.writeBlock(stream, inst),
378378
379 .condbr,379 .condbr,
380 .condbr_inline,380 .condbr_inline,
381 => try self.writePlNodeCondBr(stream, inst),381 => try self.writeCondBr(stream, inst),
382
383 .@"try",
384 .try_ptr,
385 => try self.writeTry(stream, inst),
382386
383 .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),387 .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),
384 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),388 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
385 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),389 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
386390
387 .switch_block => try self.writePlNodeSwitchBlock(stream, inst),391 .switch_block => try self.writeSwitchBlock(stream, inst),
388392
389 .field_ptr,393 .field_ptr,
390 .field_val,394 .field_val,
...@@ -1171,7 +1175,7 @@ const Writer = struct {...@@ -1171,7 +1175,7 @@ const Writer = struct {
1171 try self.writeSrc(stream, inst_data.src());1175 try self.writeSrc(stream, inst_data.src());
1172 }1176 }
11731177
1174 fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1178 fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1175 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1179 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1176 try self.writePlNodeBlockWithoutSrc(stream, inst);1180 try self.writePlNodeBlockWithoutSrc(stream, inst);
1177 try self.writeSrc(stream, inst_data.src());1181 try self.writeSrc(stream, inst_data.src());
...@@ -1185,7 +1189,7 @@ const Writer = struct {...@@ -1185,7 +1189,7 @@ const Writer = struct {
1185 try stream.writeAll(") ");1189 try stream.writeAll(") ");
1186 }1190 }
11871191
1188 fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1192 fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1189 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1193 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1190 const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);1194 const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
1191 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];1195 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
...@@ -1199,6 +1203,17 @@ const Writer = struct {...@@ -1199,6 +1203,17 @@ const Writer = struct {
1199 try self.writeSrc(stream, inst_data.src());1203 try self.writeSrc(stream, inst_data.src());
1200 }1204 }
12011205
1206 fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1207 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1208 const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1209 const body = self.code.extra[extra.end..][0..extra.data.body_len];
1210 try self.writeInstRef(stream, extra.data.operand);
1211 try stream.writeAll(", ");
1212 try self.writeBracedBody(stream, body);
1213 try stream.writeAll(") ");
1214 try self.writeSrc(stream, inst_data.src());
1215 }
1216
1202 fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {1217 fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1203 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);1218 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
12041219
...@@ -1746,7 +1761,7 @@ const Writer = struct {...@@ -1746,7 +1761,7 @@ const Writer = struct {
1746 try self.writeSrc(stream, inst_data.src());1761 try self.writeSrc(stream, inst_data.src());
1747 }1762 }
17481763
1749 fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1764 fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1750 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1765 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1751 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);1766 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
17521767