Commit 285d5bc4 authored by lrn@chromium.org's avatar lrn@chromium.org

Native construction of RegExp result objects, with in-object index and input.

Avoid cloning using CloneRegExpResult for results that are just arrays.
Made a more direct path for string.match with string argument.

Review URL: http://codereview.chromium.org/1645001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4395 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 4418b589
......@@ -4021,6 +4021,100 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
// No stub. This code only occurs a few times in regexp.js.
const int kMaxInlineLength = 100;
ASSERT_EQ(3, args->length());
Load(args->at(0)); // Size of array, smi.
Load(args->at(1)); // "index" property value.
Load(args->at(2)); // "input" property value.
{
VirtualFrame::SpilledScope spilled_scope(frame_);
Label slowcase;
Label done;
__ ldr(r1, MemOperand(sp, kPointerSize * 2));
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
__ tst(r1, Operand(kSmiTagMask));
__ b(ne, &slowcase);
__ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
__ b(hi, &slowcase);
// Smi-tagging is equivalent to multiplying by 2.
// Allocate RegExpResult followed by FixedArray with size in ebx.
// JSArray: [Map][empty properties][Elements][Length-smi][index][input]
// Elements: [Map][Length][..elements..]
// Size of JSArray with two in-object properties and the header of a
// FixedArray.
int objects_size =
(JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
__ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
__ add(r2, r5, Operand(objects_size));
__ AllocateInNewSpace(r2, // In: Size, in words.
r0, // Out: Start of allocation (tagged).
r3, // Scratch register.
r4, // Scratch register.
&slowcase,
TAG_OBJECT);
// r0: Start of allocated area, object-tagged.
// r1: Number of elements in array, as smi.
// r5: Number of elements, untagged.
// Set JSArray map to global.regexp_result_map().
// Set empty properties FixedArray.
// Set elements to point to FixedArray allocated right after the JSArray.
// Interleave operations for better latency.
__ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
__ add(r3, r0, Operand(JSRegExpResult::kSize));
__ mov(r4, Operand(Factory::empty_fixed_array()));
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
__ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
__ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
__ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
__ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
// Set input, index and length fields from arguments.
__ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
__ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
__ add(sp, sp, Operand(kPointerSize));
__ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
__ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
// Fill out the elements FixedArray.
// r0: JSArray, tagged.
// r3: FixedArray, tagged.
// r5: Number of elements in array, untagged.
// Set map.
__ mov(r2, Operand(Factory::fixed_array_map()));
__ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
// Set FixedArray length.
__ str(r5, FieldMemOperand(r3, FixedArray::kLengthOffset));
// Fill contents of fixed-array with the-hole.
__ mov(r2, Operand(Factory::the_hole_value()));
__ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
// Fill fixed array elements with hole.
// r0: JSArray, tagged.
// r2: the hole.
// r3: Start of elements in FixedArray.
// r5: Number of elements to fill.
Label loop;
__ tst(r5, Operand(r5));
__ bind(&loop);
__ b(le, &done); // Jump if r1 is negative or zero.
__ sub(r5, r5, Operand(1), SetCC);
__ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
__ jmp(&loop);
__ bind(&slowcase);
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
__ bind(&done);
}
frame_->Forget(3);
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
......
......@@ -407,6 +407,8 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
......
......@@ -1237,6 +1237,62 @@ bool Genesis::InstallNatives() {
apply->shared()->set_length(2);
}
// Create a constructor for RegExp results (a variant of Array that
// predefines the two properties index and match).
{
// RegExpResult initial map.
// Find global.Array.prototype to inherit from.
Handle<JSFunction> array_constructor(global_context()->array_function());
Handle<JSObject> array_prototype(
JSObject::cast(array_constructor->instance_prototype()));
// Add initial map.
Handle<Map> initial_map =
Factory::NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
initial_map->set_constructor(*array_constructor);
// Set prototype on map.
initial_map->set_non_instance_prototype(false);
initial_map->set_prototype(*array_prototype);
// Update map with length accessor from Array and add "index" and "input".
Handle<Map> array_map(global_context()->js_array_map());
Handle<DescriptorArray> array_descriptors(
array_map->instance_descriptors());
ASSERT_EQ(1, array_descriptors->number_of_descriptors());
Handle<DescriptorArray> reresult_descriptors =
Factory::NewDescriptorArray(3);
reresult_descriptors->CopyFrom(0, *array_descriptors, 0);
int enum_index = 0;
{
FieldDescriptor index_field(Heap::index_symbol(),
JSRegExpResult::kIndexIndex,
NONE,
enum_index++);
reresult_descriptors->Set(1, &index_field);
}
{
FieldDescriptor input_field(Heap::input_symbol(),
JSRegExpResult::kInputIndex,
NONE,
enum_index++);
reresult_descriptors->Set(2, &input_field);
}
reresult_descriptors->Sort();
initial_map->set_inobject_properties(2);
initial_map->set_pre_allocated_property_fields(2);
initial_map->set_unused_property_fields(0);
initial_map->set_instance_descriptors(*reresult_descriptors);
global_context()->set_regexp_result_map(*initial_map);
}
#ifdef DEBUG
builtins->Verify();
#endif
......
......@@ -123,6 +123,7 @@ namespace internal {
F(SubString, 3, 1) \
F(StringCompare, 2, 1) \
F(RegExpExec, 4, 1) \
F(RegExpConstructResult, 3, 1) \
F(NumberToString, 1, 1) \
F(MathPow, 2, 1) \
F(MathSin, 1, 1) \
......
......@@ -76,6 +76,7 @@ enum ContextLookupFlags {
V(FUNCTION_MAP_INDEX, Map, function_map) \
V(FUNCTION_INSTANCE_MAP_INDEX, Map, function_instance_map) \
V(JS_ARRAY_MAP_INDEX, Map, js_array_map)\
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \
V(MESSAGE_LISTENERS_INDEX, JSObject, message_listeners) \
V(MAKE_MESSAGE_FUN_INDEX, JSFunction, make_message_fun) \
......@@ -175,6 +176,7 @@ class Context: public FixedArray {
SECURITY_TOKEN_INDEX,
ARGUMENTS_BOILERPLATE_INDEX,
JS_ARRAY_MAP_INDEX,
REGEXP_RESULT_MAP_INDEX,
FUNCTION_MAP_INDEX,
FUNCTION_INSTANCE_MAP_INDEX,
INITIAL_OBJECT_PROTOTYPE_INDEX,
......
......@@ -153,6 +153,8 @@ class ZoneScopeInfo;
V(global_symbol, "global") \
V(ignore_case_symbol, "ignoreCase") \
V(multiline_symbol, "multiline") \
V(input_symbol, "input") \
V(index_symbol, "index") \
V(last_index_symbol, "lastIndex") \
V(object_symbol, "object") \
V(prototype_symbol, "prototype") \
......
......@@ -6523,7 +6523,7 @@ void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 4);
ASSERT_EQ(4, args->length());
// Load the arguments on the stack and call the stub.
Load(args->at(0));
......@@ -6536,6 +6536,95 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
// No stub. This code only occurs a few times in regexp.js.
const int kMaxInlineLength = 100;
ASSERT_EQ(3, args->length());
Load(args->at(0)); // Size of array, smi.
Load(args->at(1)); // "index" property value.
Load(args->at(2)); // "input" property value.
{
VirtualFrame::SpilledScope spilled_scope;
Label slowcase;
Label done;
__ mov(ebx, Operand(esp, kPointerSize * 2));
__ test(ebx, Immediate(kSmiTagMask));
__ j(not_zero, &slowcase);
__ cmp(Operand(ebx), Immediate(Smi::FromInt(kMaxInlineLength)));
__ j(above, &slowcase);
// Smi-tagging is equivalent to multiplying by 2.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
// Allocate RegExpResult followed by FixedArray with size in ebx.
// JSArray: [Map][empty properties][Elements][Length-smi][index][input]
// Elements: [Map][Length][..elements..]
__ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
times_half_pointer_size,
ebx, // In: Number of elements (times 2, being a smi)
eax, // Out: Start of allocation (tagged).
ecx, // Out: End of allocation.
edx, // Scratch register
&slowcase,
TAG_OBJECT);
// eax: Start of allocated area, object-tagged.
// Set JSArray map to global.regexp_result_map().
// Set empty properties FixedArray.
// Set elements to point to FixedArray allocated right after the JSArray.
// Interleave operations for better latency.
__ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
__ mov(ecx, Immediate(Factory::empty_fixed_array()));
__ lea(ebx, Operand(eax, JSRegExpResult::kSize));
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
__ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
__ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
__ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
__ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
// Set input, index and length fields from arguments.
__ pop(FieldOperand(eax, JSRegExpResult::kInputOffset));
__ pop(FieldOperand(eax, JSRegExpResult::kIndexOffset));
__ pop(ecx);
__ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
// Fill out the elements FixedArray.
// eax: JSArray.
// ebx: FixedArray.
// ecx: Number of elements in array, as smi.
// Set map.
__ mov(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
// Set length.
__ SmiUntag(ecx);
__ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
// Fill contents of fixed-array with the-hole.
__ mov(edx, Immediate(Factory::the_hole_value()));
__ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
// Fill fixed array elements with hole.
// eax: JSArray.
// ecx: Number of elements to fill.
// ebx: Start of elements in FixedArray.
// edx: the hole.
Label loop;
__ test(ecx, Operand(ecx));
__ bind(&loop);
__ j(less_equal, &done); // Jump if ecx is negative or zero.
__ sub(Operand(ecx), Immediate(1));
__ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
__ jmp(&loop);
__ bind(&slowcase);
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
__ bind(&done);
}
frame_->Forget(3);
frame_->Push(eax);
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
......
......@@ -628,6 +628,8 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
......
......@@ -4632,6 +4632,26 @@ class JSArray: public JSObject {
};
// JSRegExpResult is just a JSArray with a specific initial map.
// This initial map adds in-object properties for "index" and "input"
// properties, as assigned by RegExp.prototype.exec, which allows
// faster creation of RegExp exec results.
// This class just holds constants used when creating the result.
// After creation the result must be treated as a JSArray in all regards.
class JSRegExpResult: public JSArray {
public:
// Offsets of object fields.
static const int kIndexOffset = JSArray::kSize;
static const int kInputOffset = kIndexOffset + kPointerSize;
static const int kSize = kInputOffset + kPointerSize;
// Indices of in-object properties.
static const int kIndexIndex = 0;
static const int kInputIndex = 1;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSRegExpResult);
};
// An accessor must have a getter, but can have no setter.
//
// When setting a property, V8 searches accessors in prototypes.
......
......@@ -135,19 +135,52 @@ function RegExpCache() {
var regExpCache = new RegExpCache();
function CloneRegexpAnswer(array) {
function CloneRegExpResult(array) {
if (array == null) return null;
var len = array.length;
var answer = new $Array(len);
for (var i = 0; i < len; i++) {
var length = array.length;
var answer = %_RegExpConstructResult(length, array.index, array.input);
for (var i = 0; i < length; i++) {
answer[i] = array[i];
}
answer.index = array.index;
answer.input = array.input;
return answer;
}
function BuildResultFromMatchInfo(lastMatchInfo, s) {
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
if (numResults === 1) {
var matchStart = lastMatchInfo[CAPTURE(0)];
var matchEnd = lastMatchInfo[CAPTURE(1)];
result[0] = SubString(s, matchStart, matchEnd);
} else {
for (var i = 0; i < numResults; i++) {
var matchStart = lastMatchInfo[CAPTURE(i << 1)];
var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
if (matchStart != -1 && matchEnd != -1) {
result[i] = SubString(s, matchStart, matchEnd);
} else {
// Make sure the element is present. Avoid reading the undefined
// property from the global object since this may change.
result[i] = void 0;
}
}
}
return result;
}
function RegExpExecNoTests(regexp, string, start) {
// Must be called with RegExp, string and positive integer as arguments.
var matchInfo = DoRegExpExec(regexp, string, start);
var result = null;
if (matchInfo !== null) {
result = BuildResultFromMatchInfo(matchInfo, string);
}
return result;
}
function RegExpExec(string) {
if (!IS_REGEXP(this)) {
throw MakeTypeError('incompatible_method_receiver',
......@@ -162,7 +195,7 @@ function RegExpExec(string) {
%_ObjectEquals(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
return CloneRegexpAnswer(cache.answer);
return CloneRegExpResult(cache.answer);
} else {
saveAnswer = true;
}
......@@ -205,36 +238,15 @@ function RegExpExec(string) {
return matchIndices; // No match.
}
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result;
if (numResults === 1) {
var matchStart = lastMatchInfo[CAPTURE(0)];
var matchEnd = lastMatchInfo[CAPTURE(1)];
result = [SubString(s, matchStart, matchEnd)];
} else {
result = new $Array(numResults);
for (var i = 0; i < numResults; i++) {
var matchStart = lastMatchInfo[CAPTURE(i << 1)];
var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
if (matchStart != -1 && matchEnd != -1) {
result[i] = SubString(s, matchStart, matchEnd);
} else {
// Make sure the element is present. Avoid reading the undefined
// property from the global object since this may change.
result[i] = void 0;
}
}
}
var result = BuildResultFromMatchInfo(matchIndices, s);
result.index = lastMatchInfo[CAPTURE0];
result.input = s;
if (this.global) {
this.lastIndex = lastMatchInfo[CAPTURE1];
} else {
cache.regExp = this;
cache.subject = s;
cache.lastIndex = lastIndex;
if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
if (saveAnswer) cache.answer = CloneRegExpResult(result);
cache.answerSaved = saveAnswer;
cache.type = 'exec';
}
......
......@@ -1228,6 +1228,36 @@ static Object* Runtime_RegExpExec(Arguments args) {
}
static Object* Runtime_RegExpConstructResult(Arguments args) {
ASSERT(args.length() == 3);
CONVERT_SMI_CHECKED(elements_count, args[0]);
if (elements_count > JSArray::kMaxFastElementsLength) {
return Top::ThrowIllegalOperation();
}
Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
if (new_object->IsFailure()) return new_object;
FixedArray* elements = FixedArray::cast(new_object);
new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
NEW_SPACE,
OLD_POINTER_SPACE);
if (new_object->IsFailure()) return new_object;
{
AssertNoAllocation no_gc;
HandleScope scope;
reinterpret_cast<HeapObject*>(new_object)->
set_map(Top::global_context()->regexp_result_map());
}
JSArray* array = JSArray::cast(new_object);
array->set_properties(Heap::empty_fixed_array());
array->set_elements(elements);
array->set_length(Smi::FromInt(elements_count));
// Write in-object properties after the length of the array.
array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
return array;
}
static Object* Runtime_RegExpInitializeObject(Arguments args) {
AssertNoAllocation no_alloc;
ASSERT(args.length() == 5);
......
......@@ -157,6 +157,7 @@ namespace internal {
F(RegExpExec, 4, 1) \
F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \
F(RegExpConstructResult, 3, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \
......
......@@ -149,6 +149,16 @@ function StringLastIndexOf(searchString /* position */) { // length == 1
}
function CloneDenseArray(array) {
if (array === null) return null;
var clone = new $Array(array.length);
for (var i = 0; i < array.length; i++) {
clone[i] = array[i];
}
return clone;
}
// ECMA-262 section 15.5.4.9
//
// This function is implementation specific. For now, we do not
......@@ -164,33 +174,37 @@ function StringLocaleCompare(other) {
// ECMA-262 section 15.5.4.10
function StringMatch(regexp) {
if (!IS_REGEXP(regexp)) regexp = new $RegExp(regexp);
var subject = TO_STRING_INLINE(this);
if (!regexp.global) return regexp.exec(subject);
var cache = regExpCache;
var saveAnswer = false;
if (%_ObjectEquals(cache.type, 'match') &&
%_ObjectEquals(cache.regExp, regexp) &&
%_ObjectEquals(cache.subject, subject)) {
if (cache.answerSaved) {
return CloneRegexpAnswer(cache.answer);
} else {
saveAnswer = true;
if (IS_REGEXP(regexp)) {
if (!regexp.global) return regexp.exec(subject);
var cache = regExpCache;
var saveAnswer = false;
if (%_ObjectEquals(cache.type, 'match') &&
%_ObjectEquals(cache.regExp, regexp) &&
%_ObjectEquals(cache.subject, subject)) {
if (cache.answerSaved) {
return CloneDenseArray(cache.answer);
} else {
saveAnswer = true;
}
}
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
// lastMatchInfo is defined in regexp.js.
var result = %StringMatch(subject, regexp, lastMatchInfo);
cache.type = 'match';
cache.regExp = regexp;
cache.subject = subject;
if (saveAnswer) cache.answer = CloneDenseArray(result);
cache.answerSaved = saveAnswer;
return result;
}
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
// lastMatchInfo is defined in regexp.js.
var result = %StringMatch(subject, regexp, lastMatchInfo);
cache.type = 'match';
cache.regExp = regexp;
cache.subject = subject;
if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
cache.answerSaved = saveAnswer;
return result;
// Non-regexp argument.
regexp = new $RegExp(regexp);
// Don't check regexp exec cache, since the regexp is new.
// TODO(lrn): Change this if we start caching regexps here.
return RegExpExecNoTests(regexp, subject, 0);
}
......@@ -599,7 +613,7 @@ function StringSplit(separator, limit) {
%_ObjectEquals(cache.regExp, separator) &&
%_ObjectEquals(cache.subject, subject)) {
if (cache.answerSaved) {
return CloneRegexpAnswer(cache.answer);
return CloneDenseArray(cache.answer);
} else {
saveAnswer = true;
}
......@@ -665,10 +679,9 @@ function StringSplit(separator, limit) {
startIndex = currentIndex = endIndex;
}
if (saveAnswer) cache.answer = CloneRegexpAnswer(result);
if (saveAnswer) cache.answer = CloneDenseArray(result);
cache.answerSaved = saveAnswer;
return result;
}
......
......@@ -4130,6 +4130,97 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
// No stub. This code only occurs a few times in regexp.js.
const int kMaxInlineLength = 100;
ASSERT_EQ(3, args->length());
Load(args->at(0)); // Size of array, smi.
Load(args->at(1)); // "index" property value.
Load(args->at(2)); // "input" property value.
{
VirtualFrame::SpilledScope spilled_scope;
Label slowcase;
Label done;
__ movq(r8, Operand(rsp, kPointerSize * 2));
__ JumpIfNotSmi(r8, &slowcase);
__ SmiToInteger32(rbx, r8);
__ cmpl(rbx, Immediate(kMaxInlineLength));
__ j(above, &slowcase);
// Smi-tagging is equivalent to multiplying by 2.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
// Allocate RegExpResult followed by FixedArray with size in ebx.
// JSArray: [Map][empty properties][Elements][Length-smi][index][input]
// Elements: [Map][Length][..elements..]
__ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
times_pointer_size,
rbx, // In: Number of elements.
rax, // Out: Start of allocation (tagged).
rcx, // Out: End of allocation.
rdx, // Scratch register
&slowcase,
TAG_OBJECT);
// rax: Start of allocated area, object-tagged.
// rbx: Number of array elements as int32.
// r8: Number of array elements as smi.
// Set JSArray map to global.regexp_result_map().
__ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
__ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
__ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
// Set empty properties FixedArray.
__ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
Factory::empty_fixed_array());
// Set elements to point to FixedArray allocated right after the JSArray.
__ lea(rcx, Operand(rax, JSRegExpResult::kSize));
__ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
// Set input, index and length fields from arguments.
__ pop(FieldOperand(rax, JSRegExpResult::kInputOffset));
__ pop(FieldOperand(rax, JSRegExpResult::kIndexOffset));
__ lea(rsp, Operand(rsp, kPointerSize));
__ movq(FieldOperand(rax, JSArray::kLengthOffset), r8);
// Fill out the elements FixedArray.
// rax: JSArray.
// rcx: FixedArray.
// rbx: Number of elements in array as int32.
// Set map.
__ Move(FieldOperand(rcx, HeapObject::kMapOffset),
Factory::fixed_array_map());
// Set length.
__ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rbx);
// Fill contents of fixed-array with the-hole.
__ Move(rdx, Factory::the_hole_value());
__ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
// Fill fixed array elements with hole.
// rax: JSArray.
// rbx: Number of elements in array that remains to be filled, as int32.
// rcx: Start of elements in FixedArray.
// rdx: the hole.
Label loop;
__ testl(rbx, rbx);
__ bind(&loop);
__ j(less_equal, &done); // Jump if ecx is negative or zero.
__ subl(rbx, Immediate(1));
__ movq(Operand(rcx, rbx, times_pointer_size, 0), rdx);
__ jmp(&loop);
__ bind(&slowcase);
__ CallRuntime(Runtime::kRegExpConstructResult, 3);
__ bind(&done);
}
frame_->Forget(3);
frame_->Push(rax);
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
......
......@@ -582,6 +582,8 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
......
......@@ -163,6 +163,12 @@ var knownProblems = {
// Performance critical function which cannot afford type checks.
"_CallFunction": true,
// Tries to allocate based on argument, and (correctly) throws
// out-of-memory if the request is too large. In practice, the
// size will be the number of captures of a RegExp.
"RegExpConstructResult": true,
"_RegExpConstructResult": true,
// LiveEdit feature is under development currently and has fragile input.
"LiveEditFindSharedFunctionInfosForScript": true,
"LiveEditGatherCompileInfo": true,
......
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