| author | |
| committer | |
| log | d1bfc83774ffaeeb7646f3003038bc4e4e94f143 |
| tree | cef9979f6d0034dcc3f99567bf12e0b7f3b643c1 |
| parent | be639eecc209d8039176c9750f28e8c960400fb2 |
| parent | 8ca6dc33d1523061f198474c84a68330085d6667 |
| signature |
introduce a "try" ZIR and AIR instruction15 files changed, 919 insertions(+), 153 deletions(-)
src/Air.zig+33| ... | ... | @@ -320,6 +320,20 @@ pub const Inst = struct { |
| 320 | 320 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 321 | 321 | /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. |
| 322 | 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 | 337 | /// A comptime-known value. Uses the `ty_pl` field, payload is index of |
| 324 | 338 | /// `values` array. |
| 325 | 339 | constant, |
| ... | ... | @@ -780,6 +794,19 @@ pub const SwitchBr = struct { |
| 780 | 794 | }; |
| 781 | 795 | }; |
| 782 | 796 | |
| 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 | ||
| 783 | 810 | pub const StructField = struct { |
| 784 | 811 | /// Whether this is a pointer or byval is determined by the AIR tag. |
| 785 | 812 | struct_operand: Inst.Ref, |
| ... | ... | @@ -991,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 991 | 1018 | .shl_with_overflow, |
| 992 | 1019 | .ptr_add, |
| 993 | 1020 | .ptr_sub, |
| 1021 | .try_ptr, | |
| 994 | 1022 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 995 | 1023 | |
| 996 | 1024 | .not, |
| ... | ... | @@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1102 | 1130 | const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; |
| 1103 | 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 | } |
| 1107 | 1140 |
src/AstGen.zig+47-52| ... | ... | @@ -2425,6 +2425,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2425 | 2425 | .param_type, |
| 2426 | 2426 | .ret_ptr, |
| 2427 | 2427 | .ret_type, |
| 2428 | .@"try", | |
| 2429 | .try_ptr, | |
| 2430 | //.try_inline, | |
| 2431 | //.try_ptr_inline, | |
| 2428 | 2432 | => break :b false, |
| 2429 | 2433 | |
| 2430 | 2434 | .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { |
| ... | ... | @@ -4871,68 +4875,43 @@ fn tryExpr( |
| 4871 | 4875 | |
| 4872 | 4876 | if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); |
| 4873 | 4877 | |
| 4874 | var block_scope = parent_gz.makeSubBlock(scope); | |
| 4875 | block_scope.setBreakResultLoc(rl); | |
| 4876 | defer block_scope.unstack(); | |
| 4877 | ||
| 4878 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { | |
| 4878 | const operand_rl: ResultLoc = switch (rl) { | |
| 4879 | 4879 | .ref => .ref, |
| 4880 | 4880 | else => .none, |
| 4881 | 4881 | }; |
| 4882 | const err_ops = switch (operand_rl) { | |
| 4883 | // zig fmt: off | |
| 4884 | .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, | |
| 4885 | else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, | |
| 4886 | // zig fmt: on | |
| 4887 | }; | |
| 4888 | // This could be a pointer or value depending on the `operand_rl` parameter. | |
| 4889 | // We cannot use `block_scope.break_result_loc` because that has the bare | |
| 4890 | // type, whereas this expression has the optional type. Later we make | |
| 4891 | // up for this fact by calling rvalue on the else branch. | |
| 4892 | const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node); | |
| 4893 | const cond = try block_scope.addUnNode(err_ops[0], operand, node); | |
| 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), | |
| 4882 | // This could be a pointer or value depending on the `rl` parameter. | |
| 4883 | const operand = try expr(parent_gz, scope, operand_rl, operand_node); | |
| 4884 | const is_inline = parent_gz.force_comptime; | |
| 4885 | const is_inline_bit = @as(u2, @boolToInt(is_inline)); | |
| 4886 | const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1; | |
| 4887 | const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) { | |
| 4888 | 0b00 => .@"try", | |
| 4889 | 0b01 => .@"try", | |
| 4890 | //0b01 => .try_inline, | |
| 4891 | 0b10 => .try_ptr, | |
| 4892 | 0b11 => .try_ptr, | |
| 4893 | //0b11 => .try_ptr_inline, | |
| 4910 | 4894 | }; |
| 4895 | const try_inst = try parent_gz.makeBlockInst(block_tag, node); | |
| 4896 | try parent_gz.instructions.append(astgen.gpa, try_inst); | |
| 4911 | 4897 | |
| 4912 | // else_scope will be stacked on then_scope as both are stacked on parent_gz | |
| 4913 | 4898 | var else_scope = parent_gz.makeSubBlock(scope); |
| 4914 | 4899 | defer else_scope.unstack(); |
| 4915 | 4900 | |
| 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 | 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); | |
| 4919 | 4908 | |
| 4920 | const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; | |
| 4921 | return finishThenElseBlock( | |
| 4922 | parent_gz, | |
| 4923 | rl, | |
| 4924 | node, | |
| 4925 | &block_scope, | |
| 4926 | &then_scope, | |
| 4927 | &else_scope, | |
| 4928 | condbr, | |
| 4929 | cond, | |
| 4930 | then_result, | |
| 4931 | else_result, | |
| 4932 | block, | |
| 4933 | block, | |
| 4934 | break_tag, | |
| 4935 | ); | |
| 4909 | try else_scope.setTryBody(try_inst, operand); | |
| 4910 | const result = indexToRef(try_inst); | |
| 4911 | switch (rl) { | |
| 4912 | .ref => return result, | |
| 4913 | else => return rvalue(parent_gz, rl, result, node), | |
| 4914 | } | |
| 4936 | 4915 | } |
| 4937 | 4916 | |
| 4938 | 4917 | fn orelseCatchExpr( |
| ... | ... | @@ -10018,6 +9997,22 @@ const GenZir = struct { |
| 10018 | 9997 | gz.unstack(); |
| 10019 | 9998 | } |
| 10020 | 9999 | |
| 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 | 10016 | /// Must be called with the following stack set up: |
| 10022 | 10017 | /// * gz (bottom) |
| 10023 | 10018 | /// * align_gz |
src/Liveness.zig+19| ... | ... | @@ -478,6 +478,12 @@ pub fn categorizeOperand( |
| 478 | 478 | .block => { |
| 479 | 479 | return .complex; |
| 480 | 480 | }, |
| 481 | .@"try" => { | |
| 482 | return .complex; | |
| 483 | }, | |
| 484 | .try_ptr => { | |
| 485 | return .complex; | |
| 486 | }, | |
| 481 | 487 | .loop => { |
| 482 | 488 | return .complex; |
| 483 | 489 | }, |
| ... | ... | @@ -1019,6 +1025,19 @@ fn analyzeInst( |
| 1019 | 1025 | try analyzeWithContext(a, new_set, body); |
| 1020 | 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 | 1041 | .cond_br => { |
| 1023 | 1042 | // Each death that occurs inside one branch, but not the other, needs |
| 1024 | 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 | 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 | 1426 | if (sema.typeOf(air_inst).isNoReturn()) |
| 1327 | 1427 | break always_noreturn; |
| ... | ... | @@ -6426,32 +6526,43 @@ fn zirErrUnionPayload( |
| 6426 | 6526 | const src = inst_data.src(); |
| 6427 | 6527 | const operand = try sema.resolveInst(inst_data.operand); |
| 6428 | 6528 | const operand_src = src; |
| 6429 | const operand_ty = sema.typeOf(operand); | |
| 6430 | if (operand_ty.zigTypeTag() != .ErrorUnion) { | |
| 6529 | const err_union_ty = sema.typeOf(operand); | |
| 6530 | if (err_union_ty.zigTypeTag() != .ErrorUnion) { | |
| 6431 | 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 | } | |
| 6435 | 6537 | |
| 6436 | const result_ty = operand_ty.errorUnionPayload(); | |
| 6437 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { | |
| 6538 | fn analyzeErrUnionPayload( | |
| 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 | 6549 | if (val.getError()) |name| { |
| 6439 | 6550 | return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); |
| 6440 | 6551 | } |
| 6441 | 6552 | const data = val.castTag(.eu_payload).?.data; |
| 6442 | return sema.addConstant(result_ty, data); | |
| 6553 | return sema.addConstant(payload_ty, data); | |
| 6443 | 6554 | } |
| 6444 | 6555 | |
| 6445 | 6556 | try sema.requireRuntimeBlock(block, src); |
| 6446 | 6557 | |
| 6447 | 6558 | // If the error set has no fields then no safety check is needed. |
| 6448 | 6559 | if (safety_check and block.wantSafety() and |
| 6449 | operand_ty.errorUnionSet().errorSetCardinality() != .zero) | |
| 6560 | err_union_ty.errorUnionSet().errorSetCardinality() != .zero) | |
| 6450 | 6561 | { |
| 6451 | 6562 | try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); |
| 6452 | 6563 | } |
| 6453 | 6564 | |
| 6454 | return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); | |
| 6565 | return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); | |
| 6455 | 6566 | } |
| 6456 | 6567 | |
| 6457 | 6568 | /// Pointer in, pointer out. |
| ... | ... | @@ -12969,6 +13080,108 @@ fn zirCondbr( |
| 12969 | 13080 | return always_noreturn; |
| 12970 | 13081 | } |
| 12971 | 13082 | |
| 13083 | fn 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 | ||
| 13128 | fn 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 | 13185 | // A `break` statement is inside a runtime condition, but trying to |
| 12973 | 13186 | // break from an inline loop. In such case we must convert it to |
| 12974 | 13187 | // a runtime break. |
| ... | ... | @@ -21622,7 +21835,7 @@ fn analyzeIsNull( |
| 21622 | 21835 | return block.addUnOp(air_tag, operand); |
| 21623 | 21836 | } |
| 21624 | 21837 | |
| 21625 | fn analyzeIsNonErr( | |
| 21838 | fn analyzeIsNonErrComptimeOnly( | |
| 21626 | 21839 | sema: *Sema, |
| 21627 | 21840 | block: *Block, |
| 21628 | 21841 | src: LazySrcLoc, |
| ... | ... | @@ -21674,8 +21887,22 @@ fn analyzeIsNonErr( |
| 21674 | 21887 | return Air.Inst.Ref.bool_false; |
| 21675 | 21888 | } |
| 21676 | 21889 | } |
| 21677 | try sema.requireRuntimeBlock(block, src); | |
| 21678 | return block.addUnOp(.is_non_err, operand); | |
| 21890 | return Air.Inst.Ref.none; | |
| 21891 | } | |
| 21892 | ||
| 21893 | fn 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 | } |
| 21680 | 21907 | |
| 21681 | 21908 | fn analyzeSlice( |
src/Zir.zig+43| ... | ... | @@ -319,6 +319,23 @@ pub const Inst = struct { |
| 319 | 319 | /// only the taken branch is analyzed. The then block and else block must |
| 320 | 320 | /// terminate with an "inline" variant of a noreturn instruction. |
| 321 | 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 | 339 | /// An error set type definition. Contains a list of field names. |
| 323 | 340 | /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. |
| 324 | 341 | error_set_decl, |
| ... | ... | @@ -1231,6 +1248,10 @@ pub const Inst = struct { |
| 1231 | 1248 | .closure_capture, |
| 1232 | 1249 | .ret_ptr, |
| 1233 | 1250 | .ret_type, |
| 1251 | .@"try", | |
| 1252 | .try_ptr, | |
| 1253 | //.try_inline, | |
| 1254 | //.try_ptr_inline, | |
| 1234 | 1255 | => false, |
| 1235 | 1256 | |
| 1236 | 1257 | .@"break", |
| ... | ... | @@ -1509,6 +1530,10 @@ pub const Inst = struct { |
| 1509 | 1530 | .repeat, |
| 1510 | 1531 | .repeat_inline, |
| 1511 | 1532 | .panic, |
| 1533 | .@"try", | |
| 1534 | .try_ptr, | |
| 1535 | //.try_inline, | |
| 1536 | //.try_ptr_inline, | |
| 1512 | 1537 | => false, |
| 1513 | 1538 | |
| 1514 | 1539 | .extended => switch (data.extended.opcode) { |
| ... | ... | @@ -1569,6 +1594,10 @@ pub const Inst = struct { |
| 1569 | 1594 | .coerce_result_ptr = .bin, |
| 1570 | 1595 | .condbr = .pl_node, |
| 1571 | 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 | 1601 | .error_set_decl = .pl_node, |
| 1573 | 1602 | .error_set_decl_anon = .pl_node, |
| 1574 | 1603 | .error_set_decl_func = .pl_node, |
| ... | ... | @@ -2808,6 +2837,14 @@ pub const Inst = struct { |
| 2808 | 2837 | else_body_len: u32, |
| 2809 | 2838 | }; |
| 2810 | 2839 | |
| 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 | 2848 | /// Stored in extra. Depending on the flags in Data, there will be up to 5 |
| 2812 | 2849 | /// trailing Ref fields: |
| 2813 | 2850 | /// 0. sentinel: Ref // if `has_sentinel` flag is set |
| ... | ... | @@ -3744,6 +3781,12 @@ fn findDeclsInner( |
| 3744 | 3781 | try zir.findDeclsBody(list, then_body); |
| 3745 | 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 | 3790 | .switch_block => return findDeclsSwitch(zir, list, inst), |
| 3748 | 3791 | |
| 3749 | 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 | 665 | .prefetch => try self.airPrefetch(inst), |
| 666 | 666 | .mul_add => try self.airMulAdd(inst), |
| 667 | 667 | |
| 668 | .@"try" => try self.airTry(inst), | |
| 669 | .try_ptr => try self.airTryPtr(inst), | |
| 670 | ||
| 668 | 671 | .dbg_var_ptr, |
| 669 | 672 | .dbg_var_val, |
| 670 | 673 | => try self.airDbgVar(inst), |
| ... | ... | @@ -2305,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 2305 | 2308 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2306 | 2309 | } |
| 2307 | 2310 | |
| 2311 | /// Given an error union, returns the error | |
| 2312 | fn 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 | ||
| 2308 | 2335 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2309 | 2336 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2310 | 2337 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2311 | 2338 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 2312 | const payload_ty = error_union_ty.errorUnionPayload(); | |
| 2313 | 2339 | const mcv = try self.resolveInst(ty_op.operand); |
| 2314 | if (!payload_ty.hasRuntimeBits()) break :result mcv; | |
| 2315 | ||
| 2316 | return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); | |
| 2340 | break :result try self.errUnionErr(mcv, error_union_ty); | |
| 2317 | 2341 | }; |
| 2318 | 2342 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2319 | 2343 | } |
| 2320 | 2344 | |
| 2345 | /// Given an error union, returns the payload | |
| 2346 | fn 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 | ||
| 2321 | 2369 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2322 | 2370 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2323 | 2371 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2324 | 2372 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 2325 | const payload_ty = error_union_ty.errorUnionPayload(); | |
| 2326 | if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; | |
| 2327 | ||
| 2328 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); | |
| 2373 | const error_union = try self.resolveInst(ty_op.operand); | |
| 2374 | break :result try self.errUnionPayload(error_union, error_union_ty); | |
| 2329 | 2375 | }; |
| 2330 | 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 | 3432 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); |
| 3387 | 3433 | } |
| 3388 | 3434 | |
| 3389 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | |
| 3390 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 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) { | |
| 3435 | fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { | |
| 3436 | switch (condition) { | |
| 3398 | 3437 | .compare_flags_signed, |
| 3399 | 3438 | .compare_flags_unsigned, |
| 3400 | => try self.addInst(.{ | |
| 3439 | => return try self.addInst(.{ | |
| 3401 | 3440 | .tag = .b_cond, |
| 3402 | 3441 | .data = .{ |
| 3403 | 3442 | .inst_cond = .{ |
| 3404 | 3443 | .inst = undefined, // populated later through performReloc |
| 3405 | .cond = switch (cond) { | |
| 3444 | .cond = switch (condition) { | |
| 3406 | 3445 | .compare_flags_signed => |cmp_op| blk: { |
| 3407 | 3446 | // Here we map to the opposite condition because the jump is to the false branch. |
| 3408 | const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op); | |
| 3409 | break :blk condition.negate(); | |
| 3447 | const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op); | |
| 3448 | break :blk condition_code.negate(); | |
| 3410 | 3449 | }, |
| 3411 | 3450 | .compare_flags_unsigned => |cmp_op| blk: { |
| 3412 | 3451 | // Here we map to the opposite condition because the jump is to the false branch. |
| 3413 | const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); | |
| 3414 | break :blk condition.negate(); | |
| 3452 | const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); | |
| 3453 | break :blk condition_code.negate(); | |
| 3415 | 3454 | }, |
| 3416 | 3455 | else => unreachable, |
| 3417 | 3456 | }, |
| 3418 | 3457 | }, |
| 3419 | 3458 | }, |
| 3420 | 3459 | }), |
| 3421 | else => blk: { | |
| 3422 | const reg = switch (cond) { | |
| 3460 | else => { | |
| 3461 | const reg = switch (condition) { | |
| 3423 | 3462 | .register => |r| r, |
| 3424 | else => try self.copyToTmpRegister(Type.bool, cond), | |
| 3463 | else => try self.copyToTmpRegister(Type.bool, condition), | |
| 3425 | 3464 | }; |
| 3426 | 3465 | |
| 3427 | break :blk try self.addInst(.{ | |
| 3466 | return try self.addInst(.{ | |
| 3428 | 3467 | .tag = .cbz, |
| 3429 | 3468 | .data = .{ |
| 3430 | 3469 | .r_inst = .{ |
| ... | ... | @@ -3434,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3434 | 3473 | }, |
| 3435 | 3474 | }); |
| 3436 | 3475 | }, |
| 3437 | }; | |
| 3476 | } | |
| 3477 | } | |
| 3478 | ||
| 3479 | fn 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); | |
| 3438 | 3488 | |
| 3439 | 3489 | // If the condition dies here in this condbr instruction, process |
| 3440 | 3490 | // 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 | 4516 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); |
| 4467 | 4517 | } |
| 4468 | 4518 | |
| 4519 | fn 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 | ||
| 4537 | fn 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 | ||
| 4469 | 4546 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4470 | 4547 | // First section of indexes correspond to a set number of constant values. |
| 4471 | 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 | 677 | .prefetch => try self.airPrefetch(inst), |
| 678 | 678 | .mul_add => try self.airMulAdd(inst), |
| 679 | 679 | |
| 680 | .@"try" => try self.airTry(inst), | |
| 681 | .try_ptr => try self.airTryPtr(inst), | |
| 682 | ||
| 680 | 683 | .dbg_var_ptr, |
| 681 | 684 | .dbg_var_val, |
| 682 | 685 | => try self.airDbgVar(inst), |
| ... | ... | @@ -1834,8 +1837,8 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1834 | 1837 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1835 | 1838 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1836 | 1839 | const error_union_ty = self.air.typeOf(ty_op.operand); |
| 1837 | const mcv = try self.resolveInst(ty_op.operand); | |
| 1838 | break :result try self.errUnionPayload(mcv, error_union_ty); | |
| 1840 | const error_union = try self.resolveInst(ty_op.operand); | |
| 1841 | break :result try self.errUnionPayload(error_union, error_union_ty); | |
| 1839 | 1842 | }; |
| 1840 | 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 | 3705 | return self.finishAir(inst, .dead, .{ operand, .none, .none }); |
| 3703 | 3706 | } |
| 3704 | 3707 | |
| 3708 | /// Given a boolean condition, emit a jump that is taken when that | |
| 3709 | /// condition is false. | |
| 3710 | fn 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 | ||
| 3705 | 3744 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3706 | 3745 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 3707 | 3746 | const cond_inst = try self.resolveInst(pl_op.operand); |
| ... | ... | @@ -3710,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3710 | 3749 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 3711 | 3750 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 3712 | 3751 | |
| 3713 | const reloc: Mir.Inst.Index = reloc: { | |
| 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 | }; | |
| 3752 | const reloc: Mir.Inst.Index = try self.condBr(cond_inst); | |
| 3746 | 3753 | |
| 3747 | 3754 | // If the condition dies here in this condbr instruction, process |
| 3748 | 3755 | // 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 | 4161 | .lhs = condition, |
| 4155 | 4162 | .rhs = item, |
| 4156 | 4163 | } }; |
| 4157 | const cmp_result = try self.cmp(operands, condition_ty, .neq); | |
| 4158 | ||
| 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 | const cmp_result = try self.cmp(operands, condition_ty, .eq); | |
| 4165 | relocs[0] = try self.condBr(cmp_result); | |
| 4164 | 4166 | } else { |
| 4165 | 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 | 5147 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); |
| 5146 | 5148 | } |
| 5147 | 5149 | |
| 5150 | fn 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 | ||
| 5168 | fn 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 | ||
| 5148 | 5177 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 5149 | 5178 | // First section of indexes correspond to a set number of constant values. |
| 5150 | 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 | 604 | .prefetch => try self.airPrefetch(inst), |
| 605 | 605 | .mul_add => try self.airMulAdd(inst), |
| 606 | 606 | |
| 607 | .@"try" => @panic("TODO"), | |
| 608 | .try_ptr => @panic("TODO"), | |
| 609 | ||
| 607 | 610 | .dbg_var_ptr, |
| 608 | 611 | .dbg_var_val, |
| 609 | 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 | 604 | .prefetch => @panic("TODO try self.airPrefetch(inst)"), |
| 605 | 605 | .mul_add => @panic("TODO try self.airMulAdd(inst)"), |
| 606 | 606 | |
| 607 | .@"try" => @panic("TODO try self.airTry(inst)"), | |
| 608 | .try_ptr => @panic("TODO try self.airTryPtr(inst)"), | |
| 609 | ||
| 607 | 610 | .dbg_var_ptr, |
| 608 | 611 | .dbg_var_val, |
| 609 | 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 | 1490 | .int_to_float => self.airIntToFloat(inst), |
| 1491 | 1491 | .get_union_tag => self.airGetUnionTag(inst), |
| 1492 | 1492 | |
| 1493 | .@"try" => self.airTry(inst), | |
| 1494 | .try_ptr => self.airTryPtr(inst), | |
| 1495 | ||
| 1493 | 1496 | // TODO |
| 1494 | 1497 | .dbg_inline_begin, |
| 1495 | 1498 | .dbg_inline_end, |
| ... | ... | @@ -4623,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { |
| 4623 | 4626 | } }); |
| 4624 | 4627 | return WValue{ .none = {} }; |
| 4625 | 4628 | } |
| 4629 | ||
| 4630 | fn 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 | ||
| 4639 | fn 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 | ||
| 4648 | fn 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 | 681 | .prefetch => try self.airPrefetch(inst), |
| 682 | 682 | .mul_add => try self.airMulAdd(inst), |
| 683 | 683 | |
| 684 | .@"try" => try self.airTry(inst), | |
| 685 | .try_ptr => try self.airTryPtr(inst), | |
| 686 | ||
| 684 | 687 | .dbg_var_ptr, |
| 685 | 688 | .dbg_var_val, |
| 686 | 689 | => try self.airDbgVar(inst), |
| ... | ... | @@ -1804,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1804 | 1807 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 1805 | 1808 | } |
| 1806 | 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 | ||
| 1815 | fn genUnwrapErrorUnionPayloadMir( | |
| 1816 | self: *Self, | |
| 1817 | maybe_inst: ?Air.Inst.Index, | |
| 1818 | err_union_ty: Type, | |
| 1819 | err_union: MCValue, | |
| 1820 | ) !MCValue { | |
| 1807 | 1821 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 1808 | 1822 | const err_ty = err_union_ty.errorUnionSet(); |
| 1809 | const operand = try self.resolveInst(ty_op.operand); | |
| 1810 | 1823 | |
| 1811 | 1824 | const result: MCValue = result: { |
| 1812 | 1825 | if (err_ty.errorSetCardinality() == .zero) { |
| 1813 | 1826 | // TODO check if we can reuse |
| 1814 | break :result operand; | |
| 1827 | break :result err_union; | |
| 1815 | 1828 | } |
| 1816 | 1829 | |
| 1817 | 1830 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| ... | ... | @@ -1819,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1819 | 1832 | } |
| 1820 | 1833 | |
| 1821 | 1834 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 1822 | switch (operand) { | |
| 1835 | switch (err_union) { | |
| 1823 | 1836 | .stack_offset => |off| { |
| 1824 | 1837 | const offset = off - @intCast(i32, payload_off); |
| 1825 | 1838 | break :result MCValue{ .stack_offset = offset }; |
| ... | ... | @@ -1828,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1828 | 1841 | // TODO reuse operand |
| 1829 | 1842 | const lock = self.register_manager.lockRegAssumeUnused(reg); |
| 1830 | 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 | 1848 | if (payload_off > 0) { |
| 1833 | 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 | 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 | } |
| 1845 | 1862 | |
| 1846 | 1863 | // *(E!T) -> E |
| ... | ... | @@ -4228,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { |
| 4228 | 4245 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4229 | 4246 | } |
| 4230 | 4247 | |
| 4248 | fn 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 | ||
| 4258 | fn 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 | ||
| 4268 | fn 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 | ||
| 4231 | 4287 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 4232 | 4288 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 4233 | 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 | 4649 | return MCValue{ .eflags = is_null_res.eflags.negate() }; |
| 4594 | 4650 | } |
| 4595 | 4651 | |
| 4596 | fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { | |
| 4652 | fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { | |
| 4597 | 4653 | const err_type = ty.errorUnionSet(); |
| 4598 | 4654 | |
| 4599 | 4655 | if (err_type.errorSetCardinality() == .zero) { |
| ... | ... | @@ -4601,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue |
| 4601 | 4657 | } |
| 4602 | 4658 | |
| 4603 | 4659 | try self.spillEflagsIfOccupied(); |
| 4604 | self.eflags_inst = inst; | |
| 4660 | if (maybe_inst) |inst| { | |
| 4661 | self.eflags_inst = inst; | |
| 4662 | } | |
| 4605 | 4663 | |
| 4606 | 4664 | const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); |
| 4607 | 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 | 1875 | .union_init => try airUnionInit(f, inst), |
| 1876 | 1876 | .prefetch => try airPrefetch(f, inst), |
| 1877 | 1877 | |
| 1878 | .@"try" => try airTry(f, inst), | |
| 1879 | .try_ptr => try airTryPtr(f, inst), | |
| 1880 | ||
| 1878 | 1881 | .dbg_var_ptr, |
| 1879 | 1882 | .dbg_var_val, |
| 1880 | 1883 | => try airDbgVar(f, inst), |
| ... | ... | @@ -2861,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2861 | 2864 | return result; |
| 2862 | 2865 | } |
| 2863 | 2866 | |
| 2867 | fn 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 | ||
| 2877 | fn 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 | ||
| 2887 | fn 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 | ||
| 2864 | 2952 | fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2865 | 2953 | const branch = f.air.instructions.items(.data)[inst].br; |
| 2866 | 2954 | const block = f.blocks.get(branch.block_inst).?; |
| ... | ... | @@ -4221,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool { |
| 4221 | 4309 | } |
| 4222 | 4310 | return false; |
| 4223 | 4311 | } |
| 4312 | ||
| 4313 | fn 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 | 4040 | .ret_addr => try self.airRetAddr(inst), |
| 4041 | 4041 | .frame_addr => try self.airFrameAddress(inst), |
| 4042 | 4042 | .cond_br => try self.airCondBr(inst), |
| 4043 | .@"try" => try self.airTry(inst), | |
| 4044 | .try_ptr => try self.airTryPtr(inst), | |
| 4043 | 4045 | .intcast => try self.airIntCast(inst), |
| 4044 | 4046 | .trunc => try self.airTrunc(inst), |
| 4045 | 4047 | .fptrunc => try self.airFptrunc(inst), |
| ... | ... | @@ -4731,6 +4733,75 @@ pub const FuncGen = struct { |
| 4731 | 4733 | return null; |
| 4732 | 4734 | } |
| 4733 | 4735 | |
| 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 | 4805 | fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 4735 | 4806 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4736 | 4807 | const cond = try self.resolveInst(pl_op.operand); |
| ... | ... | @@ -5673,15 +5744,14 @@ pub const FuncGen = struct { |
| 5673 | 5744 | const operand = try self.resolveInst(ty_op.operand); |
| 5674 | 5745 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 5675 | 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 | 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 | 5750 | return operand; |
| 5680 | 5751 | } |
| 5681 | 5752 | const result_ty = self.air.typeOfIndex(inst); |
| 5682 | 5753 | const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; |
| 5683 | 5754 | const target = self.dg.module.getTarget(); |
| 5684 | const offset = errUnionPayloadOffset(payload_ty, target); | |
| 5685 | 5755 | |
| 5686 | 5756 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5687 | 5757 | if (!operand_is_ptr) return null; |
| ... | ... | @@ -5690,6 +5760,7 @@ pub const FuncGen = struct { |
| 5690 | 5760 | const res_ptr_ty = try self.dg.lowerType(result_ty); |
| 5691 | 5761 | return self.builder.buildBitCast(operand, res_ptr_ty, ""); |
| 5692 | 5762 | } |
| 5763 | const offset = errUnionPayloadOffset(payload_ty, target); | |
| 5693 | 5764 | if (operand_is_ptr or isByRef(payload_ty)) { |
| 5694 | 5765 | return self.builder.buildStructGEP(operand, offset, ""); |
| 5695 | 5766 | } |
src/print_air.zig+32| ... | ... | @@ -258,6 +258,8 @@ const Writer = struct { |
| 258 | 258 | .union_init => try w.writeUnionInit(s, inst), |
| 259 | 259 | .br => try w.writeBr(s, inst), |
| 260 | 260 | .cond_br => try w.writeCondBr(s, inst), |
| 261 | .@"try" => try w.writeTry(s, inst), | |
| 262 | .try_ptr => try w.writeTryPtr(s, inst), | |
| 261 | 263 | .switch_br => try w.writeSwitchBr(s, inst), |
| 262 | 264 | .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), |
| 263 | 265 | .fence => try w.writeFence(s, inst), |
| ... | ... | @@ -624,6 +626,36 @@ const Writer = struct { |
| 624 | 626 | try w.writeOperand(s, inst, 0, br.operand); |
| 625 | 627 | } |
| 626 | 628 | |
| 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 | 659 | fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 628 | 660 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; |
| 629 | 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 | 374 | .validate_array_init_comptime, |
| 375 | 375 | .c_import, |
| 376 | 376 | .typeof_builtin, |
| 377 | => try self.writePlNodeBlock(stream, inst), | |
| 377 | => try self.writeBlock(stream, inst), | |
| 378 | 378 | |
| 379 | 379 | .condbr, |
| 380 | 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), | |
| 382 | 386 | |
| 383 | 387 | .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), |
| 384 | 388 | .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), |
| 385 | 389 | .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), |
| 386 | 390 | |
| 387 | .switch_block => try self.writePlNodeSwitchBlock(stream, inst), | |
| 391 | .switch_block => try self.writeSwitchBlock(stream, inst), | |
| 388 | 392 | |
| 389 | 393 | .field_ptr, |
| 390 | 394 | .field_val, |
| ... | ... | @@ -1171,7 +1175,7 @@ const Writer = struct { |
| 1171 | 1175 | try self.writeSrc(stream, inst_data.src()); |
| 1172 | 1176 | } |
| 1173 | 1177 | |
| 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 | 1179 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1176 | 1180 | try self.writePlNodeBlockWithoutSrc(stream, inst); |
| 1177 | 1181 | try self.writeSrc(stream, inst_data.src()); |
| ... | ... | @@ -1185,7 +1189,7 @@ const Writer = struct { |
| 1185 | 1189 | try stream.writeAll(") "); |
| 1186 | 1190 | } |
| 1187 | 1191 | |
| 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 | 1193 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1190 | 1194 | const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); |
| 1191 | 1195 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; |
| ... | ... | @@ -1199,6 +1203,17 @@ const Writer = struct { |
| 1199 | 1203 | try self.writeSrc(stream, inst_data.src()); |
| 1200 | 1204 | } |
| 1201 | 1205 | |
| 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 | 1217 | fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { |
| 1203 | 1218 | const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); |
| 1204 | 1219 | |
| ... | ... | @@ -1746,7 +1761,7 @@ const Writer = struct { |
| 1746 | 1761 | try self.writeSrc(stream, inst_data.src()); |
| 1747 | 1762 | } |
| 1748 | 1763 | |
| 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 | 1765 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1751 | 1766 | const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); |
| 1752 | 1767 |