| ... | ... | @@ -499,20 +499,29 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 499 | 499 | .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), |
| 500 | 500 | |
| 501 | 501 | .add => try self.airBinOp(inst, .add), |
| 502 | | .addwrap => @panic("TODO try self.airAddWrap(inst)"), |
| 503 | | .add_sat => @panic("TODO try self.airAddSat(inst)"), |
| 504 | | .sub => @panic("TODO try self.airBinOp(inst)"), |
| 505 | | .subwrap => @panic("TODO try self.airSubWrap(inst)"), |
| 506 | | .sub_sat => @panic("TODO try self.airSubSat(inst)"), |
| 507 | | .mul => @panic("TODO try self.airMul(inst)"), |
| 508 | | .mulwrap => @panic("TODO try self.airMulWrap(inst)"), |
| 509 | | .mul_sat => @panic("TODO try self.airMulSat(inst)"), |
| 510 | | .rem => try self.airRem(inst), |
| 511 | | .mod => try self.airMod(inst), |
| 512 | | .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), |
| 513 | | .shl_sat => @panic("TODO try self.airShlSat(inst)"), |
| 502 | .addwrap => try self.airBinOp(inst, .addwrap), |
| 503 | .sub => try self.airBinOp(inst, .sub), |
| 504 | .subwrap => try self.airBinOp(inst, .subwrap), |
| 505 | .mul => try self.airBinOp(inst, .mul), |
| 506 | .mulwrap => try self.airBinOp(inst, .mulwrap), |
| 507 | .shl => try self.airBinOp(inst, .shl), |
| 508 | .shl_exact => try self.airBinOp(inst, .shl_exact), |
| 509 | .shr => try self.airBinOp(inst, .shr), |
| 510 | .shr_exact => try self.airBinOp(inst, .shr_exact), |
| 511 | .bool_and => try self.airBinOp(inst, .bool_and), |
| 512 | .bool_or => try self.airBinOp(inst, .bool_or), |
| 513 | .bit_and => try self.airBinOp(inst, .bit_and), |
| 514 | .bit_or => try self.airBinOp(inst, .bit_or), |
| 515 | .xor => try self.airBinOp(inst, .xor), |
| 516 | |
| 517 | .add_sat => try self.airAddSat(inst), |
| 518 | .sub_sat => try self.airSubSat(inst), |
| 519 | .mul_sat => try self.airMulSat(inst), |
| 520 | .shl_sat => try self.airShlSat(inst), |
| 514 | 521 | .min => @panic("TODO try self.airMin(inst)"), |
| 515 | 522 | .max => @panic("TODO try self.airMax(inst)"), |
| 523 | .rem => try self.airRem(inst), |
| 524 | .mod => try self.airMod(inst), |
| 516 | 525 | .slice => try self.airSlice(inst), |
| 517 | 526 | |
| 518 | 527 | .sqrt, |
| ... | ... | @@ -530,12 +539,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 530 | 539 | .round, |
| 531 | 540 | .trunc_float, |
| 532 | 541 | .neg, |
| 533 | | => @panic("TODO try self.airUnaryMath(inst)"), |
| 542 | => try self.airUnaryMath(inst), |
| 534 | 543 | |
| 535 | 544 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 536 | 545 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 537 | | .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), |
| 538 | | .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), |
| 546 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 547 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 539 | 548 | |
| 540 | 549 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 541 | 550 | |
| ... | ... | @@ -546,14 +555,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 546 | 555 | .cmp_gt => try self.airCmp(inst, .gt), |
| 547 | 556 | .cmp_neq => try self.airCmp(inst, .neq), |
| 548 | 557 | .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), |
| 549 | | .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), |
| 550 | | |
| 551 | | .bool_and => @panic("TODO try self.airBoolOp(inst)"), |
| 552 | | .bool_or => @panic("TODO try self.airBoolOp(inst)"), |
| 553 | | .bit_and => try self.airBinOp(inst, .bit_and), |
| 554 | | .bit_or => try self.airBinOp(inst, .bit_or), |
| 555 | | .xor => try self.airBinOp(inst, .xor), |
| 556 | | .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), |
| 558 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 557 | 559 | |
| 558 | 560 | .alloc => try self.airAlloc(inst), |
| 559 | 561 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -584,15 +586,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 584 | 586 | .load => try self.airLoad(inst), |
| 585 | 587 | .loop => try self.airLoop(inst), |
| 586 | 588 | .not => try self.airNot(inst), |
| 587 | | .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), |
| 589 | .ptrtoint => try self.airPtrToInt(inst), |
| 588 | 590 | .ret => try self.airRet(inst), |
| 589 | 591 | .ret_load => try self.airRetLoad(inst), |
| 590 | 592 | .store => try self.airStore(inst), |
| 591 | 593 | .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), |
| 592 | 594 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 593 | 595 | .array_to_slice => try self.airArrayToSlice(inst), |
| 594 | | .int_to_float => @panic("TODO try self.airIntToFloat(inst)"), |
| 595 | | .float_to_int => @panic("TODO try self.airFloatToInt(inst)"), |
| 596 | .int_to_float => try self.airIntToFloat(inst), |
| 597 | .float_to_int => try self.airFloatToInt(inst), |
| 596 | 598 | .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"), |
| 597 | 599 | .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"), |
| 598 | 600 | .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"), |
| ... | ... | @@ -601,12 +603,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 601 | 603 | .memset => try self.airMemset(inst), |
| 602 | 604 | .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"), |
| 603 | 605 | .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"), |
| 604 | | .clz => @panic("TODO try self.airClz(inst)"), |
| 605 | | .ctz => @panic("TODO try self.airCtz(inst)"), |
| 606 | | .popcount => @panic("TODO try self.airPopcount(inst)"), |
| 606 | .clz => try self.airClz(inst), |
| 607 | .ctz => try self.airCtz(inst), |
| 608 | .popcount => try self.airPopcount(inst), |
| 607 | 609 | .byte_swap => @panic("TODO try self.airByteSwap(inst)"), |
| 608 | 610 | .bit_reverse => @panic("TODO try self.airBitReverse(inst)"), |
| 609 | | .tag_name => @panic("TODO try self.airTagName(inst)"), |
| 611 | .tag_name => try self.airTagName(inst), |
| 610 | 612 | .error_name => @panic("TODO try self.airErrorName(inst)"), |
| 611 | 613 | .splat => @panic("TODO try self.airSplat(inst)"), |
| 612 | 614 | .select => @panic("TODO try self.airSelect(inst)"), |
| ... | ... | @@ -614,7 +616,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 614 | 616 | .reduce => @panic("TODO try self.airReduce(inst)"), |
| 615 | 617 | .aggregate_init => try self.airAggregateInit(inst), |
| 616 | 618 | .union_init => @panic("TODO try self.airUnionInit(inst)"), |
| 617 | | .prefetch => @panic("TODO try self.airPrefetch(inst)"), |
| 619 | .prefetch => try self.airPrefetch(inst), |
| 618 | 620 | .mul_add => @panic("TODO try self.airMulAdd(inst)"), |
| 619 | 621 | |
| 620 | 622 | .@"try" => try self.airTry(inst), |
| ... | ... | @@ -650,7 +652,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 650 | 652 | .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"), |
| 651 | 653 | |
| 652 | 654 | .switch_br => try self.airSwitch(inst), |
| 653 | | .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"), |
| 655 | .slice_ptr => try self.airSlicePtr(inst), |
| 654 | 656 | .slice_len => try self.airSliceLen(inst), |
| 655 | 657 | |
| 656 | 658 | .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), |
| ... | ... | @@ -659,16 +661,16 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 659 | 661 | .array_elem_val => try self.airArrayElemVal(inst), |
| 660 | 662 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 661 | 663 | .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), |
| 662 | | .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), |
| 664 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| 663 | 665 | .ptr_elem_ptr => try self.airPtrElemPtr(inst), |
| 664 | 666 | |
| 665 | 667 | .constant => unreachable, // excluded from function bodies |
| 666 | 668 | .const_ty => unreachable, // excluded from function bodies |
| 667 | 669 | .unreach => self.finishAirBookkeeping(), |
| 668 | 670 | |
| 669 | | .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"), |
| 670 | | .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), |
| 671 | | .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"), |
| 671 | .optional_payload => try self.airOptionalPayload(inst), |
| 672 | .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), |
| 673 | .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), |
| 672 | 674 | .unwrap_errunion_err => try self.airUnwrapErrErr(inst), |
| 673 | 675 | .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), |
| 674 | 676 | .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), |
| ... | ... | @@ -677,7 +679,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 677 | 679 | .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"), |
| 678 | 680 | .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"), |
| 679 | 681 | |
| 680 | | .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), |
| 682 | .wrap_optional => try self.airWrapOptional(inst), |
| 681 | 683 | .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"), |
| 682 | 684 | .wrap_errunion_err => try self.airWrapErrUnionErr(inst), |
| 683 | 685 | |
| ... | ... | @@ -723,6 +725,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 723 | 725 | } |
| 724 | 726 | } |
| 725 | 727 | |
| 728 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 729 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 730 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); |
| 731 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 732 | } |
| 733 | |
| 726 | 734 | fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 727 | 735 | const tag = self.air.instructions.items(.tag)[inst]; |
| 728 | 736 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -763,7 +771,6 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 763 | 771 | }; |
| 764 | 772 | |
| 765 | 773 | try self.spillConditionFlagsIfOccupied(); |
| 766 | | self.condition_flags_inst = inst; |
| 767 | 774 | |
| 768 | 775 | const dest = blk: { |
| 769 | 776 | if (rhs_immediate_ok) { |
| ... | ... | @@ -1241,6 +1248,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1241 | 1248 | return bt.finishAir(result); |
| 1242 | 1249 | } |
| 1243 | 1250 | |
| 1251 | fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 1252 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1253 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); |
| 1254 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1255 | } |
| 1256 | |
| 1244 | 1257 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1245 | 1258 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1246 | 1259 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -1299,6 +1312,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1299 | 1312 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1300 | 1313 | } |
| 1301 | 1314 | |
| 1315 | fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1316 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1317 | const operand = try self.resolveInst(un_op); |
| 1318 | _ = operand; |
| 1319 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); |
| 1320 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 1321 | } |
| 1322 | |
| 1302 | 1323 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1303 | 1324 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1304 | 1325 | const condition = try self.resolveInst(pl_op.operand); |
| ... | ... | @@ -1446,6 +1467,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1446 | 1467 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 1447 | 1468 | } |
| 1448 | 1469 | |
| 1470 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 1471 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1472 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); |
| 1473 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1474 | } |
| 1475 | |
| 1449 | 1476 | fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 1450 | 1477 | // TODO emit debug info lexical block |
| 1451 | 1478 | return self.finishAir(inst, .dead, .{ .none, .none, .none }); |
| ... | ... | @@ -1519,6 +1546,14 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void { |
| 1519 | 1546 | return self.finishAir(inst, .dead, .{ .none, .none, .none }); |
| 1520 | 1547 | } |
| 1521 | 1548 | |
| 1549 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 1550 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1551 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{ |
| 1552 | self.target.cpu.arch, |
| 1553 | }); |
| 1554 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1555 | } |
| 1556 | |
| 1522 | 1557 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1523 | 1558 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1524 | 1559 | if (self.liveness.isUnused(inst)) |
| ... | ... | @@ -1537,6 +1572,14 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1537 | 1572 | return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); |
| 1538 | 1573 | } |
| 1539 | 1574 | |
| 1575 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 1576 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1577 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{ |
| 1578 | self.target.cpu.arch, |
| 1579 | }); |
| 1580 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1581 | } |
| 1582 | |
| 1540 | 1583 | fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1541 | 1584 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1542 | 1585 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -1768,6 +1811,78 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1768 | 1811 | return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1769 | 1812 | } |
| 1770 | 1813 | |
| 1814 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1815 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1816 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); |
| 1817 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1818 | } |
| 1819 | |
| 1820 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1821 | //const tag = self.air.instructions.items(.tag)[inst]; |
| 1822 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1823 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1824 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1825 | const lhs = try self.resolveInst(extra.lhs); |
| 1826 | const rhs = try self.resolveInst(extra.rhs); |
| 1827 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1828 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1829 | |
| 1830 | switch (lhs_ty.zigTypeTag()) { |
| 1831 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 1832 | .Int => { |
| 1833 | const mod = self.bin_file.options.module.?; |
| 1834 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1835 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1836 | switch (int_info.bits) { |
| 1837 | 1...32 => { |
| 1838 | try self.spillConditionFlagsIfOccupied(); |
| 1839 | |
| 1840 | const dest = try self.binOp(.mul, lhs, rhs, lhs_ty, rhs_ty, null); |
| 1841 | |
| 1842 | const dest_reg = dest.register; |
| 1843 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1844 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 1845 | |
| 1846 | const truncated_reg = try self.register_manager.allocReg(null, gp); |
| 1847 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 1848 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 1849 | |
| 1850 | try self.truncRegister( |
| 1851 | dest_reg, |
| 1852 | truncated_reg, |
| 1853 | int_info.signedness, |
| 1854 | int_info.bits, |
| 1855 | ); |
| 1856 | |
| 1857 | _ = try self.addInst(.{ |
| 1858 | .tag = .cmp, |
| 1859 | .data = .{ .arithmetic_2op = .{ |
| 1860 | .is_imm = false, |
| 1861 | .rs1 = dest_reg, |
| 1862 | .rs2_or_imm = .{ .rs2 = truncated_reg }, |
| 1863 | } }, |
| 1864 | }); |
| 1865 | |
| 1866 | const cond = Instruction.ICondition.ne; |
| 1867 | const ccr = Instruction.CCR.xcc; |
| 1868 | |
| 1869 | break :result MCValue{ .register_with_overflow = .{ |
| 1870 | .reg = truncated_reg, |
| 1871 | .flag = .{ .cond = cond, .ccr = ccr }, |
| 1872 | } }; |
| 1873 | }, |
| 1874 | // XXX DO NOT call __multi3 directly as it'll result in us doing six multiplications, |
| 1875 | // which is far more than strictly necessary |
| 1876 | 33...64 => return self.fail("TODO copy compiler-rt's mulddi3 for a 64x64->128 multiply", .{}), |
| 1877 | else => return self.fail("TODO overflow operations on other integer sizes", .{}), |
| 1878 | } |
| 1879 | }, |
| 1880 | else => unreachable, |
| 1881 | } |
| 1882 | }; |
| 1883 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1884 | } |
| 1885 | |
| 1771 | 1886 | fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1772 | 1887 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1773 | 1888 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -1863,6 +1978,43 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1863 | 1978 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1864 | 1979 | } |
| 1865 | 1980 | |
| 1981 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1982 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1983 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); |
| 1984 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1985 | } |
| 1986 | |
| 1987 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1988 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1989 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); |
| 1990 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1991 | } |
| 1992 | |
| 1993 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1994 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1995 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); |
| 1996 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1997 | } |
| 1998 | |
| 1999 | fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 2000 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2001 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); |
| 2002 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2003 | } |
| 2004 | |
| 2005 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { |
| 2006 | const prefetch = self.air.instructions.items(.data)[inst].prefetch; |
| 2007 | // TODO Emit a PREFETCH/IPREFETCH as necessary, see A.7 and A.42 |
| 2008 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 2009 | } |
| 2010 | |
| 2011 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2012 | const is_volatile = false; // TODO |
| 2013 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2014 | const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}); |
| 2015 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2016 | } |
| 2017 | |
| 1866 | 2018 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1867 | 2019 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1868 | 2020 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | ... | @@ -1870,6 +2022,12 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1870 | 2022 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1871 | 2023 | } |
| 1872 | 2024 | |
| 2025 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 2026 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2027 | const result = try self.resolveInst(un_op); |
| 2028 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2029 | } |
| 2030 | |
| 1873 | 2031 | fn airRem(self: *Self, inst: Air.Inst.Index) !void { |
| 1874 | 2032 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1875 | 2033 | const lhs = try self.resolveInst(bin_op.lhs); |
| ... | ... | @@ -1911,6 +2069,101 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1911 | 2069 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 1912 | 2070 | } |
| 1913 | 2071 | |
| 2072 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2073 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2074 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 2075 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2076 | } |
| 2077 | |
| 2078 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2079 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2080 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2081 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2082 | const lhs = try self.resolveInst(extra.lhs); |
| 2083 | const rhs = try self.resolveInst(extra.rhs); |
| 2084 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2085 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2086 | |
| 2087 | switch (lhs_ty.zigTypeTag()) { |
| 2088 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 2089 | .Int => { |
| 2090 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2091 | if (int_info.bits <= 64) { |
| 2092 | try self.spillConditionFlagsIfOccupied(); |
| 2093 | |
| 2094 | const lhs_lock: ?RegisterLock = if (lhs == .register) |
| 2095 | self.register_manager.lockRegAssumeUnused(lhs.register) |
| 2096 | else |
| 2097 | null; |
| 2098 | // TODO this currently crashes stage1 |
| 2099 | // defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2100 | |
| 2101 | // Increase shift amount (i.e, rhs) by shamt_bits - int_info.bits |
| 2102 | // e.g if shifting a i48 then use sr*x (shamt_bits == 64) but increase rhs by 16 |
| 2103 | // and if shifting a i24 then use sr* (shamt_bits == 32) but increase rhs by 8 |
| 2104 | const new_rhs = switch (int_info.bits) { |
| 2105 | 1...31 => if (rhs == .immediate) MCValue{ |
| 2106 | .immediate = rhs.immediate + 32 - int_info.bits, |
| 2107 | } else try self.binOp(.add, rhs, .{ .immediate = 32 - int_info.bits }, rhs_ty, rhs_ty, null), |
| 2108 | 33...63 => if (rhs == .immediate) MCValue{ |
| 2109 | .immediate = rhs.immediate + 64 - int_info.bits, |
| 2110 | } else try self.binOp(.add, rhs, .{ .immediate = 64 - int_info.bits }, rhs_ty, rhs_ty, null), |
| 2111 | 32, 64 => rhs, |
| 2112 | else => unreachable, |
| 2113 | }; |
| 2114 | |
| 2115 | const new_rhs_lock: ?RegisterLock = if (new_rhs == .register) |
| 2116 | self.register_manager.lockRegAssumeUnused(new_rhs.register) |
| 2117 | else |
| 2118 | null; |
| 2119 | // TODO this currently crashes stage1 |
| 2120 | // defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2121 | |
| 2122 | const dest = try self.binOp(.shl, lhs, new_rhs, lhs_ty, rhs_ty, null); |
| 2123 | const dest_reg = dest.register; |
| 2124 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2125 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2126 | |
| 2127 | const shr = try self.binOp(.shr, dest, new_rhs, lhs_ty, rhs_ty, null); |
| 2128 | |
| 2129 | _ = try self.addInst(.{ |
| 2130 | .tag = .cmp, |
| 2131 | .data = .{ .arithmetic_2op = .{ |
| 2132 | .is_imm = false, |
| 2133 | .rs1 = dest_reg, |
| 2134 | .rs2_or_imm = .{ .rs2 = shr.register }, |
| 2135 | } }, |
| 2136 | }); |
| 2137 | |
| 2138 | const cond = Instruction.ICondition.ne; |
| 2139 | const ccr = switch (int_info.bits) { |
| 2140 | 1...32 => Instruction.CCR.icc, |
| 2141 | 33...64 => Instruction.CCR.xcc, |
| 2142 | else => unreachable, |
| 2143 | }; |
| 2144 | |
| 2145 | // TODO Those should really be written as defers, however stage1 currently |
| 2146 | // panics when those are turned into defer statements so those are |
| 2147 | // written here at the end as ordinary statements. |
| 2148 | // Because of that, on failure, the lock on those registers wouldn't be |
| 2149 | // released. |
| 2150 | if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2151 | if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2152 | |
| 2153 | break :result MCValue{ .register_with_overflow = .{ |
| 2154 | .reg = dest_reg, |
| 2155 | .flag = .{ .cond = cond, .ccr = ccr }, |
| 2156 | } }; |
| 2157 | } else { |
| 2158 | return self.fail("TODO overflow operations on other integer sizes", .{}); |
| 2159 | } |
| 2160 | }, |
| 2161 | else => unreachable, |
| 2162 | } |
| 2163 | }; |
| 2164 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2165 | } |
| 2166 | |
| 1914 | 2167 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1915 | 2168 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1916 | 2169 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| ... | ... | @@ -1996,6 +2249,25 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1996 | 2249 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1997 | 2250 | } |
| 1998 | 2251 | |
| 2252 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2253 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2254 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2255 | const mcv = try self.resolveInst(ty_op.operand); |
| 2256 | switch (mcv) { |
| 2257 | .dead, .unreach, .none => unreachable, |
| 2258 | .register => unreachable, // a slice doesn't fit in one register |
| 2259 | .stack_offset => |off| { |
| 2260 | break :result MCValue{ .stack_offset = off }; |
| 2261 | }, |
| 2262 | .memory => |addr| { |
| 2263 | break :result MCValue{ .memory = addr }; |
| 2264 | }, |
| 2265 | else => return self.fail("TODO implement slice_len for {}", .{mcv}), |
| 2266 | } |
| 2267 | }; |
| 2268 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2269 | } |
| 2270 | |
| 1999 | 2271 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 2000 | 2272 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2001 | 2273 | const ptr = try self.resolveInst(bin_op.lhs); |
| ... | ... | @@ -2083,11 +2355,27 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2083 | 2355 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 2084 | 2356 | } |
| 2085 | 2357 | |
| 2358 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2359 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2360 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); |
| 2361 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2362 | } |
| 2363 | |
| 2086 | 2364 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2087 | 2365 | _ = inst; |
| 2088 | 2366 | return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); |
| 2089 | 2367 | } |
| 2090 | 2368 | |
| 2369 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 2370 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2371 | const operand = try self.resolveInst(un_op); |
| 2372 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { |
| 2373 | _ = operand; |
| 2374 | return self.fail("TODO implement airTagName for {}", .{self.target.cpu.arch}); |
| 2375 | }; |
| 2376 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2377 | } |
| 2378 | |
| 2091 | 2379 | fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 2092 | 2380 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2093 | 2381 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| ... | ... | @@ -2106,6 +2394,15 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { |
| 2106 | 2394 | return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); |
| 2107 | 2395 | } |
| 2108 | 2396 | |
| 2397 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2398 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2399 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 2400 | .dead |
| 2401 | else |
| 2402 | return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); |
| 2403 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2404 | } |
| 2405 | |
| 2109 | 2406 | fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2110 | 2407 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2111 | 2408 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| ... | ... | @@ -2145,6 +2442,20 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2145 | 2442 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2146 | 2443 | } |
| 2147 | 2444 | |
| 2445 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2446 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2447 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2448 | const optional_ty = self.air.typeOfIndex(inst); |
| 2449 | |
| 2450 | // Optional with a zero-bit payload type is just a boolean true |
| 2451 | if (optional_ty.abiSize(self.target.*) == 1) |
| 2452 | break :result MCValue{ .immediate = 1 }; |
| 2453 | |
| 2454 | return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); |
| 2455 | }; |
| 2456 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2457 | } |
| 2458 | |
| 2148 | 2459 | // Common helper functions |
| 2149 | 2460 | |
| 2150 | 2461 | /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, |
| ... | ... | @@ -2264,6 +2575,10 @@ fn binOp( |
| 2264 | 2575 | switch (tag) { |
| 2265 | 2576 | .add, |
| 2266 | 2577 | .sub, |
| 2578 | .mul, |
| 2579 | .bit_and, |
| 2580 | .bit_or, |
| 2581 | .xor, |
| 2267 | 2582 | .cmp_eq, |
| 2268 | 2583 | => { |
| 2269 | 2584 | switch (lhs_ty.zigTypeTag()) { |
| ... | ... | @@ -2278,12 +2593,20 @@ fn binOp( |
| 2278 | 2593 | // operands |
| 2279 | 2594 | const lhs_immediate_ok = switch (tag) { |
| 2280 | 2595 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2596 | .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2597 | .bit_and => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2598 | .bit_or => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2599 | .xor => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2281 | 2600 | .sub, .cmp_eq => false, |
| 2282 | 2601 | else => unreachable, |
| 2283 | 2602 | }; |
| 2284 | 2603 | const rhs_immediate_ok = switch (tag) { |
| 2285 | 2604 | .add, |
| 2286 | 2605 | .sub, |
| 2606 | .mul, |
| 2607 | .bit_and, |
| 2608 | .bit_or, |
| 2609 | .xor, |
| 2287 | 2610 | .cmp_eq, |
| 2288 | 2611 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2289 | 2612 | else => unreachable, |
| ... | ... | @@ -2292,6 +2615,10 @@ fn binOp( |
| 2292 | 2615 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2293 | 2616 | .add => .add, |
| 2294 | 2617 | .sub => .sub, |
| 2618 | .mul => .mulx, |
| 2619 | .bit_and => .@"and", |
| 2620 | .bit_or => .@"or", |
| 2621 | .xor => .xor, |
| 2295 | 2622 | .cmp_eq => .cmp, |
| 2296 | 2623 | else => unreachable, |
| 2297 | 2624 | }; |
| ... | ... | @@ -2313,72 +2640,60 @@ fn binOp( |
| 2313 | 2640 | } |
| 2314 | 2641 | }, |
| 2315 | 2642 | |
| 2316 | | .div_trunc => { |
| 2643 | .addwrap, |
| 2644 | .subwrap, |
| 2645 | .mulwrap, |
| 2646 | => { |
| 2647 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2648 | .addwrap => .add, |
| 2649 | .subwrap => .sub, |
| 2650 | .mulwrap => .mul, |
| 2651 | else => unreachable, |
| 2652 | }; |
| 2653 | |
| 2654 | // Generate the base operation |
| 2655 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2656 | |
| 2657 | // Truncate if necessary |
| 2317 | 2658 | switch (lhs_ty.zigTypeTag()) { |
| 2318 | 2659 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2319 | 2660 | .Int => { |
| 2320 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2321 | 2661 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2322 | 2662 | if (int_info.bits <= 64) { |
| 2323 | | const rhs_immediate_ok = switch (tag) { |
| 2324 | | .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2325 | | else => unreachable, |
| 2326 | | }; |
| 2327 | | |
| 2328 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2329 | | .div_trunc => switch (int_info.signedness) { |
| 2330 | | .signed => Mir.Inst.Tag.sdivx, |
| 2331 | | .unsigned => Mir.Inst.Tag.udivx, |
| 2332 | | }, |
| 2333 | | else => unreachable, |
| 2334 | | }; |
| 2335 | | |
| 2336 | | if (rhs_immediate_ok) { |
| 2337 | | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata); |
| 2338 | | } else { |
| 2339 | | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2340 | | } |
| 2663 | const result_reg = result.register; |
| 2664 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2665 | return result; |
| 2341 | 2666 | } else { |
| 2342 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2667 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2343 | 2668 | } |
| 2344 | 2669 | }, |
| 2345 | 2670 | else => unreachable, |
| 2346 | 2671 | } |
| 2347 | 2672 | }, |
| 2348 | 2673 | |
| 2349 | | .mul => { |
| 2674 | .div_trunc => { |
| 2350 | 2675 | switch (lhs_ty.zigTypeTag()) { |
| 2351 | 2676 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2352 | 2677 | .Int => { |
| 2353 | 2678 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2354 | 2679 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2355 | 2680 | if (int_info.bits <= 64) { |
| 2356 | | // Only say yes if the operation is |
| 2357 | | // commutative, i.e. we can swap both of the |
| 2358 | | // operands |
| 2359 | | const lhs_immediate_ok = switch (tag) { |
| 2360 | | .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2361 | | else => unreachable, |
| 2362 | | }; |
| 2363 | 2681 | const rhs_immediate_ok = switch (tag) { |
| 2364 | | .mul => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2682 | .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2365 | 2683 | else => unreachable, |
| 2366 | 2684 | }; |
| 2367 | 2685 | |
| 2368 | 2686 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2369 | | .mul => .mulx, |
| 2687 | .div_trunc => switch (int_info.signedness) { |
| 2688 | .signed => Mir.Inst.Tag.sdivx, |
| 2689 | .unsigned => Mir.Inst.Tag.udivx, |
| 2690 | }, |
| 2370 | 2691 | else => unreachable, |
| 2371 | 2692 | }; |
| 2372 | 2693 | |
| 2373 | 2694 | if (rhs_immediate_ok) { |
| 2374 | | // At this point, rhs is an immediate |
| 2375 | | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); |
| 2376 | | } else if (lhs_immediate_ok) { |
| 2377 | | // swap lhs and rhs |
| 2378 | | // At this point, lhs is an immediate |
| 2379 | | return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); |
| 2695 | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata); |
| 2380 | 2696 | } else { |
| 2381 | | // TODO convert large immediates to register before adding |
| 2382 | 2697 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2383 | 2698 | } |
| 2384 | 2699 | } else { |
| ... | ... | @@ -2419,88 +2734,61 @@ fn binOp( |
| 2419 | 2734 | } |
| 2420 | 2735 | }, |
| 2421 | 2736 | |
| 2422 | | .bit_and, |
| 2423 | | .bit_or, |
| 2424 | | .xor, |
| 2737 | .bool_and, |
| 2738 | .bool_or, |
| 2425 | 2739 | => { |
| 2426 | 2740 | switch (lhs_ty.zigTypeTag()) { |
| 2427 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2428 | | .Int => { |
| 2429 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2430 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 2431 | | if (int_info.bits <= 64) { |
| 2432 | | // Only say yes if the operation is |
| 2433 | | // commutative, i.e. we can swap both of the |
| 2434 | | // operands |
| 2435 | | const lhs_immediate_ok = switch (tag) { |
| 2436 | | .bit_and, |
| 2437 | | .bit_or, |
| 2438 | | .xor, |
| 2439 | | => lhs == .immediate and lhs.immediate <= std.math.maxInt(u13), |
| 2440 | | else => unreachable, |
| 2441 | | }; |
| 2442 | | const rhs_immediate_ok = switch (tag) { |
| 2443 | | .bit_and, |
| 2444 | | .bit_or, |
| 2445 | | .xor, |
| 2446 | | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u13), |
| 2447 | | else => unreachable, |
| 2448 | | }; |
| 2449 | | |
| 2450 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2451 | | .bit_and => .@"and", |
| 2452 | | .bit_or => .@"or", |
| 2453 | | .xor => .xor, |
| 2454 | | else => unreachable, |
| 2455 | | }; |
| 2741 | .Bool => { |
| 2742 | assert(lhs != .immediate); // should have been handled by Sema |
| 2743 | assert(rhs != .immediate); // should have been handled by Sema |
| 2744 | |
| 2745 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2746 | .bool_and => .@"and", |
| 2747 | .bool_or => .@"or", |
| 2748 | else => unreachable, |
| 2749 | }; |
| 2456 | 2750 | |
| 2457 | | if (rhs_immediate_ok) { |
| 2458 | | return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); |
| 2459 | | } else if (lhs_immediate_ok) { |
| 2460 | | // swap lhs and rhs |
| 2461 | | return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); |
| 2462 | | } else { |
| 2463 | | // TODO convert large immediates to register before adding |
| 2464 | | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2465 | | } |
| 2466 | | } else { |
| 2467 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2468 | | } |
| 2751 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2469 | 2752 | }, |
| 2470 | 2753 | else => unreachable, |
| 2471 | 2754 | } |
| 2472 | 2755 | }, |
| 2473 | 2756 | |
| 2474 | | .shl => { |
| 2757 | .shl, |
| 2758 | .shr, |
| 2759 | => { |
| 2475 | 2760 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2476 | 2761 | .shl => .shl_exact, |
| 2762 | .shr => .shr_exact, |
| 2477 | 2763 | else => unreachable, |
| 2478 | 2764 | }; |
| 2479 | 2765 | |
| 2480 | | // Generate a shl_exact/shr_exact |
| 2766 | // Generate the base operation |
| 2481 | 2767 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2482 | 2768 | |
| 2483 | 2769 | // Truncate if necessary |
| 2484 | | switch (tag) { |
| 2485 | | .shl => switch (lhs_ty.zigTypeTag()) { |
| 2486 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2487 | | .Int => { |
| 2488 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 2489 | | if (int_info.bits <= 64) { |
| 2490 | | const result_reg = result.register; |
| 2491 | | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2492 | | return result; |
| 2493 | | } else { |
| 2494 | | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2495 | | } |
| 2496 | | }, |
| 2497 | | else => unreachable, |
| 2770 | switch (lhs_ty.zigTypeTag()) { |
| 2771 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2772 | .Int => { |
| 2773 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2774 | if (int_info.bits <= 64) { |
| 2775 | // 32 and 64 bit operands doesn't need truncating |
| 2776 | if (int_info.bits == 32 or int_info.bits == 64) return result; |
| 2777 | |
| 2778 | const result_reg = result.register; |
| 2779 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2780 | return result; |
| 2781 | } else { |
| 2782 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2783 | } |
| 2498 | 2784 | }, |
| 2499 | 2785 | else => unreachable, |
| 2500 | 2786 | } |
| 2501 | 2787 | }, |
| 2502 | 2788 | |
| 2503 | | .shl_exact => { |
| 2789 | .shl_exact, |
| 2790 | .shr_exact, |
| 2791 | => { |
| 2504 | 2792 | switch (lhs_ty.zigTypeTag()) { |
| 2505 | 2793 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2506 | 2794 | .Int => { |
| ... | ... | @@ -2509,7 +2797,11 @@ fn binOp( |
| 2509 | 2797 | const rhs_immediate_ok = rhs == .immediate; |
| 2510 | 2798 | |
| 2511 | 2799 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2512 | | .shl_exact => .sllx, |
| 2800 | .shl_exact => if (int_info.bits <= 32) Mir.Inst.Tag.sll else Mir.Inst.Tag.sllx, |
| 2801 | .shr_exact => switch (int_info.signedness) { |
| 2802 | .signed => if (int_info.bits <= 32) Mir.Inst.Tag.sra else Mir.Inst.Tag.srax, |
| 2803 | .unsigned => if (int_info.bits <= 32) Mir.Inst.Tag.srl else Mir.Inst.Tag.srlx, |
| 2804 | }, |
| 2513 | 2805 | else => unreachable, |
| 2514 | 2806 | }; |
| 2515 | 2807 | |
| ... | ... | @@ -2616,7 +2908,21 @@ fn binOpImmediate( |
| 2616 | 2908 | .rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) }, |
| 2617 | 2909 | }, |
| 2618 | 2910 | }, |
| 2619 | | .sllx => .{ |
| 2911 | .sll, |
| 2912 | .srl, |
| 2913 | .sra, |
| 2914 | => .{ |
| 2915 | .shift = .{ |
| 2916 | .is_imm = true, |
| 2917 | .rd = dest_reg, |
| 2918 | .rs1 = lhs_reg, |
| 2919 | .rs2_or_imm = .{ .imm = @intCast(u5, rhs.immediate) }, |
| 2920 | }, |
| 2921 | }, |
| 2922 | .sllx, |
| 2923 | .srlx, |
| 2924 | .srax, |
| 2925 | => .{ |
| 2620 | 2926 | .shift = .{ |
| 2621 | 2927 | .is_imm = true, |
| 2622 | 2928 | .rd = dest_reg, |
| ... | ... | @@ -2740,7 +3046,13 @@ fn binOpRegister( |
| 2740 | 3046 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 2741 | 3047 | }, |
| 2742 | 3048 | }, |
| 2743 | | .sllx => .{ |
| 3049 | .sll, |
| 3050 | .srl, |
| 3051 | .sra, |
| 3052 | .sllx, |
| 3053 | .srlx, |
| 3054 | .srax, |
| 3055 | => .{ |
| 2744 | 3056 | .shift = .{ |
| 2745 | 3057 | .is_imm = false, |
| 2746 | 3058 | .rd = dest_reg, |