| ... | @@ -125,6 +125,12 @@ const MCValue = union(enum) { | ... | @@ -125,6 +125,12 @@ const MCValue = union(enum) { |
| 125 | stack_offset: u32, | 125 | stack_offset: u32, |
| 126 | /// The value is a pointer to one of the stack variables (payload is stack offset). | 126 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 127 | ptr_stack_offset: u32, | 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 | fn isMemory(mcv: MCValue) bool { | 135 | fn isMemory(mcv: MCValue) bool { |
| 130 | return switch (mcv) { | 136 | return switch (mcv) { |
| ... | @@ -536,9 +542,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -536,9 +542,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 536 | .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), | 542 | .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), |
| 537 | .is_null => @panic("TODO try self.airIsNull(inst)"), | 543 | .is_null => @panic("TODO try self.airIsNull(inst)"), |
| 538 | .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), | 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 | .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), | 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 | .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), | 548 | .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), |
| 543 | .load => @panic("TODO try self.airLoad(inst)"), | 549 | .load => @panic("TODO try self.airLoad(inst)"), |
| 544 | .loop => @panic("TODO try self.airLoop(inst)"), | 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,6 +878,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 872 | .unreach => unreachable, | 878 | .unreach => unreachable, |
| 873 | .dead => unreachable, | 879 | .dead => unreachable, |
| 874 | .memory => unreachable, | 880 | .memory => unreachable, |
| | 881 | .compare_flags_signed => unreachable, |
| | 882 | .compare_flags_unsigned => unreachable, |
| 875 | .register => |reg| { | 883 | .register => |reg| { |
| 876 | try self.register_manager.getReg(reg, null); | 884 | try self.register_manager.getReg(reg, null); |
| 877 | try self.genSetReg(arg_ty, reg, arg_mcv); | 885 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | @@ -1004,6 +1012,26 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1004,6 +1012,26 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1004 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 1035 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 1008 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1036 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1009 | const operand = try self.resolveInst(un_op); | 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,6 +1287,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 1259 | switch (mcv) { | 1287 | switch (mcv) { |
| 1260 | .dead => unreachable, | 1288 | .dead => unreachable, |
| 1261 | .unreach, .none => return, // Nothing to do. | 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 | .undef => { | 1292 | .undef => { |
| 1263 | if (!self.wantSafety()) | 1293 | if (!self.wantSafety()) |
| 1264 | return; // The already existing value will do just fine. | 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,6 +1456,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 1426 | else => return self.fail("TODO implement memset", .{}), | 1456 | else => return self.fail("TODO implement memset", .{}), |
| 1427 | } | 1457 | } |
| 1428 | }, | 1458 | }, |
| | 1459 | .compare_flags_unsigned, |
| | 1460 | .compare_flags_signed, |
| 1429 | .immediate, | 1461 | .immediate, |
| 1430 | .ptr_stack_offset, | 1462 | .ptr_stack_offset, |
| 1431 | => { | 1463 | => { |
| ... | @@ -1522,6 +1554,54 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | ... | @@ -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 | fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { | 1605 | fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { |
| 1526 | try self.ensureProcessDeathCapacity(operand_count + 1); | 1606 | try self.ensureProcessDeathCapacity(operand_count + 1); |
| 1527 | return BigTomb{ | 1607 | return BigTomb{ |