| // Copyright 2011 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 "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; |
| } |
| |
| |
| STATIC_ASSERT(DEFAULT_STRING_STUB == Code::kNoExtraICState); |
| |
| |
| TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code, |
| Handle<Context> global_context) { |
| global_context_ = global_context; |
| PopulateMap(code); |
| ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue); |
| } |
| |
| |
| Handle<Object> TypeFeedbackOracle::GetInfo(int pos) { |
| int entry = dictionary_->FindEntry(pos); |
| return entry != NumberDictionary::kNotFound |
| ? Handle<Object>(dictionary_->ValueAt(entry)) |
| : Isolate::Current()->factory()->undefined_value(); |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsMonomorphic(Property* expr) { |
| Handle<Object> map_or_code(GetInfo(expr->position())); |
| if (map_or_code->IsMap()) return true; |
| if (map_or_code->IsCode()) { |
| Handle<Code> code(Code::cast(*map_or_code)); |
| return code->kind() == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC && |
| code->FindFirstMap() != NULL; |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::StoreIsMonomorphic(Expression* expr) { |
| Handle<Object> map_or_code(GetInfo(expr->position())); |
| if (map_or_code->IsMap()) return true; |
| if (map_or_code->IsCode()) { |
| Handle<Code> code(Code::cast(*map_or_code)); |
| return code->kind() == Code::KEYED_EXTERNAL_ARRAY_STORE_IC && |
| code->FindFirstMap() != NULL; |
| } |
| return false; |
| } |
| |
| |
| bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) { |
| Handle<Object> value = GetInfo(expr->position()); |
| return value->IsMap() || value->IsSmi(); |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) { |
| ASSERT(LoadIsMonomorphic(expr)); |
| Handle<Object> map_or_code( |
| Handle<HeapObject>::cast(GetInfo(expr->position()))); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code(Code::cast(*map_or_code)); |
| return Handle<Map>(code->FindFirstMap()); |
| } |
| return Handle<Map>(Map::cast(*map_or_code)); |
| } |
| |
| |
| Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) { |
| ASSERT(StoreIsMonomorphic(expr)); |
| Handle<HeapObject> map_or_code( |
| Handle<HeapObject>::cast(GetInfo(expr->position()))); |
| if (map_or_code->IsCode()) { |
| Handle<Code> code(Code::cast(*map_or_code)); |
| return Handle<Map>(code->FindFirstMap()); |
| } |
| return Handle<Map>(Map::cast(*map_or_code)); |
| } |
| |
| |
| ZoneMapList* TypeFeedbackOracle::LoadReceiverTypes(Property* expr, |
| Handle<String> name) { |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); |
| return CollectReceiverTypes(expr->position(), name, flags); |
| } |
| |
| |
| ZoneMapList* TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr, |
| Handle<String> name) { |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL); |
| return CollectReceiverTypes(expr->position(), name, flags); |
| } |
| |
| |
| ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr, |
| Handle<String> name) { |
| int arity = expr->arguments()->length(); |
| // Note: these flags won't let us get maps from stubs with |
| // non-default extra ic state in the megamorphic case. In the more |
| // important monomorphic case the map is obtained directly, so it's |
| // not a problem until we decide to emit more polymorphic code. |
| Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, |
| NORMAL, |
| Code::kNoExtraICState, |
| OWN_MAP, |
| NOT_IN_LOOP, |
| arity); |
| return CollectReceiverTypes(expr->position(), name, flags); |
| } |
| |
| |
| CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) { |
| Handle<Object> value = GetInfo(expr->position()); |
| if (!value->IsSmi()) return RECEIVER_MAP_CHECK; |
| CheckType check = static_cast<CheckType>(Smi::cast(*value)->value()); |
| ASSERT(check != RECEIVER_MAP_CHECK); |
| return check; |
| } |
| |
| ExternalArrayType TypeFeedbackOracle::GetKeyedLoadExternalArrayType( |
| Property* expr) { |
| Handle<Object> stub = GetInfo(expr->position()); |
| ASSERT(stub->IsCode()); |
| return Code::cast(*stub)->external_array_type(); |
| } |
| |
| ExternalArrayType TypeFeedbackOracle::GetKeyedStoreExternalArrayType( |
| Expression* expr) { |
| Handle<Object> stub = GetInfo(expr->position()); |
| ASSERT(stub->IsCode()); |
| return Code::cast(*stub)->external_array_type(); |
| } |
| |
| 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())); |
| } |
| |
| |
| bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) { |
| return *GetInfo(expr->position()) == |
| Isolate::Current()->builtins()->builtin(id); |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) { |
| Handle<Object> object = GetInfo(expr->position()); |
| 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::HEAP_NUMBERS: |
| return TypeInfo::Number(); |
| case CompareIC::OBJECTS: |
| // TODO(kasperl): We really need a type for JS objects here. |
| return TypeInfo::NonPrimitive(); |
| case CompareIC::GENERIC: |
| default: |
| return unknown; |
| } |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) { |
| Handle<Object> object = GetInfo(expr->position()); |
| TypeInfo unknown = TypeInfo::Unknown(); |
| if (!object->IsCode()) return unknown; |
| Handle<Code> code = Handle<Code>::cast(object); |
| if (code->is_type_recording_binary_op_stub()) { |
| TRBinaryOpIC::TypeInfo type = static_cast<TRBinaryOpIC::TypeInfo>( |
| code->type_recording_binary_op_type()); |
| TRBinaryOpIC::TypeInfo result_type = static_cast<TRBinaryOpIC::TypeInfo>( |
| code->type_recording_binary_op_result_type()); |
| |
| switch (type) { |
| case TRBinaryOpIC::UNINITIALIZED: |
| // Uninitialized means never executed. |
| // TODO(fschneider): Introduce a separate value for never-executed ICs |
| return unknown; |
| case TRBinaryOpIC::SMI: |
| switch (result_type) { |
| case TRBinaryOpIC::UNINITIALIZED: |
| case TRBinaryOpIC::SMI: |
| return TypeInfo::Smi(); |
| case TRBinaryOpIC::INT32: |
| return TypeInfo::Integer32(); |
| case TRBinaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| default: |
| return unknown; |
| } |
| case TRBinaryOpIC::INT32: |
| if (expr->op() == Token::DIV || |
| result_type == TRBinaryOpIC::HEAP_NUMBER) { |
| return TypeInfo::Double(); |
| } |
| return TypeInfo::Integer32(); |
| case TRBinaryOpIC::HEAP_NUMBER: |
| return TypeInfo::Double(); |
| case TRBinaryOpIC::STRING: |
| case TRBinaryOpIC::GENERIC: |
| return unknown; |
| default: |
| return unknown; |
| } |
| } |
| return unknown; |
| } |
| |
| |
| TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) { |
| Handle<Object> object = GetInfo(clause->position()); |
| 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::HEAP_NUMBERS: |
| return TypeInfo::Number(); |
| case CompareIC::OBJECTS: |
| // TODO(kasperl): We really need a type for JS objects here. |
| return TypeInfo::NonPrimitive(); |
| case CompareIC::GENERIC: |
| default: |
| return unknown; |
| } |
| } |
| |
| |
| ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(int position, |
| Handle<String> name, |
| Code::Flags flags) { |
| Isolate* isolate = Isolate::Current(); |
| Handle<Object> object = GetInfo(position); |
| if (object->IsUndefined() || object->IsSmi()) return NULL; |
| |
| 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); |
| return NULL; |
| } else if (object->IsMap()) { |
| ZoneMapList* types = new ZoneMapList(1); |
| types->Add(Handle<Map>::cast(object)); |
| return types; |
| } else if (Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) { |
| ZoneMapList* types = new ZoneMapList(4); |
| ASSERT(object->IsCode()); |
| isolate->stub_cache()->CollectMatchingMaps(types, *name, flags); |
| return types->length() > 0 ? types : NULL; |
| } else { |
| return NULL; |
| } |
| } |
| |
| |
| void TypeFeedbackOracle::SetInfo(int position, Object* target) { |
| MaybeObject* maybe_result = dictionary_->AtNumberPut(position, target); |
| USE(maybe_result); |
| #ifdef DEBUG |
| Object* result; |
| // Dictionary has been allocated with sufficient size for all elements. |
| ASSERT(maybe_result->ToObject(&result)); |
| ASSERT(*dictionary_ == result); |
| #endif |
| } |
| |
| |
| void TypeFeedbackOracle::PopulateMap(Handle<Code> code) { |
| Isolate* isolate = Isolate::Current(); |
| HandleScope scope(isolate); |
| |
| const int kInitialCapacity = 16; |
| List<int> code_positions(kInitialCapacity); |
| List<int> source_positions(kInitialCapacity); |
| CollectPositions(*code, &code_positions, &source_positions); |
| |
| ASSERT(dictionary_.is_null()); // Only initialize once. |
| dictionary_ = isolate->factory()->NewNumberDictionary( |
| code_positions.length()); |
| |
| int length = code_positions.length(); |
| ASSERT(source_positions.length() == length); |
| for (int i = 0; i < length; i++) { |
| AssertNoAllocation no_allocation; |
| RelocInfo info(code->instruction_start() + code_positions[i], |
| RelocInfo::CODE_TARGET, 0); |
| Code* target = Code::GetCodeFromTargetAddress(info.target_address()); |
| int position = source_positions[i]; |
| InlineCacheState state = target->ic_state(); |
| Code::Kind kind = target->kind(); |
| |
| if (kind == Code::TYPE_RECORDING_BINARY_OP_IC || |
| kind == Code::COMPARE_IC) { |
| // TODO(kasperl): Avoid having multiple ICs with the same |
| // position by making sure that we have position information |
| // recorded for all binary ICs. |
| int entry = dictionary_->FindEntry(position); |
| if (entry == NumberDictionary::kNotFound) { |
| SetInfo(position, target); |
| } |
| } else if (state == MONOMORPHIC) { |
| if (kind == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC || |
| kind == Code::KEYED_EXTERNAL_ARRAY_STORE_IC) { |
| SetInfo(position, target); |
| } else if (target->kind() != Code::CALL_IC || |
| target->check_type() == RECEIVER_MAP_CHECK) { |
| Map* map = target->FindFirstMap(); |
| if (map == NULL) { |
| SetInfo(position, target); |
| } else { |
| SetInfo(position, map); |
| } |
| } else { |
| ASSERT(target->kind() == Code::CALL_IC); |
| CheckType check = target->check_type(); |
| ASSERT(check != RECEIVER_MAP_CHECK); |
| SetInfo(position, Smi::FromInt(check)); |
| } |
| } else if (state == MEGAMORPHIC) { |
| SetInfo(position, target); |
| } |
| } |
| // Allocate handle in the parent scope. |
| dictionary_ = scope.CloseAndEscape(dictionary_); |
| } |
| |
| |
| void TypeFeedbackOracle::CollectPositions(Code* code, |
| List<int>* code_positions, |
| List<int>* source_positions) { |
| AssertNoAllocation no_allocation; |
| int position = 0; |
| // Because the ICs we use for global variables access in the full |
| // code generator do not have any meaningful positions, we avoid |
| // collecting those by filtering out contextual code targets. |
| int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | |
| RelocInfo::kPositionMask; |
| for (RelocIterator it(code, mask); !it.done(); it.next()) { |
| RelocInfo* info = it.rinfo(); |
| RelocInfo::Mode mode = info->rmode(); |
| if (RelocInfo::IsCodeTarget(mode)) { |
| Code* target = Code::GetCodeFromTargetAddress(info->target_address()); |
| if (target->is_inline_cache_stub()) { |
| InlineCacheState state = target->ic_state(); |
| Code::Kind kind = target->kind(); |
| if (kind == Code::TYPE_RECORDING_BINARY_OP_IC) { |
| if (target->type_recording_binary_op_type() == |
| TRBinaryOpIC::GENERIC) { |
| continue; |
| } |
| } else if (kind == Code::COMPARE_IC) { |
| if (target->compare_state() == CompareIC::GENERIC) continue; |
| } else { |
| if (state != MONOMORPHIC && state != MEGAMORPHIC) continue; |
| } |
| code_positions->Add( |
| static_cast<int>(info->pc() - code->instruction_start())); |
| source_positions->Add(position); |
| } |
| } else { |
| ASSERT(RelocInfo::IsPosition(mode)); |
| position = static_cast<int>(info->data()); |
| } |
| } |
| } |
| |
| } } // namespace v8::internal |