| ... | ... | @@ -119,6 +119,12 @@ pub const MCValue = union(enum) { |
| 119 | 119 | immediate: u64, |
| 120 | 120 | /// The value is in a target-specific register. |
| 121 | 121 | register: Register, |
| 122 | /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, |
| 123 | /// and the operation is an unsigned operation. |
| 124 | register_overflow_unsigned: Register, |
| 125 | /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, |
| 126 | /// and the operation is a signed operation. |
| 127 | register_overflow_signed: Register, |
| 122 | 128 | /// The value is in memory at a hard-coded address. |
| 123 | 129 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 124 | 130 | memory: u64, |
| ... | ... | @@ -144,7 +150,10 @@ pub const MCValue = union(enum) { |
| 144 | 150 | |
| 145 | 151 | fn isMemory(mcv: MCValue) bool { |
| 146 | 152 | return switch (mcv) { |
| 147 | | .memory, .stack_offset => true, |
| 153 | .memory, |
| 154 | .stack_offset, |
| 155 | .ptr_stack_offset, |
| 156 | => true, |
| 148 | 157 | else => false, |
| 149 | 158 | }; |
| 150 | 159 | } |
| ... | ... | @@ -168,6 +177,8 @@ pub const MCValue = union(enum) { |
| 168 | 177 | .compare_flags_signed, |
| 169 | 178 | .ptr_stack_offset, |
| 170 | 179 | .undef, |
| 180 | .register_overflow_unsigned, |
| 181 | .register_overflow_signed, |
| 171 | 182 | => false, |
| 172 | 183 | |
| 173 | 184 | .register, |
| ... | ... | @@ -176,16 +187,33 @@ pub const MCValue = union(enum) { |
| 176 | 187 | }; |
| 177 | 188 | } |
| 178 | 189 | |
| 190 | fn usesCompareFlags(mcv: MCValue) bool { |
| 191 | return switch (mcv) { |
| 192 | .compare_flags_unsigned, |
| 193 | .compare_flags_signed, |
| 194 | .register_overflow_unsigned, |
| 195 | .register_overflow_signed, |
| 196 | => true, |
| 197 | else => false, |
| 198 | }; |
| 199 | } |
| 200 | |
| 179 | 201 | fn isRegister(mcv: MCValue) bool { |
| 180 | 202 | return switch (mcv) { |
| 181 | | .register => true, |
| 203 | .register, |
| 204 | .register_overflow_unsigned, |
| 205 | .register_overflow_signed, |
| 206 | => true, |
| 182 | 207 | else => false, |
| 183 | 208 | }; |
| 184 | 209 | } |
| 185 | 210 | |
| 186 | 211 | fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { |
| 187 | 212 | switch (mcv) { |
| 188 | | .register => |reg| { |
| 213 | .register, |
| 214 | .register_overflow_signed, |
| 215 | .register_overflow_unsigned, |
| 216 | => |reg| { |
| 189 | 217 | mgr.freezeRegs(&.{reg}); |
| 190 | 218 | }, |
| 191 | 219 | else => {}, |
| ... | ... | @@ -194,12 +222,25 @@ pub const MCValue = union(enum) { |
| 194 | 222 | |
| 195 | 223 | fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { |
| 196 | 224 | switch (mcv) { |
| 197 | | .register => |reg| { |
| 225 | .register, |
| 226 | .register_overflow_signed, |
| 227 | .register_overflow_unsigned, |
| 228 | => |reg| { |
| 198 | 229 | mgr.unfreezeRegs(&.{reg}); |
| 199 | 230 | }, |
| 200 | 231 | else => {}, |
| 201 | 232 | } |
| 202 | 233 | } |
| 234 | |
| 235 | fn asRegister(mcv: MCValue) ?Register { |
| 236 | return switch (mcv) { |
| 237 | .register, |
| 238 | .register_overflow_signed, |
| 239 | .register_overflow_unsigned, |
| 240 | => |reg| reg, |
| 241 | else => null, |
| 242 | }; |
| 243 | } |
| 203 | 244 | }; |
| 204 | 245 | |
| 205 | 246 | const Branch = struct { |
| ... | ... | @@ -807,8 +848,11 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 807 | 848 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| 808 | 849 | switch (prev_value) { |
| 809 | 850 | .register => |reg| { |
| 810 | | const canon_reg = reg.to64(); |
| 811 | | self.register_manager.freeReg(canon_reg); |
| 851 | self.register_manager.freeReg(reg.to64()); |
| 852 | }, |
| 853 | .register_overflow_signed, .register_overflow_unsigned => |reg| { |
| 854 | self.register_manager.freeReg(reg.to64()); |
| 855 | self.compare_flags_inst = null; |
| 812 | 856 | }, |
| 813 | 857 | .compare_flags_signed, .compare_flags_unsigned => { |
| 814 | 858 | self.compare_flags_inst = null; |
| ... | ... | @@ -841,18 +885,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 841 | 885 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 842 | 886 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| 843 | 887 | |
| 844 | | switch (result) { |
| 845 | | .register => |reg| { |
| 846 | | // In some cases (such as bitcast), an operand |
| 847 | | // may be the same MCValue as the result. If |
| 848 | | // that operand died and was a register, it |
| 849 | | // was freed by processDeath. We have to |
| 850 | | // "re-allocate" the register. |
| 851 | | if (self.register_manager.isRegFree(reg)) { |
| 852 | | self.register_manager.getRegAssumeFree(reg, inst); |
| 853 | | } |
| 854 | | }, |
| 855 | | else => {}, |
| 888 | if (result.asRegister()) |reg| { |
| 889 | // In some cases (such as bitcast), an operand |
| 890 | // may be the same MCValue as the result. If |
| 891 | // that operand died and was a register, it |
| 892 | // was freed by processDeath. We have to |
| 893 | // "re-allocate" the register. |
| 894 | if (self.register_manager.isRegFree(reg)) { |
| 895 | self.register_manager.getRegAssumeFree(reg, inst); |
| 896 | } |
| 856 | 897 | } |
| 857 | 898 | } |
| 858 | 899 | self.finishAirBookkeeping(); |
| ... | ... | @@ -924,7 +965,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 924 | 965 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 925 | 966 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 926 | 967 | const reg_mcv = self.getResolvedInstValue(inst); |
| 927 | | assert(reg.to64() == reg_mcv.register.to64()); |
| 968 | assert(reg.to64() == reg_mcv.asRegister().?.to64()); |
| 928 | 969 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 929 | 970 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 930 | 971 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); |
| ... | ... | @@ -933,7 +974,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 933 | 974 | pub fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 934 | 975 | if (self.compare_flags_inst) |inst_to_save| { |
| 935 | 976 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 936 | | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| 977 | assert(mcv.usesCompareFlags()); |
| 937 | 978 | |
| 938 | 979 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 939 | 980 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | ... | @@ -1291,17 +1332,21 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1291 | 1332 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1292 | 1333 | } |
| 1293 | 1334 | |
| 1335 | /// Result is always a register. |
| 1294 | 1336 | fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { |
| 1295 | | const dst_ty = self.air.typeOfIndex(inst); |
| 1337 | const dst_ty = self.air.typeOf(op_lhs); |
| 1338 | |
| 1296 | 1339 | const lhs = try self.resolveInst(op_lhs); |
| 1297 | | const rhs = try self.resolveInst(op_rhs); |
| 1340 | lhs.freezeIfRegister(&self.register_manager); |
| 1341 | defer lhs.unfreezeIfRegister(&self.register_manager); |
| 1298 | 1342 | |
| 1343 | const rhs = try self.resolveInst(op_rhs); |
| 1299 | 1344 | rhs.freezeIfRegister(&self.register_manager); |
| 1300 | 1345 | defer rhs.unfreezeIfRegister(&self.register_manager); |
| 1301 | 1346 | |
| 1302 | 1347 | const dst_mcv = blk: { |
| 1303 | | if (self.reuseOperand(inst, op_lhs, 0, lhs)) { |
| 1304 | | if (lhs.isMemory() or lhs.isRegister()) break :blk lhs; |
| 1348 | if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { |
| 1349 | break :blk lhs; |
| 1305 | 1350 | } |
| 1306 | 1351 | break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); |
| 1307 | 1352 | }; |
| ... | ... | @@ -1310,7 +1355,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air |
| 1310 | 1355 | defer dst_mcv.unfreezeIfRegister(&self.register_manager); |
| 1311 | 1356 | |
| 1312 | 1357 | const rhs_mcv = blk: { |
| 1313 | | if (rhs.isRegister()) break :blk rhs; |
| 1358 | if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; |
| 1314 | 1359 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; |
| 1315 | 1360 | }; |
| 1316 | 1361 | |
| ... | ... | @@ -1377,18 +1422,78 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1377 | 1422 | } |
| 1378 | 1423 | |
| 1379 | 1424 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1380 | | _ = inst; |
| 1381 | | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); |
| 1425 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1426 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1427 | |
| 1428 | if (self.liveness.isUnused(inst)) { |
| 1429 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1430 | } |
| 1431 | |
| 1432 | const ty = self.air.typeOf(bin_op.lhs); |
| 1433 | const signedness: std.builtin.Signedness = blk: { |
| 1434 | if (ty.zigTypeTag() != .Int) { |
| 1435 | return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()}); |
| 1436 | } |
| 1437 | break :blk ty.intInfo(self.target.*).signedness; |
| 1438 | }; |
| 1439 | |
| 1440 | const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); |
| 1441 | const result: MCValue = switch (signedness) { |
| 1442 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1443 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1444 | }; |
| 1445 | |
| 1446 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1382 | 1447 | } |
| 1383 | 1448 | |
| 1384 | 1449 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1385 | | _ = inst; |
| 1386 | | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); |
| 1450 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1451 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1452 | |
| 1453 | if (self.liveness.isUnused(inst)) { |
| 1454 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1455 | } |
| 1456 | |
| 1457 | const ty = self.air.typeOf(bin_op.lhs); |
| 1458 | const signedness: std.builtin.Signedness = blk: { |
| 1459 | if (ty.zigTypeTag() != .Int) { |
| 1460 | return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()}); |
| 1461 | } |
| 1462 | break :blk ty.intInfo(self.target.*).signedness; |
| 1463 | }; |
| 1464 | |
| 1465 | const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); |
| 1466 | const result: MCValue = switch (signedness) { |
| 1467 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1468 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1469 | }; |
| 1470 | |
| 1471 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1387 | 1472 | } |
| 1388 | 1473 | |
| 1389 | 1474 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1390 | | _ = inst; |
| 1391 | | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); |
| 1475 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1476 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1477 | |
| 1478 | if (self.liveness.isUnused(inst)) { |
| 1479 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1480 | } |
| 1481 | |
| 1482 | const ty = self.air.typeOf(bin_op.lhs); |
| 1483 | const signedness: std.builtin.Signedness = blk: { |
| 1484 | if (ty.zigTypeTag() != .Int) { |
| 1485 | return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()}); |
| 1486 | } |
| 1487 | break :blk ty.intInfo(self.target.*).signedness; |
| 1488 | }; |
| 1489 | |
| 1490 | const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); |
| 1491 | const result: MCValue = switch (signedness) { |
| 1492 | .signed => .{ .register_overflow_signed = partial.register }, |
| 1493 | .unsigned => .{ .register_overflow_unsigned = partial.register }, |
| 1494 | }; |
| 1495 | |
| 1496 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1392 | 1497 | } |
| 1393 | 1498 | |
| 1394 | 1499 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2422,6 +2527,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2422 | 2527 | .dead => unreachable, |
| 2423 | 2528 | .compare_flags_unsigned => unreachable, |
| 2424 | 2529 | .compare_flags_signed => unreachable, |
| 2530 | .register_overflow_unsigned => unreachable, |
| 2531 | .register_overflow_signed => unreachable, |
| 2425 | 2532 | .immediate => |imm| { |
| 2426 | 2533 | try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); |
| 2427 | 2534 | }, |
| ... | ... | @@ -2545,6 +2652,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2545 | 2652 | .dead => unreachable, |
| 2546 | 2653 | .compare_flags_unsigned => unreachable, |
| 2547 | 2654 | .compare_flags_signed => unreachable, |
| 2655 | .register_overflow_unsigned => unreachable, |
| 2656 | .register_overflow_signed => unreachable, |
| 2548 | 2657 | .immediate => |imm| { |
| 2549 | 2658 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 2550 | 2659 | }, |
| ... | ... | @@ -2899,6 +3008,38 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2899 | 3008 | |
| 2900 | 3009 | break :result dst_mcv; |
| 2901 | 3010 | }, |
| 3011 | .register_overflow_unsigned, |
| 3012 | .register_overflow_signed, |
| 3013 | => |reg| { |
| 3014 | switch (index) { |
| 3015 | 0 => { |
| 3016 | // Get wrapped value for overflow operation. |
| 3017 | break :result MCValue{ .register = reg }; |
| 3018 | }, |
| 3019 | 1 => { |
| 3020 | // Get overflow bit. |
| 3021 | mcv.freezeIfRegister(&self.register_manager); |
| 3022 | defer mcv.unfreezeIfRegister(&self.register_manager); |
| 3023 | |
| 3024 | const dst_reg = try self.register_manager.allocReg(inst); |
| 3025 | const flags: u2 = switch (mcv) { |
| 3026 | .register_overflow_unsigned => 0b10, |
| 3027 | .register_overflow_signed => 0b00, |
| 3028 | else => unreachable, |
| 3029 | }; |
| 3030 | _ = try self.addInst(.{ |
| 3031 | .tag = .cond_set_byte_overflow, |
| 3032 | .ops = (Mir.Ops{ |
| 3033 | .reg1 = dst_reg.to8(), |
| 3034 | .flags = flags, |
| 3035 | }).encode(), |
| 3036 | .data = undefined, |
| 3037 | }); |
| 3038 | break :result MCValue{ .register = dst_reg.to8() }; |
| 3039 | }, |
| 3040 | else => unreachable, |
| 3041 | } |
| 3042 | }, |
| 2902 | 3043 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2903 | 3044 | } |
| 2904 | 3045 | }; |
| ... | ... | @@ -2915,84 +3056,47 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2915 | 3056 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2916 | 3057 | } |
| 2917 | 3058 | |
| 2918 | | /// Perform "binary" operators, excluding comparisons. |
| 2919 | | /// Currently, the following ops are supported: |
| 2920 | | /// ADD, SUB, XOR, OR, AND |
| 3059 | /// Result is always a register. |
| 2921 | 3060 | fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { |
| 2922 | | // TODO: make this algorithm less bad |
| 3061 | const dst_ty = self.air.typeOf(op_lhs); |
| 3062 | |
| 2923 | 3063 | const lhs = try self.resolveInst(op_lhs); |
| 3064 | lhs.freezeIfRegister(&self.register_manager); |
| 3065 | defer lhs.unfreezeIfRegister(&self.register_manager); |
| 3066 | |
| 2924 | 3067 | const rhs = try self.resolveInst(op_rhs); |
| 3068 | rhs.freezeIfRegister(&self.register_manager); |
| 3069 | defer rhs.unfreezeIfRegister(&self.register_manager); |
| 2925 | 3070 | |
| 2926 | | // There are 2 operands, destination and source. |
| 2927 | | // Either one, but not both, can be a memory operand. |
| 2928 | | // Source operand can be an immediate, 8 bits or 32 bits. |
| 2929 | | // So, if either one of the operands dies with this instruction, we can use it |
| 2930 | | // as the result MCValue. |
| 2931 | | const dst_ty = self.air.typeOfIndex(inst); |
| 2932 | | var dst_mcv: MCValue = undefined; |
| 2933 | | var src_mcv: MCValue = undefined; |
| 2934 | | |
| 2935 | | if (self.reuseOperand(inst, op_lhs, 0, lhs)) { |
| 2936 | | // LHS dies; use it as the destination. |
| 2937 | | // Both operands cannot be memory. |
| 2938 | | if (lhs.isMemory() and rhs.isMemory()) { |
| 2939 | | dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); |
| 2940 | | src_mcv = rhs; |
| 2941 | | } else { |
| 2942 | | dst_mcv = lhs; |
| 2943 | | src_mcv = rhs; |
| 2944 | | } |
| 2945 | | } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) { |
| 2946 | | // RHS dies; use it as the destination. |
| 2947 | | // Both operands cannot be memory. |
| 2948 | | if (lhs.isMemory() and rhs.isMemory()) { |
| 2949 | | dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs); |
| 2950 | | src_mcv = lhs; |
| 2951 | | } else { |
| 2952 | | dst_mcv = rhs; |
| 2953 | | src_mcv = lhs; |
| 3071 | var flipped: bool = false; |
| 3072 | const dst_mcv = blk: { |
| 3073 | if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { |
| 3074 | break :blk lhs; |
| 2954 | 3075 | } |
| 2955 | | } else { |
| 2956 | | if (lhs.isMemory()) { |
| 2957 | | rhs.freezeIfRegister(&self.register_manager); |
| 2958 | | defer rhs.unfreezeIfRegister(&self.register_manager); |
| 2959 | | |
| 2960 | | dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); |
| 2961 | | src_mcv = rhs; |
| 2962 | | } else { |
| 2963 | | lhs.freezeIfRegister(&self.register_manager); |
| 2964 | | defer lhs.unfreezeIfRegister(&self.register_manager); |
| 2965 | | |
| 2966 | | dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs); |
| 2967 | | src_mcv = lhs; |
| 3076 | if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { |
| 3077 | flipped = true; |
| 3078 | break :blk rhs; |
| 2968 | 3079 | } |
| 2969 | | } |
| 2970 | | // This instruction supports only signed 32-bit immediates at most. If the immediate |
| 2971 | | // value is larger than this, we put it in a register. |
| 2972 | | // A potential opportunity for future optimization here would be keeping track |
| 2973 | | // of the fact that the instruction is available both as an immediate |
| 2974 | | // and as a register. |
| 2975 | | // TODO consolidate with limitImmediateType() function |
| 2976 | | switch (src_mcv) { |
| 2977 | | .immediate => |imm| { |
| 2978 | | if (imm > math.maxInt(u31)) { |
| 2979 | | dst_mcv.freezeIfRegister(&self.register_manager); |
| 2980 | | defer dst_mcv.unfreezeIfRegister(&self.register_manager); |
| 3080 | break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); |
| 3081 | }; |
| 3082 | dst_mcv.freezeIfRegister(&self.register_manager); |
| 3083 | defer dst_mcv.unfreezeIfRegister(&self.register_manager); |
| 2981 | 3084 | |
| 2982 | | src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.usize, src_mcv) }; |
| 2983 | | } |
| 2984 | | }, |
| 2985 | | else => {}, |
| 2986 | | } |
| 3085 | const src_mcv = blk: { |
| 3086 | const mcv = if (flipped) lhs else rhs; |
| 3087 | if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; |
| 3088 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; |
| 3089 | }; |
| 3090 | src_mcv.freezeIfRegister(&self.register_manager); |
| 3091 | defer src_mcv.unfreezeIfRegister(&self.register_manager); |
| 2987 | 3092 | |
| 2988 | 3093 | const tag = self.air.instructions.items(.tag)[inst]; |
| 2989 | 3094 | switch (tag) { |
| 2990 | | .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), |
| 3095 | .add, .addwrap, .add_with_overflow => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), |
| 2991 | 3096 | .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), |
| 2992 | 3097 | .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), |
| 2993 | | .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv), |
| 2994 | 3098 | .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), |
| 2995 | | .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), |
| 3099 | .mul, .mulwrap, .mul_with_overflow => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), |
| 2996 | 3100 | else => unreachable, |
| 2997 | 3101 | } |
| 2998 | 3102 | return dst_mcv; |
| ... | ... | @@ -3007,11 +3111,15 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3007 | 3111 | .compare_flags_unsigned => unreachable, |
| 3008 | 3112 | .compare_flags_signed => unreachable, |
| 3009 | 3113 | .ptr_stack_offset => unreachable, |
| 3114 | .register_overflow_unsigned => unreachable, |
| 3115 | .register_overflow_signed => unreachable, |
| 3010 | 3116 | .register => |dst_reg| { |
| 3011 | 3117 | switch (src_mcv) { |
| 3012 | 3118 | .none => unreachable, |
| 3013 | 3119 | .undef => try self.genSetReg(dst_ty, dst_reg, .undef), |
| 3014 | 3120 | .dead, .unreach => unreachable, |
| 3121 | .register_overflow_unsigned => unreachable, |
| 3122 | .register_overflow_signed => unreachable, |
| 3015 | 3123 | .ptr_stack_offset => { |
| 3016 | 3124 | self.register_manager.freezeRegs(&.{dst_reg}); |
| 3017 | 3125 | defer self.register_manager.unfreezeRegs(&.{dst_reg}); |
| ... | ... | @@ -3078,6 +3186,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3078 | 3186 | .undef => return self.genSetStack(dst_ty, off, .undef, .{}), |
| 3079 | 3187 | .dead, .unreach => unreachable, |
| 3080 | 3188 | .ptr_stack_offset => unreachable, |
| 3189 | .register_overflow_unsigned => unreachable, |
| 3190 | .register_overflow_signed => unreachable, |
| 3081 | 3191 | .register => |src_reg| { |
| 3082 | 3192 | _ = try self.addInst(.{ |
| 3083 | 3193 | .tag = mir_tag, |
| ... | ... | @@ -3144,6 +3254,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 3144 | 3254 | |
| 3145 | 3255 | // Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. |
| 3146 | 3256 | fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 3257 | const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 3147 | 3258 | switch (dst_mcv) { |
| 3148 | 3259 | .none => unreachable, |
| 3149 | 3260 | .undef => unreachable, |
| ... | ... | @@ -3151,19 +3262,23 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! |
| 3151 | 3262 | .compare_flags_unsigned => unreachable, |
| 3152 | 3263 | .compare_flags_signed => unreachable, |
| 3153 | 3264 | .ptr_stack_offset => unreachable, |
| 3265 | .register_overflow_unsigned => unreachable, |
| 3266 | .register_overflow_signed => unreachable, |
| 3154 | 3267 | .register => |dst_reg| { |
| 3155 | 3268 | switch (src_mcv) { |
| 3156 | 3269 | .none => unreachable, |
| 3157 | 3270 | .undef => try self.genSetReg(dst_ty, dst_reg, .undef), |
| 3158 | 3271 | .dead, .unreach => unreachable, |
| 3159 | 3272 | .ptr_stack_offset => unreachable, |
| 3273 | .register_overflow_unsigned => unreachable, |
| 3274 | .register_overflow_signed => unreachable, |
| 3160 | 3275 | .register => |src_reg| { |
| 3161 | 3276 | // register, register |
| 3162 | 3277 | _ = try self.addInst(.{ |
| 3163 | 3278 | .tag = .imul_complex, |
| 3164 | 3279 | .ops = (Mir.Ops{ |
| 3165 | | .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), |
| 3166 | | .reg2 = src_reg, |
| 3280 | .reg1 = registerAlias(dst_reg, abi_size), |
| 3281 | .reg2 = registerAlias(src_reg, abi_size), |
| 3167 | 3282 | }).encode(), |
| 3168 | 3283 | .data = undefined, |
| 3169 | 3284 | }); |
| ... | ... | @@ -3191,7 +3306,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! |
| 3191 | 3306 | _ = try self.addInst(.{ |
| 3192 | 3307 | .tag = .imul_complex, |
| 3193 | 3308 | .ops = (Mir.Ops{ |
| 3194 | | .reg1 = dst_reg, |
| 3309 | .reg1 = registerAlias(dst_reg, abi_size), |
| 3195 | 3310 | .reg2 = .rbp, |
| 3196 | 3311 | .flags = 0b01, |
| 3197 | 3312 | }).encode(), |
| ... | ... | @@ -3218,6 +3333,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! |
| 3218 | 3333 | .undef => return self.genSetStack(dst_ty, off, .undef, .{}), |
| 3219 | 3334 | .dead, .unreach => unreachable, |
| 3220 | 3335 | .ptr_stack_offset => unreachable, |
| 3336 | .register_overflow_unsigned => unreachable, |
| 3337 | .register_overflow_signed => unreachable, |
| 3221 | 3338 | .register => |src_reg| { |
| 3222 | 3339 | // copy dst to a register |
| 3223 | 3340 | const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); |
| ... | ... | @@ -3226,8 +3343,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! |
| 3226 | 3343 | _ = try self.addInst(.{ |
| 3227 | 3344 | .tag = .imul_complex, |
| 3228 | 3345 | .ops = (Mir.Ops{ |
| 3229 | | .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), |
| 3230 | | .reg2 = src_reg, |
| 3346 | .reg1 = registerAlias(dst_reg, abi_size), |
| 3347 | .reg2 = registerAlias(src_reg, abi_size), |
| 3231 | 3348 | }).encode(), |
| 3232 | 3349 | .data = undefined, |
| 3233 | 3350 | }); |
| ... | ... | @@ -3366,6 +3483,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3366 | 3483 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| 3367 | 3484 | }, |
| 3368 | 3485 | .stack_offset => |off| { |
| 3486 | // TODO rewrite using `genSetStack` |
| 3369 | 3487 | try self.genSetStackArg(arg_ty, off, arg_mcv); |
| 3370 | 3488 | }, |
| 3371 | 3489 | .ptr_stack_offset => { |
| ... | ... | @@ -3380,6 +3498,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3380 | 3498 | .direct_load => unreachable, |
| 3381 | 3499 | .compare_flags_signed => unreachable, |
| 3382 | 3500 | .compare_flags_unsigned => unreachable, |
| 3501 | .register_overflow_signed => unreachable, |
| 3502 | .register_overflow_unsigned => unreachable, |
| 3383 | 3503 | } |
| 3384 | 3504 | } |
| 3385 | 3505 | |
| ... | ... | @@ -4610,6 +4730,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 4610 | 4730 | .{ .dest_stack_base = .rsp }, |
| 4611 | 4731 | ); |
| 4612 | 4732 | }, |
| 4733 | .register_overflow_unsigned, |
| 4734 | .register_overflow_signed, |
| 4735 | => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), |
| 4613 | 4736 | .compare_flags_unsigned, |
| 4614 | 4737 | .compare_flags_signed, |
| 4615 | 4738 | => { |
| ... | ... | @@ -4712,6 +4835,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 4712 | 4835 | ), |
| 4713 | 4836 | } |
| 4714 | 4837 | }, |
| 4838 | .register_overflow_unsigned, |
| 4839 | .register_overflow_signed, |
| 4840 | => |reg| { |
| 4841 | self.register_manager.freezeRegs(&.{reg}); |
| 4842 | defer self.register_manager.unfreezeRegs(&.{reg}); |
| 4843 | |
| 4844 | const wrapped_ty = ty.structFieldType(0); |
| 4845 | try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); |
| 4846 | |
| 4847 | const overflow_bit_ty = ty.structFieldType(1); |
| 4848 | const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); |
| 4849 | const tmp_reg = try self.register_manager.allocReg(null); |
| 4850 | const flags: u2 = switch (mcv) { |
| 4851 | .register_overflow_unsigned => 0b10, |
| 4852 | .register_overflow_signed => 0b00, |
| 4853 | else => unreachable, |
| 4854 | }; |
| 4855 | _ = try self.addInst(.{ |
| 4856 | .tag = .cond_set_byte_overflow, |
| 4857 | .ops = (Mir.Ops{ |
| 4858 | .reg1 = tmp_reg.to8(), |
| 4859 | .flags = flags, |
| 4860 | }).encode(), |
| 4861 | .data = undefined, |
| 4862 | }); |
| 4863 | |
| 4864 | return self.genSetStack( |
| 4865 | overflow_bit_ty, |
| 4866 | stack_offset - @intCast(i32, overflow_bit_offset), |
| 4867 | .{ .register = tmp_reg.to8() }, |
| 4868 | .{}, |
| 4869 | ); |
| 4870 | }, |
| 4715 | 4871 | .compare_flags_unsigned, |
| 4716 | 4872 | .compare_flags_signed, |
| 4717 | 4873 | => { |
| ... | ... | @@ -5170,6 +5326,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5170 | 5326 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 5171 | 5327 | switch (mcv) { |
| 5172 | 5328 | .dead => unreachable, |
| 5329 | .register_overflow_unsigned, |
| 5330 | .register_overflow_signed, |
| 5331 | => unreachable, |
| 5173 | 5332 | .ptr_stack_offset => |off| { |
| 5174 | 5333 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 5175 | 5334 | return self.fail("stack offset too large", .{}); |