| // Copyright 2012 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "v8.h" |
| |
| #include "ast.h" |
| #include "code-stubs.h" |
| #include "compiler.h" |
| #include "ic.h" |
| #include "macro-assembler.h" |
| #include "stub-cache.h" |
| #include "type-info.h" |
| |
| #include "ic-inl.h" |
| #include "objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) { |
| TypeInfo info; |
| if (value->IsSmi()) { |
| info = TypeInfo::Smi(); |
| } else if (value->IsHeapNumber()) { |
| info = TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value()) |
| ? TypeInfo::Integer32() |
| : TypeInfo::Double(); |
| } else if (value->IsString()) { |
| info = TypeInfo::String(); |
| } else { |
| info = TypeInfo::Unknown(); |
| } |
| return info; |
| } |
| |
| |
| TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code, |
| Handle<Context> global_context, |
| Isolate* isolate) { |
| global_context_ = global_context; |
| isolate_ = isolate; |
| BuildDictionary(code); |
| ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue); |
| } |
| |
| |
| Handle<Object> TypeFeedbackOracle::GetInfo(unsigned ast_id) { |
| int entry = dictionary_->FindEntry(ast_id); |
| return entry != UnseededNumberDictionary::kNotFound |
| ? Handle<Object>(dictionary_->ValueAt(entry)) |
| : Handle<Object>::cast(isolate_->factory()->undefined_value()); |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsUninitialized(Property* expr) { |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsMap()) return false; |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| return code->is_inline_cache_stub() && code->ic_state() == UNINITIALIZED; |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) { |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsMap()) return true; |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| return code->is_keyed_load_stub() && |
| code->ic_state() == MONOMORPHIC && |
| Code::ExtractTypeFromFlags(code->flags()) == NORMAL && |
| code->FindFirstMap() != NULL && |
| !CanRetainOtherContext(code->FindFirstMap(), *global_context_); |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) { |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| Builtins* builtins = isolate_->builtins(); |
| return code->is_keyed_load_stub() && |
| *code != builtins->builtin(Builtins::kKeyedLoadIC_Generic) && |
| code->ic_state() == MEGAMORPHIC; |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) { |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsMap()) return true; |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| bool allow_growth = |
| Code::GetKeyedAccessGrowMode(code->extra_ic_state()) == |
| ALLOW_JSARRAY_GROWTH; |
| return code->is_keyed_store_stub() && |
| !allow_growth && |
| code->ic_state() == MONOMORPHIC && |
| Code::ExtractTypeFromFlags(code->flags()) == NORMAL && |
| code->FindFirstMap() != NULL && |
| !CanRetainOtherContext(code->FindFirstMap(), *global_context_); |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) { |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| Builtins* builtins = isolate_->builtins(); |
| bool allow_growth = |
| Code::GetKeyedAccessGrowMode(code->extra_ic_state()) == |
| ALLOW_JSARRAY_GROWTH; |
| return code->is_keyed_store_stub() && |
| !allow_growth && |
| *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic) && |
| *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic_Strict) && |
| code->ic_state() == MEGAMORPHIC; |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) { |
| Handle<Object> value = GetInfo(expr->id()); |
| return value->IsMap() || value->IsSmi() || value->IsJSFunction(); |
| } |
| |
| |
| bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) { |
| Handle<Object> value = GetInfo(expr->id()); |
| return value->IsJSFunction(); |
| } |
| |
| |
| bool TypeFeedbackOracle::ObjectLiteralStoreIsMonomorphic( |
| ObjectLiteral::Property* prop) { |
| Handle<Object> map_or_code = GetInfo(prop->key()->id()); |
| return map_or_code->IsMap(); |
| } |
| |
| |
| bool TypeFeedbackOracle::IsForInFastCase(ForInStatement* stmt) { |
| Handle<Object> value = GetInfo(stmt->PrepareId()); |
| return value->IsSmi() && |
| Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker; |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) { |
| ASSERT(LoadIsMonomorphicNormal(expr)); |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| Map* first_map = code->FindFirstMap(); |
| ASSERT(first_map != NULL); |
| return CanRetainOtherContext(first_map, *global_context_) |
| ? Handle<Map>::null() |
| : Handle<Map>(first_map); |
| } |
| return Handle<Map>::cast(map_or_code); |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) { |
| ASSERT(StoreIsMonomorphicNormal(expr)); |
| Handle<Object> map_or_code = GetInfo(expr->id()); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code = Handle<Code>::cast(map_or_code); |
| Map* first_map = code->FindFirstMap(); |
| ASSERT(first_map != NULL); |
| return CanRetainOtherContext(first_map, *global_context_) |
| ? Handle<Map>::null() |
| : Handle<Map>(first_map); |
| } |
| return Handle<Map>::cast(map_or_code); |
| } |
| |
| |
| void TypeFeedbackOracle::LoadReceiverTypes(Property* expr, |
| Handle<String> name, |
| SmallMapList* types) { |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); |
| CollectReceiverTypes(expr->id(), name, flags, types); |
| } |
| |
| |
| void TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr, |
| Handle<String> name, |
| SmallMapList* types) { |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL); |
| CollectReceiverTypes(expr->id(), name, flags, types); |
| } |
| |
| |
| void TypeFeedbackOracle::CallReceiverTypes(Call* expr, |
| Handle<String> name, |
| CallKind call_kind, |
| SmallMapList* types) { |
| int arity = expr->arguments()->length(); |
| |
| // Note: Currently we do not take string extra ic data into account |
| // here. |
| Code::ExtraICState extra_ic_state = |
| CallIC::Contextual::encode(call_kind == CALL_AS_FUNCTION); |
| |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, |
| NORMAL, |
| extra_ic_state, |
| OWN_MAP, |
| arity); |
| CollectReceiverTypes(expr->id(), name, flags, types); |
| } |
| |
| |
| CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) { |
| Handle<Object> value = GetInfo(expr->id()); |
| if (!value->IsSmi()) return RECEIVER_MAP_CHECK; |
| CheckType check = static_cast<CheckType>(Smi::cast(*value)->value()); |
| ASSERT(check != RECEIVER_MAP_CHECK); |
| return check; |
| } |
| |
| |
| Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck( |
| CheckType check) { |
| JSFunction* function = NULL; |
| switch (check) { |
| case RECEIVER_MAP_CHECK: |
| UNREACHABLE(); |
| break; |
| case STRING_CHECK: |
| function = global_context_->string_function(); |
| break; |
| case NUMBER_CHECK: |
| function = global_context_->number_function(); |
| break; |
| case BOOLEAN_CHECK: |
| function = global_context_->boolean_function(); |
| break; |
| } |
| ASSERT(function != NULL); |
| return Handle<JSObject>(JSObject::cast(function->instance_prototype())); |
| } |
| |
| |
| Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) { |
| return Handle<JSFunction>::cast(GetInfo(expr->id())); |
| } |
| |
| |
| Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) { |
| return Handle<JSFunction>::cast(GetInfo(expr->id())); |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::GetObjectLiteralStoreMap( |
| ObjectLiteral::Property* prop) { |
| ASSERT(ObjectLiteralStoreIsMonomorphic(prop)); |
| return Handle<Map>::cast(GetInfo(prop->key()->id())); |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) { |
| return *GetInfo(expr->id()) == |
| isolate_->builtins()->builtin(id); |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) { |
| Handle<Object> object = GetInfo(expr->id()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (!code->is_compare_ic_stub()) return unknown; |
| |
| CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); |
| switch (state) { |
| case CompareIC::UNINITIALIZED: |
| // Uninitialized means never executed. |
| return TypeInfo::Uninitialized(); |
| case CompareIC::SMIS: |
| return TypeInfo::Smi(); |
| case CompareIC::HEAP_NUMBERS: |
| return TypeInfo::Number(); |
| case CompareIC::SYMBOLS: |
| case CompareIC::STRINGS: |
| return TypeInfo::String(); |
| case CompareIC::OBJECTS: |
| case CompareIC::KNOWN_OBJECTS: |
| // TODO(kasperl): We really need a type for JS objects here. |
| return TypeInfo::NonPrimitive(); |
| case CompareIC::GENERIC: |
| default: |
| return unknown; |
| } |
| } |
| |
| |
| bool TypeFeedbackOracle::IsSymbolCompare(CompareOperation* expr) { |
| Handle<Object> object = GetInfo(expr->id()); |
| if (!object->IsCode()) return false; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (!code->is_compare_ic_stub()) return false; |
| CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); |
| return state == CompareIC::SYMBOLS; |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) { |
| Handle<Object> object = GetInfo(expr->id()); |
| if (!object->IsCode()) return Handle<Map>::null(); |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (!code->is_compare_ic_stub()) return Handle<Map>::null(); |
| CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); |
| if (state != CompareIC::KNOWN_OBJECTS) { |
| return Handle<Map>::null(); |
| } |
| Map* first_map = code->FindFirstMap(); |
| ASSERT(first_map != NULL); |
| return CanRetainOtherContext(first_map, *global_context_) |
| ? Handle<Map>::null() |
| : Handle<Map>(first_map); |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) { |
| Handle<Object> object = GetInfo(expr->id()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| ASSERT(code->is_unary_op_stub()); |
| UnaryOpIC::TypeInfo type = static_cast<UnaryOpIC::TypeInfo>( |
| code->unary_op_type()); |
| switch (type) { |
| case UnaryOpIC::SMI: |
| return TypeInfo::Smi(); |
| case UnaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| default: |
| return unknown; |
| } |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) { |
| Handle<Object> object = GetInfo(expr->id()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (code->is_binary_op_stub()) { |
| BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>( |
| code->binary_op_type()); |
| BinaryOpIC::TypeInfo result_type = static_cast<BinaryOpIC::TypeInfo>( |
| code->binary_op_result_type()); |
| |
| switch (type) { |
| case BinaryOpIC::UNINITIALIZED: |
| // Uninitialized means never executed. |
| return TypeInfo::Uninitialized(); |
| case BinaryOpIC::SMI: |
| switch (result_type) { |
| case BinaryOpIC::UNINITIALIZED: |
| if (expr->op() == Token::DIV) { |
| return TypeInfo::Double(); |
| } |
| return TypeInfo::Smi(); |
| case BinaryOpIC::SMI: |
| return TypeInfo::Smi(); |
| case BinaryOpIC::INT32: |
| return TypeInfo::Integer32(); |
| case BinaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| default: |
| return unknown; |
| } |
| case BinaryOpIC::INT32: |
| if (expr->op() == Token::DIV || |
| result_type == BinaryOpIC::HEAP_NUMBER) { |
| return TypeInfo::Double(); |
| } |
| return TypeInfo::Integer32(); |
| case BinaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| case BinaryOpIC::BOTH_STRING: |
| return TypeInfo::String(); |
| case BinaryOpIC::STRING: |
| case BinaryOpIC::GENERIC: |
| return unknown; |
| default: |
| return unknown; |
| } |
| } |
| return unknown; |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) { |
| Handle<Object> object = GetInfo(clause->CompareId()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (!code->is_compare_ic_stub()) return unknown; |
| |
| CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); |
| switch (state) { |
| case CompareIC::UNINITIALIZED: |
| // Uninitialized means never executed. |
| // TODO(fschneider): Introduce a separate value for never-executed ICs. |
| return unknown; |
| case CompareIC::SMIS: |
| return TypeInfo::Smi(); |
| case CompareIC::STRINGS: |
| return TypeInfo::String(); |
| case CompareIC::SYMBOLS: |
| return TypeInfo::Symbol(); |
| case CompareIC::HEAP_NUMBERS: |
| return TypeInfo::Number(); |
| case CompareIC::OBJECTS: |
| case CompareIC::KNOWN_OBJECTS: |
| // TODO(kasperl): We really need a type for JS objects here. |
| return TypeInfo::NonPrimitive(); |
| case CompareIC::GENERIC: |
| default: |
| return unknown; |
| } |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) { |
| Handle<Object> object = GetInfo(expr->CountId()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (!code->is_binary_op_stub()) return unknown; |
| |
| BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>( |
| code->binary_op_type()); |
| switch (type) { |
| case BinaryOpIC::UNINITIALIZED: |
| case BinaryOpIC::SMI: |
| return TypeInfo::Smi(); |
| case BinaryOpIC::INT32: |
| return TypeInfo::Integer32(); |
| case BinaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| case BinaryOpIC::BOTH_STRING: |
| case BinaryOpIC::STRING: |
| case BinaryOpIC::GENERIC: |
| return unknown; |
| default: |
| return unknown; |
| } |
| UNREACHABLE(); |
| return unknown; |
| } |
| |
| |
| void TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id, |
| Handle<String> name, |
| Code::Flags flags, |
| SmallMapList* types) { |
| Handle<Object> object = GetInfo(ast_id); |
| if (object->IsUndefined() || object->IsSmi()) return; |
| |
| if (*object == |
| isolate_->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) { |
| // TODO(fschneider): We could collect the maps and signal that |
| // we need a generic store (or load) here. |
| ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC); |
| } else if (object->IsMap()) { |
| types->Add(Handle<Map>::cast(object)); |
| } else if (FLAG_collect_megamorphic_maps_from_stub_cache && |
| Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) { |
| types->Reserve(4); |
| ASSERT(object->IsCode()); |
| isolate_->stub_cache()->CollectMatchingMaps(types, |
| *name, |
| flags, |
| global_context_); |
| } |
| } |
| |
| |
| // Check if a map originates from a given global context. We use this |
| // information to filter out maps from different context to avoid |
| // retaining objects from different tabs in Chrome via optimized code. |
| bool TypeFeedbackOracle::CanRetainOtherContext(Map* map, |
| Context* global_context) { |
| Object* constructor = NULL; |
| while (!map->prototype()->IsNull()) { |
| constructor = map->constructor(); |
| if (!constructor->IsNull()) { |
| // If the constructor is not null or a JSFunction, we have to |
| // conservatively assume that it may retain a global context. |
| if (!constructor->IsJSFunction()) return true; |
| // Check if the constructor directly references a foreign context. |
| if (CanRetainOtherContext(JSFunction::cast(constructor), |
| global_context)) { |
| return true; |
| } |
| } |
| map = HeapObject::cast(map->prototype())->map(); |
| } |
| constructor = map->constructor(); |
| if (constructor->IsNull()) return false; |
| JSFunction* function = JSFunction::cast(constructor); |
| return CanRetainOtherContext(function, global_context); |
| } |
| |
| |
| bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function, |
| Context* global_context) { |
| return function->context()->global() != global_context->global() |
| && function->context()->global() != global_context->builtins(); |
| } |
| |
| |
| static void AddMapIfMissing(Handle<Map> map, SmallMapList* list) { |
| for (int i = 0; i < list->length(); ++i) { |
| if (list->at(i).is_identical_to(map)) return; |
| } |
| list->Add(map); |
| } |
| |
| |
| void TypeFeedbackOracle::CollectKeyedReceiverTypes(unsigned ast_id, |
| SmallMapList* types) { |
| Handle<Object> object = GetInfo(ast_id); |
| if (!object->IsCode()) return; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (code->kind() == Code::KEYED_LOAD_IC || |
| code->kind() == Code::KEYED_STORE_IC) { |
| AssertNoAllocation no_allocation; |
| int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); |
| for (RelocIterator it(*code, mask); !it.done(); it.next()) { |
| RelocInfo* info = it.rinfo(); |
| Object* object = info->target_object(); |
| if (object->IsMap()) { |
| Map* map = Map::cast(object); |
| if (!CanRetainOtherContext(map, *global_context_)) { |
| AddMapIfMissing(Handle<Map>(map), types); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| byte TypeFeedbackOracle::ToBooleanTypes(unsigned ast_id) { |
| Handle<Object> object = GetInfo(ast_id); |
| return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0; |
| } |
| |
| |
| // Things are a bit tricky here: The iterator for the RelocInfos and the infos |
| // themselves are not GC-safe, so we first get all infos, then we create the |
| // dictionary (possibly triggering GC), and finally we relocate the collected |
| // infos before we process them. |
| void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) { |
| AssertNoAllocation no_allocation; |
| ZoneList<RelocInfo> infos(16); |
| HandleScope scope; |
| GetRelocInfos(code, &infos); |
| CreateDictionary(code, &infos); |
| ProcessRelocInfos(&infos); |
| ProcessTypeFeedbackCells(code); |
| // Allocate handle in the parent scope. |
| dictionary_ = scope.CloseAndEscape(dictionary_); |
| } |
| |
| |
| void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code, |
| ZoneList<RelocInfo>* infos) { |
| int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); |
| for (RelocIterator it(*code, mask); !it.done(); it.next()) { |
| infos->Add(*it.rinfo()); |
| } |
| } |
| |
| |
| void TypeFeedbackOracle::CreateDictionary(Handle<Code> code, |
| ZoneList<RelocInfo>* infos) { |
| DisableAssertNoAllocation allocation_allowed; |
| int cell_count = code->type_feedback_info()->IsTypeFeedbackInfo() |
| ? TypeFeedbackInfo::cast(code->type_feedback_info())-> |
| type_feedback_cells()->CellCount() |
| : 0; |
| int length = infos->length() + cell_count; |
| byte* old_start = code->instruction_start(); |
| dictionary_ = FACTORY->NewUnseededNumberDictionary(length); |
| byte* new_start = code->instruction_start(); |
| RelocateRelocInfos(infos, old_start, new_start); |
| } |
| |
| |
| void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos, |
| byte* old_start, |
| byte* new_start) { |
| for (int i = 0; i < infos->length(); i++) { |
| RelocInfo* info = &(*infos)[i]; |
| info->set_pc(new_start + (info->pc() - old_start)); |
| } |
| } |
| |
| |
| void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) { |
| for (int i = 0; i < infos->length(); i++) { |
| RelocInfo reloc_entry = (*infos)[i]; |
| Address target_address = reloc_entry.target_address(); |
| unsigned ast_id = static_cast<unsigned>((*infos)[i].data()); |
| Code* target = Code::GetCodeFromTargetAddress(target_address); |
| switch (target->kind()) { |
| case Code::LOAD_IC: |
| case Code::STORE_IC: |
| case Code::CALL_IC: |
| case Code::KEYED_CALL_IC: |
| if (target->ic_state() == MONOMORPHIC) { |
| if (target->kind() == Code::CALL_IC && |
| target->check_type() != RECEIVER_MAP_CHECK) { |
| SetInfo(ast_id, Smi::FromInt(target->check_type())); |
| } else { |
| Object* map = target->FindFirstMap(); |
| if (map == NULL) { |
| SetInfo(ast_id, static_cast<Object*>(target)); |
| } else if (!CanRetainOtherContext(Map::cast(map), |
| *global_context_)) { |
| SetInfo(ast_id, map); |
| } |
| } |
| } else { |
| SetInfo(ast_id, target); |
| } |
| break; |
| |
| case Code::KEYED_LOAD_IC: |
| case Code::KEYED_STORE_IC: |
| if (target->ic_state() == MONOMORPHIC || |
| target->ic_state() == MEGAMORPHIC) { |
| SetInfo(ast_id, target); |
| } |
| break; |
| |
| case Code::UNARY_OP_IC: |
| case Code::BINARY_OP_IC: |
| case Code::COMPARE_IC: |
| case Code::TO_BOOLEAN_IC: |
| SetInfo(ast_id, target); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| |
| void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) { |
| Object* raw_info = code->type_feedback_info(); |
| if (!raw_info->IsTypeFeedbackInfo()) return; |
| Handle<TypeFeedbackCells> cache( |
| TypeFeedbackInfo::cast(raw_info)->type_feedback_cells()); |
| for (int i = 0; i < cache->CellCount(); i++) { |
| unsigned ast_id = cache->AstId(i)->value(); |
| Object* value = cache->Cell(i)->value(); |
| if (value->IsSmi() || |
| (value->IsJSFunction() && |
| !CanRetainOtherContext(JSFunction::cast(value), |
| *global_context_))) { |
| SetInfo(ast_id, value); |
| } |
| } |
| } |
| |
| |
| void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) { |
| ASSERT(dictionary_->FindEntry(ast_id) == UnseededNumberDictionary::kNotFound); |
| MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target); |
| USE(maybe_result); |
| #ifdef DEBUG |
| Object* result = NULL; |
| // Dictionary has been allocated with sufficient size for all elements. |
| ASSERT(maybe_result->ToObject(&result)); |
| ASSERT(*dictionary_ == result); |
| #endif |
| } |
| |
| } } // namespace v8::internal |