From b4ff318e4b0b2c59f397a22b1b8f6dfb3918e71d Mon Sep 17 00:00:00 2001 From: "vitalyr@chromium.org" Date: Wed, 19 Jan 2011 20:05:22 +0000 Subject: [PATCH] Support StringCharCodeAt in hydrogen/lithium. This patch adds H- and L-variants of StringCharCodeAt and StringLength. StringCharCodeAt is used to inline a constant function call of String.prototype.charCodeAt and to implement the corresponding inline runtime function. It does not yet use the recently introduced extra IC state. (We can specialize on string encoding and avoid deopts because of out of bounds accesses.) StringLength needs more work because the stub version of it also supports strings wrappers and it matters in some cases. (We have to separate the string only case.) Review URL: http://codereview.chromium.org/6243008 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6408 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 12 ++++ src/ast.cc | 26 +++----- src/hydrogen-instructions.h | 65 ++++++++++++++++++ src/hydrogen.cc | 125 ++++++++++++++++++++++++---------- src/hydrogen.h | 7 +- src/ia32/lithium-codegen-ia32.cc | 141 ++++++++++++++++++++++++++++++++++++++- src/ia32/lithium-codegen-ia32.h | 1 + src/ia32/lithium-ia32.cc | 14 ++++ src/ia32/lithium-ia32.h | 32 +++++++++ src/objects-inl.h | 6 -- src/objects-printer.cc | 1 + src/objects.h | 5 +- src/x64/lithium-x64.cc | 12 ++++ 13 files changed, 382 insertions(+), 65 deletions(-) diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 38d0af0..d367391 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1724,6 +1724,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { } +LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { + Abort("LStringCharCodeAt instruction not implemented on ARM"); + return NULL; +} + + +LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { + Abort("LStringLength instruction not implemented on ARM"); + return NULL; +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { return MarkAsCall(DefineFixed(new LArrayLiteral, r0), instr); } diff --git a/src/ast.cc b/src/ast.cc index 4fe89be..fa01be0 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -32,7 +32,6 @@ #include "parser.h" #include "scopes.h" #include "string-stream.h" -#include "stub-cache.h" namespace v8 { namespace internal { @@ -560,20 +559,13 @@ void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) { } -static bool CallWithoutIC(Handle target, int arity) { +static bool CanCallWithoutIC(Handle target, int arity) { SharedFunctionInfo* info = target->shared(); - if (target->NeedsArgumentsAdaption()) { - // If the number of formal parameters of the target function - // does not match the number of arguments we're passing, we - // don't want to deal with it. - return info->formal_parameter_count() == arity; - } else { - // If the target doesn't need arguments adaption, we can call - // it directly, but we avoid to do so if it has a custom call - // generator, because that is likely to generate better code. - return !info->HasBuiltinFunctionId() || - !CallStubCompiler::HasCustomCallGenerator(info->builtin_function_id()); - } + // If the number of formal parameters of the target function does + // not match the number of arguments we're passing, we don't want to + // deal with it. Otherwise, we can call it directly. + return !target->NeedsArgumentsAdaption() || + info->formal_parameter_count() == arity; } @@ -589,7 +581,7 @@ bool Call::ComputeTarget(Handle type, Handle name) { type = Handle(holder()->map()); } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) { target_ = Handle(lookup.GetConstantFunctionFromMap(*type)); - return CallWithoutIC(target_, arguments()->length()); + return CanCallWithoutIC(target_, arguments()->length()); } else { return false; } @@ -609,8 +601,8 @@ bool Call::ComputeGlobalTarget(Handle global, Handle candidate(JSFunction::cast(cell_->value())); // If the function is in new space we assume it's more likely to // change and thus prefer the general IC code. - if (!Heap::InNewSpace(*candidate) - && CallWithoutIC(candidate, arguments()->length())) { + if (!Heap::InNewSpace(*candidate) && + CanCallWithoutIC(candidate, arguments()->length())) { target_ = candidate; return true; } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index eebec5a..d8d3f6f 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -81,6 +81,7 @@ class LChunkBuilder; // HStoreNamed // HStoreNamedField // HStoreNamedGeneric +// HStringCharCodeAt // HBlockEntry // HCall // HCallConstantFunction @@ -137,6 +138,7 @@ class LChunkBuilder; // HLoadNamedGeneric // HLoadFunctionPrototype // HPushArgument +// HStringLength // HTypeof // HUnaryMathOperation // HUnaryPredicate @@ -248,6 +250,8 @@ class LChunkBuilder; V(StoreKeyedGeneric) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ + V(StringCharCodeAt) \ + V(StringLength) \ V(Sub) \ V(Throw) \ V(Typeof) \ @@ -1579,6 +1583,12 @@ class HCheckInstanceType: public HUnaryOperation { ASSERT(first <= last); set_representation(Representation::Tagged()); SetFlag(kUseGVN); + if ((FIRST_STRING_TYPE < first && last <= LAST_STRING_TYPE) || + (FIRST_STRING_TYPE <= first && last < LAST_STRING_TYPE)) { + // A particular string instance type can change because of GC or + // externalization, but the value still remains a string. + SetFlag(kDependsOnMaps); + } } virtual bool IsCheckInstruction() const { return true; } @@ -2937,6 +2947,61 @@ class HStoreKeyedGeneric: public HStoreKeyed { }; +class HStringCharCodeAt: public HBinaryOperation { + public: + HStringCharCodeAt(HValue* string, HValue* index) + : HBinaryOperation(string, index) { + set_representation(Representation::Integer32()); + SetFlag(kUseGVN); + } + + virtual Representation RequiredInputRepresentation(int index) const { + // The index is supposed to be Integer32. + return (index == 1) ? Representation::Integer32() + : Representation::Tagged(); + } + + virtual bool DataEquals(HValue* other) const { return true; } + + HValue* string() const { return OperandAt(0); } + HValue* index() const { return OperandAt(1); } + + DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string_char_code_at") + + protected: + virtual Range* InferRange() { + return new Range(0, String::kMaxUC16CharCode); + } +}; + + +class HStringLength: public HUnaryOperation { + public: + explicit HStringLength(HValue* string) : HUnaryOperation(string) { + set_representation(Representation::Tagged()); + SetFlag(kUseGVN); + } + + virtual Representation RequiredInputRepresentation(int index) const { + return Representation::Tagged(); + } + + virtual HType CalculateInferredType() const { + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue); + return HType::Smi(); + } + + virtual bool DataEquals(HValue* other) const { return true; } + + DECLARE_CONCRETE_INSTRUCTION(StringLength, "string_length") + + protected: + virtual Range* InferRange() { + return new Range(0, String::kMaxLength); + } +}; + + class HMaterializedLiteral: public HInstruction { public: HMaterializedLiteral(int index, int depth) diff --git a/src/hydrogen.cc b/src/hydrogen.cc index da41ef9..b906127 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -34,6 +34,7 @@ #include "lithium-allocator.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" #if V8_TARGET_ARCH_IA32 #include "ia32/lithium-codegen-ia32.h" @@ -4146,12 +4147,29 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { } -bool HGraphBuilder::TryMathFunctionInline(Call* expr) { +bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, + HValue* receiver, + Handle receiver_map, + CheckType check_type) { + ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null()); // Try to inline calls like Math.* as operations in the calling function. - if (!expr->target()->shared()->IsBuiltinMathFunction()) return false; + if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); int argument_count = expr->arguments()->length() + 1; // Plus receiver. switch (id) { + case kStringCharCodeAt: + if (argument_count == 2 && check_type == STRING_CHECK) { + HValue* index = Pop(); + HValue* string = Pop(); + ASSERT(!expr->holder().is_null()); + AddInstruction(new HCheckPrototypeMaps( + oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK), + expr->holder())); + HStringCharCodeAt* result = BuildStringCharCodeAt(string, index); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } + break; case kMathRound: case kMathFloor: case kMathAbs: @@ -4159,7 +4177,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { case kMathLog: case kMathSin: case kMathCos: - if (argument_count == 2) { + if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) { + AddCheckConstantFunction(expr, receiver, receiver_map, true); HValue* argument = Pop(); Drop(1); // Receiver. HUnaryMathOperation* op = new HUnaryMathOperation(argument, id); @@ -4169,7 +4188,8 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { } break; case kMathPow: - if (argument_count == 3) { + if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { + AddCheckConstantFunction(expr, receiver, receiver_map, true); HValue* right = Pop(); HValue* left = Pop(); Pop(); // Pop receiver. @@ -4179,8 +4199,6 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { double exponent = HConstant::cast(right)->DoubleValue(); if (exponent == 0.5) { result = new HUnaryMathOperation(left, kMathPowHalf); - ast_context()->ReturnInstruction(result, expr->id()); - return true; } else if (exponent == -0.5) { HConstant* double_one = new HConstant(Handle(Smi::FromInt(1)), @@ -4193,22 +4211,18 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { // an environment simulation here. ASSERT(!square_root->HasSideEffects()); result = new HDiv(double_one, square_root); - ast_context()->ReturnInstruction(result, expr->id()); - return true; } else if (exponent == 2.0) { result = new HMul(left, left); - ast_context()->ReturnInstruction(result, expr->id()); - return true; } } else if (right->IsConstant() && - HConstant::cast(right)->HasInteger32Value() && - HConstant::cast(right)->Integer32Value() == 2) { + HConstant::cast(right)->HasInteger32Value() && + HConstant::cast(right)->Integer32Value() == 2) { result = new HMul(left, left); - ast_context()->ReturnInstruction(result, expr->id()); - return true; } - result = new HPower(left, right); + if (result == NULL) { + result = new HPower(left, right); + } ast_context()->ReturnInstruction(result, expr->id()); return true; } @@ -4263,6 +4277,13 @@ bool HGraphBuilder::TryCallApply(Call* expr) { } +static bool HasCustomCallGenerator(Handle function) { + SharedFunctionInfo* info = function->shared(); + return info->HasBuiltinFunctionId() && + CallStubCompiler::HasCustomCallGenerator(info->builtin_function_id()); +} + + void HGraphBuilder::VisitCall(Call* expr) { Expression* callee = expr->expression(); int argument_count = expr->arguments()->length() + 1; // Plus receiver. @@ -4309,30 +4330,44 @@ void HGraphBuilder::VisitCall(Call* expr) { expr->RecordTypeFeedback(oracle()); ZoneMapList* types = expr->GetReceiverTypes(); - if (expr->IsMonomorphic() && expr->check_type() == RECEIVER_MAP_CHECK) { - AddCheckConstantFunction(expr, receiver, types->first(), true); - - if (TryMathFunctionInline(expr)) { - return; - } else if (TryInline(expr)) { - if (subgraph()->HasExit()) { - HValue* return_value = Pop(); - // If we inlined a function in a test context then we need to emit - // a simulate here to shadow the ones at the end of the - // predecessor blocks. Those environments contain the return - // value on top and do not correspond to any actual state of the - // unoptimized code. - if (ast_context()->IsEffect()) AddSimulate(expr->id()); - ast_context()->ReturnValue(return_value); - } + if (expr->IsMonomorphic()) { + Handle receiver_map = + (types == NULL) ? Handle::null() : types->first(); + if (TryInlineBuiltinFunction(expr, + receiver, + receiver_map, + expr->check_type())) { return; - } else { - // Check for bailout, as the TryInline call in the if condition above - // might return false due to bailout during hydrogen processing. - CHECK_BAILOUT; - call = new HCallConstantFunction(expr->target(), argument_count); } + if (HasCustomCallGenerator(expr->target()) || + expr->check_type() != RECEIVER_MAP_CHECK) { + // When the target has a custom call IC generator, use the IC, + // because it is likely to generate better code. Also use the + // IC when a primitive receiver check is required. + call = new HCallNamed(name, argument_count); + } else { + AddCheckConstantFunction(expr, receiver, receiver_map, true); + + if (TryInline(expr)) { + if (subgraph()->HasExit()) { + HValue* return_value = Pop(); + // If we inlined a function in a test context then we need to emit + // a simulate here to shadow the ones at the end of the + // predecessor blocks. Those environments contain the return + // value on top and do not correspond to any actual state of the + // unoptimized code. + if (ast_context()->IsEffect()) AddSimulate(expr->id()); + ast_context()->ReturnValue(return_value); + } + return; + } else { + // Check for bailout, as the TryInline call in the if condition above + // might return false due to bailout during hydrogen processing. + CHECK_BAILOUT; + call = new HCallConstantFunction(expr->target(), argument_count); + } + } } else if (types != NULL && types->length() > 1) { ASSERT(expr->check_type() == RECEIVER_MAP_CHECK); HandlePolymorphicCallNamed(expr, receiver, types, name); @@ -4720,6 +4755,18 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } +HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string, + HValue* index) { + AddInstruction(new HCheckNonSmi(string)); + AddInstruction(new HCheckInstanceType( + string, FIRST_STRING_TYPE, LAST_STRING_TYPE)); + HStringLength* length = new HStringLength(string); + AddInstruction(length); + AddInstruction(new HBoundsCheck(index, length)); + return new HStringCharCodeAt(string, index); +} + + HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, HValue* left, HValue* right) { @@ -5129,7 +5176,11 @@ void HGraphBuilder::GenerateSetValueOf(int argument_count, int ast_id) { // Fast support for charCodeAt(n). void HGraphBuilder::GenerateStringCharCodeAt(int argument_count, int ast_id) { - BAILOUT("inlined runtime function: StringCharCodeAt"); + ASSERT(argument_count == 2); + HValue* index = Pop(); + HValue* string = Pop(); + HStringCharCodeAt* result = BuildStringCharCodeAt(string, index); + ast_context()->ReturnInstruction(result, ast_id); } diff --git a/src/hydrogen.h b/src/hydrogen.h index 19f8983..3524df9 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -748,7 +748,10 @@ class HGraphBuilder: public AstVisitor { bool TryArgumentsAccess(Property* expr); bool TryCallApply(Call* expr); bool TryInline(Call* expr); - bool TryMathFunctionInline(Call* expr); + bool TryInlineBuiltinFunction(Call* expr, + HValue* receiver, + Handle receiver_map, + CheckType check_type); void TraceInline(Handle target, bool result); void HandleGlobalVariableAssignment(Variable* var, @@ -772,6 +775,8 @@ class HGraphBuilder: public AstVisitor { ZoneMapList* types, Handle name); + HStringCharCodeAt* BuildStringCharCodeAt(HValue* string, + HValue* index); HInstruction* BuildBinaryOperation(BinaryOperation* expr, HValue* left, HValue* right); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 22e79d1..79d44c1 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2650,6 +2650,135 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { + class DeferredStringCharCodeAt: public LDeferredCode { + public: + DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + private: + LStringCharCodeAt* instr_; + }; + + DeferredStringCharCodeAt* deferred = new DeferredStringCharCodeAt(this, + instr); + + Register string = ToRegister(instr->string()); + Register index = no_reg; + int const_index = -1; + if (instr->index()->IsConstantOperand()) { + const_index = ToInteger32(LConstantOperand::cast(instr->index())); + } else { + index = ToRegister(instr->index()); + } + Register result = ToRegister(instr->result()); + + NearLabel flat_string, ascii_string, done; + + // Fetch the instance type of the receiver into result register. + __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for non-flat strings. + STATIC_ASSERT(kSeqStringTag == 0); + __ test(result, Immediate(kStringRepresentationMask)); + __ j(zero, &flat_string); + + // Handle non-flat strings. + __ test(result, Immediate(kIsConsStringMask)); + __ j(zero, deferred->entry()); + + // 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. + __ cmp(FieldOperand(string, ConsString::kSecondOffset), + Immediate(Factory::empty_string())); + __ j(not_equal, deferred->entry()); + // Get the first of the two strings and load its instance type. + __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); + __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); + // If the first cons component is also non-flat, then go to runtime. + STATIC_ASSERT(kSeqStringTag == 0); + __ test(result, Immediate(kStringRepresentationMask)); + __ j(not_zero, deferred->entry()); + + // Check for 1-byte or 2-byte string. + __ bind(&flat_string); + STATIC_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. + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + if (index.is_valid()) { + __ movzx_w(result, FieldOperand(string, + index, times_2, + SeqTwoByteString::kHeaderSize)); + } else { + __ movzx_w(result, FieldOperand( + string, SeqTwoByteString::kHeaderSize + 2 * const_index)); + } + __ jmp(&done); + + // ASCII string. + // Load the byte into the result register. + __ bind(&ascii_string); + if (index.is_valid()) { + __ movzx_b(result, FieldOperand(string, + index, times_1, + SeqAsciiString::kHeaderSize)); + } else { + __ movzx_b(result, FieldOperand(string, + SeqAsciiString::kHeaderSize + const_index)); + } + __ bind(&done); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) { + Register string = ToRegister(instr->string()); + Register result = ToRegister(instr->result()); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ Set(result, Immediate(0)); + + __ PushSafepointRegisters(); + __ push(string); + // Push the index as a smi. + if (instr->index()->IsConstantOperand()) { + int const_index = ToInteger32(LConstantOperand::cast(instr->index())); + __ push(Immediate(Smi::FromInt(const_index))); + } else { + Register index = ToRegister(instr->index()); + __ SmiTag(index); + __ push(index); + } + __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt); + RecordSafepointWithRegisters( + instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex); + if (FLAG_debug_code) { + __ AbortIfNotSmi(eax); + } + __ SmiUntag(eax); + __ mov(Operand(esp, EspIndexForPushAll(result) * kPointerSize), eax); + __ PopSafepointRegisters(); +} + + +void LCodeGen::DoStringLength(LStringLength* instr) { + Register string = ToRegister(instr->string()); + Register result = ToRegister(instr->result()); + __ mov(result, FieldOperand(string, String::kLengthOffset)); +} + + void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { LOperand* input = instr->InputAt(0); ASSERT(input->IsRegister() || input->IsStackSlot()); @@ -3076,13 +3205,19 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { InstanceType last = instr->hydrogen()->last(); __ mov(temp, FieldOperand(input, HeapObject::kMapOffset)); - __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), - static_cast(first)); // If there is only one type in the interval check for equality. if (first == last) { + __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), + static_cast(first)); DeoptimizeIf(not_equal, instr->environment()); - } else { + } else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) { + // String has a dedicated bit in instance type. + __ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), kIsNotStringMask); + DeoptimizeIf(not_zero, instr->environment()); + } else { + __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset), + static_cast(first)); DeoptimizeIf(below, instr->environment()); // Omit check for the last type. if (last != LAST_TYPE) { diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index ab62e6f..d501cb7 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -92,6 +92,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); void DoDeferredStackCheck(LGoto* instr); + void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index cca07c8..799e077 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1741,6 +1741,20 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { } +LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { + LOperand* string = UseRegister(instr->string()); + LOperand* index = UseRegisterOrConstant(instr->index()); + return AssignEnvironment(AssignPointerMap(DefineAsRegister( + new LStringCharCodeAt(string, index)))); +} + + +LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { + LOperand* string = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new LStringLength(string)); +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { return MarkAsCall(DefineFixed(new LArrayLiteral, eax), instr); } diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index 67f8751..57bd5f6 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -114,6 +114,7 @@ class LCodeGen; // LStoreNamed // LStoreNamedField // LStoreNamedGeneric +// LStringCharCodeAt // LBitNotI // LCallNew // LCheckFunction @@ -141,6 +142,7 @@ class LCodeGen; // LReturn // LSmiTag // LStoreGlobal +// LStringLength // LTaggedToI // LThrow // LTypeof @@ -253,6 +255,8 @@ class LCodeGen; V(StoreKeyedGeneric) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ + V(StringCharCodeAt) \ + V(StringLength) \ V(SubI) \ V(TaggedToI) \ V(Throw) \ @@ -1590,6 +1594,34 @@ class LStoreKeyedGeneric: public LStoreKeyed { }; +class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> { + public: + LStringCharCodeAt(LOperand* string, LOperand* index) { + inputs_[0] = string; + inputs_[1] = index; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") + DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) + + LOperand* string() { return inputs_[0]; } + LOperand* index() { return inputs_[1]; } +}; + + +class LStringLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LStringLength(LOperand* string) { + inputs_[0] = string; + } + + DECLARE_CONCRETE_INSTRUCTION(StringLength, "string-length") + DECLARE_HYDROGEN_ACCESSOR(StringLength) + + LOperand* string() { return inputs_[0]; } +}; + + class LCheckFunction: public LTemplateInstruction<0, 1> { public: explicit LCheckFunction(LOperand* value) { diff --git a/src/objects-inl.h b/src/objects-inl.h index df44674..21e318b 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3079,12 +3079,6 @@ bool SharedFunctionInfo::HasBuiltinFunctionId() { } -bool SharedFunctionInfo::IsBuiltinMathFunction() { - return HasBuiltinFunctionId() && - builtin_function_id() >= kFirstMathFunctionId; -} - - BuiltinFunctionId SharedFunctionInfo::builtin_function_id() { ASSERT(HasBuiltinFunctionId()); return static_cast(Smi::cast(function_data())->value()); diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 9879da2..9f05b0f 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -395,6 +395,7 @@ static const char* TypeToString(InstanceType type) { case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT"; case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY"; case PROXY_TYPE: return "PROXY"; + case LAST_STRING_TYPE: return "LAST_STRING_TYPE"; #define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME; STRUCT_LIST(MAKE_STRUCT_CASE) #undef MAKE_STRUCT_CASE diff --git a/src/objects.h b/src/objects.h index 547f985..8c63022 100644 --- a/src/objects.h +++ b/src/objects.h @@ -455,6 +455,7 @@ const uint32_t kShortcutTypeTag = kConsStringTag; enum InstanceType { // String types. + // FIRST_STRING_TYPE SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag, ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag, CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag, @@ -471,6 +472,7 @@ enum InstanceType { EXTERNAL_STRING_TYPE = kTwoByteStringTag | kExternalStringTag, EXTERNAL_STRING_WITH_ASCII_DATA_TYPE = kTwoByteStringTag | kExternalStringTag | kAsciiDataHintTag, + // LAST_STRING_TYPE EXTERNAL_ASCII_STRING_TYPE = kAsciiStringTag | kExternalStringTag, PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE, @@ -533,6 +535,8 @@ enum InstanceType { LAST_TYPE = JS_FUNCTION_TYPE, INVALID_TYPE = FIRST_TYPE - 1, FIRST_NONSTRING_TYPE = MAP_TYPE, + FIRST_STRING_TYPE = FIRST_TYPE, + LAST_STRING_TYPE = FIRST_NONSTRING_TYPE - 1, // Boundaries for testing for an external array. FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE, LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_FLOAT_ARRAY_TYPE, @@ -4070,7 +4074,6 @@ class SharedFunctionInfo: public HeapObject { inline bool IsApiFunction(); inline FunctionTemplateInfo* get_api_func_data(); inline bool HasBuiltinFunctionId(); - inline bool IsBuiltinMathFunction(); inline BuiltinFunctionId builtin_function_id(); // [script info]: Script from which the function originates. diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index e1cc485..ff6d8cf 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1465,6 +1465,18 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { } +LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { + Abort("Unimplemented: %s", "DoStringCharCodeAt"); + return NULL; +} + + +LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { + Abort("Unimplemented: %s", "DoStringLength"); + return NULL; +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { Abort("Unimplemented: %s", "DoArrayLiteral"); return NULL; -- 2.7.4