Commit 3e2712e0 authored by lrn@chromium.org's avatar lrn@chromium.org

Lots of small optimizations, and one that is measurable (speeds up celtickane-array signficantly).


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1276 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5344151e
...@@ -620,7 +620,11 @@ function ArraySort(comparefn) { ...@@ -620,7 +620,11 @@ function ArraySort(comparefn) {
var custom_compare = IS_FUNCTION(comparefn); var custom_compare = IS_FUNCTION(comparefn);
function Compare(x,y) { function Compare(x,y) {
// Assume the comparefn, if any, is a consistent comparison function.
// If it isn't, we are allowed arbitrary behavior by ECMA 15.4.4.11.
if (x === y) return 0;
if (custom_compare) { if (custom_compare) {
// Don't call directly to avoid exposing the builtin's global object.
return comparefn.call(null, x, y); return comparefn.call(null, x, y);
} }
if (%_IsSmi(x) && %_IsSmi(y)) { if (%_IsSmi(x) && %_IsSmi(y)) {
...@@ -635,6 +639,10 @@ function ArraySort(comparefn) { ...@@ -635,6 +639,10 @@ function ArraySort(comparefn) {
function InsertionSort(a, from, to) { function InsertionSort(a, from, to) {
for (var i = from + 1; i < to; i++) { for (var i = from + 1; i < to; i++) {
var element = a[i]; var element = a[i];
// Pre-convert the element to a string for comparison if we know
// it will happen on each compare anyway.
var key =
(custom_compare || %_IsSmi(element)) ? element : ToString(element);
// place element in a[from..i[ // place element in a[from..i[
// binary search // binary search
var min = from; var min = from;
...@@ -642,7 +650,7 @@ function ArraySort(comparefn) { ...@@ -642,7 +650,7 @@ function ArraySort(comparefn) {
// The search interval is a[min..max[ // The search interval is a[min..max[
while (min < max) { while (min < max) {
var mid = min + ((max - min) >> 1); var mid = min + ((max - min) >> 1);
var order = Compare(a[mid], element); var order = Compare(a[mid], key);
if (order == 0) { if (order == 0) {
min = max = mid; min = max = mid;
break; break;
...@@ -663,43 +671,49 @@ function ArraySort(comparefn) { ...@@ -663,43 +671,49 @@ function ArraySort(comparefn) {
function QuickSort(a, from, to) { function QuickSort(a, from, to) {
// Insertion sort is faster for short arrays. // Insertion sort is faster for short arrays.
if (to - from <= 22) { if (to - from <= 22) {
InsertionSort(a, from, to); InsertionSort(a, from, to);
return; return;
} }
var pivot_index = $floor($random() * (to - from)) + from; var pivot_index = $floor($random() * (to - from)) + from;
var pivot = a[pivot_index]; var pivot = a[pivot_index];
// Pre-convert the element to a string for comparison if we know
// it will happen on each compare anyway.
var pivot_key =
(custom_compare || %_IsSmi(pivot)) ? pivot : ToString(pivot);
// Issue 95: Keep the pivot element out of the comparisons to avoid // Issue 95: Keep the pivot element out of the comparisons to avoid
// infinite recursion if comparefn(pivot, pivot) != 0. // infinite recursion if comparefn(pivot, pivot) != 0.
a[pivot_index] = a[to - 1]; var low_end = from; // Upper bound of the elements lower than pivot.
a[to - 1] = pivot; var high_start = to; // Lower bound of the elements greater than pivot.
var low_end = from; // Upper bound of the elements lower than pivot. var eq_start = to - 1; // Lower bound of elements equal to pivot.
var high_start = to - 1; // Lower bound of the elements greater than pivot. a[pivot_index] = a[eq_start];
for (var i = from; i < high_start; ) { a[eq_start] = pivot;
var element = a[i]; // From eq_start to high_start are elements equal to the pivot
var order = Compare(element, pivot); // (including the pivot).
// From low_end to eq_start are elements that have not been compared yet.
while (low_end < eq_start) {
var element = a[low_end];
var order = Compare(element, pivot_key);
if (order < 0) { if (order < 0) {
a[i] = a[low_end];
a[low_end] = element;
low_end++; low_end++;
i++;
} else if (order > 0) { } else if (order > 0) {
eq_start--;
high_start--; high_start--;
a[i] = a[high_start]; a[low_end] = a[eq_start];
a[eq_start] = a[high_start];
a[high_start] = element; a[high_start] = element;
} else { // order == 0 } else { // order == 0
i++; eq_start--;
a[low_end] = a[eq_start];
a[eq_start] = element;
} }
} }
// Restore the pivot element to its rightful place.
a[to - 1] = a[high_start];
a[high_start] = pivot;
high_start++;
QuickSort(a, from, low_end); QuickSort(a, from, low_end);
QuickSort(a, high_start, to); QuickSort(a, high_start, to);
} }
var old_length = ToUint32(this.length); var old_length = ToUint32(this.length);
if (old_length < 2) return this;
%RemoveArrayHoles(this); %RemoveArrayHoles(this);
...@@ -741,10 +755,11 @@ function ArrayFilter(f, receiver) { ...@@ -741,10 +755,11 @@ function ArrayFilter(f, receiver) {
// loop will not affect the looping. // loop will not affect the looping.
var length = this.length; var length = this.length;
var result = []; var result = [];
var result_length = 0;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
var current = this[i]; var current = this[i];
if (!IS_UNDEFINED(current) || i in this) { if (!IS_UNDEFINED(current) || i in this) {
if (f.call(receiver, current, i, this)) result.push(current); if (f.call(receiver, current, i, this)) result[result_length++] = current;
} }
} }
return result; return result;
......
...@@ -1156,12 +1156,20 @@ void CodeGenerator::SmiOperation(Token::Value op, ...@@ -1156,12 +1156,20 @@ void CodeGenerator::SmiOperation(Token::Value op,
__ test(eax, Immediate(kSmiTagMask)); __ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, deferred->enter(), not_taken); __ j(not_zero, deferred->enter(), not_taken);
if (op == Token::BIT_AND) { if (op == Token::BIT_AND) {
__ and_(Operand(eax), Immediate(value)); if (int_value == 0) {
__ xor_(Operand(eax), eax);
} else {
__ and_(Operand(eax), Immediate(value));
}
} else if (op == Token::BIT_XOR) { } else if (op == Token::BIT_XOR) {
__ xor_(Operand(eax), Immediate(value)); if (int_value != 0) {
__ xor_(Operand(eax), Immediate(value));
}
} else { } else {
ASSERT(op == Token::BIT_OR); ASSERT(op == Token::BIT_OR);
__ or_(Operand(eax), Immediate(value)); if (int_value != 0) {
__ or_(Operand(eax), Immediate(value));
}
} }
__ bind(deferred->exit()); __ bind(deferred->exit());
frame_->Push(eax); frame_->Push(eax);
...@@ -3268,6 +3276,23 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { ...@@ -3268,6 +3276,23 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
__ CallRuntime(Runtime::kTypeof, 1); __ CallRuntime(Runtime::kTypeof, 1);
frame_->Push(eax); frame_->Push(eax);
} else if (op == Token::VOID) {
Expression* expression = node->expression();
if (expression && expression->AsLiteral() && (
expression->AsLiteral()->IsTrue() ||
expression->AsLiteral()->IsFalse() ||
expression->AsLiteral()->handle()->IsNumber() ||
expression->AsLiteral()->handle()->IsString() ||
expression->AsLiteral()->handle()->IsJSRegExp() ||
expression->AsLiteral()->IsNull())) {
// Omit evaluating the value of the primitive literal.
// It will be discarded anyway, and can have no side effect.
frame_->Push(Immediate(Factory::undefined_value()));
} else {
Load(node->expression());
__ mov(frame_->Top(), Factory::undefined_value());
}
} else { } else {
Load(node->expression()); Load(node->expression());
switch (op) { switch (op) {
...@@ -3306,10 +3331,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { ...@@ -3306,10 +3331,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
break; break;
} }
case Token::VOID:
__ mov(frame_->Top(), Factory::undefined_value());
break;
case Token::ADD: { case Token::ADD: {
// Smi check. // Smi check.
Label continue_label; Label continue_label;
......
...@@ -232,7 +232,7 @@ function TimeInYear(year) { ...@@ -232,7 +232,7 @@ function TimeInYear(year) {
function ToJulianDay(year, month, date) { function ToJulianDay(year, month, date) {
var jy = (month > 1) ? year : year - 1; var jy = (month > 1) ? year : year - 1;
var jm = (month > 1) ? month + 2 : month + 14; var jm = (month > 1) ? month + 2 : month + 14;
var ja = FLOOR(0.01*jy); var ja = FLOOR(jy / 100);
return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja); return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja);
} }
...@@ -247,22 +247,22 @@ function CalculateDateTable() { ...@@ -247,22 +247,22 @@ function CalculateDateTable() {
var position = 0; var position = 0;
var leap_position = 0; var leap_position = 0;
for (var month = 0; month < 12; month++) { for (var month = 0; month < 12; month++) {
var month_bits = month << kMonthShift;
var length = month_lengths[month]; var length = month_lengths[month];
for (var day = 1; day <= length; day++) { for (var day = 1; day <= length; day++) {
four_year_cycle_table[leap_position] = four_year_cycle_table[leap_position] =
(month << kMonthShift) + day; month_bits + day;
four_year_cycle_table[366 + position] = four_year_cycle_table[366 + position] =
(1 << kYearShift) + (month << kMonthShift) + day; (1 << kYearShift) + month_bits + day;
four_year_cycle_table[731 + position] = four_year_cycle_table[731 + position] =
(2 << kYearShift) + (month << kMonthShift) + day; (2 << kYearShift) + month_bits + day;
four_year_cycle_table[1096 + position] = four_year_cycle_table[1096 + position] =
(3 << kYearShift) + (month << kMonthShift) + day; (3 << kYearShift) + month_bits + day;
leap_position++; leap_position++;
position++; position++;
} }
if (month == 1) { if (month == 1) {
four_year_cycle_table[leap_position++] = four_year_cycle_table[leap_position++] = month_bits + 29;
(month << kMonthShift) + 29;
} }
} }
return four_year_cycle_table; return four_year_cycle_table;
...@@ -277,10 +277,16 @@ function DayTriplet(year, month, date) { ...@@ -277,10 +277,16 @@ function DayTriplet(year, month, date) {
this.date = date; this.date = date;
} }
var julian_day_cache_triplet;
var julian_day_cache_day = $NaN;
// Compute year, month, and day from modified Julian day. // Compute year, month, and day from modified Julian day.
// The missing days in 1582 are ignored for JavaScript compatibility. // The missing days in 1582 are ignored for JavaScript compatibility.
function FromJulianDay(julian) { function FromJulianDay(julian) {
if (julian_day_cache_day == julian) {
return julian_day_cache_triplet;
}
var result;
// Avoid floating point and non-Smi maths in common case. This is also a period of // Avoid floating point and non-Smi maths in common case. This is also a period of
// time where leap years are very regular. The range is not too large to avoid overflow // time where leap years are very regular. The range is not too large to avoid overflow
// when doing the multiply-to-divide trick. // when doing the multiply-to-divide trick.
...@@ -293,21 +299,25 @@ function FromJulianDay(julian) { ...@@ -293,21 +299,25 @@ function FromJulianDay(julian) {
y += after_1968 << 2; y += after_1968 << 2;
jsimple -= 1461 * after_1968; jsimple -= 1461 * after_1968;
var four_year_cycle = four_year_cycle_table[jsimple]; var four_year_cycle = four_year_cycle_table[jsimple];
return new DayTriplet(y + (four_year_cycle >> kYearShift), result = new DayTriplet(y + (four_year_cycle >> kYearShift),
(four_year_cycle & kMonthMask) >> kMonthShift, (four_year_cycle & kMonthMask) >> kMonthShift,
four_year_cycle & kDayMask); four_year_cycle & kDayMask);
} else {
var jalpha = FLOOR((julian - 1867216.25) / 36524.25);
var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524;
var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25);
var jd = FLOOR(365 * jc + (0.25 * jc));
var je = FLOOR((jb - jd)/30.6001);
var m = je - 1;
if (m > 12) m -= 13;
var y = jc - 4715;
if (m > 2) { --y; --m; }
var d = jb - jd - FLOOR(30.6001 * je);
result = new DayTriplet(y, m, d);
} }
var jalpha = FLOOR((julian - 1867216.25) / 36524.25); julian_day_cache_day = julian;
var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524; julian_day_cache_triplet = result;
var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25); return result;
var jd = FLOOR(365 * jc + (0.25 * jc));
var je = FLOOR((jb - jd)/30.6001);
var m = je - 1;
if (m > 12) m -= 13;
var y = jc - 4715;
if (m > 2) { --y; --m; }
var d = jb - jd - FLOOR(30.6001 * je);
return new DayTriplet(y, m, d);
} }
......
...@@ -62,10 +62,14 @@ function StringValueOf() { ...@@ -62,10 +62,14 @@ function StringValueOf() {
// ECMA-262, section 15.5.4.4 // ECMA-262, section 15.5.4.4
function StringCharAt(pos) { function StringCharAt(pos) {
var subject = ToString(this); var char_code = %_FastCharCodeAt(subject, index);
var index = TO_INTEGER(pos); if (!%_IsSmi(char_code)) {
if (index >= subject.length || index < 0) return ""; var subject = ToString(this);
return %CharFromCode(%StringCharCodeAt(subject, index)); var index = TO_INTEGER(pos);
if (index >= subject.length || index < 0) return "";
char_code = %StringCharCodeAt(subject, index);
}
return %CharFromCode(char_code);
} }
...@@ -175,7 +179,13 @@ function StringMatch(regexp) { ...@@ -175,7 +179,13 @@ function StringMatch(regexp) {
// otherwise we call the runtime system. // otherwise we call the runtime system.
function SubString(string, start, end) { function SubString(string, start, end) {
// Use the one character string cache. // Use the one character string cache.
if (start + 1 == end) return %CharFromCode(%StringCharCodeAt(string, start)); if (start + 1 == end) {
var char_code = %_FastCharCodeAt(string, start);
if (!%_IsSmi(char_code)) {
char_code = %StringCharCodeAt(string, start);
}
return %CharFromCode(char_code);
}
return %StringSlice(string, start, end); return %StringSlice(string, start, end);
} }
...@@ -280,7 +290,10 @@ function ExpandReplacement(string, subject, captures, builder) { ...@@ -280,7 +290,10 @@ function ExpandReplacement(string, subject, captures, builder) {
var expansion = '$'; var expansion = '$';
var position = next + 1; var position = next + 1;
if (position < length) { if (position < length) {
var peek = %StringCharCodeAt(string, position); var peek = %_FastCharCodeAt(string, position);
if (!%_IsSmi(peek)) {
peek = %StringCharCodeAt(string, position);
}
if (peek == 36) { // $$ if (peek == 36) { // $$
++position; ++position;
builder.add('$'); builder.add('$');
...@@ -297,7 +310,10 @@ function ExpandReplacement(string, subject, captures, builder) { ...@@ -297,7 +310,10 @@ function ExpandReplacement(string, subject, captures, builder) {
++position; ++position;
var n = peek - 48; var n = peek - 48;
if (position < length) { if (position < length) {
peek = %StringCharCodeAt(string, position); peek = %_FastCharCodeAt(string, position);
if (!%_IsSmi(peek)) {
peek = %StringCharCodeAt(string, position);
}
// $nn, 01 <= nn <= 99 // $nn, 01 <= nn <= 99
if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) { if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
var nn = n * 10 + (peek - 48); var nn = n * 10 + (peek - 48);
...@@ -366,7 +382,7 @@ function addCaptureString(builder, captures, index) { ...@@ -366,7 +382,7 @@ function addCaptureString(builder, captures, index) {
var start = captures[scaled]; var start = captures[scaled];
var end = captures[scaled + 1]; var end = captures[scaled + 1];
// If either start or end is missing return. // If either start or end is missing return.
if (start < 0 || end < 0) return; if (start < 0 || end <= start) return;
builder.addSpecialSlice(start, end); builder.addSpecialSlice(start, end);
}; };
...@@ -437,6 +453,7 @@ function ApplyReplacementFunction(replace, captures, subject) { ...@@ -437,6 +453,7 @@ function ApplyReplacementFunction(replace, captures, subject) {
var m = captures.length >> 1; var m = captures.length >> 1;
if (m == 1) { if (m == 1) {
var s = CaptureString(subject, captures, 0); var s = CaptureString(subject, captures, 0);
// Don't call directly to avoid exposing the built-in global object.
return ToString(replace.call(null, s, index, subject)); return ToString(replace.call(null, s, index, subject));
} }
var parameters = $Array(m + 2); var parameters = $Array(m + 2);
......
...@@ -81,11 +81,11 @@ function GlobalParseInt(string, radix) { ...@@ -81,11 +81,11 @@ function GlobalParseInt(string, radix) {
// non-Smi number 9 times faster (230ns vs 2070ns). Together // non-Smi number 9 times faster (230ns vs 2070ns). Together
// they make parseInt on a string 1.4% slower (274ns vs 270ns). // they make parseInt on a string 1.4% slower (274ns vs 270ns).
if (%_IsSmi(string)) return string; if (%_IsSmi(string)) return string;
if (IS_NUMBER(string)) { if (IS_NUMBER(string) &&
if (string >= 0.01 && string < 1e9) ((string < -0.01 && -1e9 < string) ||
return $floor(string); (0.01 < string && string < 1e9))) {
if (string <= -0.01 && string > -1e9) // Truncate number.
return - $floor(-string); return string | 0;
} }
} else { } else {
radix = TO_INT32(radix); radix = TO_INT32(radix);
......
...@@ -42,6 +42,20 @@ assertEquals(date0, date1); ...@@ -42,6 +42,20 @@ assertEquals(date0, date1);
assertEquals(date1, date2); assertEquals(date1, date2);
assertEquals(date2, date3); assertEquals(date2, date3);
// Test limits (+/-1e8 days from epoch)
var dMax = new Date(8.64e15);
assertEquals(8.64e15, dMax.getTime());
var dOverflow = new Date(8.64e15+1);
assertTrue(isNaN(dOverflow.getTime()));
var dMin = new Date(-8.64e15);
assertEquals(-8.64e15, dMin.getTime());
var dUnderflow = new Date(-8.64e15-1);
assertTrue(isNaN(dUnderflow.getTime()));
// Tests inspired by js1_5/Date/regress-346363.js // Tests inspired by js1_5/Date/regress-346363.js
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment