Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: src/arm/codegen-arm.cc

Issue 155047: ARM improvements to constant div, mod and mul.... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/arm/codegen-arm.h ('k') | src/arm/disasm-arm.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/arm/codegen-arm.cc
===================================================================
--- src/arm/codegen-arm.cc (revision 2339)
+++ src/arm/codegen-arm.cc (working copy)
@@ -50,6 +50,11 @@
bool strict);
static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm);
+static void MultiplyByKnownInt(MacroAssembler* masm,
+ Register source,
+ Register destination,
+ int known_int);
+static bool IsEasyToMultiplyBy(int x);
@@ -695,33 +700,69 @@
class GenericBinaryOpStub : public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
- OverwriteMode mode)
- : op_(op), mode_(mode) { }
+ OverwriteMode mode,
+ int known_rhs = CodeGenerator::kUnknownIntValue)
+ : op_(op),
+ mode_(mode),
+ known_rhs_(known_rhs),
+ specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, known_rhs)) { }
private:
Token::Value op_;
OverwriteMode mode_;
+ int known_rhs_;
+ bool specialized_on_rhs_;
+ static const int kMaxKnownRhs = 0x40000000;
+
// Minor key encoding in 16 bits.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 14> {};
+ class OpBits: public BitField<Token::Value, 2, 6> {};
+ class KnownIntBits: public BitField<int, 8, 8> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
- | ModeBits::encode(mode_);
+ | ModeBits::encode(mode_)
+ | KnownIntBits::encode(MinorKeyForKnownInt());
}
void Generate(MacroAssembler* masm);
void HandleNonSmiBitwiseOp(MacroAssembler* masm);
+ static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int known_rhs) {
+ if (known_rhs == CodeGenerator::kUnknownIntValue) return false;
+ if (op == Token::DIV) return known_rhs >= 2 && known_rhs <= 3;
+ if (op == Token::MOD) {
+ if (known_rhs <= 1) return false;
+ if (known_rhs <= 10) return true;
+ if (known_rhs <= kMaxKnownRhs && IsPowerOf2(known_rhs)) return true;
+ return false;
+ }
+ return false;
+ }
+
+ int MinorKeyForKnownInt() {
+ if (!specialized_on_rhs_) return 0;
+ if (known_rhs_ <= 10) return known_rhs_ + 1;
+ ASSERT(IsPowerOf2(known_rhs_));
+ int key = 12;
+ int d = known_rhs_;
+ while ((d & 1) == 0) {
+ key++;
+ d >>= 1;
+ }
+ return key;
+ }
+
const char* GetName() {
switch (op_) {
case Token::ADD: return "GenericBinaryOpStub_ADD";
case Token::SUB: return "GenericBinaryOpStub_SUB";
case Token::MUL: return "GenericBinaryOpStub_MUL";
case Token::DIV: return "GenericBinaryOpStub_DIV";
+ case Token::MOD: return "GenericBinaryOpStub_MOD";
case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
@@ -733,13 +774,22 @@
}
#ifdef DEBUG
- void Print() { PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); }
+ void Print() {
+ if (specialized_on_rhs_) {
+ PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_));
William Hesse 2009/07/03 11:27:01 Why not make this message more informative, by inc
Erik Corry 2009/07/03 17:43:38 Oops, that is what I was trying to do, but I got t
+ } else {
+ PrintF("GenericBinaryOpStub (%s by %d)\n",
+ Token::String(op_),
+ known_rhs_);
+ }
+ }
#endif
};
void CodeGenerator::GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode) {
+ OverwriteMode overwrite_mode,
+ int known_rhs) {
William Hesse 2009/07/03 11:27:01 Would "constant_rhs" be a better name than "known_
Erik Corry 2009/07/03 17:43:38 Probably
VirtualFrame::SpilledScope spilled_scope;
// sp[0] : y
// sp[1] : x
@@ -750,6 +800,8 @@
case Token::ADD: // fall through.
case Token::SUB: // fall through.
case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR:
@@ -758,27 +810,11 @@
case Token::SAR: {
frame_->EmitPop(r0); // r0 : y
frame_->EmitPop(r1); // r1 : x
- GenericBinaryOpStub stub(op, overwrite_mode);
+ GenericBinaryOpStub stub(op, overwrite_mode, known_rhs);
frame_->CallStub(&stub, 0);
break;
}
- case Token::DIV: {
- Result arg_count = allocator_->Allocate(r0);
- ASSERT(arg_count.is_valid());
- __ mov(arg_count.reg(), Operand(1));
- frame_->InvokeBuiltin(Builtins::DIV, CALL_JS, &arg_count, 2);
- break;
- }
-
- case Token::MOD: {
- Result arg_count = allocator_->Allocate(r0);
- ASSERT(arg_count.is_valid());
- __ mov(arg_count.reg(), Operand(1));
- frame_->InvokeBuiltin(Builtins::MOD, CALL_JS, &arg_count, 2);
- break;
- }
-
case Token::COMMA:
frame_->EmitPop(r0);
// simply discard left value
@@ -842,6 +878,10 @@
break;
}
+ // For these operations there is no optimistic operation that needs to be
+ // reverted.
+ case Token::MUL:
+ case Token::MOD:
case Token::BIT_OR:
case Token::BIT_XOR:
case Token::BIT_AND: {
@@ -872,11 +912,55 @@
break;
}
- GenericBinaryOpStub stub(op_, overwrite_mode_);
+ GenericBinaryOpStub stub(op_, overwrite_mode_, value_);
__ CallStub(&stub);
}
+static bool PopCountLessThanEqual2(unsigned int x) {
+ int popcnt = 0;
+ while (x != 0) {
+ if ((x & 1) != 0) {
+ popcnt++;
+ if (popcnt > 2) return false;
William Hesse 2009/07/03 11:27:01 The whole function can be replaced by: x &= x - 1;
Erik Corry 2009/07/03 17:43:38 Cool
+ }
+ x >>= 1;
+ }
+ return true;
+}
+
+
+// Return an int that is <= x that can be encoded as an 8 bit constant shifted
+// left by an even number of bits.
+static int ArmEncodableLessThan(unsigned int x) {
+ int shift_distance = 0;
+ while (x > 0xffff) {
+ x >>= 8;
+ shift_distance += 8;
+ }
+ while (x > 0xff) {
+ x >>= 2;
+ shift_distance += 2;
+ }
+ return x << shift_distance;
+}
+
+
+// Returns the index of the lowest bit set.
+static int BitPosition(unsigned x) {
+ int bit_posn = 0;
+ while ((x & 0xf) == 0) {
+ bit_posn += 4;
+ x >>= 4;
+ }
+ while ((x & 1) == 0) {
+ bit_posn++;
+ x >>= 1;
+ }
+ return bit_posn;
+}
+
+
void CodeGenerator::SmiOperation(Token::Value op,
Handle<Object> value,
bool reversed,
@@ -896,6 +980,7 @@
JumpTarget exit;
frame_->EmitPop(r0);
+ bool something_to_inline = true;
switch (op) {
case Token::ADD: {
DeferredCode* deferred =
@@ -925,6 +1010,7 @@
break;
}
+
case Token::BIT_OR:
case Token::BIT_XOR:
case Token::BIT_AND: {
@@ -946,70 +1032,113 @@
case Token::SHR:
case Token::SAR: {
if (reversed) {
- __ mov(ip, Operand(value));
- frame_->EmitPush(ip);
- frame_->EmitPush(r0);
- GenericBinaryOperation(op, mode);
-
- } else {
- int shift_value = int_value & 0x1f; // least significant 5 bits
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, shift_value, false, mode);
- __ tst(r0, Operand(kSmiTagMask));
- deferred->Branch(ne);
- __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags
- switch (op) {
- case Token::SHL: {
- __ mov(r2, Operand(r2, LSL, shift_value));
- // check that the *unsigned* result fits in a smi
- __ add(r3, r2, Operand(0x40000000), SetCC);
- deferred->Branch(mi);
- break;
+ something_to_inline = false;
+ break;
+ }
+ int shift_value = int_value & 0x1f; // least significant 5 bits
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op, shift_value, false, mode);
+ __ tst(r0, Operand(kSmiTagMask));
+ deferred->Branch(ne);
+ __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags
+ switch (op) {
+ case Token::SHL: {
William Hesse 2009/07/03 11:27:01 Why no check of shift_value with 0 for SHL? If it
Erik Corry 2009/07/03 17:43:38 Shift left by zero is the way reg-reg mov is encod
+ __ mov(r2, Operand(r2, LSL, shift_value));
+ // check that the *unsigned* result fits in a smi
+ __ add(r3, r2, Operand(0x40000000), SetCC);
+ deferred->Branch(mi);
+ break;
+ }
+ case Token::SHR: {
+ // LSR by immediate 0 means shifting 32 bits.
+ if (shift_value != 0) {
+ __ mov(r2, Operand(r2, LSR, shift_value));
}
- case Token::SHR: {
- // LSR by immediate 0 means shifting 32 bits.
- if (shift_value != 0) {
- __ mov(r2, Operand(r2, LSR, shift_value));
- }
- // check that the *unsigned* result fits in a smi
- // neither of the two high-order bits can be set:
- // - 0x80000000: high bit would be lost when smi tagging
- // - 0x40000000: this number would convert to negative when
- // smi tagging these two cases can only happen with shifts
- // by 0 or 1 when handed a valid smi
- __ and_(r3, r2, Operand(0xc0000000), SetCC);
- deferred->Branch(ne);
- break;
+ // check that the *unsigned* result fits in a smi
+ // neither of the two high-order bits can be set:
+ // - 0x80000000: high bit would be lost when smi tagging
+ // - 0x40000000: this number would convert to negative when
+ // smi tagging these two cases can only happen with shifts
+ // by 0 or 1 when handed a valid smi
+ __ and_(r3, r2, Operand(0xc0000000), SetCC);
+ deferred->Branch(ne);
+ break;
+ }
+ case Token::SAR: {
+ if (shift_value != 0) {
+ // ASR by immediate 0 means shifting 32 bits.
+ __ mov(r2, Operand(r2, ASR, shift_value));
}
- case Token::SAR: {
- if (shift_value != 0) {
- // ASR by immediate 0 means shifting 32 bits.
- __ mov(r2, Operand(r2, ASR, shift_value));
- }
- break;
- }
- default: UNREACHABLE();
+ break;
}
- __ mov(r0, Operand(r2, LSL, kSmiTagSize));
- deferred->BindExit();
+ default: UNREACHABLE();
}
+ __ mov(r0, Operand(r2, LSL, kSmiTagSize));
+ deferred->BindExit();
break;
}
- default:
- if (!reversed) {
- frame_->EmitPush(r0);
- __ mov(r0, Operand(value));
- frame_->EmitPush(r0);
- } else {
- __ mov(ip, Operand(value));
- frame_->EmitPush(ip);
- frame_->EmitPush(r0);
+ case Token::MOD: {
+ if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
+ something_to_inline = false;
+ break;
}
- GenericBinaryOperation(op, mode);
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op, int_value, reversed, mode);
+ unsigned mask = (0x80000000u | kSmiTagMask);
+ __ tst(r0, Operand(mask));
+ deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
+ mask = (int_value << kSmiTagSize) - 1;
+ __ and_(r0, r0, Operand(mask));
+ deferred->BindExit();
break;
+ }
+
+ case Token::MUL: {
+ if (!IsEasyToMultiplyBy(int_value)) {
+ something_to_inline = false;
+ break;
+ }
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op, int_value, reversed, mode);
+ unsigned max_smi_that_wont_overflow =
+ ArmEncodableLessThan(Smi::kMaxValue / int_value);
+ max_smi_that_wont_overflow <<= kSmiTagSize;
+ unsigned mask = 0x80000000u;
+ while ((mask & max_smi_that_wont_overflow) == 0) {
+ mask |= mask >> 1;
+ }
+ mask |= kSmiTagMask;
+ // This does a single mask that checks for a too high value in a
+ // conservative way and for a non-Smi. It also filters out negative
+ // numbers, unfortunately, but since this code is inline we prefer
+ // brevity to comprehensiveness.
+ __ tst(r0, Operand(mask));
+ deferred->Branch(ne);
+ MultiplyByKnownInt(masm_, r0, r0, int_value);
+ deferred->BindExit();
+ break;
+ }
+
+ default:
+ something_to_inline = false;
+ break;
}
+ if (!something_to_inline) {
+ if (!reversed) {
+ frame_->EmitPush(r0);
+ __ mov(r0, Operand(value));
+ frame_->EmitPush(r0);
+ GenericBinaryOperation(op, mode, int_value);
+ } else {
+ __ mov(ip, Operand(value));
+ frame_->EmitPush(ip);
+ frame_->EmitPush(r0);
+ GenericBinaryOperation(op, mode, kUnknownIntValue);
+ }
+ }
+
exit.Bind();
}
@@ -3308,7 +3437,7 @@
ASSERT(args->length() == 1);
LoadAndSpill(args->at(0));
frame_->EmitPop(r0);
- __ tst(r0, Operand(kSmiTagMask | 0x80000000));
+ __ tst(r0, Operand(kSmiTagMask | 0x80000000u));
cc_reg_ = eq;
}
@@ -4354,7 +4483,7 @@
__ add(zeros, zeros, Operand(2), LeaveCC, eq);
__ mov(scratch, Operand(scratch, LSL, 2), LeaveCC, eq);
// Top bit.
- __ tst(scratch, Operand(0x80000000));
+ __ tst(scratch, Operand(0x80000000u));
__ add(zeros, zeros, Operand(1), LeaveCC, eq);
#endif
}
@@ -4506,7 +4635,7 @@
// We test for the special value that has a different exponent. This test
// has the neat side effect of setting the flags according to the sign.
ASSERT(HeapNumber::kSignMask == 0x80000000u);
- __ cmp(the_int_, Operand(0x80000000));
+ __ cmp(the_int_, Operand(0x80000000u));
__ b(eq, &max_negative_int);
// Set up the correct exponent in scratch_. All non-Smi int32s have the same.
// A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
@@ -5320,6 +5449,84 @@
}
+// Can we multiply by x with max two shifts and an add.
+// This answers yes to all integers from 2 to 10.
+static bool IsEasyToMultiplyBy(int x) {
+ if (x < 2) return false; // Avoid special cases.
+ if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
+ if (IsPowerOf2(x)) return true; // Simple shift.
+ if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
+ if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
+ return false;
+}
+
+
+// Can multiply by anything that IsEasyToMultiplyBy returns true for.
+// Source and destination may be the same register.
+static void MultiplyByKnownInt(MacroAssembler* masm,
+ Register source,
+ Register destination,
+ int known_int) {
+ if (IsPowerOf2(known_int)) {
+ __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
+ } else if (PopCountLessThanEqual2(known_int)) {
+ int first_bit = BitPosition(known_int);
+ int second_bit = BitPosition(known_int ^ (1 << first_bit));
+ __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
+ if (first_bit != 0) {
+ __ mov(destination, Operand(destination, LSL, first_bit));
William Hesse 2009/07/03 11:27:01 Are you going to find out if the value overflowed
Erik Corry 2009/07/03 17:43:38 No, the flags are not set by this routine. Commen
+ }
+ } else {
+ ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
+ int the_bit = BitPosition(known_int + 1);
+ __ rsb(destination, source, Operand(source, LSL, the_bit));
+ }
+}
+
+
+// This function (as opposed to MultiplyByKnownInt) takes the known int in a
+// a register for the cases where it doesn't know a good trick, and may deliver
+// a result that needs shifting.
+static void MultiplyByKnownInt2(
+ MacroAssembler* masm,
+ Register result,
+ Register source,
+ Register known_int_register, // Smi tagged.
+ int known_int,
+ int* result_needs_shifting) { // Including Smi tag shift
William Hesse 2009/07/03 11:27:01 I would call this shift_amount or required_shift.
+ switch (known_int) {
+ case 3:
+ __ add(result, source, Operand(source, LSL, 1));
+ *result_needs_shifting = 1;
+ break;
+ case 5:
+ __ add(result, source, Operand(source, LSL, 2));
+ *result_needs_shifting = 1;
+ break;
+ case 6:
+ __ add(result, source, Operand(source, LSL, 1));
+ *result_needs_shifting = 2;
+ break;
+ case 7:
+ __ rsb(result, source, Operand(source, LSL, 3));
+ *result_needs_shifting = 1;
+ break;
+ case 9:
+ __ add(result, source, Operand(source, LSL, 3));
+ *result_needs_shifting = 1;
+ break;
+ case 10:
+ __ add(result, source, Operand(source, LSL, 2));
+ *result_needs_shifting = 2;
+ break;
+ default:
+ ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
+ __ mul(result, source, known_int_register);
+ *result_needs_shifting = 0;
+ }
+}
+
+
void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// r1 : x
// r0 : y
@@ -5398,10 +5605,97 @@
&not_smi,
Builtins::MUL,
Token::MUL,
- mode_);
+ mode_);
break;
}
+ case Token::DIV:
+ case Token::MOD: {
+ Label not_smi;
+ if (specialized_on_rhs_) {
+ Label smi_is_unsuitable;
+ __ BranchOnNotSmi(r1, &not_smi);
+ if (IsPowerOf2(known_rhs_)) {
+ if (op_ == Token::MOD) {
+ __ and_(r0,
+ r1,
+ Operand(0x80000000u | ((known_rhs_ << kSmiTagSize) - 1)),
+ SetCC);
+ // We now have the answer, but if the input was negative we also
+ // have the sign bit. Our work is done if the result is
+ // positive or zero:
+ __ Ret(pl);
+ // A mod of a negative left hand side must return a negative number.
+ // Unfortunately if the answer is 0 then we must return -0. And we
+ // already optimistically trashed r0 so we may need to restore it.
+ __ eor(r0, r0, Operand(0x80000000u), SetCC);
+ __ mov(r0, Operand(Smi::FromInt(known_rhs_)), LeaveCC, eq); // -0.
+ __ b(eq, &smi_is_unsuitable); // -0.
+ __ sub(r0, r0, Operand(Smi::FromInt(known_rhs_))); // -3 % 4 == -3.
+ } else {
+ ASSERT(op_ == Token::DIV);
+ __ tst(r1,
+ Operand(0x80000000u | ((known_rhs_ << kSmiTagSize) - 1)));
+ __ b(ne, &smi_is_unsuitable); // Go slow on negative or remainder.
+ int shift = 0;
+ int d = known_rhs_;
+ while ((d & 1) == 0) {
+ d >>= 1;
+ shift++;
+ }
+ __ mov(r0, Operand(r1, LSR, shift));
+ __ bic(r0, r0, Operand(kSmiTagMask));
+ }
+ } else {
+ // Not a power of 2.
+ __ tst(r1, Operand(0x80000000u));
+ __ b(ne, &smi_is_unsuitable);
+ // Find a fixed point reciprocal of the divisor so we can divide by
+ // multiplying.
+ double divisor = 1.0 / known_rhs_;
+ int shift = 32;
+ double scale = 4294967296.0; // 1 << 32.
+ uint32_t mul;
+ // Maximise the precision of the fixed point reciprocal.
+ while (true) {
+ mul = static_cast<uint32_t>(scale * divisor);
+ if (mul >= 0x7fffffff) break;
+ scale *= 2.0;
+ shift++;
+ }
+ mul++;
+ __ mov(r2, Operand(mul));
+ __ umull(r3, r2, r2, r1);
+ __ mov(r2, Operand(r2, LSR, shift - 31));
+ // r2 is r1 / rhs. r2 is not Smi tagged.
+ // r0 is still the known rhs. r0 is Smi tagged.
+ // r1 is still the unkown lhs. r1 is Smi tagged.
+ int r4_needs_shifting = 0; // Including the Smi tag shift of 1.
+ // r4 = r2 * r0.
+ MultiplyByKnownInt2(masm, r4, r2, r0, known_rhs_, &r4_needs_shifting);
+ // r4 << r4_needs_shifting is now the Smi tagged rhs * (r1 / rhs).
+ if (op_ == Token::DIV) {
+ __ sub(r3, r1, Operand(r4, LSL, r4_needs_shifting), SetCC);
+ __ b(ne, &smi_is_unsuitable); // There was a remainder.
+ __ mov(r0, Operand(r2, LSL, kSmiTagSize));
+ } else {
+ ASSERT(op_ == Token::MOD);
+ __ sub(r0, r1, Operand(r4, LSL, r4_needs_shifting));
+ }
+ }
+ __ Ret();
+ __ bind(&smi_is_unsuitable);
+ } else {
+ __ jmp(&not_smi);
+ }
+ HandleBinaryOpSlowCases(masm,
+ &not_smi,
+ op_ == Token::MOD ? Builtins::MOD : Builtins::DIV,
+ op_,
+ mode_);
+ break;
+ }
+
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR:
@@ -5423,7 +5717,7 @@
__ and_(r2, r2, Operand(0x1f));
__ mov(r0, Operand(r1, ASR, r2));
// Smi tag result.
- __ and_(r0, r0, Operand(~kSmiTagMask));
+ __ bic(r0, r0, Operand(kSmiTagMask));
break;
case Token::SHR:
// Remove tags from operands. We can't do this on a 31 bit number
« no previous file with comments | « src/arm/codegen-arm.h ('k') | src/arm/disasm-arm.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698