| ... | ... | @@ -2334,18 +2334,28 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | 2334 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2335 | 2335 | } |
| 2336 | 2336 | |
| 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", .{}); |
| 2337 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2338 | if (ty.isPtrLikeOptional()) { |
| 2339 | assert(ty.abiSize(self.target.*) == 4); |
| 2340 | |
| 2341 | const reg_mcv: MCValue = switch (operand) { |
| 2342 | .register => operand, |
| 2343 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, |
| 2344 | }; |
| 2345 | |
| 2346 | try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); |
| 2347 | |
| 2348 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 2349 | } else { |
| 2350 | return self.fail("TODO implement non-pointer optionals", .{}); |
| 2351 | } |
| 2342 | 2352 | } |
| 2343 | 2353 | |
| 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", .{}); |
| 2354 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2355 | const is_null_result = try self.isNull(ty, operand); |
| 2356 | assert(is_null_result.compare_flags_unsigned == .eq); |
| 2357 | |
| 2358 | return MCValue{ .compare_flags_unsigned = .neq }; |
| 2349 | 2359 | } |
| 2350 | 2360 | |
| 2351 | 2361 | fn isErr(self: *Self, operand: MCValue) !MCValue { |
| ... | ... | @@ -2364,9 +2374,14 @@ fn isNonErr(self: *Self, operand: MCValue) !MCValue { |
| 2364 | 2374 | |
| 2365 | 2375 | fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2366 | 2376 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2377 | |
| 2378 | try self.spillCompareFlagsIfOccupied(); |
| 2379 | self.compare_flags_inst = inst; |
| 2380 | |
| 2367 | 2381 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2368 | 2382 | const operand = try self.resolveInst(un_op); |
| 2369 | | break :result try self.isNull(operand); |
| 2383 | const ty = self.air.typeOf(un_op); |
| 2384 | break :result try self.isNull(ty, operand); |
| 2370 | 2385 | }; |
| 2371 | 2386 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2372 | 2387 | } |
| ... | ... | @@ -2375,6 +2390,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2375 | 2390 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2376 | 2391 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2377 | 2392 | const operand_ptr = try self.resolveInst(un_op); |
| 2393 | const ptr_ty = self.air.typeOf(un_op); |
| 2378 | 2394 | const operand: MCValue = blk: { |
| 2379 | 2395 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2380 | 2396 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2383,8 +2399,8 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2383 | 2399 | break :blk try self.allocRegOrMem(inst, true); |
| 2384 | 2400 | } |
| 2385 | 2401 | }; |
| 2386 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2387 | | break :result try self.isNull(operand); |
| 2402 | try self.load(operand, operand_ptr, ptr_ty); |
| 2403 | break :result try self.isNull(ptr_ty.elemType(), operand); |
| 2388 | 2404 | }; |
| 2389 | 2405 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2390 | 2406 | } |
| ... | ... | @@ -2393,7 +2409,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2393 | 2409 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2394 | 2410 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2395 | 2411 | const operand = try self.resolveInst(un_op); |
| 2396 | | break :result try self.isNonNull(operand); |
| 2412 | const ty = self.air.typeOf(un_op); |
| 2413 | break :result try self.isNonNull(ty, operand); |
| 2397 | 2414 | }; |
| 2398 | 2415 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2399 | 2416 | } |
| ... | ... | @@ -2402,6 +2419,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2402 | 2419 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2403 | 2420 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2404 | 2421 | const operand_ptr = try self.resolveInst(un_op); |
| 2422 | const ptr_ty = self.air.typeOf(un_op); |
| 2405 | 2423 | const operand: MCValue = blk: { |
| 2406 | 2424 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 2407 | 2425 | // The MCValue that holds the pointer can be re-used as the value. |
| ... | ... | @@ -2410,8 +2428,8 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2410 | 2428 | break :blk try self.allocRegOrMem(inst, true); |
| 2411 | 2429 | } |
| 2412 | 2430 | }; |
| 2413 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 2414 | | break :result try self.isNonNull(operand); |
| 2431 | try self.load(operand, operand_ptr, ptr_ty); |
| 2432 | break :result try self.isNonNull(ptr_ty.elemType(), operand); |
| 2415 | 2433 | }; |
| 2416 | 2434 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2417 | 2435 | } |