| ... | ... | @@ -415,9 +415,14 @@ fn gen(self: *Self) InnerError!void { |
| 415 | 415 | |
| 416 | 416 | try self.genBody(self.air.getMainBody()); |
| 417 | 417 | |
| 418 | | if (self.exitlude_jump_relocs.items.len == 1) { |
| 419 | | self.mir_instructions.len -= 1; |
| 420 | | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 418 | // TODO can single exitlude jump reloc be elided? What if it is not at the end of the code? |
| 419 | // Example: |
| 420 | // pub fn main() void { |
| 421 | // maybeErr() catch return; |
| 422 | // unreachable; |
| 423 | // } |
| 424 | // Eliding the reloc will cause a miscompilation in this case. |
| 425 | for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 421 | 426 | self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len); |
| 422 | 427 | } |
| 423 | 428 | |
| ... | ... | @@ -1180,19 +1185,24 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1180 | 1185 | |
| 1181 | 1186 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1182 | 1187 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1183 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1184 | | .dead |
| 1185 | | else |
| 1186 | | return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}); |
| 1188 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1189 | const err_union_ty = self.air.typeOf(ty_op.operand); |
| 1190 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 1191 | const mcv = try self.resolveInst(ty_op.operand); |
| 1192 | if (!payload_ty.hasCodeGenBits()) break :result mcv; |
| 1193 | return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); |
| 1194 | }; |
| 1187 | 1195 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1188 | 1196 | } |
| 1189 | 1197 | |
| 1190 | 1198 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1191 | 1199 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1192 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1193 | | .dead |
| 1194 | | else |
| 1195 | | return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); |
| 1200 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1201 | const err_union_ty = self.air.typeOf(ty_op.operand); |
| 1202 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 1203 | if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; |
| 1204 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| 1205 | }; |
| 1196 | 1206 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1197 | 1207 | } |
| 1198 | 1208 | |
| ... | ... | @@ -2396,19 +2406,35 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2396 | 2406 | } |
| 2397 | 2407 | |
| 2398 | 2408 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2399 | | _ = ty; |
| 2400 | | _ = operand; |
| 2401 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2402 | | // will call isNonErr and invert the result. |
| 2403 | | return self.fail("TODO call isNonErr and invert the result", .{}); |
| 2409 | const err_type = ty.errorUnionSet(); |
| 2410 | const payload_type = ty.errorUnionPayload(); |
| 2411 | if (!err_type.hasCodeGenBits()) { |
| 2412 | return MCValue{ .immediate = 0 }; // always false |
| 2413 | } else if (!payload_type.hasCodeGenBits()) { |
| 2414 | if (err_type.abiSize(self.target.*) <= 8) { |
| 2415 | try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); |
| 2416 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 2417 | } else { |
| 2418 | return self.fail("TODO isErr for errors with size larger than register size", .{}); |
| 2419 | } |
| 2420 | } else { |
| 2421 | return self.fail("TODO isErr for non-empty payloads", .{}); |
| 2422 | } |
| 2404 | 2423 | } |
| 2405 | 2424 | |
| 2406 | 2425 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2407 | | _ = ty; |
| 2408 | | _ = operand; |
| 2409 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2410 | | // will call isErr and invert the result. |
| 2411 | | return self.fail("TODO call isErr and invert the result", .{}); |
| 2426 | const is_err_res = try self.isErr(ty, operand); |
| 2427 | switch (is_err_res) { |
| 2428 | .compare_flags_unsigned => |op| { |
| 2429 | assert(op == .gt); |
| 2430 | return MCValue{ .compare_flags_unsigned = .lte }; |
| 2431 | }, |
| 2432 | .immediate => |imm| { |
| 2433 | assert(imm == 0); |
| 2434 | return MCValue{ .immediate = 1 }; |
| 2435 | }, |
| 2436 | else => unreachable, |
| 2437 | } |
| 2412 | 2438 | } |
| 2413 | 2439 | |
| 2414 | 2440 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3435,31 +3461,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3435 | 3461 | } |
| 3436 | 3462 | }, |
| 3437 | 3463 | .ErrorSet => { |
| 3438 | | switch (typed_value.val.tag()) { |
| 3439 | | .@"error" => { |
| 3440 | | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3441 | | const module = self.bin_file.options.module.?; |
| 3442 | | const global_error_set = module.global_error_set; |
| 3443 | | const error_index = global_error_set.get(err_name).?; |
| 3444 | | return MCValue{ .immediate = error_index }; |
| 3445 | | }, |
| 3446 | | else => { |
| 3447 | | // In this case we are rendering an error union which has a 0 bits payload. |
| 3448 | | return MCValue{ .immediate = 0 }; |
| 3449 | | }, |
| 3450 | | } |
| 3464 | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3465 | const module = self.bin_file.options.module.?; |
| 3466 | const global_error_set = module.global_error_set; |
| 3467 | const error_index = global_error_set.get(err_name).?; |
| 3468 | return MCValue{ .immediate = error_index }; |
| 3451 | 3469 | }, |
| 3452 | 3470 | .ErrorUnion => { |
| 3453 | 3471 | const error_type = typed_value.ty.errorUnionSet(); |
| 3454 | 3472 | const payload_type = typed_value.ty.errorUnionPayload(); |
| 3455 | | const sub_val = typed_value.val.castTag(.eu_payload).?.data; |
| 3456 | 3473 | |
| 3457 | | if (!payload_type.hasCodeGenBits()) { |
| 3458 | | // We use the error type directly as the type. |
| 3459 | | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); |
| 3474 | if (typed_value.val.castTag(.eu_payload)) |pl| { |
| 3475 | if (!payload_type.hasCodeGenBits()) { |
| 3476 | // We use the error type directly as the type. |
| 3477 | return MCValue{ .immediate = 0 }; |
| 3478 | } |
| 3479 | |
| 3480 | _ = pl; |
| 3481 | return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); |
| 3482 | } else { |
| 3483 | if (!payload_type.hasCodeGenBits()) { |
| 3484 | // We use the error type directly as the type. |
| 3485 | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); |
| 3486 | } |
| 3460 | 3487 | } |
| 3461 | 3488 | |
| 3462 | | return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); |
| 3489 | return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); |
| 3463 | 3490 | }, |
| 3464 | 3491 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), |
| 3465 | 3492 | } |