From f106c9c9f73de87ee6aa2d6bf77cf57c4382e011 Mon Sep 17 00:00:00 2001 From: "ulan@chromium.org" Date: Mon, 5 Nov 2012 13:28:10 +0000 Subject: [PATCH] Add rotate-right instruction to hydrogen and use it instead of bitwise operations of the form ((x >>> i) | (x << (32 - i))). This CL is based on https://chromiumcodereview.appspot.com/10984057/ by Jay Conrod . R=danno@chromium.org,mstarzinger@chromium.org,dconrod@codeaurora.org Review URL: https://chromiumcodereview.appspot.com/11033005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12855 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 6 + src/arm/lithium-codegen-arm.cc | 10 ++ src/arm/simulator-arm.cc | 9 +- src/hydrogen-instructions.h | 20 +++ src/hydrogen.cc | 80 +++++++++-- src/hydrogen.h | 6 + src/ia32/assembler-ia32.cc | 19 +++ src/ia32/assembler-ia32.h | 2 + src/ia32/lithium-codegen-ia32.cc | 15 +++ src/ia32/lithium-ia32.cc | 6 + src/token.h | 1 + src/x64/assembler-x64.h | 8 ++ src/x64/lithium-codegen-x64.cc | 8 ++ src/x64/lithium-x64.cc | 6 + test/mjsunit/compiler/rotate.js | 224 +++++++++++++++++++++++++++++++ 15 files changed, 407 insertions(+), 13 deletions(-) create mode 100644 test/mjsunit/compiler/rotate.js diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index ea661831f..ff6da03bb 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -177,6 +177,7 @@ const char* LArithmeticT::Mnemonic() const { case Token::BIT_AND: return "bit-and-t"; case Token::BIT_OR: return "bit-or-t"; case Token::BIT_XOR: return "bit-xor-t"; + case Token::ROR: return "ror-t"; case Token::SHL: return "shl-t"; case Token::SAR: return "sar-t"; case Token::SHR: return "shr-t"; @@ -1099,6 +1100,11 @@ LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { } +LInstruction* LChunkBuilder::DoRor(HRor* instr) { + return DoShift(Token::ROR, instr); +} + + LInstruction* LChunkBuilder::DoShr(HShr* instr) { return DoShift(Token::SHR, instr); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 458639dd8..8c107d95f 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -1489,6 +1489,9 @@ void LCodeGen::DoShiftI(LShiftI* instr) { // Mask the right_op operand. __ and_(scratch, ToRegister(right_op), Operand(0x1F)); switch (instr->op()) { + case Token::ROR: + __ mov(result, Operand(left, ROR, scratch)); + break; case Token::SAR: __ mov(result, Operand(left, ASR, scratch)); break; @@ -1512,6 +1515,13 @@ void LCodeGen::DoShiftI(LShiftI* instr) { int value = ToInteger32(LConstantOperand::cast(right_op)); uint8_t shift_count = static_cast(value & 0x1F); switch (instr->op()) { + case Token::ROR: + if (shift_count != 0) { + __ mov(result, Operand(left, ROR, shift_count)); + } else { + __ Move(result, left); + } + break; case Token::SAR: if (shift_count != 0) { __ mov(result, Operand(left, ASR, shift_count)); diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 5b8ba2ada..5cdba9c46 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -1459,7 +1459,14 @@ int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) { } case ROR: { - UNIMPLEMENTED(); + if (shift_amount == 0) { + *carry_out = c_flag_; + } else { + uint32_t left = static_cast(result) >> shift_amount; + uint32_t right = static_cast(result) << (32 - shift_amount); + result = right | left; + *carry_out = (static_cast(result) >> 31) != 0; + } break; } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index c253d06cf..4ad98b83c 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -152,6 +152,7 @@ class LChunkBuilder; V(Random) \ V(RegExpLiteral) \ V(Return) \ + V(Ror) \ V(Sar) \ V(Shl) \ V(Shr) \ @@ -3729,6 +3730,25 @@ class HSar: public HBitwiseBinaryOperation { }; +class HRor: public HBitwiseBinaryOperation { + public: + HRor(HValue* context, HValue* left, HValue* right) + : HBitwiseBinaryOperation(context, left, right) { + ChangeRepresentation(Representation::Integer32()); + } + + static HInstruction* NewHRor(Zone* zone, + HValue* context, + HValue* left, + HValue* right); + + DECLARE_CONCRETE_INSTRUCTION(Ror) + + protected: + virtual bool DataEquals(HValue* other) { return true; } +}; + + class HOsrEntry: public HTemplateInstruction<0> { public: explicit HOsrEntry(BailoutId ast_id) : ast_id_(ast_id) { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 7381d022f..3c50e5fcb 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -8226,6 +8226,61 @@ HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* context, return new(zone()) HStringCharCodeAt(context, string, checked_index); } +// Checks if the given shift amounts have form: (sa) and (32 - sa). +static bool ShiftAmountsAllowReplaceByRotate(HValue* sa, + HValue* const32_minus_sa) { + if (!const32_minus_sa->IsSub()) return false; + HSub* sub = HSub::cast(const32_minus_sa); + HValue* const32 = sub->left(); + if (!const32->IsConstant() || + HConstant::cast(const32)->Integer32Value() != 32) { + return false; + } + return (sub->right() == sa); +} + + +// Checks if the left and the right are shift instructions with the oposite +// directions that can be replaced by one rotate right instruction or not. +// Returns the operand and the shift amount for the rotate instruction in the +// former case. +bool HGraphBuilder::MatchRotateRight(HValue* left, + HValue* right, + HValue** operand, + HValue** shift_amount) { + HShl* shl; + HShr* shr; + if (left->IsShl() && right->IsShr()) { + shl = HShl::cast(left); + shr = HShr::cast(right); + } else if (left->IsShr() && right->IsShl()) { + shl = HShl::cast(right); + shr = HShr::cast(left); + } else { + return false; + } + + if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) && + !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) { + return false; + } + *operand= shr->left(); + *shift_amount = shr->right(); + return true; +} + + +bool CanBeZero(HValue *right) { + if (right->IsConstant()) { + HConstant* right_const = HConstant::cast(right); + if (right_const->HasInteger32Value() && + (right_const->Integer32Value() & 0x1f) != 0) { + return false; + } + } + return true; +} + HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, HValue* left, @@ -8264,25 +8319,26 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, break; case Token::BIT_XOR: case Token::BIT_AND: - case Token::BIT_OR: instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); break; + case Token::BIT_OR: { + HValue* operand, *shift_amount; + if (info.IsInteger32() && + MatchRotateRight(left, right, &operand, &shift_amount)) { + instr = new(zone()) HRor(context, operand, shift_amount); + } else { + instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); + } + break; + } case Token::SAR: instr = HSar::NewHSar(zone(), context, left, right); break; case Token::SHR: instr = HShr::NewHShr(zone(), context, left, right); - if (FLAG_opt_safe_uint32_operations && instr->IsShr()) { - bool can_be_shift_by_zero = true; - if (right->IsConstant()) { - HConstant* right_const = HConstant::cast(right); - if (right_const->HasInteger32Value() && - (right_const->Integer32Value() & 0x1f) != 0) { - can_be_shift_by_zero = false; - } - } - - if (can_be_shift_by_zero) graph()->RecordUint32Instruction(instr); + if (FLAG_opt_safe_uint32_operations && instr->IsShr() && + CanBeZero(right)) { + graph()->RecordUint32Instruction(instr); } break; case Token::SHL: diff --git a/src/hydrogen.h b/src/hydrogen.h index b05e92773..374897058 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -270,6 +270,7 @@ class HGraph: public ZoneObject { void DehoistSimpleArrayIndexComputations(); void DeadCodeElimination(); void PropagateDeoptimizingMark(); + void EliminateUnusedInstructions(); // Returns false if there are phi-uses of the arguments-object // which are not supported by the optimizing compiler. @@ -1220,6 +1221,11 @@ class HGraphBuilder: public AstVisitor { HValue* receiver, Handle receiver_map); + bool MatchRotateRight(HValue* left, + HValue* right, + HValue** operand, + HValue** shift_amount); + Zone* zone() const { return zone_; } // The translation state of the currently-being-translated function. diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 986395259..641c0eac4 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1064,6 +1064,25 @@ void Assembler::rcr(Register dst, uint8_t imm8) { } } +void Assembler::ror(Register dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + ASSERT(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + EMIT(0xC8 | dst.code()); + } else { + EMIT(0xC1); + EMIT(0xC8 | dst.code()); + EMIT(imm8); + } +} + +void Assembler::ror_cl(Register dst) { + EnsureSpace ensure_space(this); + EMIT(0xD3); + EMIT(0xC8 | dst.code()); +} + void Assembler::sar(Register dst, uint8_t imm8) { EnsureSpace ensure_space(this); diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index b0f4651d1..6e079dee9 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -817,6 +817,8 @@ class Assembler : public AssemblerBase { void rcl(Register dst, uint8_t imm8); void rcr(Register dst, uint8_t imm8); + void ror(Register dst, uint8_t imm8); + void ror_cl(Register dst); void sar(Register dst, uint8_t imm8); void sar_cl(Register dst); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index b1c9a1755..8bd4575a2 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -1300,6 +1300,13 @@ void LCodeGen::DoShiftI(LShiftI* instr) { ASSERT(ToRegister(right).is(ecx)); switch (instr->op()) { + case Token::ROR: + __ ror_cl(ToRegister(left)); + if (instr->can_deopt()) { + __ test(ToRegister(left), Immediate(0x80000000)); + DeoptimizeIf(not_zero, instr->environment()); + } + break; case Token::SAR: __ sar_cl(ToRegister(left)); break; @@ -1321,6 +1328,14 @@ void LCodeGen::DoShiftI(LShiftI* instr) { int value = ToInteger32(LConstantOperand::cast(right)); uint8_t shift_count = static_cast(value & 0x1F); switch (instr->op()) { + case Token::ROR: + if (shift_count == 0 && instr->can_deopt()) { + __ test(ToRegister(left), Immediate(0x80000000)); + DeoptimizeIf(not_zero, instr->environment()); + } else { + __ ror(ToRegister(left), shift_count); + } + break; case Token::SAR: if (shift_count != 0) { __ sar(ToRegister(left), shift_count); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 0945848ea..4207410cd 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -179,6 +179,7 @@ const char* LArithmeticT::Mnemonic() const { case Token::BIT_AND: return "bit-and-t"; case Token::BIT_OR: return "bit-or-t"; case Token::BIT_XOR: return "bit-xor-t"; + case Token::ROR: return "ror-t"; case Token::SHL: return "sal-t"; case Token::SAR: return "sar-t"; case Token::SHR: return "shr-t"; @@ -1160,6 +1161,11 @@ LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { } +LInstruction* LChunkBuilder::DoRor(HRor* instr) { + return DoShift(Token::ROR, instr); +} + + LInstruction* LChunkBuilder::DoShr(HShr* instr) { return DoShift(Token::SHR, instr); } diff --git a/src/token.h b/src/token.h index 3036e5512..863ba6285 100644 --- a/src/token.h +++ b/src/token.h @@ -99,6 +99,7 @@ namespace internal { T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ + T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ T(ADD, "+", 12) \ T(SUB, "-", 12) \ T(MUL, "*", 13) \ diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index e8b0be9ba..cd10d723f 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -1021,6 +1021,14 @@ class Assembler : public AssemblerBase { shift(dst, imm8, 0x1); } + void rorl(Register dst, Immediate imm8) { + shift_32(dst, imm8, 0x1); + } + + void rorl_cl(Register dst) { + shift_32(dst, 0x1); + } + // Shifts dst:src left by cl bits, affecting only dst. void shld(Register dst, Register src); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 629e11fd7..4f8f10e96 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -1210,6 +1210,9 @@ void LCodeGen::DoShiftI(LShiftI* instr) { ASSERT(ToRegister(right).is(rcx)); switch (instr->op()) { + case Token::ROR: + __ rorl_cl(ToRegister(left)); + break; case Token::SAR: __ sarl_cl(ToRegister(left)); break; @@ -1231,6 +1234,11 @@ void LCodeGen::DoShiftI(LShiftI* instr) { int value = ToInteger32(LConstantOperand::cast(right)); uint8_t shift_count = static_cast(value & 0x1F); switch (instr->op()) { + case Token::ROR: + if (shift_count != 0) { + __ rorl(ToRegister(left), Immediate(shift_count)); + } + break; case Token::SAR: if (shift_count != 0) { __ sarl(ToRegister(left), Immediate(shift_count)); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 23ccd8abc..a874a2400 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -179,6 +179,7 @@ const char* LArithmeticT::Mnemonic() const { case Token::BIT_AND: return "bit-and-t"; case Token::BIT_OR: return "bit-or-t"; case Token::BIT_XOR: return "bit-xor-t"; + case Token::ROR: return "ror-t"; case Token::SHL: return "sal-t"; case Token::SAR: return "sar-t"; case Token::SHR: return "shr-t"; @@ -1100,6 +1101,11 @@ LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { } +LInstruction* LChunkBuilder::DoRor(HRor* instr) { + return DoShift(Token::ROR, instr); +} + + LInstruction* LChunkBuilder::DoShr(HShr* instr) { return DoShift(Token::SHR, instr); } diff --git a/test/mjsunit/compiler/rotate.js b/test/mjsunit/compiler/rotate.js new file mode 100644 index 000000000..14fe9da3e --- /dev/null +++ b/test/mjsunit/compiler/rotate.js @@ -0,0 +1,224 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --expose-gc + +// Test shift operations that can be replaced by rotate operation. + +function SideEffect() { + with ({}) { } // not inlinable +} + +function Twenty() { + SideEffect(); + return 20; +} + +function Twelve() { + SideEffect(); + return 12; +} + + +function ROR(x, sa) { + return (x >>> sa) | (x << (32 - sa)); +} + +function ROR1(x, sa) { + return (x >>> sa) | (x << (32 - sa)); +} + +function ROR2(x, sa) { + return (x >>> (32 - sa)) | (x << (sa)); +} + +function ROR3(x, sa) { + return (x << (32 - sa)) | (x >>> sa); +} + +function ROR4(x, sa) { + return (x << (sa)) | (x >>> (32 - sa)); +} + +assertEquals(1 << ((2 % 32)), ROR(1, 30)); +assertEquals(1 << ((2 % 32)), ROR(1, 30)); +%OptimizeFunctionOnNextCall(ROR); +assertEquals(1 << ((2 % 32)), ROR(1, 30)); + +assertEquals(0xF0000FFF | 0, ROR1(0x0000FFFF, 4)); +assertEquals(0xF0000FFF | 0, ROR1(0x0000FFFF, 4)); +%OptimizeFunctionOnNextCall(ROR1); +assertEquals(0xF0000FFF | 0, ROR1(0x0000FFFF, 4)); + +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, 20)); +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, 20)); +%OptimizeFunctionOnNextCall(ROR1); +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, 20)); + +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, Twenty())); +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, Twenty())); +%OptimizeFunctionOnNextCall(ROR1); +assertEquals(0x0FFFF000 | 0, ROR1(0x0000FFFF, Twenty())); + +for (var i = 0; i <= 100; i++) { + assertEquals(0xFFFFFFFF | 0, ROR1(0xFFFFFFFF, i)); + assertEquals(0xFFFFFFFF | 0, ROR1(0xFFFFFFFF, i)); + %OptimizeFunctionOnNextCall(ROR1); + assertEquals(0xFFFFFFFF | 0, ROR1(0xFFFFFFFF, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(-1, ROR1(-1, i)); + assertEquals(-1, ROR1(-1, i)); + %OptimizeFunctionOnNextCall(ROR1); + assertEquals(-1, ROR1(-1, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(1 << (32 - (i % 32)), ROR1(1, i)); + assertEquals(1 << (32 - (i % 32)), ROR1(1, i)); + %OptimizeFunctionOnNextCall(ROR1); + assertEquals(1 << (32 - (i % 32)), ROR1(1, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(1 << (32 - (i % 32)), ROR1(1.4, i)); + assertEquals(1 << (32 - (i % 32)), ROR1(1.4, i)); + %OptimizeFunctionOnNextCall(ROR1); + assertEquals(1 << (32 - (i % 32)), ROR1(1.4, i)); +} + + + +assertEquals(0xF0000FFF | 0, ROR2(0x0000FFFF, 28)); +assertEquals(0xF0000FFF | 0, ROR2(0x0000FFFF, 28)); +%OptimizeFunctionOnNextCall(ROR2); +assertEquals(0xF0000FFF | 0, ROR2(0x0000FFFF, 28)); + +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, 12)); +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, 12)); +%OptimizeFunctionOnNextCall(ROR2); +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, 12)); + +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, Twelve())); +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, Twelve())); +%OptimizeFunctionOnNextCall(ROR2); +assertEquals(0x0FFFF000 | 0, ROR2(0x0000FFFF, Twelve())); + +for (var i = 0; i <= 100; i++) { + assertEquals(0xFFFFFFFF | 0, ROR2(0xFFFFFFFF, i)); + assertEquals(0xFFFFFFFF | 0, ROR2(0xFFFFFFFF, i)); + %OptimizeFunctionOnNextCall(ROR2); + assertEquals(0xFFFFFFFF | 0, ROR2(0xFFFFFFFF, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(-1, ROR2(-1, i)); + assertEquals(-1, ROR2(-1, i)); + %OptimizeFunctionOnNextCall(ROR2); + assertEquals(-1, ROR2(-1, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(1 << ((i % 32)), ROR2(1, i)); + assertEquals(1 << ((i % 32)), ROR2(1, i)); + %OptimizeFunctionOnNextCall(ROR2); + assertEquals(1 << ((i % 32)), ROR2(1, i)); +} + +assertEquals(0xF0000FFF | 0, ROR3(0x0000FFFF, 4)); +assertEquals(0xF0000FFF | 0, ROR3(0x0000FFFF, 4)); +%OptimizeFunctionOnNextCall(ROR3); +assertEquals(0xF0000FFF | 0, ROR3(0x0000FFFF, 4)); + +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, 20)); +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, 20)); +%OptimizeFunctionOnNextCall(ROR3); +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, 20)); + +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, Twenty())); +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, Twenty())); +%OptimizeFunctionOnNextCall(ROR3); +assertEquals(0x0FFFF000 | 0, ROR3(0x0000FFFF, Twenty())); + +for (var i = 0; i <= 100; i++) { + assertEquals(0xFFFFFFFF | 0, ROR3(0xFFFFFFFF, i)); + assertEquals(0xFFFFFFFF | 0, ROR3(0xFFFFFFFF, i)); + %OptimizeFunctionOnNextCall(ROR3); + assertEquals(0xFFFFFFFF | 0, ROR3(0xFFFFFFFF, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(-1, ROR3(-1, i)); + assertEquals(-1, ROR3(-1, i)); + %OptimizeFunctionOnNextCall(ROR3); + assertEquals(-1, ROR3(-1, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(1 << (32 - (i % 32)), ROR3(1, i)); + assertEquals(1 << (32 - (i % 32)), ROR3(1, i)); + %OptimizeFunctionOnNextCall(ROR3); + assertEquals(1 << (32 - (i % 32)), ROR3(1, i)); +} + +assertEquals(0xF0000FFF | 0, ROR4(0x0000FFFF, 28)); +assertEquals(0xF0000FFF | 0, ROR4(0x0000FFFF, 28)); +%OptimizeFunctionOnNextCall(ROR4); +assertEquals(0xF0000FFF | 0, ROR4(0x0000FFFF, 28)); + +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, 12)); +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, 12)); +%OptimizeFunctionOnNextCall(ROR4); +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, 12)); + +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, Twelve())); +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, Twelve())); +%OptimizeFunctionOnNextCall(ROR4); +assertEquals(0x0FFFF000 | 0, ROR4(0x0000FFFF, Twelve())); + +for (var i = 0; i <= 100; i++) { + assertEquals(0xFFFFFFFF | 0, ROR4(0xFFFFFFFF, i)); + assertEquals(0xFFFFFFFF | 0, ROR4(0xFFFFFFFF, i)); + %OptimizeFunctionOnNextCall(ROR4); + assertEquals(0xFFFFFFFF | 0, ROR4(0xFFFFFFFF, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(-1, ROR4(-1, i)); + assertEquals(-1, ROR4(-1, i)); + %OptimizeFunctionOnNextCall(ROR4); + assertEquals(-1, ROR4(-1, i)); +} + +for (var i = 0; i <= 100; i++) { + assertEquals(1 << ((i % 32)), ROR4(1, i)); + assertEquals(1 << ((i % 32)), ROR4(1, i)); + %OptimizeFunctionOnNextCall(ROR4); + assertEquals(1 << ((i % 32)), ROR4(1, i)); +} + -- 2.34.1