Fully implement integer modulo in intrinsifier, fall-through only for throwing exceptions. Also intrinsified remainder operation. TODO: generate inline code for both % and remainder.

Review URL: https://codereview.chromium.org//14205023

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21754 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 0a1afd9..1b851aa 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -23,6 +23,7 @@
   V(_IntegerImplementation, _mulFromInteger, Integer_mulFromInteger, 726019207)\
   V(_IntegerImplementation, *, Integer_mul, 1853182047)                        \
   V(_IntegerImplementation, %, Integer_modulo, 1211518976)                     \
+  V(_IntegerImplementation, remainder, Integer_remainder, 1131753683)          \
   V(_IntegerImplementation, ~/, Integer_truncDivide, 142750059)                \
   V(_IntegerImplementation, unary-, Integer_negate, 676633254)                 \
   V(_IntegerImplementation, _bitAndFromInteger,                                \
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 436ae29..6b006a9 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -131,6 +131,11 @@
 }
 
 
+bool Intrinsifier::Integer_remainder(Assembler* assembler) {
+  return false;
+}
+
+
 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) {
   return false;
 }
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index c82d138..2afcd70 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -667,35 +667,107 @@
 }
 
 
-bool Intrinsifier::Integer_modulo(Assembler* assembler) {
-  Label fall_through, return_zero, try_modulo;
-  TestBothArgumentsSmis(assembler, &fall_through);
-  // EAX: right argument (divisor)
-  // Check if modulo by zero -> exception thrown in main function.
+// Optimizations:
+// - result is 0 if:
+//   - left is 0
+//   - left equals right
+// - result is left if
+//   - left > 0 && left < right
+// EAX: Tagged left (dividend).
+// EBX: Tagged right (divisor).
+// EDX: Untagged result (remainder).
+void EmitRemainderOperation(Assembler* assembler) {
+  Label return_zero, modulo;
+  // Check for quick zero results.
   __ cmpl(EAX, Immediate(0));
-  __ j(EQUAL, &fall_through,  Assembler::kNearJump);
-  __ movl(EBX, Address(ESP, + 2 * kWordSize));  // Left argument (dividend).
-  __ cmpl(EBX, Immediate(0));
-  __ j(LESS, &fall_through, Assembler::kNearJump);
-  __ cmpl(EBX, EAX);
   __ j(EQUAL, &return_zero, Assembler::kNearJump);
-  __ j(GREATER, &try_modulo, Assembler::kNearJump);
-  __ movl(EAX, EBX);  // Return dividend as it is smaller than divisor.
+  __ cmpl(EAX, EBX);
+  __ j(EQUAL, &return_zero, Assembler::kNearJump);
+
+  // Check if result equals left.
+  __ cmpl(EAX, Immediate(0));
+  __ j(LESS, &modulo, Assembler::kNearJump);
+  // left is positive.
+  __ cmpl(EAX, EBX);
+  __ j(GREATER, &modulo,  Assembler::kNearJump);
+  // left is less than right, result is left (EAX).
   __ ret();
+
   __ Bind(&return_zero);
-  __ xorl(EAX, EAX);  // Return zero.
+  __ xorl(EAX, EAX);
   __ ret();
-  __ Bind(&try_modulo);
-  // EAX: right (non-null divisor).
-  __ movl(EBX, EAX);
+
+  __ Bind(&modulo);
   __ SmiUntag(EBX);
-  __ movl(EAX, Address(ESP, + 2 * kWordSize));  // Left argument (dividend).
   __ SmiUntag(EAX);
   __ cdq();
   __ idivl(EBX);
+}
+
+
+// Implementation:
+//  res = left % right;
+//  if (res < 0) {
+//    if (right < 0) {
+//      res = res - right;
+//    } else {
+//      res = res + right;
+//    }
+//  }
+bool Intrinsifier::Integer_modulo(Assembler* assembler) {
+  Label fall_through, subtract;
+  TestBothArgumentsSmis(assembler, &fall_through);
+  // EAX: right argument (divisor)
+  __ movl(EBX, EAX);
+  __ movl(EAX, Address(ESP, + 2 * kWordSize));  // Left argument (dividend).
+  // EAX: Tagged left (dividend).
+  // EBX: Tagged right (divisor).
+  // Check if modulo by zero -> exception thrown in main function.
+  __ cmpl(EBX, Immediate(0));
+  __ j(EQUAL, &fall_through, Assembler::kNearJump);
+  EmitRemainderOperation(assembler);
+  // Untagged remainder result in EDX.
+  Label done;
+  __ movl(EAX, EDX);
+  __ cmpl(EAX, Immediate(0));
+  __ j(GREATER_EQUAL, &done, Assembler::kNearJump);
+  // Result is negative, adjust it.
+  __ cmpl(EBX, Immediate(0));
+  __ j(LESS, &subtract, Assembler::kNearJump);
+  __ addl(EAX, EBX);
+  __ SmiTag(EAX);
+  __ ret();
+
+  __ Bind(&subtract);
+  __ subl(EAX, EBX);
+
+  __ Bind(&done);
+  // The remainder of two Smi-s is always a Smi, no overflow check needed.
+  __ SmiTag(EAX);
+  __ ret();
+
+  __ Bind(&fall_through);
+  return false;
+}
+
+
+bool Intrinsifier::Integer_remainder(Assembler* assembler) {
+  Label fall_through;
+  TestBothArgumentsSmis(assembler, &fall_through);
+  // EAX: right argument (divisor)
+  __ movl(EBX, EAX);
+  __ movl(EAX, Address(ESP, + 2 * kWordSize));  // Left argument (dividend).
+  // EAX: Tagged left (dividend).
+  // EBX: Tagged right (divisor).
+  // Check if modulo by zero -> exception thrown in main function.
+  __ cmpl(EBX, Immediate(0));
+  __ j(EQUAL, &fall_through, Assembler::kNearJump);
+  EmitRemainderOperation(assembler);
+  // Untagged remainder result in EDX.
   __ movl(EAX, EDX);
   __ SmiTag(EAX);
   __ ret();
+
   __ Bind(&fall_through);
   return false;
 }
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index c37035f..75e10dd 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -131,6 +131,11 @@
 }
 
 
+bool Intrinsifier::Integer_remainder(Assembler* assembler) {
+  return false;
+}
+
+
 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) {
   return false;
 }
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index ea07728..59f1794 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -560,7 +560,7 @@
   __ movq(RCX, Address(RSP, + 2 * kWordSize));
   __ orq(RCX, RAX);
   __ testq(RCX, Immediate(kSmiTagMask));
-  __ j(NOT_ZERO, not_smi, Assembler::kNearJump);
+  __ j(NOT_ZERO, not_smi);
 }
 
 
@@ -631,30 +631,37 @@
 }
 
 
-bool Intrinsifier::Integer_modulo(Assembler* assembler) {
-  Label fall_through, return_zero, try_modulo, not_32bit;
-  TestBothArgumentsSmis(assembler, &fall_through);
-  // RAX: right argument (divisor)
-  // Check if modulo by zero -> exception thrown in main function.
+// Optimizations:
+// - result is 0 if:
+//   - left is 0
+//   - left equals right
+// - result is left if
+//   - left > 0 && left < right
+// RAX: Tagged left (dividend).
+// RCX: Tagged right (divisor).
+// RAX: Untagged result (remainder).
+void EmitRemainderOperation(Assembler* assembler) {
+  Label return_zero, try_modulo, not_32bit, done;
+  // Check for quick zero results.
   __ cmpq(RAX, Immediate(0));
-  __ j(EQUAL, &fall_through,  Assembler::kNearJump);
-  __ movq(RCX, Address(RSP, + 2 * kWordSize));  // Left argument (dividend).
-  __ cmpq(RCX, Immediate(0));
-  __ j(LESS, &fall_through, Assembler::kNearJump);
-  __ cmpq(RCX, RAX);
   __ j(EQUAL, &return_zero, Assembler::kNearJump);
-  __ j(GREATER, &try_modulo, Assembler::kNearJump);
-  __ movq(RAX, RCX);  // Return dividend as it is smaller than divisor.
+  __ cmpq(RAX, RCX);
+  __ j(EQUAL, &return_zero, Assembler::kNearJump);
+
+  // Check if result equals left.
+  __ cmpq(RAX, Immediate(0));
+  __ j(LESS, &try_modulo, Assembler::kNearJump);
+  // left is positive.
+  __ cmpq(RAX, RCX);
+  __ j(GREATER, &try_modulo,  Assembler::kNearJump);
+  // left is less than right, result is left (RAX).
   __ ret();
 
   __ Bind(&return_zero);
-  __ xorq(RAX, RAX);  // Return zero.
+  __ xorq(RAX, RAX);
   __ ret();
 
   __ Bind(&try_modulo);
-  // RAX: right (non-null divisor).
-  __ movq(RCX, RAX);
-  __ movq(RAX, Address(RSP, + 2 * kWordSize));  // Left argument (dividend).
 
   // Check if both operands fit into 32bits as idiv with 64bit operands
   // requires twice as many cycles and has much higher latency. We are checking
@@ -662,10 +669,10 @@
   // raises exception because quotient is too large for 32bit register.
   __ movsxd(RBX, RAX);
   __ cmpq(RBX, RAX);
-  __ j(NOT_EQUAL, &not_32bit);
+  __ j(NOT_EQUAL, &not_32bit, Assembler::kNearJump);
   __ movsxd(RBX, RCX);
   __ cmpq(RBX, RCX);
-  __ j(NOT_EQUAL, &not_32bit);
+  __ j(NOT_EQUAL, &not_32bit, Assembler::kNearJump);
 
   // Both operands are 31bit smis. Divide using 32bit idiv.
   __ SmiUntag(RAX);
@@ -673,8 +680,7 @@
   __ cdq();
   __ idivl(RCX);
   __ movsxd(RAX, RDX);
-  __ SmiTag(RAX);
-  __ ret();
+  __ jmp(&done, Assembler::kNearJump);
 
   // Divide using 64bit idiv.
   __ Bind(&not_32bit);
@@ -683,9 +689,70 @@
   __ cqo();
   __ idivq(RCX);
   __ movq(RAX, RDX);
+  __ Bind(&done);
+}
+
+
+// Implementation:
+//  res = left % right;
+//  if (res < 0) {
+//    if (right < 0) {
+//      res = res - right;
+//    } else {
+//      res = res + right;
+//    }
+//  }
+bool Intrinsifier::Integer_modulo(Assembler* assembler) {
+  Label fall_through, negative_result;
+  TestBothArgumentsSmis(assembler, &fall_through);
+  // RAX: right argument (divisor)
+  __ movq(RCX, RAX);
+  __ movq(RAX, Address(RSP, + 2 * kWordSize));  // Left argument (dividend).
+  // RAX: Tagged left (dividend).
+  // RCX: Tagged right (divisor).
+  __ cmpq(RCX, Immediate(0));
+  __ j(EQUAL, &fall_through);
+  EmitRemainderOperation(assembler);
+  // Untagged remainder result in RAX.
+  __ cmpq(RAX, Immediate(0));
+  __ j(LESS, &negative_result, Assembler::kNearJump);
   __ SmiTag(RAX);
   __ ret();
 
+  __ Bind(&negative_result);
+  Label subtract;
+  // RAX: Untagged result.
+  // RCX: Untagged right.
+  __ cmpq(RCX, Immediate(0));
+  __ j(LESS, &subtract, Assembler::kNearJump);
+  __ addq(RAX, RCX);
+  __ SmiTag(RAX);
+  __ ret();
+
+  __ Bind(&subtract);
+  __ subq(RAX, RCX);
+  __ SmiTag(RAX);
+  __ ret();
+
+  __ Bind(&fall_through);
+  return false;
+}
+
+
+bool Intrinsifier::Integer_remainder(Assembler* assembler) {
+  Label fall_through;
+  TestBothArgumentsSmis(assembler, &fall_through);
+  // RAX: right argument (divisor)
+  __ movq(RCX, RAX);
+  __ movq(RAX, Address(RSP, + 2 * kWordSize));  // Left argument (dividend).
+  // RAX: Tagged left (dividend).
+  // RCX: Tagged right (divisor).
+  __ cmpq(RCX, Immediate(0));
+  __ j(EQUAL, &fall_through);
+  EmitRemainderOperation(assembler);
+  // Untagged remainder result in RAX.
+  __ SmiTag(RAX);
+  __ ret();
   __ Bind(&fall_through);
   return false;
 }