Commit ad0e3890 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Custom call IC-s for String.prototype.{charAt,charCodeAt}.

These string methods can be composed from two basic blocks: charCodeAt
and fromCharCode, both of which have fast cases for certain types of
inputs. In this patch these two blocks are refactored to allow
generating the fast cases without having to jump around the slow
cases. In the slow cases since they can now be invoked both from
inline runtime functions and from IC stubs we either have to
save/restore state of the current frame or enter/leave a new internal
frame. This is handled by new RuntimeCallHelper interface. Its
implementation for virtual frame is based on FrameRegisterState class
extracted from DeferredCode class.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4733 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 3e88d0d2
This diff is collapsed.
......@@ -445,10 +445,13 @@ class CodeGenerator: public AstVisitor {
void GenerateSetValueOf(ZoneList<Expression*>* args);
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateCharFromCode(ZoneList<Expression*>* args);
void GenerateStringCharFromCode(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateStringCharAt(ZoneList<Expression*>* args);
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
......@@ -693,38 +696,6 @@ class GenericBinaryOpStub : public CodeStub {
class StringHelper : public AllStatic {
public:
// Generates fast code for getting a char code out of a string
// object at the given index. May bail out for four reasons (in the
// listed order):
// * Receiver is not a string (receiver_not_string label).
// * Index is not a smi (index_not_smi label).
// * Index is out of range (index_out_of_range).
// * Some other reason (slow_case label). In this case it's
// guaranteed that the above conditions are not violated,
// e.g. it's safe to assume the receiver is a string and the
// index is a non-negative smi < length.
// When successful, object, index, and scratch are clobbered.
// Otherwise, scratch and result are clobbered.
static void GenerateFastCharCodeAt(MacroAssembler* masm,
Register object,
Register index,
Register scratch,
Register result,
Label* receiver_not_string,
Label* index_not_smi,
Label* index_out_of_range,
Label* slow_case);
// Generates code for creating a one-char string from the given char
// code. May do a runtime call, so any register can be clobbered
// and, if the given invoke flag specifies a call, an internal frame
// is required. In tail call mode the result must be r0 register.
static void GenerateCharFromCode(MacroAssembler* masm,
Register code,
Register scratch,
Register result,
InvokeFlag flag);
// Generate code for copying characters using a simple loop. This should only
// be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersLong adds too much
......
......@@ -1829,76 +1829,6 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
}
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
if (strcmp("_IsSmi", *name->ToCString()) == 0) {
EmitIsSmi(expr->arguments());
} else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
EmitIsNonNegativeSmi(expr->arguments());
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
EmitIsObject(expr->arguments());
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
EmitIsUndetectableObject(expr->arguments());
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
EmitIsFunction(expr->arguments());
} else if (strcmp("_IsArray", *name->ToCString()) == 0) {
EmitIsArray(expr->arguments());
} else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
EmitIsRegExp(expr->arguments());
} else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
EmitIsConstructCall(expr->arguments());
} else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
EmitObjectEquals(expr->arguments());
} else if (strcmp("_Arguments", *name->ToCString()) == 0) {
EmitArguments(expr->arguments());
} else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
EmitArgumentsLength(expr->arguments());
} else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
EmitClassOf(expr->arguments());
} else if (strcmp("_Log", *name->ToCString()) == 0) {
EmitLog(expr->arguments());
} else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
EmitRandomHeapNumber(expr->arguments());
} else if (strcmp("_SubString", *name->ToCString()) == 0) {
EmitSubString(expr->arguments());
} else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
EmitRegExpExec(expr->arguments());
} else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
EmitValueOf(expr->arguments());
} else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
EmitSetValueOf(expr->arguments());
} else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
EmitNumberToString(expr->arguments());
} else if (strcmp("_CharFromCode", *name->ToCString()) == 0) {
EmitCharFromCode(expr->arguments());
} else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) {
EmitFastCharCodeAt(expr->arguments());
} else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
EmitStringAdd(expr->arguments());
} else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
EmitStringCompare(expr->arguments());
} else if (strcmp("_MathPow", *name->ToCString()) == 0) {
EmitMathPow(expr->arguments());
} else if (strcmp("_MathSin", *name->ToCString()) == 0) {
EmitMathSin(expr->arguments());
} else if (strcmp("_MathCos", *name->ToCString()) == 0) {
EmitMathCos(expr->arguments());
} else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
EmitMathSqrt(expr->arguments());
} else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
EmitCallFunction(expr->arguments());
} else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
EmitRegExpConstructResult(expr->arguments());
} else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
EmitSwapElements(expr->arguments());
} else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
EmitGetFromCache(expr->arguments());
} else {
UNREACHABLE();
}
}
void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
......@@ -2349,49 +2279,120 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label slow_case, done;
// Fast case of Heap::LookupSingleCharacterStringFromCode.
ASSERT(kSmiTag == 0);
ASSERT(kSmiShiftSize == 0);
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
__ tst(r0, Operand(kSmiTagMask |
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
__ b(nz, &slow_case);
__ mov(r1, Operand(Factory::single_character_string_cache()));
ASSERT(kSmiTag == 0);
ASSERT(kSmiTagSize == 1);
ASSERT(kSmiShiftSize == 0);
// At this point code register contains smi tagged ascii char code.
__ add(r1, r1, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
__ ldr(r1, MemOperand(r1, FixedArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
__ cmp(r1, r2);
__ b(eq, &slow_case);
__ mov(r0, r1);
__ b(&done);
Label done;
StringCharFromCodeGenerator generator(r0, r1);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&slow_case);
__ push(r0);
__ CallRuntime(Runtime::kCharFromCode, 1);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, r0);
Apply(context_, r1);
}
void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
// TODO(fsc): Port the complete implementation from the classic back-end.
// Move the undefined value into the result register, which will
// trigger the slow case.
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
Apply(context_, r0);
void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = r1;
Register index = r0;
Register scratch = r2;
Register result = r3;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharCodeAtGenerator generator(object,
index,
scratch,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// NaN.
__ LoadRoot(result, Heap::kNanValueRootIndex);
__ jmp(&done);
__ bind(&need_conversion);
// Load the undefined value into the result register, which will
// trigger conversion.
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, result);
}
void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = r1;
Register index = r0;
Register scratch1 = r2;
Register scratch2 = r3;
Register result = r0;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharAtGenerator generator(object,
index,
scratch1,
scratch2,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// the empty string.
__ LoadRoot(result, Heap::kEmptyStringRootIndex);
__ jmp(&done);
__ bind(&need_conversion);
// Move smi zero into the result register, which will trigger
// conversion.
__ mov(result, Operand(Smi::FromInt(0)));
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, result);
}
void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
......
......@@ -808,70 +808,39 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// ---------- S t a t e --------------
// -- lr : return address
// -- r0 : key
// -- r0 : key (index)
// -- r1 : receiver
// -----------------------------------
Label miss;
Label index_not_smi;
Label index_out_of_range;
Label slow_char_code;
Label got_char_code;
Register object = r1;
Register receiver = r1;
Register index = r0;
Register code = r2;
Register scratch = r3;
Register scratch1 = r2;
Register scratch2 = r3;
Register result = r0;
StringCharAtGenerator char_at_generator(receiver,
index,
scratch1,
scratch2,
result,
&miss, // When not a string.
&miss, // When not a number.
&index_out_of_range,
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ Ret();
StringHelper::GenerateFastCharCodeAt(masm,
object,
index,
scratch,
code,
&miss, // When not a string.
&index_not_smi,
&index_out_of_range,
&slow_char_code);
// If we didn't bail out, code register contains smi tagged char
// code.
__ bind(&got_char_code);
StringHelper::GenerateCharFromCode(masm, code, scratch, r0, JUMP_FUNCTION);
#ifdef DEBUG
__ Abort("Unexpected fall-through from char from code tail call");
#endif
// Check if key is a heap number.
__ bind(&index_not_smi);
__ CheckMap(index, scratch, Factory::heap_number_map(), &miss, true);
// Push receiver and key on the stack (now that we know they are a
// string and a number), and call runtime.
__ bind(&slow_char_code);
__ EnterInternalFrame();
__ Push(object, index);
__ CallRuntime(Runtime::kStringCharCodeAt, 2);
ASSERT(!code.is(r0));
__ mov(code, r0);
__ LeaveInternalFrame();
ICRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
// Check if the runtime call returned NaN char code. If yes, return
// undefined. Otherwise, we can continue.
if (FLAG_debug_code) {
__ BranchOnSmi(code, &got_char_code);
__ ldr(scratch, FieldMemOperand(code, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch, ip);
__ Assert(eq, "StringCharCodeAt must return smi or heap number");
}
__ LoadRoot(scratch, Heap::kNanValueRootIndex);
__ cmp(code, scratch);
__ b(ne, &got_char_code);
__ bind(&index_out_of_range);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
__ Ret();
__ bind(&miss);
GenerateGeneric(masm);
GenerateMiss(masm);
}
......
......@@ -1211,6 +1211,26 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// TODO(722): implement this.
return Heap::undefined_value();
}
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// TODO(722): implement this.
return Heap::undefined_value();
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
......
This diff is collapsed.
......@@ -571,6 +571,78 @@ void FullCodeGenerator::SetSourcePosition(int pos) {
}
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
if (strcmp("_IsSmi", *name->ToCString()) == 0) {
EmitIsSmi(expr->arguments());
} else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
EmitIsNonNegativeSmi(expr->arguments());
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
EmitIsObject(expr->arguments());
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
EmitIsUndetectableObject(expr->arguments());
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
EmitIsFunction(expr->arguments());
} else if (strcmp("_IsArray", *name->ToCString()) == 0) {
EmitIsArray(expr->arguments());
} else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
EmitIsRegExp(expr->arguments());
} else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
EmitIsConstructCall(expr->arguments());
} else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
EmitObjectEquals(expr->arguments());
} else if (strcmp("_Arguments", *name->ToCString()) == 0) {
EmitArguments(expr->arguments());
} else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
EmitArgumentsLength(expr->arguments());
} else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
EmitClassOf(expr->arguments());
} else if (strcmp("_Log", *name->ToCString()) == 0) {
EmitLog(expr->arguments());
} else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
EmitRandomHeapNumber(expr->arguments());
} else if (strcmp("_SubString", *name->ToCString()) == 0) {
EmitSubString(expr->arguments());
} else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
EmitRegExpExec(expr->arguments());
} else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
EmitValueOf(expr->arguments());
} else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
EmitSetValueOf(expr->arguments());
} else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
EmitNumberToString(expr->arguments());
} else if (strcmp("_StringCharFromCode", *name->ToCString()) == 0) {
EmitStringCharFromCode(expr->arguments());
} else if (strcmp("_StringCharCodeAt", *name->ToCString()) == 0) {
EmitStringCharCodeAt(expr->arguments());
} else if (strcmp("_StringCharAt", *name->ToCString()) == 0) {
EmitStringCharAt(expr->arguments());
} else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
EmitStringAdd(expr->arguments());
} else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
EmitStringCompare(expr->arguments());
} else if (strcmp("_MathPow", *name->ToCString()) == 0) {
EmitMathPow(expr->arguments());
} else if (strcmp("_MathSin", *name->ToCString()) == 0) {
EmitMathSin(expr->arguments());
} else if (strcmp("_MathCos", *name->ToCString()) == 0) {
EmitMathCos(expr->arguments());
} else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
EmitMathSqrt(expr->arguments());
} else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
EmitCallFunction(expr->arguments());
} else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
EmitRegExpConstructResult(expr->arguments());
} else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
EmitSwapElements(expr->arguments());
} else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
EmitGetFromCache(expr->arguments());
} else {
UNREACHABLE();
}
}
void FullCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) {
Label eval_right, done;
......
......@@ -388,8 +388,9 @@ class FullCodeGenerator: public AstVisitor {
void EmitValueOf(ZoneList<Expression*>* arguments);
void EmitSetValueOf(ZoneList<Expression*>* arguments);
void EmitNumberToString(ZoneList<Expression*>* arguments);
void EmitCharFromCode(ZoneList<Expression*>* arguments);
void EmitFastCharCodeAt(ZoneList<Expression*>* arguments);
void EmitStringCharFromCode(ZoneList<Expression*>* arguments);
void EmitStringCharCodeAt(ZoneList<Expression*>* arguments);
void EmitStringCharAt(ZoneList<Expression*>* arguments);
void EmitStringCompare(ZoneList<Expression*>* arguments);
void EmitStringAdd(ZoneList<Expression*>* arguments);
void EmitLog(ZoneList<Expression*>* arguments);
......
This diff is collapsed.
......@@ -38,8 +38,10 @@ namespace internal {
// Forward declarations
class CompilationInfo;
class DeferredCode;
class FrameRegisterState;
class RegisterAllocator;
class RegisterFile;
class RuntimeCallHelper;
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
......@@ -621,10 +623,13 @@ class CodeGenerator: public AstVisitor {
void GenerateSetValueOf(ZoneList<Expression*>* args);
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateCharFromCode(ZoneList<Expression*>* args);
void GenerateStringCharFromCode(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateStringCharAt(ZoneList<Expression*>* args);
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
......@@ -910,37 +915,6 @@ class GenericBinaryOpStub: public CodeStub {
class StringHelper : public AllStatic {
public:
// Generates fast code for getting a char code out of a string
// object at the given index. May bail out for four reasons (in the
// listed order):
// * Receiver is not a string (receiver_not_string label).
// * Index is not a smi (index_not_smi label).
// * Index is out of range (index_out_of_range).
// * Some other reason (slow_case label). In this case it's
// guaranteed that the above conditions are not violated,
// e.g. it's safe to assume the receiver is a string and the
// index is a non-negative smi < length.
// When successful, object, index, and scratch are clobbered.
// Otherwise, scratch and result are clobbered.
static void GenerateFastCharCodeAt(MacroAssembler* masm,
Register object,
Register index,
Register scratch,
Register result,
Label* receiver_not_string,
Label* index_not_smi,
Label* index_out_of_range,
Label* slow_case);
// Generates code for creating a one-char string from the given char
// code. May do a runtime call, so any register can be clobbered
// and, if the given invoke flag specifies a call, an internal frame
// is required. In tail call mode the result must be eax register.
static void GenerateCharFromCode(MacroAssembler* masm,
Register code,
Register result,
InvokeFlag flag);
// Generate code for copying characters using a simple loop. This should only
// be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersREP adds too much
......
......@@ -1904,76 +1904,6 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
}
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
if (strcmp("_IsSmi", *name->ToCString()) == 0) {
EmitIsSmi(expr->arguments());
} else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
EmitIsNonNegativeSmi(expr->arguments());
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
EmitIsObject(expr->arguments());
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
EmitIsUndetectableObject(expr->arguments());
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
EmitIsFunction(expr->arguments());
} else if (strcmp("_IsArray", *name->ToCString()) == 0) {
EmitIsArray(expr->arguments());
} else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
EmitIsRegExp(expr->arguments());
} else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
EmitIsConstructCall(expr->arguments());
} else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
EmitObjectEquals(expr->arguments());
} else if (strcmp("_Arguments", *name->ToCString()) == 0) {
EmitArguments(expr->arguments());
} else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
EmitArgumentsLength(expr->arguments());
} else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
EmitClassOf(expr->arguments());
} else if (strcmp("_Log", *name->ToCString()) == 0) {
EmitLog(expr->arguments());
} else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
EmitRandomHeapNumber(expr->arguments());
} else if (strcmp("_SubString", *name->ToCString()) == 0) {
EmitSubString(expr->arguments());
} else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
EmitRegExpExec(expr->arguments());
} else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
EmitValueOf(expr->arguments());
} else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
EmitSetValueOf(expr->arguments());
} else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
EmitNumberToString(expr->arguments());
} else if (strcmp("_CharFromCode", *name->ToCString()) == 0) {
EmitCharFromCode(expr->arguments());
} else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) {
EmitFastCharCodeAt(expr->arguments());
} else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
EmitStringAdd(expr->arguments());
} else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
EmitStringCompare(expr->arguments());
} else if (strcmp("_MathPow", *name->ToCString()) == 0) {
EmitMathPow(expr->arguments());
} else if (strcmp("_MathSin", *name->ToCString()) == 0) {
EmitMathSin(expr->arguments());
} else if (strcmp("_MathCos", *name->ToCString()) == 0) {
EmitMathCos(expr->arguments());
} else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
EmitMathSqrt(expr->arguments());
} else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
EmitCallFunction(expr->arguments());
} else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
EmitRegExpConstructResult(expr->arguments());
} else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
EmitSwapElements(expr->arguments());
} else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
EmitGetFromCache(expr->arguments());
} else {
UNREACHABLE();
}
}
void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
......@@ -2432,50 +2362,120 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label slow_case, done;
// Fast case of Heap::LookupSingleCharacterStringFromCode.
ASSERT(kSmiTag == 0);
ASSERT(kSmiShiftSize == 0);
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
__ test(eax,
Immediate(kSmiTagMask |
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
__ j(not_zero, &slow_case);
__ Set(ebx, Immediate(Factory::single_character_string_cache()));
ASSERT(kSmiTag == 0);
ASSERT(kSmiTagSize == 1);
ASSERT(kSmiShiftSize == 0);
// At this point code register contains smi tagged ascii char code.
__ mov(ebx, FieldOperand(ebx,
eax, times_half_pointer_size,
FixedArray::kHeaderSize));
__ cmp(ebx, Factory::undefined_value());
__ j(equal, &slow_case);
__ mov(eax, ebx);
Label done;
StringCharFromCodeGenerator generator(eax, ebx);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&slow_case);
__ push(eax);
__ CallRuntime(Runtime::kCharFromCode, 1);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, eax);
Apply(context_, ebx);
}
void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
// TODO(fsc): Port the complete implementation from the classic back-end.
void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = ebx;
Register index = eax;
Register scratch = ecx;
Register result = edx;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharCodeAtGenerator generator(object,
index,
scratch,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// NaN.
__ Set(result, Immediate(Factory::nan_value()));
__ jmp(&done);
__ bind(&need_conversion);
// Move the undefined value into the result register, which will
// trigger the slow case.
__ Set(eax, Immediate(Factory::undefined_value()));
Apply(context_, eax);
// trigger conversion.
__ Set(result, Immediate(Factory::undefined_value()));
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, result);
}
void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = ebx;
Register index = eax;
Register scratch1 = ecx;
Register scratch2 = edx;
Register result = eax;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharAtGenerator generator(object,
index,
scratch1,
scratch2,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// the empty string.
__ Set(result, Immediate(Factory::empty_string()));
__ jmp(&done);
__ bind(&need_conversion);
// Move smi zero into the result register, which will trigger
// conversion.
__ Set(result, Immediate(Smi::FromInt(0)));
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, result);
}
void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
......
......@@ -498,60 +498,29 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// -- esp[0] : return address
// -----------------------------------
Label miss;
Label index_not_smi;
Label index_out_of_range;
Label slow_char_code;
Label got_char_code;
Register receiver = edx;
Register index = eax;
Register code = ebx;
Register scratch = ecx;
StringHelper::GenerateFastCharCodeAt(masm,
receiver,
index,
scratch,
code,
&miss, // When not a string.
&index_not_smi,
&index_out_of_range,
&slow_char_code);
// If we didn't bail out, code register contains smi tagged char
// code.
__ bind(&got_char_code);
StringHelper::GenerateCharFromCode(masm, code, eax, JUMP_FUNCTION);
#ifdef DEBUG
__ Abort("Unexpected fall-through from char from code tail call");
#endif
// Check if key is a heap number.
__ bind(&index_not_smi);
__ CheckMap(index, Factory::heap_number_map(), &miss, true);
// Push receiver and key on the stack (now that we know they are a
// string and a number), and call runtime.
__ bind(&slow_char_code);
__ EnterInternalFrame();
__ push(receiver);
__ push(index);
__ CallRuntime(Runtime::kStringCharCodeAt, 2);
ASSERT(!code.is(eax));
__ mov(code, eax);
__ LeaveInternalFrame();
Register scratch1 = ebx;
Register scratch2 = ecx;
Register result = eax;
StringCharAtGenerator char_at_generator(receiver,
index,
scratch1,
scratch2,
result,
&miss, // When not a string.
&miss, // When not a number.
&index_out_of_range,
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ ret(0);
ICRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
// Check if the runtime call returned NaN char code. If yes, return
// undefined. Otherwise, we can continue.
if (FLAG_debug_code) {
ASSERT(kSmiTag == 0);
__ test(code, Immediate(kSmiTagMask));
__ j(zero, &got_char_code);
__ mov(scratch, FieldOperand(code, HeapObject::kMapOffset));
__ cmp(scratch, Factory::heap_number_map());
__ Assert(equal, "StringCharCodeAt must return smi or heap number");
}
__ cmp(code, Factory::nan_value());
__ j(not_equal, &got_char_code);
__ bind(&index_out_of_range);
__ Set(eax, Immediate(Factory::undefined_value()));
__ ret(0);
......
......@@ -1380,6 +1380,140 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// ----------- S t a t e -------------
// -- ecx : function name
// -- esp[0] : return address
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
const int argc = arguments().immediate();
Label miss;
Label index_out_of_range;
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
ebx, edx, name, &miss);
Register receiver = ebx;
Register index = ecx;
Register scratch = edx;
Register result = eax;
__ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
if (argc > 0) {
__ mov(index, Operand(esp, (argc - 0) * kPointerSize));
} else {
__ Set(index, Immediate(Factory::undefined_value()));
}
StringCharCodeAtGenerator char_code_at_generator(receiver,
index,
scratch,
result,
&miss, // When not a string.
&miss, // When not a number.
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
char_code_at_generator.GenerateFast(masm());
__ ret((argc + 1) * kPointerSize);
ICRuntimeCallHelper call_helper;
char_code_at_generator.GenerateSlow(masm(), call_helper);
__ bind(&index_out_of_range);
__ Set(eax, Immediate(Factory::nan_value()));
__ ret((argc + 1) * kPointerSize);
__ bind(&miss);
// Restore function name in ecx.
__ Set(ecx, Immediate(Handle<String>(name)));
Handle<Code> ic = ComputeCallMiss(argc);
__ jmp(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(function);
}
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// ----------- S t a t e -------------
// -- ecx : function name
// -- esp[0] : return address
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
const int argc = arguments().immediate();
Label miss;
Label index_out_of_range;
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
ebx, edx, name, &miss);
Register receiver = eax;
Register index = ecx;
Register scratch1 = ebx;
Register scratch2 = edx;
Register result = eax;
__ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
if (argc > 0) {
__ mov(index, Operand(esp, (argc - 0) * kPointerSize));
} else {
__ Set(index, Immediate(Factory::undefined_value()));
}
StringCharAtGenerator char_at_generator(receiver,
index,
scratch1,
scratch2,
result,
&miss, // When not a string.
&miss, // When not a number.
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
char_at_generator.GenerateFast(masm());
__ ret((argc + 1) * kPointerSize);
ICRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm(), call_helper);
__ bind(&index_out_of_range);
__ Set(eax, Immediate(Factory::empty_string()));
__ ret((argc + 1) * kPointerSize);
__ bind(&miss);
// Restore function name in ecx.
__ Set(ecx, Immediate(Handle<String>(name)));
Handle<Code> ic = ComputeCallMiss(argc);
__ jmp(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(function);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
......
......@@ -615,7 +615,7 @@ class VirtualFrame: public ZoneObject {
inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
friend class DeferredCode;
friend class FrameRegisterState;
friend class JumpTarget;
};
......
......@@ -332,22 +332,10 @@ void JumpTarget::ComputeEntryFrame() {
}
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
statement_position_(masm_->current_statement_position()),
position_(masm_->current_position()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);
CodeGeneratorScope::Current()->AddDeferred(this);
#ifdef DEBUG
comment_ = "";
#endif
FrameRegisterState::FrameRegisterState(VirtualFrame* frame) {
// Copy the register locations from the code generator's frame.
// These are the registers that will be spilled on entry to the
// deferred code and restored on exit.
VirtualFrame* frame = CodeGeneratorScope::Current()->frame();
int sp_offset = frame->fp_relative(frame->stack_pointer_);
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
int loc = frame->register_location(i);
......@@ -423,4 +411,19 @@ void BreakTarget::Branch(Condition cc, Hint hint) {
}
}
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
statement_position_(masm_->current_statement_position()),
position_(masm_->current_position()),
frame_state_(CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);
CodeGeneratorScope::Current()->AddDeferred(this);
#ifdef DEBUG
comment_ = "";
#endif
}
} } // namespace v8::internal
......@@ -34,16 +34,24 @@ namespace v8 {
namespace internal {
FrameRegisterState::FrameRegisterState(VirtualFrame* frame) {
// Nothing to do when register allocation is not supported.
ASSERT(RegisterAllocator::kNumRegisters == 0);
}
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
statement_position_(masm_->current_statement_position()),
position_(masm_->current_position()) {
position_(masm_->current_position()),
frame_state_(CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);
CodeGeneratorScope::Current()->AddDeferred(this);
#ifdef DEBUG
comment_ = "";
CodeGeneratorScope::Current()->frame()->AssertIsSpilled();
#endif
}
......
......@@ -411,6 +411,7 @@ enum StringRepresentationTag {
kConsStringTag = 0x1,
kExternalStringTag = 0x3
};
const uint32_t kIsConsStringMask = 0x1;
// A ConsString with an empty string as the right side is a candidate
......
......@@ -62,26 +62,21 @@ function StringValueOf() {
// ECMA-262, section 15.5.4.4
function StringCharAt(pos) {
var char_code = %_FastCharCodeAt(this, pos);
if (!%_IsSmi(char_code)) {
var subject = TO_STRING_INLINE(this);
var index = TO_INTEGER(pos);
if (index >= subject.length || index < 0) return "";
char_code = %StringCharCodeAt(subject, index);
var result = %_StringCharAt(this, pos);
if (%_IsSmi(result)) {
result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
}
return %_CharFromCode(char_code);
return result;
}
// ECMA-262 section 15.5.4.5
function StringCharCodeAt(pos) {
var fast_answer = %_FastCharCodeAt(this, pos);
if (%_IsSmi(fast_answer)) {
return fast_answer;
var result = %_StringCharCodeAt(this, pos);
if (!%_IsSmi(result)) {
result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
}
var subject = TO_STRING_INLINE(this);
var index = TO_INTEGER(pos);
return %StringCharCodeAt(subject, index);
return result;
}
......@@ -214,11 +209,7 @@ function StringMatch(regexp) {
function SubString(string, start, end) {
// Use the one character string cache.
if (start + 1 == end) {
var char_code = %_FastCharCodeAt(string, start);
if (!%_IsSmi(char_code)) {
char_code = %StringCharCodeAt(string, start);
}
return %_CharFromCode(char_code);
return %_StringCharAt(string, start);
}
return %_SubString(string, start, end);
}
......@@ -322,10 +313,7 @@ function ExpandReplacement(string, subject, matchInfo, builder) {
var expansion = '$';
var position = next + 1;
if (position < length) {
var peek = %_FastCharCodeAt(string, position);
if (!%_IsSmi(peek)) {
peek = %StringCharCodeAt(string, position);
}
var peek = %_StringCharCodeAt(string, position);
if (peek == 36) { // $$
++position;
builder.add('$');
......@@ -343,10 +331,7 @@ function ExpandReplacement(string, subject, matchInfo, builder) {
++position;
var n = peek - 48;
if (position < length) {
peek = %_FastCharCodeAt(string, position);
if (!%_IsSmi(peek)) {
peek = %StringCharCodeAt(string, position);
}
peek = %_StringCharCodeAt(string, position);
// $nn, 01 <= nn <= 99
if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
var nn = n * 10 + (peek - 48);
......@@ -824,7 +809,7 @@ function StringFromCharCode(code) {
var n = %_ArgumentsLength();
if (n == 1) {
if (!%_IsSmi(code)) code = ToNumber(code);
return %_CharFromCode(code & 0xffff);
return %_StringCharFromCode(code & 0xffff);
}
// NOTE: This is not super-efficient, but it is necessary because we
......
......@@ -568,9 +568,11 @@ class KeyedStoreStubCompiler: public StubCompiler {
// a builtin function on its instance prototype (the one the generator
// is set for), and a name of a generator itself (used to build ids
// and generator function names).
#define CUSTOM_CALL_IC_GENERATORS(V) \
V(array, push, ArrayPush) \
V(array, pop, ArrayPop)
#define CUSTOM_CALL_IC_GENERATORS(V) \
V(array, push, ArrayPush) \
V(array, pop, ArrayPop) \
V(string, charCodeAt, StringCharCodeAt) \
V(string, charAt, StringCharAt)
class CallStubCompiler: public StubCompiler {
......
This diff is collapsed.
......@@ -571,10 +571,13 @@ class CodeGenerator: public AstVisitor {
void GenerateSetValueOf(ZoneList<Expression*>* args);
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateCharFromCode(ZoneList<Expression*>* args);
void GenerateStringCharFromCode(ZoneList<Expression*>* args);
// Fast support for string.charAt(n) and string[n].
void GenerateStringCharAt(ZoneList<Expression*>* args);
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
......@@ -846,38 +849,6 @@ class GenericBinaryOpStub: public CodeStub {
class StringHelper : public AllStatic {
public:
// Generates fast code for getting a char code out of a string
// object at the given index. May bail out for four reasons (in the
// listed order):
// * Receiver is not a string (receiver_not_string label).
// * Index is not a smi (index_not_smi label).
// * Index is out of range (index_out_of_range).
// * Some other reason (slow_case label). In this case it's
// guaranteed that the above conditions are not violated,
// e.g. it's safe to assume the receiver is a string and the
// index is a non-negative smi < length.
// When successful, object, index, and scratch are clobbered.
// Otherwise, scratch and result are clobbered.
static void GenerateFastCharCodeAt(MacroAssembler* masm,
Register object,
Register index,
Register scratch,
Register result,
Label* receiver_not_string,
Label* index_not_smi,
Label* index_out_of_range,
Label* slow_case);
// Generates code for creating a one-char string from the given char
// code. May do a runtime call, so any register can be clobbered
// and, if the given invoke flag specifies a call, an internal frame
// is required. In tail call mode the result must be rax register.
static void GenerateCharFromCode(MacroAssembler* masm,
Register code,
Register result,
Register scratch,
InvokeFlag flag);
// Generate code for copying characters using a simple loop. This should only
// be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersREP adds too much
......
......@@ -1906,76 +1906,6 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
}
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
if (strcmp("_IsSmi", *name->ToCString()) == 0) {
EmitIsSmi(expr->arguments());
} else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
EmitIsNonNegativeSmi(expr->arguments());
} else if (strcmp("_IsObject", *name->ToCString()) == 0) {
EmitIsObject(expr->arguments());
} else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
EmitIsUndetectableObject(expr->arguments());
} else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
EmitIsFunction(expr->arguments());
} else if (strcmp("_IsArray", *name->ToCString()) == 0) {
EmitIsArray(expr->arguments());
} else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
EmitIsRegExp(expr->arguments());
} else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
EmitIsConstructCall(expr->arguments());
} else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
EmitObjectEquals(expr->arguments());
} else if (strcmp("_Arguments", *name->ToCString()) == 0) {
EmitArguments(expr->arguments());
} else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
EmitArgumentsLength(expr->arguments());
} else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
EmitClassOf(expr->arguments());
} else if (strcmp("_Log", *name->ToCString()) == 0) {
EmitLog(expr->arguments());
} else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
EmitRandomHeapNumber(expr->arguments());
} else if (strcmp("_SubString", *name->ToCString()) == 0) {
EmitSubString(expr->arguments());
} else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
EmitRegExpExec(expr->arguments());
} else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
EmitValueOf(expr->arguments());
} else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
EmitSetValueOf(expr->arguments());
} else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
EmitNumberToString(expr->arguments());
} else if (strcmp("_CharFromCode", *name->ToCString()) == 0) {
EmitCharFromCode(expr->arguments());
} else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) {
EmitFastCharCodeAt(expr->arguments());
} else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
EmitStringAdd(expr->arguments());
} else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
EmitStringCompare(expr->arguments());
} else if (strcmp("_MathPow", *name->ToCString()) == 0) {
EmitMathPow(expr->arguments());
} else if (strcmp("_MathSin", *name->ToCString()) == 0) {
EmitMathSin(expr->arguments());
} else if (strcmp("_MathCos", *name->ToCString()) == 0) {
EmitMathCos(expr->arguments());
} else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
EmitMathSqrt(expr->arguments());
} else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
EmitCallFunction(expr->arguments());
} else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
EmitRegExpConstructResult(expr->arguments());
} else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
EmitSwapElements(expr->arguments());
} else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
EmitGetFromCache(expr->arguments());
} else {
UNREACHABLE();
}
}
void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
......@@ -2414,46 +2344,120 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label slow_case, done;
// Fast case of Heap::LookupSingleCharacterStringFromCode.
__ JumpIfNotSmi(rax, &slow_case);
__ SmiToInteger32(rcx, rax);
__ cmpl(rcx, Immediate(String::kMaxAsciiCharCode));
__ j(above, &slow_case);
Label done;
StringCharFromCodeGenerator generator(rax, rbx);
generator.GenerateFast(masm_);
__ jmp(&done);
__ Move(rbx, Factory::single_character_string_cache());
__ movq(rbx, FieldOperand(rbx,
rcx,
times_pointer_size,
FixedArray::kHeaderSize));
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
__ j(equal, &slow_case);
__ movq(rax, rbx);
__ bind(&done);
Apply(context_, rbx);
}
void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = rbx;
Register index = rax;
Register scratch = rcx;
Register result = rdx;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharCodeAtGenerator generator(object,
index,
scratch,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&slow_case);
__ push(rax);
__ CallRuntime(Runtime::kCharFromCode, 1);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// NaN.
__ LoadRoot(result, Heap::kNanValueRootIndex);
__ jmp(&done);
__ bind(&need_conversion);
// Move the undefined value into the result register, which will
// trigger conversion.
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, rax);
Apply(context_, result);
}
void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
// TODO(fsc): Port the complete implementation from the classic back-end.
// Move the undefined value into the result register, which will
// trigger the slow case.
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
Apply(context_, rax);
void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
VisitForValue(args->at(0), kStack);
VisitForValue(args->at(1), kAccumulator);
Register object = rbx;
Register index = rax;
Register scratch1 = rcx;
Register scratch2 = rdx;
Register result = rax;
__ pop(object);
Label need_conversion;
Label index_out_of_range;
Label done;
StringCharAtGenerator generator(object,
index,
scratch1,
scratch2,
result,
&need_conversion,
&need_conversion,
&index_out_of_range,
STRING_INDEX_IS_NUMBER);
generator.GenerateFast(masm_);
__ jmp(&done);
__ bind(&index_out_of_range);
// When the index is out of range, the spec requires us to return
// the empty string.
__ LoadRoot(result, Heap::kEmptyStringRootIndex);
__ jmp(&done);
__ bind(&need_conversion);
// Move smi zero into the result register, which will trigger
// conversion.
__ Move(result, Smi::FromInt(0));
__ jmp(&done);
NopRuntimeCallHelper call_helper;
generator.GenerateSlow(masm_, call_helper);
__ bind(&done);
Apply(context_, result);
}
void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
......
......@@ -522,68 +522,35 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rsp[0] : return address
// -- rsp[8] : name
// -- rsp[8] : name (index)
// -- rsp[16] : receiver
// -----------------------------------
Label miss;
Label index_not_smi;
Label index_out_of_range;
Label slow_char_code;
Label got_char_code;
Register receiver = rdx;
Register index = rax;
Register code = rbx;
Register scratch = rcx;
Register scratch1 = rbx;
Register scratch2 = rcx;
Register result = rax;
__ movq(index, Operand(rsp, 1 * kPointerSize));
__ movq(receiver, Operand(rsp, 2 * kPointerSize));
StringHelper::GenerateFastCharCodeAt(masm,
receiver,
index,
scratch,
code,
&miss, // When not a string.
&index_not_smi,
&index_out_of_range,
&slow_char_code);
// If we didn't bail out, code register contains smi tagged char
// code.
__ bind(&got_char_code);
StringHelper::GenerateCharFromCode(masm, code, rax, scratch, JUMP_FUNCTION);
#ifdef DEBUG
__ Abort("Unexpected fall-through from char from code tail call");
#endif
// Check if key is a heap number.
__ bind(&index_not_smi);
__ CompareRoot(FieldOperand(index, HeapObject::kMapOffset),
Heap::kHeapNumberMapRootIndex);
__ j(not_equal, &miss);
// Push receiver and key on the stack (now that we know they are a
// string and a number), and call runtime.
__ bind(&slow_char_code);
__ EnterInternalFrame();
__ push(receiver);
__ push(index);
__ CallRuntime(Runtime::kStringCharCodeAt, 2);
ASSERT(!code.is(rax));
__ movq(code, rax);
__ LeaveInternalFrame();
StringCharAtGenerator char_at_generator(receiver,
index,
scratch1,
scratch2,
result,
&miss, // When not a string.
&miss, // When not a number.
&index_out_of_range,
STRING_INDEX_IS_ARRAY_INDEX);
char_at_generator.GenerateFast(masm);
__ ret(0);
// Check if the runtime call returned NaN char code. If yes, return
// undefined. Otherwise, we can continue.
if (FLAG_debug_code) {
ASSERT(kSmiTag == 0);
__ JumpIfSmi(code, &got_char_code);
__ CompareRoot(FieldOperand(code, HeapObject::kMapOffset),
Heap::kHeapNumberMapRootIndex);
__ Assert(equal, "StringCharCodeAt must return smi or heap number");
}
__ CompareRoot(code, Heap::kNanValueRootIndex);
__ j(not_equal, &got_char_code);
ICRuntimeCallHelper call_helper;
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&index_out_of_range);
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
......
......@@ -1328,6 +1328,25 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// TODO(722): implement this.
return Heap::undefined_value();
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// TODO(722): implement this.
return Heap::undefined_value();
}
Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
......
......@@ -590,7 +590,7 @@ class VirtualFrame : public ZoneObject {
inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
friend class DeferredCode;
friend class FrameRegisterState;
friend class JumpTarget;
};
......
......@@ -27,29 +27,48 @@
var s = "test";
assertEquals("t", s.charAt());
assertEquals("t", s.charAt("string"));
assertEquals("t", s.charAt(null));
assertEquals("t", s.charAt(void 0));
assertEquals("t", s.charAt(false));
assertEquals("e", s.charAt(true));
assertEquals("", s.charAt(-1));
assertEquals("", s.charAt(4));
assertEquals("t", s.charAt(0));
assertEquals("t", s.charAt(3));
assertEquals("t", s.charAt(NaN));
assertEquals(116, s.charCodeAt());
assertEquals(116, s.charCodeAt("string"));
assertEquals(116, s.charCodeAt(null));
assertEquals(116, s.charCodeAt(void 0));
assertEquals(116, s.charCodeAt(false));
assertEquals(101, s.charCodeAt(true));
assertEquals(116, s.charCodeAt(0));
assertEquals(116, s.charCodeAt(3));
assertEquals(116, s.charCodeAt(NaN));
assertTrue(isNaN(s.charCodeAt(-1)));
assertTrue(isNaN(s.charCodeAt(4)));
var slowIndex1 = { valueOf: function() { return 1; } };
var slowIndex2 = { toString: function() { return "2"; } };
var slowIndexOutOfRange = { valueOf: function() { return -1; } };
function basicTest() {
assertEquals("t", s.charAt());
assertEquals("t", s.charAt("string"));
assertEquals("t", s.charAt(null));
assertEquals("t", s.charAt(void 0));
assertEquals("t", s.charAt(false));
assertEquals("e", s.charAt(true));
assertEquals("", s.charAt(-1));
assertEquals("", s.charAt(4));
assertEquals("", s.charAt(slowIndexOutOfRange));
assertEquals("t", s.charAt(0));
assertEquals("t", s.charAt(-0.0));
assertEquals("t", s.charAt(0.4));
assertEquals("e", s.charAt(slowIndex1));
assertEquals("s", s.charAt(slowIndex2));
assertEquals("t", s.charAt(3));
assertEquals("t", s.charAt(3.4));
assertEquals("t", s.charAt(NaN));
assertEquals(116, s.charCodeAt());
assertEquals(116, s.charCodeAt("string"));
assertEquals(116, s.charCodeAt(null));
assertEquals(116, s.charCodeAt(void 0));
assertEquals(116, s.charCodeAt(false));
assertEquals(101, s.charCodeAt(true));
assertEquals(116, s.charCodeAt(0));
assertEquals(116, s.charCodeAt(-0.0));
assertEquals(116, s.charCodeAt(0.4));
assertEquals(101, s.charCodeAt(slowIndex1));
assertEquals(115, s.charCodeAt(slowIndex2));
assertEquals(116, s.charCodeAt(3));
assertEquals(116, s.charCodeAt(3.4));
assertEquals(116, s.charCodeAt(NaN));
assertTrue(isNaN(s.charCodeAt(-1)));
assertTrue(isNaN(s.charCodeAt(4)));
assertTrue(isNaN(s.charCodeAt(slowIndexOutOfRange)));
}
basicTest();
// Make sure enough of the one-char string cache is filled.
var alpha = ['@'];
......@@ -64,3 +83,162 @@ for (var i = 1; i < 128; i++) {
assertEquals(alpha[i], alphaStr.charAt(i));
assertEquals(String.fromCharCode(i), alphaStr.charAt(i));
}
// Test stealing String.prototype.{charAt,charCodeAt}.
var o = {
charAt: String.prototype.charAt,
charCodeAt: String.prototype.charCodeAt,
toString: function() { return "012"; },
valueOf: function() { return "should not be called"; }
};
function stealTest() {
assertEquals("0", o.charAt(0));
assertEquals("1", o.charAt(1));
assertEquals("1", o.charAt(1.4));
assertEquals("1", o.charAt(slowIndex1));
assertEquals("2", o.charAt(2));
assertEquals("2", o.charAt(slowIndex2));
assertEquals(48, o.charCodeAt(0));
assertEquals(49, o.charCodeAt(1));
assertEquals(49, o.charCodeAt(1.4));
assertEquals(49, o.charCodeAt(slowIndex1));
assertEquals(50, o.charCodeAt(2));
assertEquals(50, o.charCodeAt(slowIndex2));
assertEquals("", o.charAt(-1));
assertEquals("", o.charAt(-1.4));
assertEquals("", o.charAt(10));
assertEquals("", o.charAt(slowIndexOutOfRange));
assertTrue(isNaN(o.charCodeAt(-1)));
assertTrue(isNaN(o.charCodeAt(-1.4)));
assertTrue(isNaN(o.charCodeAt(10)));
assertTrue(isNaN(o.charCodeAt(slowIndexOutOfRange)));
}
stealTest();
// Test custom string IC-s.
for (var i = 0; i < 20; i++) {
basicTest();
stealTest();
}
var badToString = function() { return []; };
function testBadToString_charAt() {
var goodToString = o.toString;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) o.toString = o.valueOf = badToString;
result = o.charAt(1);
numCalls++;
}
} catch (e) {
hasCaught = true;
} finally {
o.toString = goodToString;
}
assertTrue(hasCaught);
assertEquals("1", result);
assertEquals(10, numCalls);
}
testBadToString_charAt();
function testBadToString_charCodeAt() {
var goodToString = o.toString;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) o.toString = o.valueOf = badToString;
result = o.charCodeAt(1);
numCalls++;
}
} catch (e) {
hasCaught = true;
} finally {
o.toString = goodToString;
}
assertTrue(hasCaught);
assertEquals(49, result);
assertEquals(10, numCalls);
}
testBadToString_charCodeAt();
var badIndex = {
toString: badToString,
valueOf: badToString
};
function testBadIndex_charAt() {
var index = 1;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) index = badIndex;
result = o.charAt(index);
numCalls++;
}
} catch (e) {
hasCaught = true;
}
assertTrue(hasCaught);
assertEquals("1", result);
assertEquals(10, numCalls);
}
testBadIndex_charAt();
function testBadIndex_charCodeAt() {
var index = 1;
var hasCaught = false;
var numCalls = 0;
var result;
try {
for (var i = 0; i < 20; i++) {
if (i == 10) index = badIndex;
result = o.charCodeAt(index);
numCalls++;
}
} catch (e) {
hasCaught = true;
}
assertTrue(hasCaught);
assertEquals(49, result);
assertEquals(10, numCalls);
}
testBadIndex_charCodeAt();
function testPrototypeChange_charAt() {
var result, oldResult;
for (var i = 0; i < 20; i++) {
if (i == 10) {
oldResult = result;
String.prototype.charAt = function() { return "%"; };
}
result = s.charAt(1);
}
assertEquals("%", result);
assertEquals("e", oldResult);
delete String.prototype.charAt; // Restore the default.
}
testPrototypeChange_charAt();
function testPrototypeChange_charCodeAt() {
var result, oldResult;
for (var i = 0; i < 20; i++) {
if (i == 10) {
oldResult = result;
String.prototype.charCodeAt = function() { return 42; };
}
result = s.charCodeAt(1);
}
assertEquals(42, result);
assertEquals(101, oldResult);
delete String.prototype.charCodeAt; // Restore the default.
}
testPrototypeChange_charCodeAt();
......@@ -207,6 +207,28 @@ for (var i = 0; i < 100; ++i) {
assertEquals(expected, actual);
}
// Test negative zero case.
var keys = [0, -0.0];
var str = 'ab', arr = ['a', 'a'];
for (var i = 0; i < 100; ++i) {
var index = Math.floor(i / 50);
var key = keys[index];
var expected = arr[index];
var actual = str[key];
assertEquals(expected, actual);
}
// Test "not-an-array-index" case.
var keys = [0, 0.5];
var str = 'ab', arr = ['a', undefined];
for (var i = 0; i < 100; ++i) {
var index = Math.floor(i / 50);
var key = keys[index];
var expected = arr[index];
var actual = str[key];
assertEquals(expected, actual);
}
// Test out of range case.
var keys = [0, -1];
var str = 'ab', arr = ['a', undefined];
......@@ -234,4 +256,4 @@ for (var i = 0; i < 50; ++i) {
var expected = arr[0];
var actual = str[0];
assertEquals(expected, actual);
}
\ No newline at end of file
}
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