| ... | @@ -1143,10 +1143,24 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1143,10 +1143,24 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void { |
| 1143 | | 1143 | |
| 1144 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 1144 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1145 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1145 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1146 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1146 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1147 | .dead | 1147 | const operand = try self.resolveInst(ty_op.operand); |
| 1148 | else | 1148 | if (self.wantSafety()) { |
| 1149 | return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); | 1149 | // TODO check for null |
| | 1150 | return self.fail("TODO implement check for null in .optional_payload", .{}); |
| | 1151 | } |
| | 1152 | const dst_mcv: MCValue = blk: { |
| | 1153 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| | 1154 | break :blk operand; |
| | 1155 | } else { |
| | 1156 | break :blk try self.allocRegOrMem(inst, true); |
| | 1157 | } |
| | 1158 | }; |
| | 1159 | const ty = self.air.typeOf(ty_op.operand); |
| | 1160 | var buf: Type.Payload.ElemType = undefined; |
| | 1161 | try self.load(dst_mcv, operand, ty.optionalChild(&buf)); |
| | 1162 | break :result dst_mcv; |
| | 1163 | }; |
| 1150 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1164 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1151 | } | 1165 | } |
| 1152 | | 1166 | |
| ... | @@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1408,16 +1422,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1408 | .compare_flags_unsigned => unreachable, | 1422 | .compare_flags_unsigned => unreachable, |
| 1409 | .compare_flags_signed => unreachable, | 1423 | .compare_flags_signed => unreachable, |
| 1410 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), | 1424 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1411 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), | 1425 | .ptr_stack_offset => |off| { |
| | 1426 | try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); |
| | 1427 | }, |
| 1412 | .ptr_embedded_in_code => |off| { | 1428 | .ptr_embedded_in_code => |off| { |
| 1413 | try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); | 1429 | try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); |
| 1414 | }, | 1430 | }, |
| 1415 | .embedded_in_code => { | 1431 | .embedded_in_code => { |
| 1416 | return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); | 1432 | return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); |
| 1417 | }, | 1433 | }, |
| 1418 | .register => { | 1434 | .register => |reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = reg }), |
| 1419 | return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch}); | | |
| 1420 | }, | | |
| 1421 | .memory => |addr| { | 1435 | .memory => |addr| { |
| 1422 | const reg = try self.register_manager.allocReg(null, &.{}); | 1436 | const reg = try self.register_manager.allocReg(null, &.{}); |
| 1423 | try self.genSetReg(ptr_ty, reg, .{ .memory = addr }); | 1437 | try self.genSetReg(ptr_ty, reg, .{ .memory = addr }); |
| ... | @@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1479,8 +1493,8 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 1479 | .embedded_in_code => { | 1493 | .embedded_in_code => { |
| 1480 | return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); | 1494 | return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); |
| 1481 | }, | 1495 | }, |
| 1482 | .register => { | 1496 | .register => |reg| { |
| 1483 | return self.fail("TODO implement storing to MCValue.register", .{}); | 1497 | try self.genSetPtrReg(elem_ty, reg, value); |
| 1484 | }, | 1498 | }, |
| 1485 | .memory => { | 1499 | .memory => { |
| 1486 | return self.fail("TODO implement storing to MCValue.memory", .{}); | 1500 | return self.fail("TODO implement storing to MCValue.memory", .{}); |
| ... | @@ -2371,31 +2385,30 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2371,31 +2385,30 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2371 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); | 2385 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); |
| 2372 | } | 2386 | } |
| 2373 | | 2387 | |
| 2374 | fn isNull(self: *Self, operand: MCValue) !MCValue { | 2388 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2375 | _ = operand; | 2389 | try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 }); |
| 2376 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2390 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 2377 | // will call isNonNull and invert the result. | | |
| 2378 | return self.fail("TODO call isNonNull and invert the result", .{}); | | |
| 2379 | } | 2391 | } |
| 2380 | | 2392 | |
| 2381 | fn isNonNull(self: *Self, operand: MCValue) !MCValue { | 2393 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2382 | _ = operand; | 2394 | const is_null_res = try self.isNull(ty, operand); |
| 2383 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2395 | assert(is_null_res.compare_flags_unsigned == .eq); |
| 2384 | // will call isNull and invert the result. | 2396 | return MCValue{ .compare_flags_unsigned = .neq }; |
| 2385 | return self.fail("TODO call isNull and invert the result", .{}); | | |
| 2386 | } | 2397 | } |
| 2387 | | 2398 | |
| 2388 | fn isErr(self: *Self, operand: MCValue) !MCValue { | 2399 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| | 2400 | _ = ty; |
| 2389 | _ = operand; | 2401 | _ = operand; |
| 2390 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2402 | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2391 | // will call isNonNull and invert the result. | 2403 | // will call isNonErr and invert the result. |
| 2392 | return self.fail("TODO call isNonErr and invert the result", .{}); | 2404 | return self.fail("TODO call isNonErr and invert the result", .{}); |
| 2393 | } | 2405 | } |
| 2394 | | 2406 | |
| 2395 | fn isNonErr(self: *Self, operand: MCValue) !MCValue { | 2407 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| | 2408 | _ = ty; |
| 2396 | _ = operand; | 2409 | _ = operand; |
| 2397 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 2410 | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2398 | // will call isNull and invert the result. | 2411 | // will call isErr and invert the result. |
| 2399 | return self.fail("TODO call isErr and invert the result", .{}); | 2412 | return self.fail("TODO call isErr and invert the result", .{}); |
| 2400 | } | 2413 | } |
| 2401 | | 2414 | |
| ... | @@ -2403,7 +2416,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2403,7 +2416,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2403 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2416 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2404 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2417 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2405 | const operand = try self.resolveInst(un_op); | 2418 | const operand = try self.resolveInst(un_op); |
| 2406 | break :result try self.isNull(operand); | 2419 | const ty = self.air.typeOf(un_op); |
| | 2420 | break :result try self.isNull(ty, operand); |
| 2407 | }; | 2421 | }; |
| 2408 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2422 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2409 | } | 2423 | } |
| ... | @@ -2420,8 +2434,9 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2420,8 +2434,9 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2420 | break :blk try self.allocRegOrMem(inst, true); | 2434 | break :blk try self.allocRegOrMem(inst, true); |
| 2421 | } | 2435 | } |
| 2422 | }; | 2436 | }; |
| 2423 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2437 | const ptr_ty = self.air.typeOf(un_op); |
| 2424 | break :result try self.isNull(operand); | 2438 | try self.load(operand, operand_ptr, ptr_ty); |
| | 2439 | break :result try self.isNull(ptr_ty.elemType(), operand); |
| 2425 | }; | 2440 | }; |
| 2426 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2441 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2427 | } | 2442 | } |
| ... | @@ -2430,7 +2445,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2430,7 +2445,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 2430 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2445 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2431 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2446 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2432 | const operand = try self.resolveInst(un_op); | 2447 | const operand = try self.resolveInst(un_op); |
| 2433 | break :result try self.isNonNull(operand); | 2448 | const ty = self.air.typeOf(un_op); |
| | 2449 | break :result try self.isNonNull(ty, operand); |
| 2434 | }; | 2450 | }; |
| 2435 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2451 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2436 | } | 2452 | } |
| ... | @@ -2447,8 +2463,9 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2447,8 +2463,9 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2447 | break :blk try self.allocRegOrMem(inst, true); | 2463 | break :blk try self.allocRegOrMem(inst, true); |
| 2448 | } | 2464 | } |
| 2449 | }; | 2465 | }; |
| 2450 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2466 | const ptr_ty = self.air.typeOf(un_op); |
| 2451 | break :result try self.isNonNull(operand); | 2467 | try self.load(operand, operand_ptr, ptr_ty); |
| | 2468 | break :result try self.isNonNull(ptr_ty.elemType(), operand); |
| 2452 | }; | 2469 | }; |
| 2453 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2470 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2454 | } | 2471 | } |
| ... | @@ -2457,7 +2474,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2457,7 +2474,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2457 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2474 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2458 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2475 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2459 | const operand = try self.resolveInst(un_op); | 2476 | const operand = try self.resolveInst(un_op); |
| 2460 | break :result try self.isErr(operand); | 2477 | const ty = self.air.typeOf(un_op); |
| | 2478 | break :result try self.isErr(ty, operand); |
| 2461 | }; | 2479 | }; |
| 2462 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2480 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2463 | } | 2481 | } |
| ... | @@ -2474,8 +2492,9 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2474,8 +2492,9 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2474 | break :blk try self.allocRegOrMem(inst, true); | 2492 | break :blk try self.allocRegOrMem(inst, true); |
| 2475 | } | 2493 | } |
| 2476 | }; | 2494 | }; |
| 2477 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2495 | const ptr_ty = self.air.typeOf(un_op); |
| 2478 | break :result try self.isErr(operand); | 2496 | try self.load(operand, operand_ptr, ptr_ty); |
| | 2497 | break :result try self.isErr(ptr_ty.elemType(), operand); |
| 2479 | }; | 2498 | }; |
| 2480 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2499 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2481 | } | 2500 | } |
| ... | @@ -2484,7 +2503,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2484,7 +2503,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2484 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 2503 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2485 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2504 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2486 | const operand = try self.resolveInst(un_op); | 2505 | const operand = try self.resolveInst(un_op); |
| 2487 | break :result try self.isNonErr(operand); | 2506 | const ty = self.air.typeOf(un_op); |
| | 2507 | break :result try self.isNonErr(ty, operand); |
| 2488 | }; | 2508 | }; |
| 2489 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2509 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2490 | } | 2510 | } |
| ... | @@ -2501,8 +2521,9 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2501,8 +2521,9 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2501 | break :blk try self.allocRegOrMem(inst, true); | 2521 | break :blk try self.allocRegOrMem(inst, true); |
| 2502 | } | 2522 | } |
| 2503 | }; | 2523 | }; |
| 2504 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | 2524 | const ptr_ty = self.air.typeOf(un_op); |
| 2505 | break :result try self.isNonErr(operand); | 2525 | try self.load(operand, operand_ptr, ptr_ty); |
| | 2526 | break :result try self.isNonErr(ptr_ty.elemType(), operand); |
| 2506 | }; | 2527 | }; |
| 2507 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 2528 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2508 | } | 2529 | } |
| ... | @@ -2899,10 +2920,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -2899,10 +2920,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2899 | } | 2920 | } |
| 2900 | } | 2921 | } |
| 2901 | | 2922 | |
| | 2923 | /// Set pointee via pointer stored in a register. |
| | 2924 | /// mov [reg], value |
| | 2925 | fn genSetPtrReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| | 2926 | switch (mcv) { |
| | 2927 | .dead => unreachable, |
| | 2928 | .unreach, .none => return, // Nothing to do. |
| | 2929 | .immediate => |imm| { |
| | 2930 | const abi_size = ty.abiSize(self.target.*); |
| | 2931 | switch (abi_size) { |
| | 2932 | 1, 2, 4 => { |
| | 2933 | // TODO this is wasteful! |
| | 2934 | // introduce new MIR tag specifically for mov [reg + 0], imm |
| | 2935 | const payload = try self.addExtra(Mir.ImmPair{ |
| | 2936 | .dest_off = 0, |
| | 2937 | .operand = @bitCast(i32, @intCast(u32, imm)), |
| | 2938 | }); |
| | 2939 | _ = try self.addInst(.{ |
| | 2940 | .tag = .mov_mem_imm, |
| | 2941 | .ops = (Mir.Ops{ |
| | 2942 | .reg1 = reg.to64(), |
| | 2943 | .flags = switch (abi_size) { |
| | 2944 | 1 => 0b00, |
| | 2945 | 2 => 0b01, |
| | 2946 | 4 => 0b10, |
| | 2947 | else => unreachable, |
| | 2948 | }, |
| | 2949 | }).encode(), |
| | 2950 | .data = .{ .payload = payload }, |
| | 2951 | }); |
| | 2952 | }, |
| | 2953 | else => { |
| | 2954 | return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size}); |
| | 2955 | }, |
| | 2956 | } |
| | 2957 | }, |
| | 2958 | else => |other| { |
| | 2959 | return self.fail("TODO implement set pointee with {}", .{other}); |
| | 2960 | }, |
| | 2961 | } |
| | 2962 | } |
| | 2963 | |
| 2902 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { | 2964 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| 2903 | switch (mcv) { | 2965 | switch (mcv) { |
| 2904 | .dead => unreachable, | 2966 | .dead => unreachable, |
| 2905 | .ptr_stack_offset => unreachable, | 2967 | .ptr_stack_offset => |unadjusted_off| { |
| | 2968 | const ptr_abi_size = ty.abiSize(self.target.*); |
| | 2969 | const elem_ty = ty.childType(); |
| | 2970 | const elem_abi_size = elem_ty.abiSize(self.target.*); |
| | 2971 | const off = unadjusted_off + elem_abi_size; |
| | 2972 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| | 2973 | return self.fail("stack offset too large", .{}); |
| | 2974 | } |
| | 2975 | _ = try self.addInst(.{ |
| | 2976 | .tag = .lea, |
| | 2977 | .ops = (Mir.Ops{ |
| | 2978 | .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)), |
| | 2979 | .reg2 = .rbp, |
| | 2980 | }).encode(), |
| | 2981 | .data = .{ .imm = -@intCast(i32, off) }, |
| | 2982 | }); |
| | 2983 | }, |
| 2906 | .ptr_embedded_in_code => unreachable, | 2984 | .ptr_embedded_in_code => unreachable, |
| 2907 | .unreach, .none => return, // Nothing to do. | 2985 | .unreach, .none => return, // Nothing to do. |
| 2908 | .undef => { | 2986 | .undef => { |