| ... | ... | @@ -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 | |
| ... | ... | @@ -2334,39 +2340,81 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | 2340 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2335 | 2341 | } |
| 2336 | 2342 | |
| 2337 | | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| 2338 | | _ = operand; |
| 2339 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2340 | | // will call isNonNull and invert the result. |
| 2341 | | return self.fail("TODO call isNonNull and invert the result", .{}); |
| 2343 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2344 | if (ty.isPtrLikeOptional()) { |
| 2345 | assert(ty.abiSize(self.target.*) == 4); |
| 2346 | |
| 2347 | const reg_mcv: MCValue = switch (operand) { |
| 2348 | .register => operand, |
| 2349 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, |
| 2350 | }; |
| 2351 | |
| 2352 | try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); |
| 2353 | |
| 2354 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 2355 | } else { |
| 2356 | return self.fail("TODO implement non-pointer optionals", .{}); |
| 2357 | } |
| 2342 | 2358 | } |
| 2343 | 2359 | |
| 2344 | | fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 2345 | | _ = operand; |
| 2346 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2347 | | // will call isNull and invert the result. |
| 2348 | | return self.fail("TODO call isNull and invert the result", .{}); |
| 2360 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2361 | const is_null_result = try self.isNull(ty, operand); |
| 2362 | assert(is_null_result.compare_flags_unsigned == .eq); |
| 2363 | |
| 2364 | return MCValue{ .compare_flags_unsigned = .neq }; |
| 2349 | 2365 | } |
| 2350 | 2366 | |
| 2351 | | fn isErr(self: *Self, operand: MCValue) !MCValue { |
| 2367 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2352 | 2368 | _ = operand; |
| 2353 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2354 | | // will call isNonNull and invert the result. |
| 2355 | | 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 | } |
| 2356 | 2391 | } |
| 2357 | 2392 | |
| 2358 | | fn isNonErr(self: *Self, operand: MCValue) !MCValue { |
| 2359 | | _ = operand; |
| 2360 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2361 | | // will call isNull and invert the result. |
| 2362 | | 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 | } |
| 2363 | 2406 | } |
| 2364 | 2407 | |
| 2365 | 2408 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2366 | 2409 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2410 | |
| 2411 | try self.spillCompareFlagsIfOccupied(); |
| 2412 | self.compare_flags_inst = inst; |
| 2413 | |
| 2367 | 2414 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2368 | 2415 | const operand = try self.resolveInst(un_op); |
| 2369 | | break :result try self.isNull(operand); |
| 2416 | const ty = self.air.typeOf(un_op); |
| 2417 | break :result try self.isNull(ty, operand); |
| 2370 | 2418 | }; |
| 2371 | 2419 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2372 | 2420 | } |
| ... | ... | @@ -2375,6 +2423,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2375 | 2423 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2376 | 2424 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2377 | 2425 | const operand_ptr = try self.resolveInst(un_op); |
| 2426 | const ptr_ty = self.air.typeOf(un_op); |
| 2378 | 2427 | const operand: MCValue = blk: { |
| 2379 | 2428 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2380 | 2429 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2383,8 +2432,8 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2383 | 2432 | break :blk try self.allocRegOrMem(inst, true); |
| 2384 | 2433 | } |
| 2385 | 2434 | }; |
| 2386 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2387 | | break :result try self.isNull(operand); |
| 2435 | try self.load(operand, operand_ptr, ptr_ty); |
| 2436 | break :result try self.isNull(ptr_ty.elemType(), operand); |
| 2388 | 2437 | }; |
| 2389 | 2438 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2390 | 2439 | } |
| ... | ... | @@ -2393,7 +2442,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2393 | 2442 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2394 | 2443 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2395 | 2444 | const operand = try self.resolveInst(un_op); |
| 2396 | | break :result try self.isNonNull(operand); |
| 2445 | const ty = self.air.typeOf(un_op); |
| 2446 | break :result try self.isNonNull(ty, operand); |
| 2397 | 2447 | }; |
| 2398 | 2448 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2399 | 2449 | } |
| ... | ... | @@ -2402,6 +2452,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2402 | 2452 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2403 | 2453 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2404 | 2454 | const operand_ptr = try self.resolveInst(un_op); |
| 2455 | const ptr_ty = self.air.typeOf(un_op); |
| 2405 | 2456 | const operand: MCValue = blk: { |
| 2406 | 2457 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2407 | 2458 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2410,8 +2461,8 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2410 | 2461 | break :blk try self.allocRegOrMem(inst, true); |
| 2411 | 2462 | } |
| 2412 | 2463 | }; |
| 2413 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2414 | | break :result try self.isNonNull(operand); |
| 2464 | try self.load(operand, operand_ptr, ptr_ty); |
| 2465 | break :result try self.isNonNull(ptr_ty.elemType(), operand); |
| 2415 | 2466 | }; |
| 2416 | 2467 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2417 | 2468 | } |
| ... | ... | @@ -2420,7 +2471,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2420 | 2471 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2421 | 2472 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2422 | 2473 | const operand = try self.resolveInst(un_op); |
| 2423 | | break :result try self.isErr(operand); |
| 2474 | const ty = self.air.typeOf(un_op); |
| 2475 | break :result try self.isErr(ty, operand); |
| 2424 | 2476 | }; |
| 2425 | 2477 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2426 | 2478 | } |
| ... | ... | @@ -2429,6 +2481,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2429 | 2481 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2430 | 2482 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2431 | 2483 | const operand_ptr = try self.resolveInst(un_op); |
| 2484 | const ptr_ty = self.air.typeOf(un_op); |
| 2432 | 2485 | const operand: MCValue = blk: { |
| 2433 | 2486 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2434 | 2487 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2437,8 +2490,8 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2437 | 2490 | break :blk try self.allocRegOrMem(inst, true); |
| 2438 | 2491 | } |
| 2439 | 2492 | }; |
| 2440 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2441 | | 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); |
| 2442 | 2495 | }; |
| 2443 | 2496 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2444 | 2497 | } |
| ... | ... | @@ -2447,7 +2500,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2447 | 2500 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2448 | 2501 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2449 | 2502 | const operand = try self.resolveInst(un_op); |
| 2450 | | break :result try self.isNonErr(operand); |
| 2503 | const ty = self.air.typeOf(un_op); |
| 2504 | break :result try self.isNonErr(ty, operand); |
| 2451 | 2505 | }; |
| 2452 | 2506 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2453 | 2507 | } |
| ... | ... | @@ -2456,6 +2510,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2456 | 2510 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2457 | 2511 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2458 | 2512 | const operand_ptr = try self.resolveInst(un_op); |
| 2513 | const ptr_ty = self.air.typeOf(un_op); |
| 2459 | 2514 | const operand: MCValue = blk: { |
| 2460 | 2515 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2461 | 2516 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2464,8 +2519,8 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2464 | 2519 | break :blk try self.allocRegOrMem(inst, true); |
| 2465 | 2520 | } |
| 2466 | 2521 | }; |
| 2467 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2468 | | 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); |
| 2469 | 2524 | }; |
| 2470 | 2525 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2471 | 2526 | } |
| ... | ... | @@ -3365,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3365 | 3420 | } |
| 3366 | 3421 | }, |
| 3367 | 3422 | .ErrorSet => { |
| 3368 | | switch (typed_value.val.tag()) { |
| 3369 | | .@"error" => { |
| 3370 | | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3371 | | const module = self.bin_file.options.module.?; |
| 3372 | | const global_error_set = module.global_error_set; |
| 3373 | | const error_index = global_error_set.get(err_name).?; |
| 3374 | | return MCValue{ .immediate = error_index }; |
| 3375 | | }, |
| 3376 | | else => { |
| 3377 | | // In this case we are rendering an error union which has a 0 bits payload. |
| 3378 | | return MCValue{ .immediate = 0 }; |
| 3379 | | }, |
| 3380 | | } |
| 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 }; |
| 3381 | 3428 | }, |
| 3382 | 3429 | .ErrorUnion => { |
| 3383 | 3430 | const error_type = typed_value.ty.errorUnionSet(); |
| 3384 | 3431 | const payload_type = typed_value.ty.errorUnionPayload(); |
| 3385 | | const sub_val = typed_value.val.castTag(.eu_payload).?.data; |
| 3386 | 3432 | |
| 3387 | | if (!payload_type.hasCodeGenBits()) { |
| 3388 | | // We use the error type directly as the type. |
| 3389 | | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); |
| 3390 | | } |
| 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 | } |
| 3391 | 3438 | |
| 3392 | | 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 | } |
| 3393 | 3449 | }, |
| 3394 | 3450 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), |
| 3395 | 3451 | } |