| Index: src/ia32/codegen-ia32.cc
|
| diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
|
| index b94df7aa42209ba5134fc0c396eaa32d6207ccd3..a3a4bde00dd4858ca90c6f416a3d58b85e059d37 100644
|
| --- a/src/ia32/codegen-ia32.cc
|
| +++ b/src/ia32/codegen-ia32.cc
|
| @@ -46,12 +46,12 @@
|
| 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) {
|
| @@ -63,7 +63,7 @@ void DeferredCode::SaveRegisters() {
|
| }
|
|
|
|
|
| -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];
|
| @@ -77,6 +77,45 @@ void DeferredCode::RestoreRegisters() {
|
| }
|
|
|
|
|
| +#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.
|
|
|
| @@ -6018,29 +6057,67 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
|
| }
|
|
|
|
|
| -// 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_ANY_NUMBER_INDEX) {}
|
| +
|
| + 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();
|
| @@ -6048,33 +6125,40 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
| 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));
|
| @@ -6083,16 +6167,97 @@ void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
|
| 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_ANY_NUMBER_INDEX) {}
|
| +
|
| + 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);
|
| }
|
|
|
| @@ -12374,152 +12539,208 @@ const char* CompareStub::GetName() {
|
| }
|
|
|
|
|
| -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_ANY_NUMBER_INDEX) {
|
| + // 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_REQUIRE_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);
|
| }
|
|
|
|
|
|
|