namespace internal {
-#define __ ACCESS_MASM(masm_)
-
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cc,
static bool IsEasyToMultiplyBy(int x);
+#define __ ACCESS_MASM(masm)
// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
+// Platform-specific FrameRegisterState functions.
-void DeferredCode::SaveRegisters() {
+void FrameRegisterState::Save(MacroAssembler* masm) const {
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
int action = registers_[i];
if (action == kPush) {
}
-void DeferredCode::RestoreRegisters() {
+void FrameRegisterState::Restore(MacroAssembler* masm) const {
// Restore registers in reverse order due to the stack.
for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
int action = registers_[i];
}
+#undef __
+#define __ ACCESS_MASM(masm_)
+
+// -------------------------------------------------------------------------
+// Platform-specific DeferredCode functions.
+
+void DeferredCode::SaveRegisters() {
+ frame_state_.Save(masm_);
+}
+
+
+void DeferredCode::RestoreRegisters() {
+ frame_state_.Restore(masm_);
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ frame_state_->Save(masm);
+}
+
+
+void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ frame_state_->Restore(masm);
+}
+
+
+void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterInternalFrame();
+}
+
+
+void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveInternalFrame();
+}
+
+
// -------------------------------------------------------------------------
// CodeGenState implementation.
}
-// This generates code that performs a charCodeAt() call or returns
-// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
-// It can handle flat, 8 and 16 bit characters and cons strings where the
-// answer is found in the left hand branch of the cons. The slow case will
-// flatten the string, which will ensure that the answer is in the left hand
-// side the next time around.
-void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
+class DeferredStringCharCodeAt : public DeferredCode {
+ public:
+ DeferredStringCharCodeAt(Register object,
+ Register index,
+ Register scratch,
+ Register result)
+ : result_(result),
+ char_code_at_generator_(object,
+ index,
+ scratch,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharCodeAtGenerator* fast_case_generator() {
+ return &char_code_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_code_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
+ __ jmp(exit_label());
+
+ __ bind(&index_out_of_range_);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result_, Heap::kNanValueRootIndex);
+ __ jmp(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharCodeAtGenerator char_code_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charCodeAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
+ VirtualFrame::SpilledScope spilled_scope(frame_);
+ Comment(masm_, "[ GenerateStringCharCodeAt");
ASSERT(args->length() == 2);
- Comment(masm_, "[ GenerateFastCharCodeAt");
- Load(args->at(0));
- Load(args->at(1));
- Register index = frame_->PopToRegister(); // Index.
- Register string = frame_->PopToRegister(index); // String.
- Register result = VirtualFrame::scratch0();
- Register scratch = VirtualFrame::scratch1();
+ LoadAndSpill(args->at(0));
+ LoadAndSpill(args->at(1));
- Label slow_case;
- Label exit;
- StringHelper::GenerateFastCharCodeAt(masm_,
- string,
- index,
- scratch,
- result,
- &slow_case,
- &slow_case,
- &slow_case,
- &slow_case);
- __ jmp(&exit);
+ Register index = r1;
+ Register object = r2;
- __ bind(&slow_case);
- // Move the undefined value into the result register, which will
- // trigger the slow case.
- __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
+ frame_->EmitPop(r1);
+ frame_->EmitPop(r2);
+
+ // We need two extra registers.
+ Register scratch = r3;
+ Register result = r0;
- __ bind(&exit);
+ DeferredStringCharCodeAt* deferred =
+ new DeferredStringCharCodeAt(object,
+ index,
+ scratch,
+ result);
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->EmitPush(result);
}
-void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateCharFromCode");
+class DeferredStringCharFromCode : public DeferredCode {
+ public:
+ DeferredStringCharFromCode(Register code,
+ Register result)
+ : char_from_code_generator_(code, result) {}
+
+ StringCharFromCodeGenerator* fast_case_generator() {
+ return &char_from_code_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_from_code_generator_.GenerateSlow(masm(), call_helper);
+ }
+
+ private:
+ StringCharFromCodeGenerator char_from_code_generator_;
+};
+
+
+// Generates code for creating a one-char string from a char code.
+void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
+ VirtualFrame::SpilledScope spilled_scope(frame_);
+ Comment(masm_, "[ GenerateStringCharFromCode");
ASSERT(args->length() == 1);
+ LoadAndSpill(args->at(0));
+
Register code = r1;
- Register scratch = ip;
Register result = r0;
- LoadAndSpill(args->at(0));
frame_->EmitPop(code);
- StringHelper::GenerateCharFromCode(masm_,
- code,
- scratch,
- result,
- CALL_FUNCTION);
+ DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
+ code, result);
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
+ frame_->EmitPush(result);
+}
+
+
+class DeferredStringCharAt : public DeferredCode {
+ public:
+ DeferredStringCharAt(Register object,
+ Register index,
+ Register scratch1,
+ Register scratch2,
+ Register result)
+ : result_(result),
+ char_at_generator_(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharAtGenerator* fast_case_generator() {
+ return &char_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ mov(result_, Operand(Smi::FromInt(0)));
+ __ jmp(exit_label());
+
+ __ 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(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharAtGenerator char_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
+ VirtualFrame::SpilledScope spilled_scope(frame_);
+ Comment(masm_, "[ GenerateStringCharAt");
+ ASSERT(args->length() == 2);
+
+ LoadAndSpill(args->at(0));
+ LoadAndSpill(args->at(1));
+
+ Register index = r1;
+ Register object = r2;
+
+ frame_->EmitPop(r1);
+ frame_->EmitPop(r2);
+
+ // We need three extra registers.
+ Register scratch1 = r3;
+ Register scratch2 = r4;
+ Register result = r0;
+
+ DeferredStringCharAt* deferred =
+ new DeferredStringCharAt(object,
+ index,
+ scratch1,
+ scratch2,
+ result);
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->EmitPush(result);
}
}
-void StringHelper::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) {
- Label not_a_flat_string;
- Label try_again_with_new_string;
+// StringCharCodeAtGenerator
+
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ Label flat_string;
Label ascii_string;
Label got_char_code;
// If the receiver is a smi trigger the non-string case.
- __ BranchOnSmi(object, receiver_not_string);
+ __ BranchOnSmi(object_, receiver_not_string_);
// Fetch the instance type of the receiver into result register.
- __ ldr(result, FieldMemOperand(object, HeapObject::kMapOffset));
- __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
+ __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
// If the receiver is not a string trigger the non-string case.
- __ tst(result, Operand(kIsNotStringMask));
- __ b(ne, receiver_not_string);
+ __ tst(result_, Operand(kIsNotStringMask));
+ __ b(ne, receiver_not_string_);
// If the index is non-smi trigger the non-smi case.
- __ BranchOnNotSmi(index, index_not_smi);
+ __ BranchOnNotSmi(index_, &index_not_smi_);
+
+ // Put smi-tagged index into scratch register.
+ __ mov(scratch_, index_);
+ __ bind(&got_smi_index_);
// Check for index out of range.
- __ ldr(scratch, FieldMemOperand(object, String::kLengthOffset));
- // Now scratch has the length of the string. Compare with the index.
- __ cmp(scratch, Operand(index));
- __ b(ls, index_out_of_range);
-
- __ bind(&try_again_with_new_string);
- // ----------- S t a t e -------------
- // -- object : string to access
- // -- result : instance type of the string
- // -- scratch : non-negative index < length
- // -----------------------------------
+ __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
+ __ cmp(ip, Operand(scratch_));
+ __ b(ls, index_out_of_range_);
// We need special handling for non-flat strings.
- ASSERT_EQ(0, kSeqStringTag);
- __ tst(result, Operand(kStringRepresentationMask));
- __ b(ne, ¬_a_flat_string);
-
- // Check for 1-byte or 2-byte string.
- ASSERT_EQ(0, kTwoByteStringTag);
- __ tst(result, Operand(kStringEncodingMask));
- __ b(ne, &ascii_string);
-
- // 2-byte string. We can add without shifting since the Smi tag size is the
- // log2 of the number of bytes in a two-byte character.
- ASSERT_EQ(1, kSmiTagSize);
- ASSERT_EQ(0, kSmiShiftSize);
- __ add(scratch, object, Operand(index));
- __ ldrh(result, FieldMemOperand(scratch, SeqTwoByteString::kHeaderSize));
- __ jmp(&got_char_code);
+ ASSERT(kSeqStringTag == 0);
+ __ tst(result_, Operand(kStringRepresentationMask));
+ __ b(eq, &flat_string);
// Handle non-flat strings.
- __ bind(¬_a_flat_string);
- __ and_(result, result, Operand(kStringRepresentationMask));
- __ cmp(result, Operand(kConsStringTag));
- __ b(ne, slow_case);
+ __ tst(result_, Operand(kIsConsStringMask));
+ __ b(eq, &call_runtime_);
// ConsString.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
// the case we would rather go to the runtime system now to flatten
// the string.
- __ ldr(result, FieldMemOperand(object, ConsString::kSecondOffset));
- __ LoadRoot(scratch, Heap::kEmptyStringRootIndex);
- __ cmp(result, Operand(scratch));
- __ b(ne, slow_case);
-
+ __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
+ __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
+ __ cmp(result_, Operand(ip));
+ __ b(ne, &call_runtime_);
// Get the first of the two strings and load its instance type.
- __ ldr(object, FieldMemOperand(object, ConsString::kFirstOffset));
- __ ldr(result, FieldMemOperand(object, HeapObject::kMapOffset));
- __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
- __ jmp(&try_again_with_new_string);
+ __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
+ __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ // If the first cons component is also non-flat, then go to runtime.
+ ASSERT(kSeqStringTag == 0);
+ __ tst(result_, Operand(kStringRepresentationMask));
+ __ b(nz, &call_runtime_);
+
+ // Check for 1-byte or 2-byte string.
+ __ bind(&flat_string);
+ ASSERT(kAsciiStringTag != 0);
+ __ tst(result_, Operand(kStringEncodingMask));
+ __ b(nz, &ascii_string);
+
+ // 2-byte string.
+ // Load the 2-byte character code into the result register. We can
+ // add without shifting since the smi tag size is the log2 of the
+ // number of bytes in a two-byte character.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
+ __ add(scratch_, object_, Operand(scratch_));
+ __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
+ __ jmp(&got_char_code);
// ASCII string.
+ // Load the byte into the result register.
__ bind(&ascii_string);
- __ add(scratch, object, Operand(index, LSR, kSmiTagSize));
- __ ldrb(result, FieldMemOperand(scratch, SeqAsciiString::kHeaderSize));
+ __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
+ __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
__ bind(&got_char_code);
- __ mov(result, Operand(result, LSL, kSmiTagSize));
-}
+ __ mov(result_, Operand(result_, LSL, kSmiTagSize));
+ __ bind(&exit_);
+}
+
+
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
+
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_, scratch_,
+ Factory::heap_number_map(), index_not_number_, true);
+ call_helper.BeforeCall(masm);
+ __ Push(object_, index_, result_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ // Strictly speaking, NumberToInteger should be called here, but
+ // our string lengths don't exceed 32 bits and using ToUint32 maps
+ // -0 to 0, which is what is required by the spec when accessing
+ // strings.
+ __ CallRuntime(Runtime::kNumberToJSUint32, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!scratch_.is(r0)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ mov(scratch_, r0);
+ }
+ __ pop(result_);
+ __ pop(index_);
+ __ pop(object_);
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ __ BranchOnNotSmi(scratch_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ Push(object_, index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(r0)) {
+ __ mov(result_, r0);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
+}
-void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
- Register code,
- Register scratch,
- Register result,
- InvokeFlag flag) {
- ASSERT(!code.is(result));
- Label slow_case;
- Label exit;
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
// Fast case of Heap::LookupSingleCharacterStringFromCode.
ASSERT(kSmiTag == 0);
ASSERT(kSmiShiftSize == 0);
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
- __ tst(code, Operand(kSmiTagMask |
- ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
- __ b(nz, &slow_case);
+ __ tst(code_,
+ Operand(kSmiTagMask |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+ __ b(nz, &slow_case_);
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ // At this point code register contains smi tagged ascii char code.
ASSERT(kSmiTag == 0);
- __ mov(result, Operand(Factory::single_character_string_cache()));
- __ add(result, result, Operand(code, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ ldr(result, MemOperand(result, FixedArray::kHeaderSize - kHeapObjectTag));
- __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
- __ cmp(result, scratch);
- __ b(eq, &slow_case);
- __ b(&exit);
+ __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(result_, Operand(ip));
+ __ b(eq, &slow_case_);
+ __ bind(&exit_);
+}
- __ bind(&slow_case);
- if (flag == CALL_FUNCTION) {
- __ push(code);
- __ CallRuntime(Runtime::kCharFromCode, 1);
- if (!result.is(r0)) {
- __ mov(result, r0);
- }
- } else {
- ASSERT(flag == JUMP_FUNCTION);
- ASSERT(result.is(r0));
- __ push(code);
- __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
- }
- __ bind(&exit);
- if (flag == JUMP_FUNCTION) {
- ASSERT(result.is(r0));
- __ Ret();
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharFromCode slow case");
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(r0)) {
+ __ mov(result_, r0);
}
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharFromCode slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharAtGenerator
+
+void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
+}
+
+
+void StringCharAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
}
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);
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
}
-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);
}
-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());
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);
}
}
+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,
F(ClassOf, 1, 1) \
F(ValueOf, 1, 1) \
F(SetValueOf, 2, 1) \
- F(FastCharCodeAt, 2, 1) \
- F(CharFromCode, 1, 1) \
+ F(StringCharCodeAt, 2, 1) \
+ F(StringCharFromCode, 1, 1) \
+ F(StringCharAt, 2, 1) \
F(ObjectEquals, 2, 1) \
F(Log, 3, 1) \
F(RandomHeapNumber, 0, 1) \
};
+// State of used registers in a virtual frame.
+class FrameRegisterState {
+ public:
+ // Captures the current state of the given frame.
+ explicit FrameRegisterState(VirtualFrame* frame);
+
+ // Saves the state in the stack.
+ void Save(MacroAssembler* masm) const;
+
+ // Restores the state from the stack.
+ void Restore(MacroAssembler* masm) const;
+
+ private:
+ // Constants indicating special actions. They should not be multiples
+ // of kPointerSize so they will not collide with valid offsets from
+ // the frame pointer.
+ static const int kIgnore = -1;
+ static const int kPush = 1;
+
+ // This flag is ored with a valid offset from the frame pointer, so
+ // it should fit in the low zero bits of a valid offset.
+ static const int kSyncedFlag = 2;
+
+ // C++ doesn't allow zero length arrays, so we make the array length 1 even
+ // if we don't need it.
+ static const int kRegistersArrayLength =
+ (RegisterAllocator::kNumRegisters == 0) ?
+ 1 : RegisterAllocator::kNumRegisters;
+ int registers_[kRegistersArrayLength];
+};
+
+
+// Helper interface to prepare to/restore after making runtime calls.
+class RuntimeCallHelper {
+ public:
+ virtual ~RuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const = 0;
+
+ virtual void AfterCall(MacroAssembler* masm) const = 0;
+
+ protected:
+ RuntimeCallHelper() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RuntimeCallHelper);
+};
+
+
+// RuntimeCallHelper implementation that saves/restores state of a
+// virtual frame.
+class VirtualFrameRuntimeCallHelper : public RuntimeCallHelper {
+ public:
+ // Does not take ownership of |frame_state|.
+ explicit VirtualFrameRuntimeCallHelper(const FrameRegisterState* frame_state)
+ : frame_state_(frame_state) {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const;
+
+ virtual void AfterCall(MacroAssembler* masm) const;
+
+ private:
+ const FrameRegisterState* frame_state_;
+};
+
+
+// RuntimeCallHelper implementation used in IC stubs: enters/leaves a
+// newly created internal frame before/after the runtime call.
+class ICRuntimeCallHelper : public RuntimeCallHelper {
+ public:
+ ICRuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const;
+
+ virtual void AfterCall(MacroAssembler* masm) const;
+};
+
+
+// Trivial RuntimeCallHelper implementation.
+class NopRuntimeCallHelper : public RuntimeCallHelper {
+ public:
+ NopRuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const {}
+
+ virtual void AfterCall(MacroAssembler* masm) const {}
+};
+
+
// Deferred code objects are small pieces of code that are compiled
// out of line. They are used to defer the compilation of uncommon
// paths thereby avoiding expensive jumps around uncommon code parts.
inline void Branch(Condition cc);
void BindExit() { masm_->bind(&exit_label_); }
+ const FrameRegisterState* frame_state() const { return &frame_state_; }
+
void SaveRegisters();
void RestoreRegisters();
MacroAssembler* masm_;
private:
- // Constants indicating special actions. They should not be multiples
- // of kPointerSize so they will not collide with valid offsets from
- // the frame pointer.
- static const int kIgnore = -1;
- static const int kPush = 1;
-
- // This flag is ored with a valid offset from the frame pointer, so
- // it should fit in the low zero bits of a valid offset.
- static const int kSyncedFlag = 2;
-
int statement_position_;
int position_;
Label entry_label_;
Label exit_label_;
- // C++ doesn't allow zero length arrays, so we make the array length 1 even
- // if we don't need it.
- static const int kRegistersArrayLength =
- (RegisterAllocator::kNumRegisters == 0) ?
- 1 : RegisterAllocator::kNumRegisters;
- int registers_[kRegistersArrayLength];
+ FrameRegisterState frame_state_;
#ifdef DEBUG
const char* comment_;
};
+enum StringIndexFlags {
+ // Accepts smis or heap numbers.
+ STRING_INDEX_IS_NUMBER,
+
+ // Accepts smis or heap numbers that are valid array indices
+ // (ECMA-262 15.4). Invalid indices are reported as being out of
+ // range.
+ STRING_INDEX_IS_ARRAY_INDEX
+};
+
+
+// Generates code implementing String.prototype.charCodeAt.
+//
+// Only supports the case when the receiver is a string and the index
+// is a number (smi or heap number) that is a valid index into the
+// string. Additional index constraints are specified by the
+// flags. Otherwise, bails out to the provided labels.
+//
+// Register usage: |object| may be changed to another string in a way
+// that doesn't affect charCodeAt/charAt semantics, |index| is
+// preserved, |scratch| and |result| are clobbered.
+class StringCharCodeAtGenerator {
+ public:
+ StringCharCodeAtGenerator(Register object,
+ Register index,
+ Register scratch,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_number,
+ Label* index_out_of_range,
+ StringIndexFlags index_flags)
+ : object_(object),
+ index_(index),
+ scratch_(scratch),
+ result_(result),
+ receiver_not_string_(receiver_not_string),
+ index_not_number_(index_not_number),
+ index_out_of_range_(index_out_of_range),
+ index_flags_(index_flags) {
+ ASSERT(!scratch_.is(object_));
+ ASSERT(!scratch_.is(index_));
+ ASSERT(!scratch_.is(result_));
+ ASSERT(!result_.is(object_));
+ ASSERT(!result_.is(index_));
+ }
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm);
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper);
+
+ private:
+ Register object_;
+ Register index_;
+ Register scratch_;
+ Register result_;
+
+ Label* receiver_not_string_;
+ Label* index_not_number_;
+ Label* index_out_of_range_;
+
+ StringIndexFlags index_flags_;
+
+ Label call_runtime_;
+ Label index_not_smi_;
+ Label got_smi_index_;
+ Label exit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharCodeAtGenerator);
+};
+
+
+// Generates code for creating a one-char string from a char code.
+class StringCharFromCodeGenerator {
+ public:
+ StringCharFromCodeGenerator(Register code,
+ Register result)
+ : code_(code),
+ result_(result) {
+ ASSERT(!code_.is(result_));
+ }
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm);
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper);
+
+ private:
+ Register code_;
+ Register result_;
+
+ Label slow_case_;
+ Label exit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharFromCodeGenerator);
+};
+
+
+// Generates code implementing String.prototype.charAt.
+//
+// Only supports the case when the receiver is a string and the index
+// is a number (smi or heap number) that is a valid index into the
+// string. Additional index constraints are specified by the
+// flags. Otherwise, bails out to the provided labels.
+//
+// Register usage: |object| may be changed to another string in a way
+// that doesn't affect charCodeAt/charAt semantics, |index| is
+// preserved, |scratch1|, |scratch2|, and |result| are clobbered.
+class StringCharAtGenerator {
+ public:
+ StringCharAtGenerator(Register object,
+ Register index,
+ Register scratch1,
+ Register scratch2,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_number,
+ Label* index_out_of_range,
+ StringIndexFlags index_flags)
+ : char_code_at_generator_(object,
+ index,
+ scratch1,
+ scratch2,
+ receiver_not_string,
+ index_not_number,
+ index_out_of_range,
+ index_flags),
+ char_from_code_generator_(scratch2, result) {}
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm);
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper);
+
+ private:
+ StringCharCodeAtGenerator char_code_at_generator_;
+ StringCharFromCodeGenerator char_from_code_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharAtGenerator);
+};
+
+
} // namespace internal
} // namespace v8
}
+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;
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);
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm_)
+#define __ ACCESS_MASM(masm)
// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
+// Platform-specific FrameRegisterState functions.
-void DeferredCode::SaveRegisters() {
+void FrameRegisterState::Save(MacroAssembler* masm) const {
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
int action = registers_[i];
if (action == kPush) {
}
-void DeferredCode::RestoreRegisters() {
+void FrameRegisterState::Restore(MacroAssembler* masm) const {
// Restore registers in reverse order due to the stack.
for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
int action = registers_[i];
}
+#undef __
+#define __ ACCESS_MASM(masm_)
+
+// -------------------------------------------------------------------------
+// Platform-specific DeferredCode functions.
+
+void DeferredCode::SaveRegisters() {
+ frame_state_.Save(masm_);
+}
+
+
+void DeferredCode::RestoreRegisters() {
+ frame_state_.Restore(masm_);
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ frame_state_->Save(masm);
+}
+
+
+void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ frame_state_->Restore(masm);
+}
+
+
+void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterInternalFrame();
+}
+
+
+void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveInternalFrame();
+}
+
+
// -------------------------------------------------------------------------
// CodeGenState implementation.
}
-// This generates code that performs a charCodeAt() call or returns
-// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
-// It can handle flat, 8 and 16 bit characters and cons strings where the
-// answer is found in the left hand branch of the cons. The slow case will
-// flatten the string, which will ensure that the answer is in the left hand
-// side the next time around.
-void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateFastCharCodeAt");
+class DeferredStringCharCodeAt : public DeferredCode {
+ public:
+ DeferredStringCharCodeAt(Register object,
+ Register index,
+ Register scratch,
+ Register result)
+ : result_(result),
+ char_code_at_generator_(object,
+ index,
+ scratch,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharCodeAtGenerator* fast_case_generator() {
+ return &char_code_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_code_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ Set(result_, Immediate(Factory::undefined_value()));
+ __ jmp(exit_label());
+
+ __ 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(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharCodeAtGenerator char_code_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charCodeAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharCodeAt");
ASSERT(args->length() == 2);
Load(args->at(0));
Load(args->at(1));
Result index = frame_->Pop();
Result object = frame_->Pop();
-
- // We will mutate the index register and possibly the object register.
- // The case where they are somehow the same register is handled
- // because we only mutate them in the case where the receiver is a
- // heap object and the index is not.
object.ToRegister();
index.ToRegister();
+ // We might mutate the object register.
frame_->Spill(object.reg());
- frame_->Spill(index.reg());
// We need two extra registers.
Result result = allocator()->Allocate();
Result scratch = allocator()->Allocate();
ASSERT(scratch.is_valid());
- // There is no virtual frame effect from here up to the final result
- // push.
- Label slow_case;
- Label exit;
- StringHelper::GenerateFastCharCodeAt(masm_,
- object.reg(),
- index.reg(),
- scratch.reg(),
- result.reg(),
- &slow_case,
- &slow_case,
- &slow_case,
- &slow_case);
- __ jmp(&exit);
-
- __ bind(&slow_case);
- // Move the undefined value into the result register, which will
- // trigger the slow case.
- __ Set(result.reg(), Immediate(Factory::undefined_value()));
-
- __ bind(&exit);
+ DeferredStringCharCodeAt* deferred =
+ new DeferredStringCharCodeAt(object.reg(),
+ index.reg(),
+ scratch.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
-void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateCharFromCode");
+class DeferredStringCharFromCode : public DeferredCode {
+ public:
+ DeferredStringCharFromCode(Register code,
+ Register result)
+ : char_from_code_generator_(code, result) {}
+
+ StringCharFromCodeGenerator* fast_case_generator() {
+ return &char_from_code_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_from_code_generator_.GenerateSlow(masm(), call_helper);
+ }
+
+ private:
+ StringCharFromCodeGenerator char_from_code_generator_;
+};
+
+
+// Generates code for creating a one-char string from a char code.
+void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharFromCode");
ASSERT(args->length() == 1);
Load(args->at(0));
code.ToRegister();
ASSERT(code.is_valid());
- // StringHelper::GenerateCharFromCode may do a runtime call.
- frame_->SpillAll();
-
Result result = allocator()->Allocate();
ASSERT(result.is_valid());
- StringHelper::GenerateCharFromCode(masm_,
- code.reg(),
- result.reg(),
- CALL_FUNCTION);
+ DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
+ code.reg(), result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
+ frame_->Push(&result);
+}
+
+
+class DeferredStringCharAt : public DeferredCode {
+ public:
+ DeferredStringCharAt(Register object,
+ Register index,
+ Register scratch1,
+ Register scratch2,
+ Register result)
+ : result_(result),
+ char_at_generator_(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharAtGenerator* fast_case_generator() {
+ return &char_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Set(result_, Immediate(Smi::FromInt(0)));
+ __ jmp(exit_label());
+
+ __ 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(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharAtGenerator char_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharAt");
+ ASSERT(args->length() == 2);
+
+ Load(args->at(0));
+ Load(args->at(1));
+ Result index = frame_->Pop();
+ Result object = frame_->Pop();
+ object.ToRegister();
+ index.ToRegister();
+ // We might mutate the object register.
+ frame_->Spill(object.reg());
+
+ // We need three extra registers.
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ Result scratch1 = allocator()->Allocate();
+ ASSERT(scratch1.is_valid());
+ Result scratch2 = allocator()->Allocate();
+ ASSERT(scratch2.is_valid());
+
+ DeferredStringCharAt* deferred =
+ new DeferredStringCharAt(object.reg(),
+ index.reg(),
+ scratch1.reg(),
+ scratch2.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
}
-void StringHelper::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) {
- Label not_a_flat_string;
- Label try_again_with_new_string;
+// -------------------------------------------------------------------------
+// StringCharCodeAtGenerator
+
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ Label flat_string;
Label ascii_string;
Label got_char_code;
// If the receiver is a smi trigger the non-string case.
ASSERT(kSmiTag == 0);
- __ test(object, Immediate(kSmiTagMask));
- __ j(zero, receiver_not_string);
+ __ test(object_, Immediate(kSmiTagMask));
+ __ j(zero, receiver_not_string_);
// Fetch the instance type of the receiver into result register.
- __ mov(result, FieldOperand(object, HeapObject::kMapOffset));
- __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
+ __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
// If the receiver is not a string trigger the non-string case.
- __ test(result, Immediate(kIsNotStringMask));
- __ j(not_zero, receiver_not_string);
+ __ test(result_, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string_);
// If the index is non-smi trigger the non-smi case.
ASSERT(kSmiTag == 0);
- __ test(index, Immediate(kSmiTagMask));
- __ j(not_zero, index_not_smi);
+ __ test(index_, Immediate(kSmiTagMask));
+ __ j(not_zero, &index_not_smi_);
- // Check for index out of range.
- __ cmp(index, FieldOperand(object, String::kLengthOffset));
- __ j(above_equal, index_out_of_range);
+ // Put smi-tagged index into scratch register.
+ __ mov(scratch_, index_);
+ __ bind(&got_smi_index_);
- __ bind(&try_again_with_new_string);
- // ----------- S t a t e -------------
- // -- object : string to access
- // -- result : instance type of the string
- // -- scratch : non-negative index < length
- // -----------------------------------
+ // Check for index out of range.
+ __ cmp(scratch_, FieldOperand(object_, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range_);
// We need special handling for non-flat strings.
ASSERT(kSeqStringTag == 0);
- __ test(result, Immediate(kStringRepresentationMask));
- __ j(not_zero, ¬_a_flat_string);
-
- // Check for 1-byte or 2-byte string.
- ASSERT(kAsciiStringTag != 0);
- __ test(result, Immediate(kStringEncodingMask));
- __ j(not_zero, &ascii_string);
-
- // 2-byte string.
- // Load the 2-byte character code into the result register.
- ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // index is smi (powered by 2).
- __ movzx_w(result, FieldOperand(object,
- index, times_1,
- SeqTwoByteString::kHeaderSize));
- __ jmp(&got_char_code);
+ __ test(result_, Immediate(kStringRepresentationMask));
+ __ j(zero, &flat_string);
// Handle non-flat strings.
- __ bind(¬_a_flat_string);
- __ and_(result, kStringRepresentationMask);
- __ cmp(result, kConsStringTag);
- __ j(not_equal, slow_case);
+ __ test(result_, Immediate(kIsConsStringMask));
+ __ j(zero, &call_runtime_);
// ConsString.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
// the case we would rather go to the runtime system now to flatten
// the string.
- __ mov(result, FieldOperand(object, ConsString::kSecondOffset));
- __ cmp(Operand(result), Factory::empty_string());
- __ j(not_equal, slow_case);
+ __ cmp(FieldOperand(object_, ConsString::kSecondOffset),
+ Immediate(Factory::empty_string()));
+ __ j(not_equal, &call_runtime_);
// Get the first of the two strings and load its instance type.
- __ mov(object, FieldOperand(object, ConsString::kFirstOffset));
- __ mov(result, FieldOperand(object, HeapObject::kMapOffset));
- __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
- __ jmp(&try_again_with_new_string);
+ __ mov(object_, FieldOperand(object_, ConsString::kFirstOffset));
+ __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ // If the first cons component is also non-flat, then go to runtime.
+ ASSERT(kSeqStringTag == 0);
+ __ test(result_, Immediate(kStringRepresentationMask));
+ __ j(not_zero, &call_runtime_);
- // ASCII string.
- __ bind(&ascii_string);
- // Put untagged index into scratch register.
- __ mov(scratch, index);
- __ SmiUntag(scratch);
+ // Check for 1-byte or 2-byte string.
+ __ bind(&flat_string);
+ ASSERT(kAsciiStringTag != 0);
+ __ test(result_, Immediate(kStringEncodingMask));
+ __ j(not_zero, &ascii_string);
+ // 2-byte string.
+ // Load the 2-byte character code into the result register.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ movzx_w(result_, FieldOperand(object_,
+ scratch_, times_1, // Scratch is smi-tagged.
+ SeqTwoByteString::kHeaderSize));
+ __ jmp(&got_char_code);
+
+ // ASCII string.
// Load the byte into the result register.
- __ movzx_b(result, FieldOperand(object,
- scratch, times_1,
- SeqAsciiString::kHeaderSize));
+ __ bind(&ascii_string);
+ __ SmiUntag(scratch_);
+ __ movzx_b(result_, FieldOperand(object_,
+ scratch_, times_1,
+ SeqAsciiString::kHeaderSize));
__ bind(&got_char_code);
- __ SmiTag(result);
+ __ SmiTag(result_);
+ __ bind(&exit_);
}
-void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
- Register code,
- Register result,
- InvokeFlag flag) {
- ASSERT(!code.is(result));
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
- Label slow_case;
- Label exit;
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_, Factory::heap_number_map(), index_not_number_, true);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ push(result_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ // Strictly speaking, NumberToInteger should be called here, but
+ // our string lengths don't exceed 32 bits and using ToUint32 maps
+ // -0 to 0, which is what is required by the spec when accessing
+ // strings.
+ __ CallRuntime(Runtime::kNumberToJSUint32, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!scratch_.is(eax)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ mov(scratch_, eax);
+ }
+ __ pop(result_);
+ __ pop(index_);
+ __ pop(object_);
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ ASSERT(kSmiTag == 0);
+ __ test(scratch_, Immediate(kSmiTagMask));
+ __ j(not_zero, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
// Fast case of Heap::LookupSingleCharacterStringFromCode.
ASSERT(kSmiTag == 0);
ASSERT(kSmiShiftSize == 0);
ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
- __ test(code,
+ __ test(code_,
Immediate(kSmiTagMask |
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
- __ j(not_zero, &slow_case, not_taken);
+ __ j(not_zero, &slow_case_, not_taken);
- __ Set(result, Immediate(Factory::single_character_string_cache()));
+ __ Set(result_, 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(result, FieldOperand(result,
- code, times_half_pointer_size,
- FixedArray::kHeaderSize));
- __ cmp(result, Factory::undefined_value());
- __ j(equal, &slow_case, not_taken);
- __ jmp(&exit);
+ __ mov(result_, FieldOperand(result_,
+ code_, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result_, Factory::undefined_value());
+ __ j(equal, &slow_case_, not_taken);
+ __ bind(&exit_);
+}
- __ bind(&slow_case);
- if (flag == CALL_FUNCTION) {
- __ push(code);
- __ CallRuntime(Runtime::kCharFromCode, 1);
- if (!result.is(eax)) {
- __ mov(result, eax);
- }
- } else {
- ASSERT(flag == JUMP_FUNCTION);
- ASSERT(result.is(eax));
- __ pop(eax); // Save return address.
- __ push(code);
- __ push(eax); // Restore return address.
- __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
- }
- __ bind(&exit);
- if (flag == JUMP_FUNCTION) {
- ASSERT(result.is(eax));
- __ ret(0);
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharFromCode slow case");
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
}
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharFromCode slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharAtGenerator
+
+void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
+}
+
+
+void StringCharAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
}
// 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 };
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);
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
}
-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);
}
-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());
// -- 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);
}
+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,
inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
- friend class DeferredCode;
+ friend class FrameRegisterState;
friend class JumpTarget;
};
}
-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);
}
}
+
+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
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
}
kConsStringTag = 0x1,
kExternalStringTag = 0x3
};
+const uint32_t kIsConsStringMask = 0x1;
// A ConsString with an empty string as the right side is a candidate
// 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;
}
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);
}
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('$');
++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);
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
// 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 {
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm_)
+#define __ ACCESS_MASM(masm)
// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
+// Platform-specific FrameRegisterState functions.
-void DeferredCode::SaveRegisters() {
+void FrameRegisterState::Save(MacroAssembler* masm) const {
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
int action = registers_[i];
if (action == kPush) {
}
-void DeferredCode::RestoreRegisters() {
+void FrameRegisterState::Restore(MacroAssembler* masm) const {
// Restore registers in reverse order due to the stack.
for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
int action = registers_[i];
}
+#undef __
+#define __ ACCESS_MASM(masm_)
+
+// -------------------------------------------------------------------------
+// Platform-specific DeferredCode functions.
+
+void DeferredCode::SaveRegisters() {
+ frame_state_.Save(masm_);
+}
+
+
+void DeferredCode::RestoreRegisters() {
+ frame_state_.Restore(masm_);
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ frame_state_->Save(masm);
+}
+
+
+void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ frame_state_->Restore(masm);
+}
+
+
+void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterInternalFrame();
+}
+
+
+void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveInternalFrame();
+}
+
+
// -------------------------------------------------------------------------
// CodeGenState implementation.
}
-void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateFastCharCodeAt");
+class DeferredStringCharCodeAt : public DeferredCode {
+ public:
+ DeferredStringCharCodeAt(Register object,
+ Register index,
+ Register scratch,
+ Register result)
+ : result_(result),
+ char_code_at_generator_(object,
+ index,
+ scratch,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharCodeAtGenerator* fast_case_generator() {
+ return &char_code_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_code_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
+ __ jmp(exit_label());
+
+ __ bind(&index_out_of_range_);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result_, Heap::kNanValueRootIndex);
+ __ jmp(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharCodeAtGenerator char_code_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charCodeAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharCodeAt");
ASSERT(args->length() == 2);
Load(args->at(0));
Load(args->at(1));
Result index = frame_->Pop();
Result object = frame_->Pop();
-
- // We will mutate the index register and possibly the object register.
- // The case where they are somehow the same register is handled
- // because we only mutate them in the case where the receiver is a
- // heap object and the index is not.
object.ToRegister();
index.ToRegister();
+ // We might mutate the object register.
frame_->Spill(object.reg());
- frame_->Spill(index.reg());
// We need two extra registers.
Result result = allocator()->Allocate();
Result scratch = allocator()->Allocate();
ASSERT(scratch.is_valid());
- // There is no virtual frame effect from here up to the final result
- // push.
- Label slow_case;
- Label exit;
- StringHelper::GenerateFastCharCodeAt(masm_,
- object.reg(),
- index.reg(),
- scratch.reg(),
- result.reg(),
- &slow_case,
- &slow_case,
- &slow_case,
- &slow_case);
- __ jmp(&exit);
-
- __ bind(&slow_case);
- // Move the undefined value into the result register, which will
- // trigger the slow case.
- __ LoadRoot(result.reg(), Heap::kUndefinedValueRootIndex);
-
- __ bind(&exit);
+ DeferredStringCharCodeAt* deferred =
+ new DeferredStringCharCodeAt(object.reg(),
+ index.reg(),
+ scratch.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
-void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateCharFromCode");
+class DeferredStringCharFromCode : public DeferredCode {
+ public:
+ DeferredStringCharFromCode(Register code,
+ Register result)
+ : char_from_code_generator_(code, result) {}
+
+ StringCharFromCodeGenerator* fast_case_generator() {
+ return &char_from_code_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_from_code_generator_.GenerateSlow(masm(), call_helper);
+ }
+
+ private:
+ StringCharFromCodeGenerator char_from_code_generator_;
+};
+
+
+// Generates code for creating a one-char string from a char code.
+void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharFromCode");
ASSERT(args->length() == 1);
Load(args->at(0));
code.ToRegister();
ASSERT(code.is_valid());
- // StringHelper::GenerateCharFromCode may do a runtime call.
- frame_->SpillAll();
-
Result result = allocator()->Allocate();
ASSERT(result.is_valid());
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
- StringHelper::GenerateCharFromCode(masm_,
- code.reg(),
- result.reg(),
- scratch.reg(),
- CALL_FUNCTION);
+ DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
+ code.reg(), result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
+ frame_->Push(&result);
+}
+
+
+class DeferredStringCharAt : public DeferredCode {
+ public:
+ DeferredStringCharAt(Register object,
+ Register index,
+ Register scratch1,
+ Register scratch2,
+ Register result)
+ : result_(result),
+ char_at_generator_(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion_,
+ &need_conversion_,
+ &index_out_of_range_,
+ STRING_INDEX_IS_NUMBER) {}
+
+ StringCharAtGenerator* fast_case_generator() {
+ return &char_at_generator_;
+ }
+
+ virtual void Generate() {
+ VirtualFrameRuntimeCallHelper call_helper(frame_state());
+ char_at_generator_.GenerateSlow(masm(), call_helper);
+
+ __ bind(&need_conversion_);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Move(result_, Smi::FromInt(0));
+ __ jmp(exit_label());
+
+ __ 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(exit_label());
+ }
+
+ private:
+ Register result_;
+
+ Label need_conversion_;
+ Label index_out_of_range_;
+
+ StringCharAtGenerator char_at_generator_;
+};
+
+
+// This generates code that performs a String.prototype.charAt() call
+// or returns a smi in order to trigger conversion.
+void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateStringCharAt");
+ ASSERT(args->length() == 2);
+
+ Load(args->at(0));
+ Load(args->at(1));
+ Result index = frame_->Pop();
+ Result object = frame_->Pop();
+ object.ToRegister();
+ index.ToRegister();
+ // We might mutate the object register.
+ frame_->Spill(object.reg());
+
+ // We need three extra registers.
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ Result scratch1 = allocator()->Allocate();
+ ASSERT(scratch1.is_valid());
+ Result scratch2 = allocator()->Allocate();
+ ASSERT(scratch2.is_valid());
+
+ DeferredStringCharAt* deferred =
+ new DeferredStringCharAt(object.reg(),
+ index.reg(),
+ scratch1.reg(),
+ scratch2.reg(),
+ result.reg());
+ deferred->fast_case_generator()->GenerateFast(masm_);
+ deferred->BindExit();
frame_->Push(&result);
}
}
-void StringHelper::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) {
- Label not_a_flat_string;
- Label try_again_with_new_string;
+// -------------------------------------------------------------------------
+// StringCharCodeAtGenerator
+
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ Label flat_string;
Label ascii_string;
Label got_char_code;
// If the receiver is a smi trigger the non-string case.
- __ JumpIfSmi(object, receiver_not_string);
+ __ JumpIfSmi(object_, receiver_not_string_);
// Fetch the instance type of the receiver into result register.
- __ movq(result, FieldOperand(object, HeapObject::kMapOffset));
- __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
+ __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
// If the receiver is not a string trigger the non-string case.
- __ testb(result, Immediate(kIsNotStringMask));
- __ j(not_zero, receiver_not_string);
+ __ testb(result_, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string_);
// If the index is non-smi trigger the non-smi case.
- __ JumpIfNotSmi(index, index_not_smi);
+ __ JumpIfNotSmi(index_, &index_not_smi_);
- // Check for index out of range.
- __ SmiCompare(index, FieldOperand(object, String::kLengthOffset));
- __ j(above_equal, index_out_of_range);
+ // Put smi-tagged index into scratch register.
+ __ movq(scratch_, index_);
+ __ bind(&got_smi_index_);
- __ bind(&try_again_with_new_string);
- // ----------- S t a t e -------------
- // -- object : string to access
- // -- result : instance type of the string
- // -- scratch : non-negative index < length
- // -----------------------------------
+ // Check for index out of range.
+ __ SmiCompare(scratch_, FieldOperand(object_, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range_);
// We need special handling for non-flat strings.
- ASSERT_EQ(0, kSeqStringTag);
- __ testb(result, Immediate(kStringRepresentationMask));
- __ j(not_zero, ¬_a_flat_string);
+ ASSERT(kSeqStringTag == 0);
+ __ testb(result_, Immediate(kStringRepresentationMask));
+ __ j(zero, &flat_string);
+
+ // Handle non-flat strings.
+ __ testb(result_, Immediate(kIsConsStringMask));
+ __ j(zero, &call_runtime_);
- // Put untagged index into scratch register.
- __ SmiToInteger32(scratch, index);
+ // ConsString.
+ // Check whether the right hand side is the empty string (i.e. if
+ // this is really a flat string in a cons string). If that is not
+ // the case we would rather go to the runtime system now to flatten
+ // the string.
+ __ CompareRoot(FieldOperand(object_, ConsString::kSecondOffset),
+ Heap::kEmptyStringRootIndex);
+ __ j(not_equal, &call_runtime_);
+ // Get the first of the two strings and load its instance type.
+ __ movq(object_, FieldOperand(object_, ConsString::kFirstOffset));
+ __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ // If the first cons component is also non-flat, then go to runtime.
+ ASSERT(kSeqStringTag == 0);
+ __ testb(result_, Immediate(kStringRepresentationMask));
+ __ j(not_zero, &call_runtime_);
// Check for 1-byte or 2-byte string.
- ASSERT_EQ(0, kTwoByteStringTag);
- __ testb(result, Immediate(kStringEncodingMask));
+ __ bind(&flat_string);
+ ASSERT(kAsciiStringTag != 0);
+ __ testb(result_, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string);
// 2-byte string.
// Load the 2-byte character code into the result register.
- __ movzxwl(result, FieldOperand(object,
- scratch,
- times_2,
- SeqTwoByteString::kHeaderSize));
+ __ SmiToInteger32(scratch_, scratch_);
+ __ movzxwl(result_, FieldOperand(object_,
+ scratch_, times_2,
+ SeqTwoByteString::kHeaderSize));
__ jmp(&got_char_code);
- // Handle non-flat strings.
- __ bind(¬_a_flat_string);
- __ and_(result, Immediate(kStringRepresentationMask));
- __ cmpb(result, Immediate(kConsStringTag));
- __ j(not_equal, slow_case);
-
- // ConsString.
- // Check that the right hand side is the empty string (ie if this is really a
- // flat string in a cons string). If that is not the case we would rather go
- // to the runtime system now, to flatten the string.
- __ movq(result, FieldOperand(object, ConsString::kSecondOffset));
- __ CompareRoot(result, Heap::kEmptyStringRootIndex);
- __ j(not_equal, slow_case);
- // Get the first of the two strings and load its instance type.
- __ movq(object, FieldOperand(object, ConsString::kFirstOffset));
- __ movq(result, FieldOperand(object, HeapObject::kMapOffset));
- __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
- __ jmp(&try_again_with_new_string);
-
// ASCII string.
- __ bind(&ascii_string);
// Load the byte into the result register.
- __ movzxbl(result, FieldOperand(object,
- scratch,
- times_1,
- SeqAsciiString::kHeaderSize));
+ __ bind(&ascii_string);
+ __ SmiToInteger32(scratch_, scratch_);
+ __ movzxbl(result_, FieldOperand(object_,
+ scratch_, times_1,
+ SeqAsciiString::kHeaderSize));
__ bind(&got_char_code);
- __ Integer32ToSmi(result, result);
+ __ Integer32ToSmi(result_, result_);
+ __ bind(&exit_);
}
-void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
- Register code,
- Register result,
- Register scratch,
- InvokeFlag flag) {
- ASSERT(!code.is(result));
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
- Label slow_case;
- Label exit;
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_, Factory::heap_number_map(), index_not_number_, true);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ push(result_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ // Strictly speaking, NumberToInteger should be called here, but
+ // our string lengths don't exceed 32 bits and using ToUint32 maps
+ // -0 to 0, which is what is required by the spec when accessing
+ // strings.
+ __ CallRuntime(Runtime::kNumberToJSUint32, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!scratch_.is(rax)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ movq(scratch_, rax);
+ }
+ __ pop(result_);
+ __ pop(index_);
+ __ pop(object_);
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ __ JumpIfNotSmi(scratch_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(rax)) {
+ __ movq(result_, rax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
+}
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
// Fast case of Heap::LookupSingleCharacterStringFromCode.
- __ JumpIfNotSmi(code, &slow_case);
- __ SmiToInteger32(scratch, code);
- __ cmpl(scratch, Immediate(String::kMaxAsciiCharCode));
- __ j(above, &slow_case);
-
- __ Move(result, Factory::single_character_string_cache());
- __ movq(result, FieldOperand(result,
- scratch,
- times_pointer_size,
- FixedArray::kHeaderSize));
-
- __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
- __ j(equal, &slow_case);
- __ jmp(&exit);
+ __ JumpIfNotSmi(code_, &slow_case_);
+ __ SmiCompare(code_, Smi::FromInt(String::kMaxAsciiCharCode));
+ __ j(above, &slow_case_);
- __ bind(&slow_case);
- if (flag == CALL_FUNCTION) {
- __ push(code);
- __ CallRuntime(Runtime::kCharFromCode, 1);
- if (!result.is(rax)) {
- __ movq(result, rax);
- }
- } else {
- ASSERT(flag == JUMP_FUNCTION);
- ASSERT(result.is(rax));
- __ pop(rax); // Save return address.
- __ push(code);
- __ push(rax); // Restore return address.
- __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
- }
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ SmiIndex index = masm->SmiToIndex(kScratchRegister, code_, kPointerSizeLog2);
+ __ movq(result_, FieldOperand(result_, index.reg, index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(result_, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &slow_case_);
+ __ bind(&exit_);
+}
- __ bind(&exit);
- if (flag == JUMP_FUNCTION) {
- ASSERT(result.is(rax));
- __ ret(0);
+
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ __ Abort("Unexpected fallthrough to CharFromCode slow case");
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(rax)) {
+ __ movq(result_, rax);
}
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharFromCode slow case");
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharAtGenerator
+
+void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
+}
+
+
+void StringCharAtGenerator::GenerateSlow(
+ MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
}
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);
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
}
-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);
}
-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());
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);
}
+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,
inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
- friend class DeferredCode;
+ friend class FrameRegisterState;
friend class JumpTarget;
};
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 = ['@'];
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();
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];
var expected = arr[0];
var actual = str[0];
assertEquals(expected, actual);
-}
\ No newline at end of file
+}