Add rotate-right instruction to hydrogen and use it instead of bitwise operations
authorulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 5 Nov 2012 13:28:10 +0000 (13:28 +0000)
committerulan@chromium.org <ulan@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 5 Nov 2012 13:28:10 +0000 (13:28 +0000)
of the form ((x >>> i) | (x << (32 - i))).

This CL is based on https://chromiumcodereview.appspot.com/10984057/
by Jay Conrod <dconrod@codeaurora.org>.

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

15 files changed:
src/arm/lithium-arm.cc
src/arm/lithium-codegen-arm.cc
src/arm/simulator-arm.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/assembler-ia32.cc
src/ia32/assembler-ia32.h
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/token.h
src/x64/assembler-x64.h
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
test/mjsunit/compiler/rotate.js [new file with mode: 0644]

index ea661831f2af502d70096127485b07c0796f7726..ff6da03bb5811260b4add49b34bc8dff5bb838c6 100644 (file)
@@ -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);
 }
index 458639dd897d62e8b0c3c1cc5ea2564784d0485f..8c107d95f7f6e0f2d7f1179c164dafaa89f0af95 100644 (file)
@@ -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<uint8_t>(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));
index 5b8ba2adae0d5178145ef523691f11033a602ecf..5cdba9c467a65c1b23862ccbf3638837784152a6 100644 (file)
@@ -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<uint32_t>(result) >> shift_amount;
+          uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount);
+          result = right | left;
+          *carry_out = (static_cast<uint32_t>(result) >> 31) != 0;
+        }
         break;
       }
 
index c253d06cf0f0e0c241019b98cbfc4412254443c7..4ad98b83c2751a3ff2322e8df1f1a28caad18b59 100644 (file)
@@ -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) {
index 7381d022ffed8e18bb11cf933661f8bc0ea46eaa..3c50e5fcb60e02dc751de11acf54f8f0eb33b2f6 100644 (file)
@@ -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:
index b05e927730a1ec3cd20343b75d520bdd42f18554..3748970585155bb876219b0a70a777aa9b49e32f 100644 (file)
@@ -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<Map> 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.
index 986395259f07822f9d54ff07c379d4d0c728d9fd..641c0eac4ba6fb7d246306aee9a3dd5c15192f8f 100644 (file)
@@ -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);
index b0f4651d1b31a6293af7d27006d5e05446c0ea5a..6e079dee9f004cb7068f7db3a6ab0cff3e191c7e 100644 (file)
@@ -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);
index b1c9a17554e02d3f93bb65b5021f0d5622f270fc..8bd4575a2018cac02fda189dab7041d8ed82e62e 100644 (file)
@@ -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<uint8_t>(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);
index 0945848ea07a421954a23d5a666f0e51fff1181e..4207410cdf28c4dbc5c9c779da85436c7ac0a32f 100644 (file)
@@ -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);
 }
index 3036e5512a0b14f3d7adea6fd5167865d8a9e4dd..863ba6285760d4f8a015b9d22dab7962584aecf0 100644 (file)
@@ -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)                                                       \
index e8b0be9bab6d100b1a083f06f401392b86e41d3d..cd10d723ffcd4ad549852865f8b8b1bceaa4c00c 100644 (file)
@@ -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);
 
index 629e11fd70f279b6e6160c29fb5ace2912b68ac0..4f8f10e962eeba3df7e2c045412c8364a4d5014e 100644 (file)
@@ -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<uint8_t>(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));
index 23ccd8abc8668958a261388afd1247ac1c9d0efd..a874a2400aa1a850eb924ceb83c0df8f02b03519 100644 (file)
@@ -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 (file)
index 0000000..14fe9da
--- /dev/null
@@ -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));
+}
+