| ... | @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1104 | | 1104 | |
| 1105 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { | 1105 | fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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,39 +2340,81 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 2340 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2335 | } | 2341 | } |
| 2336 | | 2342 | |
| 2337 | fn isNull(self: *Self, operand: MCValue) !MCValue { | 2343 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2338 | _ = operand; | 2344 | if (ty.isPtrLikeOptional()) { |
| 2339 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2345 | assert(ty.abiSize(self.target.*) == 4); |
| 2340 | // will call isNonNull and invert the result. | 2346 | |
| 2341 | return self.fail("TODO call isNonNull and invert the result", .{}); | 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 { | 2360 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2345 | _ = operand; | 2361 | const is_null_result = try self.isNull(ty, operand); |
| 2346 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2362 | assert(is_null_result.compare_flags_unsigned == .eq); |
| 2347 | // will call isNull and invert the result. | 2363 | |
| 2348 | return self.fail("TODO call isNull and invert the result", .{}); | 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 | _ = operand; | 2368 | _ = operand; |
| 2353 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2369 | |
| 2354 | // will call isNonNull and invert the result. | 2370 | const error_type = ty.errorUnionSet(); |
| 2355 | return self.fail("TODO call isNonErr and invert the result", .{}); | 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 { | 2393 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2359 | _ = operand; | 2394 | const is_err_result = try self.isErr(ty, operand); |
| 2360 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2395 | switch (is_err_result) { |
| 2361 | // will call isNull and invert the result. | 2396 | .compare_flags_unsigned => |op| { |
| 2362 | return self.fail("TODO call isErr and invert the result", .{}); | 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 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | 2408 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2366 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 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 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2414 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2368 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,6 +2423,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2375 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2423 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2376 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2424 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2377 | const operand_ptr = try self.resolveInst(un_op); | 2425 | const operand_ptr = try self.resolveInst(un_op); |
| | 2426 | const ptr_ty = self.air.typeOf(un_op); |
| 2378 | const operand: MCValue = blk: { | 2427 | const operand: MCValue = blk: { |
| 2379 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2428 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2380 | // The MCValue that holds the pointer can be re-used as the value. | 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,8 +2432,8 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2383 | break :blk try self.allocRegOrMem(inst, true); | 2432 | break :blk try self.allocRegOrMem(inst, true); |
| 2384 | } | 2433 | } |
| 2385 | }; | 2434 | }; |
| 2386 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2435 | try self.load(operand, operand_ptr, ptr_ty); |
| 2387 | break :result try self.isNull(operand); | 2436 | break :result try self.isNull(ptr_ty.elemType(), operand); |
| 2388 | }; | 2437 | }; |
| 2389 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +2442,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2393 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2442 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2394 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2443 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2395 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,6 +2452,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2402 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2452 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2403 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2453 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2404 | const operand_ptr = try self.resolveInst(un_op); | 2454 | const operand_ptr = try self.resolveInst(un_op); |
| | 2455 | const ptr_ty = self.air.typeOf(un_op); |
| 2405 | const operand: MCValue = blk: { | 2456 | const operand: MCValue = blk: { |
| 2406 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2457 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2407 | // The MCValue that holds the pointer can be re-used as the value. | 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,8 +2461,8 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2410 | break :blk try self.allocRegOrMem(inst, true); | 2461 | break :blk try self.allocRegOrMem(inst, true); |
| 2411 | } | 2462 | } |
| 2412 | }; | 2463 | }; |
| 2413 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2464 | try self.load(operand, operand_ptr, ptr_ty); |
| 2414 | break :result try self.isNonNull(operand); | 2465 | break :result try self.isNonNull(ptr_ty.elemType(), operand); |
| 2415 | }; | 2466 | }; |
| 2416 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +2471,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2420 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2471 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2421 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2472 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2422 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,6 +2481,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2429 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2481 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2430 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2482 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2431 | const operand_ptr = try self.resolveInst(un_op); | 2483 | const operand_ptr = try self.resolveInst(un_op); |
| | 2484 | const ptr_ty = self.air.typeOf(un_op); |
| 2432 | const operand: MCValue = blk: { | 2485 | const operand: MCValue = blk: { |
| 2433 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2486 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2434 | // The MCValue that holds the pointer can be re-used as the value. | 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,8 +2490,8 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2437 | break :blk try self.allocRegOrMem(inst, true); | 2490 | break :blk try self.allocRegOrMem(inst, true); |
| 2438 | } | 2491 | } |
| 2439 | }; | 2492 | }; |
| 2440 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2493 | try self.load(operand, operand_ptr, ptr_ty); |
| 2441 | break :result try self.isErr(operand); | 2494 | break :result try self.isErr(ptr_ty.elemType(), operand); |
| 2442 | }; | 2495 | }; |
| 2443 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +2500,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2447 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2500 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2448 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2501 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2449 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,6 +2510,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2456 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2510 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2457 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2511 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2458 | const operand_ptr = try self.resolveInst(un_op); | 2512 | const operand_ptr = try self.resolveInst(un_op); |
| | 2513 | const ptr_ty = self.air.typeOf(un_op); |
| 2459 | const operand: MCValue = blk: { | 2514 | const operand: MCValue = blk: { |
| 2460 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 2515 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2461 | // The MCValue that holds the pointer can be re-used as the value. | 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,8 +2519,8 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2464 | break :blk try self.allocRegOrMem(inst, true); | 2519 | break :blk try self.allocRegOrMem(inst, true); |
| 2465 | } | 2520 | } |
| 2466 | }; | 2521 | }; |
| 2467 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2522 | try self.load(operand, operand_ptr, ptr_ty); |
| 2468 | break :result try self.isNonErr(operand); | 2523 | break :result try self.isNonErr(ptr_ty.elemType(), operand); |
| 2469 | }; | 2524 | }; |
| 2470 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3365 | } | 3420 | } |
| 3366 | }, | 3421 | }, |
| 3367 | .ErrorSet => { | 3422 | .ErrorSet => { |
| 3368 | switch (typed_value.val.tag()) { | 3423 | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 3369 | .@"error" => { | 3424 | const module = self.bin_file.options.module.?; |
| 3370 | const err_name = typed_value.val.castTag(.@"error").?.data.name; | 3425 | const global_error_set = module.global_error_set; |
| 3371 | const module = self.bin_file.options.module.?; | 3426 | const error_index = global_error_set.get(err_name).?; |
| 3372 | const global_error_set = module.global_error_set; | 3427 | return MCValue{ .immediate = error_index }; |
| 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 | } | | |
| 3381 | }, | 3428 | }, |
| 3382 | .ErrorUnion => { | 3429 | .ErrorUnion => { |
| 3383 | const error_type = typed_value.ty.errorUnionSet(); | 3430 | const error_type = typed_value.ty.errorUnionSet(); |
| 3384 | const payload_type = typed_value.ty.errorUnionPayload(); | 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()) { | 3433 | if (typed_value.val.castTag(.eu_payload)) |pl| { |
| 3388 | // We use the error type directly as the type. | 3434 | if (!payload_type.hasCodeGenBits()) { |
| 3389 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); | 3435 | // We use the error type directly as the type. |
| 3390 | } | 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 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), | 3450 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), |
| 3395 | } | 3451 | } |