| ... | ... | @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1104 | 1104 | |
| 1105 | 1105 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1106 | 1106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1107 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); |
| 1107 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1108 | const err_ty = self.air.typeOf(ty_op.operand); |
| 1109 | const payload_ty = err_ty.errorUnionPayload(); |
| 1110 | if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; |
| 1111 | |
| 1112 | return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); |
| 1113 | }; |
| 1108 | 1114 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1109 | 1115 | } |
| 1110 | 1116 | |
| ... | ... | @@ -2358,18 +2364,45 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2358 | 2364 | return MCValue{ .compare_flags_unsigned = .neq }; |
| 2359 | 2365 | } |
| 2360 | 2366 | |
| 2361 | | fn isErr(self: *Self, operand: MCValue) !MCValue { |
| 2367 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2362 | 2368 | _ = operand; |
| 2363 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2364 | | // will call isNonNull and invert the result. |
| 2365 | | return self.fail("TODO call isNonErr and invert the result", .{}); |
| 2369 | |
| 2370 | const error_type = ty.errorUnionSet(); |
| 2371 | const payload_type = ty.errorUnionPayload(); |
| 2372 | |
| 2373 | if (!error_type.hasCodeGenBits()) { |
| 2374 | return MCValue{ .immediate = 0 }; // always false |
| 2375 | } else if (!payload_type.hasCodeGenBits()) { |
| 2376 | if (error_type.abiSize(self.target.*) <= 4) { |
| 2377 | const reg_mcv: MCValue = switch (operand) { |
| 2378 | .register => operand, |
| 2379 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| 2380 | }; |
| 2381 | |
| 2382 | try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); |
| 2383 | |
| 2384 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 2385 | } else { |
| 2386 | return self.fail("TODO isErr for errors with size > 4", .{}); |
| 2387 | } |
| 2388 | } else { |
| 2389 | return self.fail("TODO isErr for non-empty payloads", .{}); |
| 2390 | } |
| 2366 | 2391 | } |
| 2367 | 2392 | |
| 2368 | | fn isNonErr(self: *Self, operand: MCValue) !MCValue { |
| 2369 | | _ = operand; |
| 2370 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2371 | | // will call isNull and invert the result. |
| 2372 | | return self.fail("TODO call isErr and invert the result", .{}); |
| 2393 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2394 | const is_err_result = try self.isErr(ty, operand); |
| 2395 | switch (is_err_result) { |
| 2396 | .compare_flags_unsigned => |op| { |
| 2397 | assert(op == .gt); |
| 2398 | return MCValue{ .compare_flags_unsigned = .lte }; |
| 2399 | }, |
| 2400 | .immediate => |imm| { |
| 2401 | assert(imm == 0); |
| 2402 | return MCValue{ .immediate = 1 }; |
| 2403 | }, |
| 2404 | else => unreachable, |
| 2405 | } |
| 2373 | 2406 | } |
| 2374 | 2407 | |
| 2375 | 2408 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2438,7 +2471,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2438 | 2471 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2439 | 2472 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2440 | 2473 | const operand = try self.resolveInst(un_op); |
| 2441 | | break :result try self.isErr(operand); |
| 2474 | const ty = self.air.typeOf(un_op); |
| 2475 | break :result try self.isErr(ty, operand); |
| 2442 | 2476 | }; |
| 2443 | 2477 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2444 | 2478 | } |
| ... | ... | @@ -2447,6 +2481,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2447 | 2481 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2448 | 2482 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2449 | 2483 | const operand_ptr = try self.resolveInst(un_op); |
| 2484 | const ptr_ty = self.air.typeOf(un_op); |
| 2450 | 2485 | const operand: MCValue = blk: { |
| 2451 | 2486 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2452 | 2487 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2455,8 +2490,8 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2455 | 2490 | break :blk try self.allocRegOrMem(inst, true); |
| 2456 | 2491 | } |
| 2457 | 2492 | }; |
| 2458 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2459 | | break :result try self.isErr(operand); |
| 2493 | try self.load(operand, operand_ptr, ptr_ty); |
| 2494 | break :result try self.isErr(ptr_ty.elemType(), operand); |
| 2460 | 2495 | }; |
| 2461 | 2496 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2462 | 2497 | } |
| ... | ... | @@ -2465,7 +2500,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2465 | 2500 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2466 | 2501 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2467 | 2502 | const operand = try self.resolveInst(un_op); |
| 2468 | | break :result try self.isNonErr(operand); |
| 2503 | const ty = self.air.typeOf(un_op); |
| 2504 | break :result try self.isNonErr(ty, operand); |
| 2469 | 2505 | }; |
| 2470 | 2506 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2471 | 2507 | } |
| ... | ... | @@ -2474,6 +2510,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2474 | 2510 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2475 | 2511 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2476 | 2512 | const operand_ptr = try self.resolveInst(un_op); |
| 2513 | const ptr_ty = self.air.typeOf(un_op); |
| 2477 | 2514 | const operand: MCValue = blk: { |
| 2478 | 2515 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2479 | 2516 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2482,8 +2519,8 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2482 | 2519 | break :blk try self.allocRegOrMem(inst, true); |
| 2483 | 2520 | } |
| 2484 | 2521 | }; |
| 2485 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2486 | | break :result try self.isNonErr(operand); |
| 2522 | try self.load(operand, operand_ptr, ptr_ty); |
| 2523 | break :result try self.isNonErr(ptr_ty.elemType(), operand); |
| 2487 | 2524 | }; |
| 2488 | 2525 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2489 | 2526 | } |
| ... | ... | @@ -3383,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3383 | 3420 | } |
| 3384 | 3421 | }, |
| 3385 | 3422 | .ErrorSet => { |
| 3386 | | switch (typed_value.val.tag()) { |
| 3387 | | .@"error" => { |
| 3388 | | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3389 | | const module = self.bin_file.options.module.?; |
| 3390 | | const global_error_set = module.global_error_set; |
| 3391 | | const error_index = global_error_set.get(err_name).?; |
| 3392 | | return MCValue{ .immediate = error_index }; |
| 3393 | | }, |
| 3394 | | else => { |
| 3395 | | // In this case we are rendering an error union which has a 0 bits payload. |
| 3396 | | return MCValue{ .immediate = 0 }; |
| 3397 | | }, |
| 3398 | | } |
| 3423 | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3424 | const module = self.bin_file.options.module.?; |
| 3425 | const global_error_set = module.global_error_set; |
| 3426 | const error_index = global_error_set.get(err_name).?; |
| 3427 | return MCValue{ .immediate = error_index }; |
| 3399 | 3428 | }, |
| 3400 | 3429 | .ErrorUnion => { |
| 3401 | 3430 | const error_type = typed_value.ty.errorUnionSet(); |
| 3402 | 3431 | const payload_type = typed_value.ty.errorUnionPayload(); |
| 3403 | | const sub_val = typed_value.val.castTag(.eu_payload).?.data; |
| 3404 | 3432 | |
| 3405 | | if (!payload_type.hasCodeGenBits()) { |
| 3406 | | // We use the error type directly as the type. |
| 3407 | | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); |
| 3408 | | } |
| 3433 | if (typed_value.val.castTag(.eu_payload)) |pl| { |
| 3434 | if (!payload_type.hasCodeGenBits()) { |
| 3435 | // We use the error type directly as the type. |
| 3436 | return MCValue{ .immediate = 0 }; |
| 3437 | } |
| 3409 | 3438 | |
| 3410 | | return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); |
| 3439 | _ = pl; |
| 3440 | return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); |
| 3441 | } else { |
| 3442 | if (!payload_type.hasCodeGenBits()) { |
| 3443 | // We use the error type directly as the type. |
| 3444 | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); |
| 3445 | } |
| 3446 | |
| 3447 | return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); |
| 3448 | } |
| 3411 | 3449 | }, |
| 3412 | 3450 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), |
| 3413 | 3451 | } |