| 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,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 of | 337 | /// 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 | }; |
| 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 | pub const StructField = struct { | 810 | pub 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), |
| 995 | 1023 | ||
| 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 | } |
| 1107 | 1140 |
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, |
| 2429 | 2433 | ||
| 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( |
| 4871 | 4875 | ||
| 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", .{}); |
| 4873 | 4877 | ||
| 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: off | 4883 | 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: on | 4886 | 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 bare | 4889 | 0b01 => .@"try", |
| 4890 | // type, whereas this expression has the optional type. Later we make | 4890 | //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); | ||
| 4911 | 4897 | ||
| 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(); |
| 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 | 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); |
| 4919 | 4908 | ||
| 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 | } |
| 4937 | 4916 | ||
| 4938 | fn orelseCatchExpr( | 4917 | fn orelseCatchExpr( |
| ... | @@ -10018,6 +9997,22 @@ const GenZir = struct { | ... | @@ -10018,6 +9997,22 @@ const GenZir = struct { |
| 10018 | gz.unstack(); | 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 | /// 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_gz | 10018 | /// * 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, needs | 1042 | // 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 | } | ||
| 6435 | 6537 | ||
| 6436 | const result_ty = operand_ty.errorUnionPayload(); | 6538 | fn 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 | } |
| 6444 | 6555 | ||
| 6445 | try sema.requireRuntimeBlock(block, src); | 6556 | try sema.requireRuntimeBlock(block, src); |
| 6446 | 6557 | ||
| 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() and | 6559 | 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 | } |
| 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 | /// 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 | } |
| 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 | // A `break` statement is inside a runtime condition, but trying to | 13185 | // A `break` statement is inside a runtime condition, but trying to |
| 12973 | // break from an inline loop. In such case we must convert it to | 13186 | // 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 | } |
| 21624 | 21837 | ||
| 21625 | fn analyzeIsNonErr( | 21838 | fn 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 | |||
| 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 | fn analyzeSlice( | 21908 | fn 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 must | 319 | /// 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, |
| 1235 | 1256 | ||
| 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, |
| 1513 | 1538 | ||
| 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 | }; |
| 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 | /// Stored in extra. Depending on the flags in Data, there will be up to 5 | 2848 | /// 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 set | 2850 | /// 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), |
| 3748 | 3791 | ||
| 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), |
| 667 | 667 | ||
| 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 | } |
| 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 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | 2335 | fn 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 | } |
| 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 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 2369 | fn 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 | } |
| 3388 | 3434 | ||
| 3389 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 3435 | fn 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 performReloc | 3443 | .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 | }; |
| 3426 | 3465 | ||
| 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 | |||
| 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 | // If the condition dies here in this condbr instruction, process | 3489 | // If the condition dies here in this condbr instruction, process |
| 3440 | // that death now instead of later as this has an effect on | 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,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 | } |
| 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 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 4546 | fn 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), |
| 679 | 679 | ||
| 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 | } |
| 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 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 3744 | fn 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); |
| 3712 | 3751 | ||
| 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 | }; | ||
| 3746 | 3753 | ||
| 3747 | // If the condition dies here in this condbr instruction, process | 3754 | // If the condition dies here in this condbr instruction, process |
| 3748 | // that death now instead of later as this has an effect on | 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,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); |
| 4158 | 4165 | 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 | } |
| 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 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 5177 | fn 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), |
| 606 | 606 | ||
| 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)"), |
| 606 | 606 | ||
| 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), |
| 1492 | 1492 | ||
| 1493 | .@"try" => self.airTry(inst), | ||
| 1494 | .try_ptr => self.airTryPtr(inst), | ||
| 1495 | |||
| 1493 | // TODO | 1496 | // 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 | |||
| 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,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), |
| 683 | 683 | ||
| 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 | |||
| 1815 | fn 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); | ||
| 1810 | 1823 | ||
| 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 reuse | 1826 | // TODO check if we can reuse |
| 1814 | break :result operand; | 1827 | break :result err_union; |
| 1815 | } | 1828 | } |
| 1816 | 1829 | ||
| 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 | } |
| 1820 | 1833 | ||
| 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 operand | 1841 | // 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 | } |
| 1845 | 1862 | ||
| 1846 | // *(E!T) -> E | 1863 | // *(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 | } |
| 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 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | 4287 | fn 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 | } |
| 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 | const err_type = ty.errorUnionSet(); | 4653 | const err_type = ty.errorUnionSet(); |
| 4598 | 4654 | ||
| 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 | } |
| 4602 | 4658 | ||
| 4603 | try self.spillEflagsIfOccupied(); | 4659 | try self.spillEflagsIfOccupied(); |
| 4604 | self.eflags_inst = inst; | 4660 | if (maybe_inst) |inst| { |
| 4661 | self.eflags_inst = inst; | ||
| 4662 | } | ||
| 4605 | 4663 | ||
| 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), |
| 1877 | 1877 | ||
| 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 | } |
| 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 | fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { | 2952 | fn 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 | |||
| 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,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 | } |
| 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 | 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); | ||
| 5685 | 5755 | ||
| 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 | } |
| 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 | 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), |
| 378 | 378 | ||
| 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), | ||
| 382 | 386 | ||
| 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), |
| 386 | 390 | ||
| 387 | .switch_block => try self.writePlNodeSwitchBlock(stream, inst), | 391 | .switch_block => try self.writeSwitchBlock(stream, inst), |
| 388 | 392 | ||
| 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 | } |
| 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 | 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 | } |
| 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 | 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 | } |
| 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 | 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); |
| 1204 | 1219 | ||
| ... | @@ -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 | } |
| 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 | 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); |
| 1752 | 1767 |