diff --git a/src/mir/Lowering.cpp b/src/mir/Lowering.cpp index b47e11ec..2efce285 100644 --- a/src/mir/Lowering.cpp +++ b/src/mir/Lowering.cpp @@ -599,61 +599,55 @@ namespace mir } if (val < 0 && (-val & (-val - 1)) == 0 && val != -1) { + // x % -2^n → 同 x % 2^n: (x & (2^n-1)) + sign fixup int abs_val = -val; - int bias = abs_val - 1; - int biased = function.CreateVReg(VRegClass::Int); - if (bias <= 4095) + int mask = abs_val - 1; + int masked = function.CreateVReg(VRegClass::Int); + if (mask <= 4095) { - block.Append(Opcode::AddRR, - {Operand::VReg(biased, VRegClass::Int), + block.Append(Opcode::AndRR, + {Operand::VReg(masked, VRegClass::Int), Operand::VReg(lhs, VRegClass::Int), - Operand::Imm(bias)}); + Operand::Imm(mask)}); } else { - int bias_reg = function.CreateVReg(VRegClass::Int); + int mask_reg = function.CreateVReg(VRegClass::Int); block.Append(Opcode::MovImm, - {Operand::VReg(bias_reg, VRegClass::Int), - Operand::Imm(bias)}).SetRematerializable(true).SetRematImm(bias); - block.Append(Opcode::AddRR, - {Operand::VReg(biased, VRegClass::Int), + {Operand::VReg(mask_reg, VRegClass::Int), + Operand::Imm(mask)}).SetRematerializable(true).SetRematImm(mask); + block.Append(Opcode::AndRR, + {Operand::VReg(masked, VRegClass::Int), Operand::VReg(lhs, VRegClass::Int), - Operand::VReg(bias_reg, VRegClass::Int)}); - } - int shift = 0; - int tmp = abs_val; - while (tmp > 1) - { - tmp >>= 1; - ++shift; + Operand::VReg(mask_reg, VRegClass::Int)}); } block.Append(Opcode::CmpImm, {Operand::VReg(lhs, VRegClass::Int), Operand::Imm(0)}); - int selected = function.CreateVReg(VRegClass::Int); + int neg_fixup = function.CreateVReg(VRegClass::Int); + if (static_cast(abs_val) <= 4095) + { + block.Append(Opcode::SubRR, + {Operand::VReg(neg_fixup, VRegClass::Int), + Operand::VReg(masked, VRegClass::Int), + Operand::Imm(abs_val)}); + } + else + { + int val_reg = function.CreateVReg(VRegClass::Int); + block.Append(Opcode::MovImm, + {Operand::VReg(val_reg, VRegClass::Int), + Operand::Imm(abs_val)}).SetRematerializable(true).SetRematImm(abs_val); + block.Append(Opcode::SubRR, + {Operand::VReg(neg_fixup, VRegClass::Int), + Operand::VReg(masked, VRegClass::Int), + Operand::VReg(val_reg, VRegClass::Int)}); + } block.Append(Opcode::Csel, - {Operand::VReg(selected, VRegClass::Int), - Operand::VReg(biased, VRegClass::Int), - Operand::VReg(lhs, VRegClass::Int), - Operand::Imm(static_cast(CondCode::LT))}); - int asr_result = function.CreateVReg(VRegClass::Int); - block.Append(Opcode::AsrRR, - {Operand::VReg(asr_result, VRegClass::Int), - Operand::VReg(selected, VRegClass::Int), - Operand::Imm(shift)}); - int q_dst = function.CreateVReg(VRegClass::Int); - block.Append(Opcode::NegRR, - {Operand::VReg(q_dst, VRegClass::Int), - Operand::VReg(asr_result, VRegClass::Int)}); - int d_reg = function.CreateVReg(VRegClass::Int); - block.Append(Opcode::MovImm, - {Operand::VReg(d_reg, VRegClass::Int), - Operand::Imm(val)}).SetRematerializable(true).SetRematImm(val); - block.Append(Opcode::Msub, {Operand::VReg(dst, VRegClass::Int), - Operand::VReg(q_dst, VRegClass::Int), - Operand::VReg(d_reg, VRegClass::Int), - Operand::VReg(lhs, VRegClass::Int)}); + Operand::VReg(neg_fixup, VRegClass::Int), + Operand::VReg(masked, VRegClass::Int), + Operand::Imm(static_cast(CondCode::LT))}); value_vregs[value] = dst; return dst; }