| ... | ... | @@ -125,6 +125,12 @@ const MCValue = union(enum) { |
| 125 | 125 | stack_offset: u32, |
| 126 | 126 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 127 | 127 | ptr_stack_offset: u32, |
| 128 | /// The value is in the compare flags assuming an unsigned operation, |
| 129 | /// with this operator applied on top of it. |
| 130 | compare_flags_unsigned: math.CompareOperator, |
| 131 | /// The value is in the compare flags assuming a signed operation, |
| 132 | /// with this operator applied on top of it. |
| 133 | compare_flags_signed: math.CompareOperator, |
| 128 | 134 | |
| 129 | 135 | fn isMemory(mcv: MCValue) bool { |
| 130 | 136 | return switch (mcv) { |
| ... | ... | @@ -536,9 +542,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 536 | 542 | .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), |
| 537 | 543 | .is_null => @panic("TODO try self.airIsNull(inst)"), |
| 538 | 544 | .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), |
| 539 | | .is_non_err => @panic("TODO try self.airIsNonErr(inst)"), |
| 545 | .is_non_err => try self.airIsNonErr(inst), |
| 540 | 546 | .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), |
| 541 | | .is_err => @panic("TODO try self.airIsErr(inst)"), |
| 547 | .is_err => try self.airIsErr(inst), |
| 542 | 548 | .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), |
| 543 | 549 | .load => @panic("TODO try self.airLoad(inst)"), |
| 544 | 550 | .loop => @panic("TODO try self.airLoop(inst)"), |
| ... | ... | @@ -872,6 +878,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 872 | 878 | .unreach => unreachable, |
| 873 | 879 | .dead => unreachable, |
| 874 | 880 | .memory => unreachable, |
| 881 | .compare_flags_signed => unreachable, |
| 882 | .compare_flags_unsigned => unreachable, |
| 875 | 883 | .register => |reg| { |
| 876 | 884 | try self.register_manager.getReg(reg, null); |
| 877 | 885 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | ... | @@ -1004,6 +1012,26 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1004 | 1012 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1005 | 1013 | } |
| 1006 | 1014 | |
| 1015 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1016 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1017 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1018 | const operand = try self.resolveInst(un_op); |
| 1019 | const ty = self.air.typeOf(un_op); |
| 1020 | break :result try self.isErr(ty, operand); |
| 1021 | }; |
| 1022 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 1023 | } |
| 1024 | |
| 1025 | fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1026 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1027 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1028 | const operand = try self.resolveInst(un_op); |
| 1029 | const ty = self.air.typeOf(un_op); |
| 1030 | break :result try self.isNonErr(ty, operand); |
| 1031 | }; |
| 1032 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 1033 | } |
| 1034 | |
| 1007 | 1035 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 1008 | 1036 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1009 | 1037 | const operand = try self.resolveInst(un_op); |
| ... | ... | @@ -1259,6 +1287,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 1259 | 1287 | switch (mcv) { |
| 1260 | 1288 | .dead => unreachable, |
| 1261 | 1289 | .unreach, .none => return, // Nothing to do. |
| 1290 | .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}), |
| 1291 | .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}), |
| 1262 | 1292 | .undef => { |
| 1263 | 1293 | if (!self.wantSafety()) |
| 1264 | 1294 | return; // The already existing value will do just fine. |
| ... | ... | @@ -1426,6 +1456,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 1426 | 1456 | else => return self.fail("TODO implement memset", .{}), |
| 1427 | 1457 | } |
| 1428 | 1458 | }, |
| 1459 | .compare_flags_unsigned, |
| 1460 | .compare_flags_signed, |
| 1429 | 1461 | .immediate, |
| 1430 | 1462 | .ptr_stack_offset, |
| 1431 | 1463 | => { |
| ... | ... | @@ -1522,6 +1554,54 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 1522 | 1554 | } |
| 1523 | 1555 | } |
| 1524 | 1556 | |
| 1557 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 1558 | const error_type = ty.errorUnionSet(); |
| 1559 | const payload_type = ty.errorUnionPayload(); |
| 1560 | |
| 1561 | if (!error_type.hasRuntimeBits()) { |
| 1562 | return MCValue{ .immediate = 0 }; // always false |
| 1563 | } else if (!payload_type.hasRuntimeBits()) { |
| 1564 | if (error_type.abiSize(self.target.*) <= 8) { |
| 1565 | const reg_mcv: MCValue = switch (operand) { |
| 1566 | .register => operand, |
| 1567 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| 1568 | }; |
| 1569 | |
| 1570 | _ = try self.addInst(.{ |
| 1571 | .tag = .subcc, |
| 1572 | .data = .{ .arithmetic_3op = .{ |
| 1573 | .is_imm = true, |
| 1574 | .rs1 = reg_mcv.register, |
| 1575 | .rs2_or_imm = .{ .imm = 0 }, |
| 1576 | .rd = .g0, |
| 1577 | } }, |
| 1578 | }); |
| 1579 | |
| 1580 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 1581 | } else { |
| 1582 | return self.fail("TODO isErr for errors with size > 8", .{}); |
| 1583 | } |
| 1584 | } else { |
| 1585 | return self.fail("TODO isErr for non-empty payloads", .{}); |
| 1586 | } |
| 1587 | } |
| 1588 | |
| 1589 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 1590 | // Call isErr, then negate the result. |
| 1591 | const is_err_result = try self.isErr(ty, operand); |
| 1592 | switch (is_err_result) { |
| 1593 | .compare_flags_unsigned => |op| { |
| 1594 | assert(op == .gt); |
| 1595 | return MCValue{ .compare_flags_unsigned = .lte }; |
| 1596 | }, |
| 1597 | .immediate => |imm| { |
| 1598 | assert(imm == 0); |
| 1599 | return MCValue{ .immediate = 1 }; |
| 1600 | }, |
| 1601 | else => unreachable, |
| 1602 | } |
| 1603 | } |
| 1604 | |
| 1525 | 1605 | fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { |
| 1526 | 1606 | try self.ensureProcessDeathCapacity(operand_count + 1); |
| 1527 | 1607 | return BigTomb{ |