c5a4c39873
Reason for revert: [Sheriff] Fails mozilla with deadcode: http://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20deadcode/builds/5357 Original issue's description: > [crankshaft] Re-add fast-case for string add left/right. > > Now the StringAddStub can optionally convert it's parameters to strings > (following the rules for the addition operator). This could be further > optimized once we have a ToPrimitiveStub, but it should be sufficient > for the moment. > > Also removed the unused Strength parameter to the HStringAdd operator, > because string addition does not depend on language mode. > > CQ_INCLUDE_TRYBOTS=tryserver.v8:v8_win_nosnap_shared_rel,v8_linux_nosnap_dbg > R=mstarzinger@chromium.org > BUG=v8:4307 > LOG=n > > Committed: https://crrev.com/d261849e53fbf8c36efae42d478271f87acff70f > Cr-Commit-Position: refs/heads/master@{#30726} TBR=mstarzinger@chromium.org,jarin@chromium.org,bmeurer@chromium.org NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=v8:4307 Review URL: https://codereview.chromium.org/1347473003 Cr-Commit-Position: refs/heads/master@{#30727}
1060 lines
30 KiB
C++
1060 lines
30 KiB
C++
// Copyright 2012 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/code-stubs.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "src/bootstrapper.h"
|
|
#include "src/cpu-profiler.h"
|
|
#include "src/factory.h"
|
|
#include "src/gdb-jit.h"
|
|
#include "src/ic/handler-compiler.h"
|
|
#include "src/ic/ic.h"
|
|
#include "src/macro-assembler.h"
|
|
#include "src/parser.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
|
|
RUNTIME_FUNCTION(UnexpectedStubMiss) {
|
|
FATAL("Unexpected deopt of a stub");
|
|
return Smi::FromInt(0);
|
|
}
|
|
|
|
|
|
CodeStubDescriptor::CodeStubDescriptor(CodeStub* stub)
|
|
: call_descriptor_(stub->GetCallInterfaceDescriptor()),
|
|
stack_parameter_count_(no_reg),
|
|
hint_stack_parameter_count_(-1),
|
|
function_mode_(NOT_JS_FUNCTION_STUB_MODE),
|
|
deoptimization_handler_(NULL),
|
|
miss_handler_(),
|
|
has_miss_handler_(false) {
|
|
stub->InitializeDescriptor(this);
|
|
}
|
|
|
|
|
|
CodeStubDescriptor::CodeStubDescriptor(Isolate* isolate, uint32_t stub_key)
|
|
: stack_parameter_count_(no_reg),
|
|
hint_stack_parameter_count_(-1),
|
|
function_mode_(NOT_JS_FUNCTION_STUB_MODE),
|
|
deoptimization_handler_(NULL),
|
|
miss_handler_(),
|
|
has_miss_handler_(false) {
|
|
CodeStub::InitializeDescriptor(isolate, stub_key, this);
|
|
}
|
|
|
|
|
|
void CodeStubDescriptor::Initialize(Address deoptimization_handler,
|
|
int hint_stack_parameter_count,
|
|
StubFunctionMode function_mode) {
|
|
deoptimization_handler_ = deoptimization_handler;
|
|
hint_stack_parameter_count_ = hint_stack_parameter_count;
|
|
function_mode_ = function_mode;
|
|
}
|
|
|
|
|
|
void CodeStubDescriptor::Initialize(Register stack_parameter_count,
|
|
Address deoptimization_handler,
|
|
int hint_stack_parameter_count,
|
|
StubFunctionMode function_mode) {
|
|
Initialize(deoptimization_handler, hint_stack_parameter_count, function_mode);
|
|
stack_parameter_count_ = stack_parameter_count;
|
|
}
|
|
|
|
|
|
bool CodeStub::FindCodeInCache(Code** code_out) {
|
|
UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs();
|
|
int index = stubs->FindEntry(GetKey());
|
|
if (index != UnseededNumberDictionary::kNotFound) {
|
|
*code_out = Code::cast(stubs->ValueAt(index));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void CodeStub::RecordCodeGeneration(Handle<Code> code) {
|
|
std::ostringstream os;
|
|
os << *this;
|
|
PROFILE(isolate(),
|
|
CodeCreateEvent(Logger::STUB_TAG, *code, os.str().c_str()));
|
|
Counters* counters = isolate()->counters();
|
|
counters->total_stubs_code_size()->Increment(code->instruction_size());
|
|
#ifdef DEBUG
|
|
code->VerifyEmbeddedObjects();
|
|
#endif
|
|
}
|
|
|
|
|
|
Code::Kind CodeStub::GetCodeKind() const {
|
|
return Code::STUB;
|
|
}
|
|
|
|
|
|
Handle<Code> CodeStub::GetCodeCopy(const Code::FindAndReplacePattern& pattern) {
|
|
Handle<Code> ic = GetCode();
|
|
ic = isolate()->factory()->CopyCode(ic);
|
|
ic->FindAndReplace(pattern);
|
|
RecordCodeGeneration(ic);
|
|
return ic;
|
|
}
|
|
|
|
|
|
Handle<Code> PlatformCodeStub::GenerateCode() {
|
|
Factory* factory = isolate()->factory();
|
|
|
|
// Generate the new code.
|
|
MacroAssembler masm(isolate(), NULL, 256);
|
|
|
|
{
|
|
// Update the static counter each time a new code stub is generated.
|
|
isolate()->counters()->code_stubs()->Increment();
|
|
|
|
// Generate the code for the stub.
|
|
masm.set_generating_stub(true);
|
|
// TODO(yangguo): remove this once we can serialize IC stubs.
|
|
masm.enable_serializer();
|
|
NoCurrentFrameScope scope(&masm);
|
|
Generate(&masm);
|
|
}
|
|
|
|
// Create the code object.
|
|
CodeDesc desc;
|
|
masm.GetCode(&desc);
|
|
// Copy the generated code into a heap object.
|
|
Code::Flags flags = Code::ComputeFlags(
|
|
GetCodeKind(),
|
|
GetICState(),
|
|
GetExtraICState(),
|
|
GetStubType());
|
|
Handle<Code> new_object = factory->NewCode(
|
|
desc, flags, masm.CodeObject(), NeedsImmovableCode());
|
|
return new_object;
|
|
}
|
|
|
|
|
|
Handle<Code> CodeStub::GetCode() {
|
|
Heap* heap = isolate()->heap();
|
|
Code* code;
|
|
if (UseSpecialCache() ? FindCodeInSpecialCache(&code)
|
|
: FindCodeInCache(&code)) {
|
|
DCHECK(GetCodeKind() == code->kind());
|
|
return Handle<Code>(code);
|
|
}
|
|
|
|
{
|
|
HandleScope scope(isolate());
|
|
|
|
Handle<Code> new_object = GenerateCode();
|
|
new_object->set_stub_key(GetKey());
|
|
FinishCode(new_object);
|
|
RecordCodeGeneration(new_object);
|
|
|
|
#ifdef ENABLE_DISASSEMBLER
|
|
if (FLAG_print_code_stubs) {
|
|
CodeTracer::Scope trace_scope(isolate()->GetCodeTracer());
|
|
OFStream os(trace_scope.file());
|
|
std::ostringstream name;
|
|
name << *this;
|
|
new_object->Disassemble(name.str().c_str(), os);
|
|
os << "\n";
|
|
}
|
|
#endif
|
|
|
|
if (UseSpecialCache()) {
|
|
AddToSpecialCache(new_object);
|
|
} else {
|
|
// Update the dictionary and the root in Heap.
|
|
Handle<UnseededNumberDictionary> dict =
|
|
UnseededNumberDictionary::AtNumberPut(
|
|
Handle<UnseededNumberDictionary>(heap->code_stubs()),
|
|
GetKey(),
|
|
new_object);
|
|
heap->SetRootCodeStubs(*dict);
|
|
}
|
|
code = *new_object;
|
|
}
|
|
|
|
Activate(code);
|
|
DCHECK(!NeedsImmovableCode() ||
|
|
heap->lo_space()->Contains(code) ||
|
|
heap->code_space()->FirstPage()->Contains(code->address()));
|
|
return Handle<Code>(code, isolate());
|
|
}
|
|
|
|
|
|
const char* CodeStub::MajorName(CodeStub::Major major_key) {
|
|
switch (major_key) {
|
|
#define DEF_CASE(name) case name: return #name "Stub";
|
|
CODE_STUB_LIST(DEF_CASE)
|
|
#undef DEF_CASE
|
|
case NoCache:
|
|
return "<NoCache>Stub";
|
|
case NUMBER_OF_IDS:
|
|
UNREACHABLE();
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void CodeStub::PrintBaseName(std::ostream& os) const { // NOLINT
|
|
os << MajorName(MajorKey());
|
|
}
|
|
|
|
|
|
void CodeStub::PrintName(std::ostream& os) const { // NOLINT
|
|
PrintBaseName(os);
|
|
PrintState(os);
|
|
}
|
|
|
|
|
|
void CodeStub::Dispatch(Isolate* isolate, uint32_t key, void** value_out,
|
|
DispatchedCall call) {
|
|
switch (MajorKeyFromKey(key)) {
|
|
#define DEF_CASE(NAME) \
|
|
case NAME: { \
|
|
NAME##Stub stub(key, isolate); \
|
|
CodeStub* pstub = &stub; \
|
|
call(pstub, value_out); \
|
|
break; \
|
|
}
|
|
CODE_STUB_LIST(DEF_CASE)
|
|
#undef DEF_CASE
|
|
case NUMBER_OF_IDS:
|
|
case NoCache:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void InitializeDescriptorDispatchedCall(CodeStub* stub,
|
|
void** value_out) {
|
|
CodeStubDescriptor* descriptor_out =
|
|
reinterpret_cast<CodeStubDescriptor*>(value_out);
|
|
stub->InitializeDescriptor(descriptor_out);
|
|
descriptor_out->set_call_descriptor(stub->GetCallInterfaceDescriptor());
|
|
}
|
|
|
|
|
|
void CodeStub::InitializeDescriptor(Isolate* isolate, uint32_t key,
|
|
CodeStubDescriptor* desc) {
|
|
void** value_out = reinterpret_cast<void**>(desc);
|
|
Dispatch(isolate, key, value_out, &InitializeDescriptorDispatchedCall);
|
|
}
|
|
|
|
|
|
void CodeStub::GetCodeDispatchCall(CodeStub* stub, void** value_out) {
|
|
Handle<Code>* code_out = reinterpret_cast<Handle<Code>*>(value_out);
|
|
// Code stubs with special cache cannot be recreated from stub key.
|
|
*code_out = stub->UseSpecialCache() ? Handle<Code>() : stub->GetCode();
|
|
}
|
|
|
|
|
|
MaybeHandle<Code> CodeStub::GetCode(Isolate* isolate, uint32_t key) {
|
|
HandleScope scope(isolate);
|
|
Handle<Code> code;
|
|
void** value_out = reinterpret_cast<void**>(&code);
|
|
Dispatch(isolate, key, value_out, &GetCodeDispatchCall);
|
|
return scope.CloseAndEscape(code);
|
|
}
|
|
|
|
|
|
// static
|
|
void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
// Generate the uninitialized versions of the stub.
|
|
for (int op = Token::BIT_OR; op <= Token::MOD; ++op) {
|
|
BinaryOpICStub stub(isolate, static_cast<Token::Value>(op), Strength::WEAK);
|
|
stub.GetCode();
|
|
}
|
|
|
|
// Generate special versions of the stub.
|
|
BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime);
|
|
}
|
|
|
|
|
|
void BinaryOpICStub::PrintState(std::ostream& os) const { // NOLINT
|
|
os << state();
|
|
}
|
|
|
|
|
|
// static
|
|
void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate,
|
|
const BinaryOpICState& state) {
|
|
BinaryOpICStub stub(isolate, state);
|
|
stub.GetCode();
|
|
}
|
|
|
|
|
|
// static
|
|
void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
// Generate special versions of the stub.
|
|
BinaryOpICState::GenerateAheadOfTime(isolate, &GenerateAheadOfTime);
|
|
}
|
|
|
|
|
|
void BinaryOpICWithAllocationSiteStub::PrintState(
|
|
std::ostream& os) const { // NOLINT
|
|
os << state();
|
|
}
|
|
|
|
|
|
// static
|
|
void BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(
|
|
Isolate* isolate, const BinaryOpICState& state) {
|
|
if (state.CouldCreateAllocationMementos()) {
|
|
BinaryOpICWithAllocationSiteStub stub(isolate, state);
|
|
stub.GetCode();
|
|
}
|
|
}
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const StringAddFlags& flags) {
|
|
switch (flags) {
|
|
case STRING_ADD_CHECK_NONE:
|
|
return os << "CheckNone";
|
|
case STRING_ADD_CHECK_LEFT:
|
|
return os << "CheckLeft";
|
|
case STRING_ADD_CHECK_RIGHT:
|
|
return os << "CheckRight";
|
|
case STRING_ADD_CHECK_BOTH:
|
|
return os << "CheckBoth";
|
|
}
|
|
UNREACHABLE();
|
|
return os;
|
|
}
|
|
|
|
|
|
void StringAddStub::PrintBaseName(std::ostream& os) const { // NOLINT
|
|
os << "StringAddStub_" << flags() << "_" << pretenure_flag();
|
|
}
|
|
|
|
|
|
void StringAddTFStub::PrintBaseName(std::ostream& os) const { // NOLINT
|
|
os << "StringAddTFStub_" << flags() << "_" << pretenure_flag();
|
|
}
|
|
|
|
|
|
InlineCacheState CompareICStub::GetICState() const {
|
|
CompareICState::State state = Max(left(), right());
|
|
switch (state) {
|
|
case CompareICState::UNINITIALIZED:
|
|
return ::v8::internal::UNINITIALIZED;
|
|
case CompareICState::SMI:
|
|
case CompareICState::NUMBER:
|
|
case CompareICState::INTERNALIZED_STRING:
|
|
case CompareICState::STRING:
|
|
case CompareICState::UNIQUE_NAME:
|
|
case CompareICState::OBJECT:
|
|
case CompareICState::KNOWN_OBJECT:
|
|
return MONOMORPHIC;
|
|
case CompareICState::GENERIC:
|
|
return ::v8::internal::GENERIC;
|
|
}
|
|
UNREACHABLE();
|
|
return ::v8::internal::UNINITIALIZED;
|
|
}
|
|
|
|
|
|
Condition CompareICStub::GetCondition() const {
|
|
return CompareIC::ComputeCondition(op());
|
|
}
|
|
|
|
|
|
void CompareICStub::AddToSpecialCache(Handle<Code> new_object) {
|
|
DCHECK(*known_map_ != NULL);
|
|
Isolate* isolate = new_object->GetIsolate();
|
|
Factory* factory = isolate->factory();
|
|
return Map::UpdateCodeCache(known_map_,
|
|
strict() ?
|
|
factory->strict_compare_ic_string() :
|
|
factory->compare_ic_string(),
|
|
new_object);
|
|
}
|
|
|
|
|
|
bool CompareICStub::FindCodeInSpecialCache(Code** code_out) {
|
|
Factory* factory = isolate()->factory();
|
|
Code::Flags flags = Code::ComputeFlags(
|
|
GetCodeKind(),
|
|
UNINITIALIZED);
|
|
DCHECK(op() == Token::EQ || op() == Token::EQ_STRICT);
|
|
Handle<Object> probe(
|
|
known_map_->FindInCodeCache(
|
|
strict() ?
|
|
*factory->strict_compare_ic_string() :
|
|
*factory->compare_ic_string(),
|
|
flags),
|
|
isolate());
|
|
if (probe->IsCode()) {
|
|
*code_out = Code::cast(*probe);
|
|
#ifdef DEBUG
|
|
CompareICStub decode((*code_out)->stub_key(), isolate());
|
|
DCHECK(op() == decode.op());
|
|
DCHECK(left() == decode.left());
|
|
DCHECK(right() == decode.right());
|
|
DCHECK(state() == decode.state());
|
|
#endif
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void CompareICStub::Generate(MacroAssembler* masm) {
|
|
switch (state()) {
|
|
case CompareICState::UNINITIALIZED:
|
|
GenerateMiss(masm);
|
|
break;
|
|
case CompareICState::SMI:
|
|
GenerateSmis(masm);
|
|
break;
|
|
case CompareICState::NUMBER:
|
|
GenerateNumbers(masm);
|
|
break;
|
|
case CompareICState::STRING:
|
|
GenerateStrings(masm);
|
|
break;
|
|
case CompareICState::INTERNALIZED_STRING:
|
|
GenerateInternalizedStrings(masm);
|
|
break;
|
|
case CompareICState::UNIQUE_NAME:
|
|
GenerateUniqueNames(masm);
|
|
break;
|
|
case CompareICState::OBJECT:
|
|
GenerateObjects(masm);
|
|
break;
|
|
case CompareICState::KNOWN_OBJECT:
|
|
DCHECK(*known_map_ != NULL);
|
|
GenerateKnownObjects(masm);
|
|
break;
|
|
case CompareICState::GENERIC:
|
|
GenerateGeneric(masm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void CompareNilICStub::UpdateStatus(Handle<Object> object) {
|
|
State state = this->state();
|
|
DCHECK(!state.Contains(GENERIC));
|
|
State old_state = state;
|
|
if (object->IsNull()) {
|
|
state.Add(NULL_TYPE);
|
|
} else if (object->IsUndefined()) {
|
|
state.Add(UNDEFINED);
|
|
} else if (object->IsUndetectableObject() ||
|
|
object->IsOddball() ||
|
|
!object->IsHeapObject()) {
|
|
state.RemoveAll();
|
|
state.Add(GENERIC);
|
|
} else if (IsMonomorphic()) {
|
|
state.RemoveAll();
|
|
state.Add(GENERIC);
|
|
} else {
|
|
state.Add(MONOMORPHIC_MAP);
|
|
}
|
|
TraceTransition(old_state, state);
|
|
set_sub_minor_key(TypesBits::update(sub_minor_key(), state.ToIntegral()));
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
Handle<JSFunction> GetFunction(Isolate* isolate, const char* name) {
|
|
v8::ExtensionConfiguration no_extensions;
|
|
MaybeHandle<Object> fun = Object::GetProperty(
|
|
isolate, isolate->factory()->code_stub_exports_object(), name);
|
|
Handle<JSFunction> function = Handle<JSFunction>::cast(fun.ToHandleChecked());
|
|
DCHECK(!function->IsUndefined() &&
|
|
"JavaScript implementation of stub not found");
|
|
return function;
|
|
}
|
|
} // namespace
|
|
|
|
|
|
Handle<Code> TurboFanCodeStub::GenerateCode() {
|
|
// Get the outer ("stub generator") function.
|
|
const char* name = CodeStub::MajorName(MajorKey());
|
|
Handle<JSFunction> outer = GetFunction(isolate(), name);
|
|
DCHECK_EQ(2, outer->shared()->length());
|
|
|
|
// Invoke the outer function to get the stub itself.
|
|
Factory* factory = isolate()->factory();
|
|
Handle<Object> call_conv = factory->InternalizeUtf8String(name);
|
|
Handle<Object> minor_key = factory->NewNumber(MinorKey());
|
|
Handle<Object> args[] = {call_conv, minor_key};
|
|
MaybeHandle<Object> result = Execution::Call(
|
|
isolate(), outer, factory->undefined_value(), 2, args, false);
|
|
Handle<JSFunction> inner = Handle<JSFunction>::cast(result.ToHandleChecked());
|
|
// Just to make sure nobody calls this...
|
|
inner->set_code(isolate()->builtins()->builtin(Builtins::kIllegal));
|
|
|
|
return Compiler::GetStubCode(inner, this).ToHandleChecked();
|
|
}
|
|
|
|
|
|
template<class StateType>
|
|
void HydrogenCodeStub::TraceTransition(StateType from, StateType to) {
|
|
// Note: Although a no-op transition is semantically OK, it is hinting at a
|
|
// bug somewhere in our state transition machinery.
|
|
DCHECK(from != to);
|
|
if (!FLAG_trace_ic) return;
|
|
OFStream os(stdout);
|
|
os << "[";
|
|
PrintBaseName(os);
|
|
os << ": " << from << "=>" << to << "]" << std::endl;
|
|
}
|
|
|
|
|
|
void CompareNilICStub::PrintBaseName(std::ostream& os) const { // NOLINT
|
|
CodeStub::PrintBaseName(os);
|
|
os << ((nil_value() == kNullValue) ? "(NullValue)" : "(UndefinedValue)");
|
|
}
|
|
|
|
|
|
void CompareNilICStub::PrintState(std::ostream& os) const { // NOLINT
|
|
os << state();
|
|
}
|
|
|
|
|
|
// TODO(svenpanne) Make this a real infix_ostream_iterator.
|
|
class SimpleListPrinter {
|
|
public:
|
|
explicit SimpleListPrinter(std::ostream& os) : os_(os), first_(true) {}
|
|
|
|
void Add(const char* s) {
|
|
if (first_) {
|
|
first_ = false;
|
|
} else {
|
|
os_ << ",";
|
|
}
|
|
os_ << s;
|
|
}
|
|
|
|
private:
|
|
std::ostream& os_;
|
|
bool first_;
|
|
};
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const CompareNilICStub::State& s) {
|
|
os << "(";
|
|
SimpleListPrinter p(os);
|
|
if (s.IsEmpty()) p.Add("None");
|
|
if (s.Contains(CompareNilICStub::UNDEFINED)) p.Add("Undefined");
|
|
if (s.Contains(CompareNilICStub::NULL_TYPE)) p.Add("Null");
|
|
if (s.Contains(CompareNilICStub::MONOMORPHIC_MAP)) p.Add("MonomorphicMap");
|
|
if (s.Contains(CompareNilICStub::GENERIC)) p.Add("Generic");
|
|
return os << ")";
|
|
}
|
|
|
|
|
|
Type* CompareNilICStub::GetType(Zone* zone, Handle<Map> map) {
|
|
State state = this->state();
|
|
if (state.Contains(CompareNilICStub::GENERIC)) return Type::Any(zone);
|
|
|
|
Type* result = Type::None(zone);
|
|
if (state.Contains(CompareNilICStub::UNDEFINED)) {
|
|
result = Type::Union(result, Type::Undefined(zone), zone);
|
|
}
|
|
if (state.Contains(CompareNilICStub::NULL_TYPE)) {
|
|
result = Type::Union(result, Type::Null(zone), zone);
|
|
}
|
|
if (state.Contains(CompareNilICStub::MONOMORPHIC_MAP)) {
|
|
Type* type =
|
|
map.is_null() ? Type::Detectable(zone) : Type::Class(map, zone);
|
|
result = Type::Union(result, type, zone);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
Type* CompareNilICStub::GetInputType(Zone* zone, Handle<Map> map) {
|
|
Type* output_type = GetType(zone, map);
|
|
Type* nil_type =
|
|
nil_value() == kNullValue ? Type::Null(zone) : Type::Undefined(zone);
|
|
return Type::Union(output_type, nil_type, zone);
|
|
}
|
|
|
|
|
|
void CallICStub::PrintState(std::ostream& os) const { // NOLINT
|
|
os << state();
|
|
}
|
|
|
|
|
|
void JSEntryStub::FinishCode(Handle<Code> code) {
|
|
Handle<FixedArray> handler_table =
|
|
code->GetIsolate()->factory()->NewFixedArray(1, TENURED);
|
|
handler_table->set(0, Smi::FromInt(handler_offset_));
|
|
code->set_handler_table(*handler_table);
|
|
}
|
|
|
|
|
|
void LoadDictionaryElementStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_KeyedLoadIC_MissFromStubFailure));
|
|
}
|
|
|
|
|
|
void KeyedLoadGenericStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(is_strong(language_mode())
|
|
? Runtime::kKeyedGetPropertyStrong
|
|
: Runtime::kKeyedGetProperty)->entry);
|
|
}
|
|
|
|
|
|
void HandlerStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
if (kind() == Code::STORE_IC) {
|
|
descriptor->Initialize(FUNCTION_ADDR(Runtime_StoreIC_MissFromStubFailure));
|
|
} else if (kind() == Code::KEYED_LOAD_IC) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_KeyedLoadIC_MissFromStubFailure));
|
|
} else if (kind() == Code::KEYED_STORE_IC) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_KeyedStoreIC_MissFromStubFailure));
|
|
}
|
|
}
|
|
|
|
|
|
CallInterfaceDescriptor HandlerStub::GetCallInterfaceDescriptor() const {
|
|
if (kind() == Code::LOAD_IC || kind() == Code::KEYED_LOAD_IC) {
|
|
return LoadWithVectorDescriptor(isolate());
|
|
} else {
|
|
DCHECK(kind() == Code::STORE_IC || kind() == Code::KEYED_STORE_IC);
|
|
return FLAG_vector_stores ? VectorStoreICDescriptor(isolate())
|
|
: StoreDescriptor(isolate());
|
|
}
|
|
}
|
|
|
|
|
|
void StoreFastElementStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_KeyedStoreIC_MissFromStubFailure));
|
|
}
|
|
|
|
|
|
void ElementsTransitionAndStoreStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_ElementsTransitionAndStoreIC_Miss));
|
|
}
|
|
|
|
|
|
void ToObjectStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(Runtime::FunctionForId(Runtime::kToObject)->entry);
|
|
}
|
|
|
|
|
|
CallInterfaceDescriptor StoreTransitionStub::GetCallInterfaceDescriptor()
|
|
const {
|
|
if (FLAG_vector_stores) {
|
|
return VectorStoreTransitionDescriptor(isolate());
|
|
}
|
|
return StoreTransitionDescriptor(isolate());
|
|
}
|
|
|
|
|
|
CallInterfaceDescriptor
|
|
ElementsTransitionAndStoreStub::GetCallInterfaceDescriptor() const {
|
|
if (FLAG_vector_stores) {
|
|
return VectorStoreTransitionDescriptor(isolate());
|
|
}
|
|
return StoreTransitionDescriptor(isolate());
|
|
}
|
|
|
|
|
|
void FastNewClosureStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(Runtime::FunctionForId(Runtime::kNewClosure)->entry);
|
|
}
|
|
|
|
|
|
void FastNewContextStub::InitializeDescriptor(CodeStubDescriptor* d) {}
|
|
|
|
|
|
void TypeofStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {}
|
|
|
|
|
|
void NumberToStringStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
NumberToStringDescriptor call_descriptor(isolate());
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kNumberToString)->entry);
|
|
}
|
|
|
|
|
|
void FastCloneShallowArrayStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
FastCloneShallowArrayDescriptor call_descriptor(isolate());
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kCreateArrayLiteralStubBailout)->entry);
|
|
}
|
|
|
|
|
|
void FastCloneShallowObjectStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
FastCloneShallowObjectDescriptor call_descriptor(isolate());
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kCreateObjectLiteral)->entry);
|
|
}
|
|
|
|
|
|
void CreateAllocationSiteStub::InitializeDescriptor(CodeStubDescriptor* d) {}
|
|
|
|
|
|
void CreateWeakCellStub::InitializeDescriptor(CodeStubDescriptor* d) {}
|
|
|
|
|
|
void RegExpConstructResultStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kRegExpConstructResult)->entry);
|
|
}
|
|
|
|
|
|
void TransitionElementsKindStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry);
|
|
}
|
|
|
|
|
|
void AllocateHeapNumberStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kAllocateHeapNumber)->entry);
|
|
}
|
|
|
|
|
|
void CompareNilICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(FUNCTION_ADDR(Runtime_CompareNilIC_Miss));
|
|
descriptor->SetMissHandler(ExternalReference(
|
|
Runtime::FunctionForId(Runtime::kCompareNilIC_Miss), isolate()));
|
|
}
|
|
|
|
|
|
void ToBooleanStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(FUNCTION_ADDR(Runtime_ToBooleanIC_Miss));
|
|
descriptor->SetMissHandler(ExternalReference(
|
|
Runtime::FunctionForId(Runtime::kToBooleanIC_Miss), isolate()));
|
|
}
|
|
|
|
|
|
void BinaryOpICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(FUNCTION_ADDR(Runtime_BinaryOpIC_Miss));
|
|
descriptor->SetMissHandler(ExternalReference(
|
|
Runtime::FunctionForId(Runtime::kBinaryOpIC_Miss), isolate()));
|
|
}
|
|
|
|
|
|
void BinaryOpWithAllocationSiteStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
FUNCTION_ADDR(Runtime_BinaryOpIC_MissWithAllocationSite));
|
|
}
|
|
|
|
|
|
void StringAddStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(Runtime::FunctionForId(Runtime::kStringAdd)->entry);
|
|
}
|
|
|
|
|
|
void GrowArrayElementsStub::InitializeDescriptor(
|
|
CodeStubDescriptor* descriptor) {
|
|
descriptor->Initialize(
|
|
Runtime::FunctionForId(Runtime::kGrowArrayElements)->entry);
|
|
}
|
|
|
|
|
|
void TypeofStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
TypeofStub stub(isolate);
|
|
stub.GetCode();
|
|
}
|
|
|
|
|
|
void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
CreateAllocationSiteStub stub(isolate);
|
|
stub.GetCode();
|
|
}
|
|
|
|
|
|
void CreateWeakCellStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
CreateWeakCellStub stub(isolate);
|
|
stub.GetCode();
|
|
}
|
|
|
|
|
|
void StoreElementStub::Generate(MacroAssembler* masm) {
|
|
switch (elements_kind()) {
|
|
case FAST_ELEMENTS:
|
|
case FAST_HOLEY_ELEMENTS:
|
|
case FAST_SMI_ELEMENTS:
|
|
case FAST_HOLEY_SMI_ELEMENTS:
|
|
case FAST_DOUBLE_ELEMENTS:
|
|
case FAST_HOLEY_DOUBLE_ELEMENTS:
|
|
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
|
case TYPE##_ELEMENTS:
|
|
|
|
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
|
#undef TYPED_ARRAY_CASE
|
|
UNREACHABLE();
|
|
break;
|
|
case DICTIONARY_ELEMENTS:
|
|
ElementHandlerCompiler::GenerateStoreSlow(masm);
|
|
break;
|
|
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
|
|
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void StoreFastElementStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS, STANDARD_STORE)
|
|
.GetCode();
|
|
StoreFastElementStub(isolate, false, FAST_HOLEY_ELEMENTS,
|
|
STORE_AND_GROW_NO_TRANSITION).GetCode();
|
|
for (int i = FIRST_FAST_ELEMENTS_KIND; i <= LAST_FAST_ELEMENTS_KIND; i++) {
|
|
ElementsKind kind = static_cast<ElementsKind>(i);
|
|
StoreFastElementStub(isolate, true, kind, STANDARD_STORE).GetCode();
|
|
StoreFastElementStub(isolate, true, kind, STORE_AND_GROW_NO_TRANSITION)
|
|
.GetCode();
|
|
}
|
|
}
|
|
|
|
|
|
void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
|
|
switch (type()) {
|
|
case READ_ELEMENT:
|
|
GenerateReadElement(masm);
|
|
break;
|
|
case NEW_SLOPPY_FAST:
|
|
GenerateNewSloppyFast(masm);
|
|
break;
|
|
case NEW_SLOPPY_SLOW:
|
|
GenerateNewSloppySlow(masm);
|
|
break;
|
|
case NEW_STRICT:
|
|
GenerateNewStrict(masm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void ArgumentsAccessStub::PrintName(std::ostream& os) const { // NOLINT
|
|
os << "ArgumentsAccessStub_";
|
|
switch (type()) {
|
|
case READ_ELEMENT:
|
|
os << "ReadElement";
|
|
break;
|
|
case NEW_SLOPPY_FAST:
|
|
os << "NewSloppyFast";
|
|
break;
|
|
case NEW_SLOPPY_SLOW:
|
|
os << "NewSloppySlow";
|
|
break;
|
|
case NEW_STRICT:
|
|
os << "NewStrict";
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void CallFunctionStub::PrintName(std::ostream& os) const { // NOLINT
|
|
os << "CallFunctionStub_Args" << argc();
|
|
}
|
|
|
|
|
|
void CallConstructStub::PrintName(std::ostream& os) const { // NOLINT
|
|
os << "CallConstructStub";
|
|
if (RecordCallTarget()) os << "_Recording";
|
|
}
|
|
|
|
|
|
void ArrayConstructorStub::PrintName(std::ostream& os) const { // NOLINT
|
|
os << "ArrayConstructorStub";
|
|
switch (argument_count()) {
|
|
case ANY:
|
|
os << "_Any";
|
|
break;
|
|
case NONE:
|
|
os << "_None";
|
|
break;
|
|
case ONE:
|
|
os << "_One";
|
|
break;
|
|
case MORE_THAN_ONE:
|
|
os << "_More_Than_One";
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
std::ostream& ArrayConstructorStubBase::BasePrintName(
|
|
std::ostream& os, // NOLINT
|
|
const char* name) const {
|
|
os << name << "_" << ElementsKindToString(elements_kind());
|
|
if (override_mode() == DISABLE_ALLOCATION_SITES) {
|
|
os << "_DISABLE_ALLOCATION_SITES";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
|
|
bool ToBooleanStub::UpdateStatus(Handle<Object> object) {
|
|
Types new_types = types();
|
|
Types old_types = new_types;
|
|
bool to_boolean_value = new_types.UpdateStatus(object);
|
|
TraceTransition(old_types, new_types);
|
|
set_sub_minor_key(TypesBits::update(sub_minor_key(), new_types.ToIntegral()));
|
|
return to_boolean_value;
|
|
}
|
|
|
|
|
|
void ToBooleanStub::PrintState(std::ostream& os) const { // NOLINT
|
|
os << types();
|
|
}
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const ToBooleanStub::Types& s) {
|
|
os << "(";
|
|
SimpleListPrinter p(os);
|
|
if (s.IsEmpty()) p.Add("None");
|
|
if (s.Contains(ToBooleanStub::UNDEFINED)) p.Add("Undefined");
|
|
if (s.Contains(ToBooleanStub::BOOLEAN)) p.Add("Bool");
|
|
if (s.Contains(ToBooleanStub::NULL_TYPE)) p.Add("Null");
|
|
if (s.Contains(ToBooleanStub::SMI)) p.Add("Smi");
|
|
if (s.Contains(ToBooleanStub::SPEC_OBJECT)) p.Add("SpecObject");
|
|
if (s.Contains(ToBooleanStub::STRING)) p.Add("String");
|
|
if (s.Contains(ToBooleanStub::SYMBOL)) p.Add("Symbol");
|
|
if (s.Contains(ToBooleanStub::HEAP_NUMBER)) p.Add("HeapNumber");
|
|
if (s.Contains(ToBooleanStub::SIMD_VALUE)) p.Add("SimdValue");
|
|
return os << ")";
|
|
}
|
|
|
|
|
|
bool ToBooleanStub::Types::UpdateStatus(Handle<Object> object) {
|
|
if (object->IsUndefined()) {
|
|
Add(UNDEFINED);
|
|
return false;
|
|
} else if (object->IsBoolean()) {
|
|
Add(BOOLEAN);
|
|
return object->IsTrue();
|
|
} else if (object->IsNull()) {
|
|
Add(NULL_TYPE);
|
|
return false;
|
|
} else if (object->IsSmi()) {
|
|
Add(SMI);
|
|
return Smi::cast(*object)->value() != 0;
|
|
} else if (object->IsSpecObject()) {
|
|
Add(SPEC_OBJECT);
|
|
return !object->IsUndetectableObject();
|
|
} else if (object->IsString()) {
|
|
Add(STRING);
|
|
return !object->IsUndetectableObject() &&
|
|
String::cast(*object)->length() != 0;
|
|
} else if (object->IsSymbol()) {
|
|
Add(SYMBOL);
|
|
return true;
|
|
} else if (object->IsHeapNumber()) {
|
|
DCHECK(!object->IsUndetectableObject());
|
|
Add(HEAP_NUMBER);
|
|
double value = HeapNumber::cast(*object)->value();
|
|
return value != 0 && !std::isnan(value);
|
|
} else if (object->IsSimd128Value()) {
|
|
Add(SIMD_VALUE);
|
|
return true;
|
|
} else {
|
|
// We should never see an internal object at runtime here!
|
|
UNREACHABLE();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
bool ToBooleanStub::Types::NeedsMap() const {
|
|
return Contains(ToBooleanStub::SPEC_OBJECT) ||
|
|
Contains(ToBooleanStub::STRING) || Contains(ToBooleanStub::SYMBOL) ||
|
|
Contains(ToBooleanStub::HEAP_NUMBER) ||
|
|
Contains(ToBooleanStub::SIMD_VALUE);
|
|
}
|
|
|
|
|
|
void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) {
|
|
StubFailureTrampolineStub stub1(isolate, NOT_JS_FUNCTION_STUB_MODE);
|
|
StubFailureTrampolineStub stub2(isolate, JS_FUNCTION_STUB_MODE);
|
|
stub1.GetCode();
|
|
stub2.GetCode();
|
|
}
|
|
|
|
|
|
void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function,
|
|
intptr_t stack_pointer,
|
|
Isolate* isolate) {
|
|
FunctionEntryHook entry_hook = isolate->function_entry_hook();
|
|
DCHECK(entry_hook != NULL);
|
|
entry_hook(function, stack_pointer);
|
|
}
|
|
|
|
|
|
ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate)
|
|
: PlatformCodeStub(isolate) {
|
|
minor_key_ = ArgumentCountBits::encode(ANY);
|
|
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
|
|
}
|
|
|
|
|
|
ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate,
|
|
int argument_count)
|
|
: PlatformCodeStub(isolate) {
|
|
if (argument_count == 0) {
|
|
minor_key_ = ArgumentCountBits::encode(NONE);
|
|
} else if (argument_count == 1) {
|
|
minor_key_ = ArgumentCountBits::encode(ONE);
|
|
} else if (argument_count >= 2) {
|
|
minor_key_ = ArgumentCountBits::encode(MORE_THAN_ONE);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
|
|
}
|
|
|
|
|
|
InternalArrayConstructorStub::InternalArrayConstructorStub(
|
|
Isolate* isolate) : PlatformCodeStub(isolate) {
|
|
InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
|
|
}
|
|
|
|
|
|
Representation RepresentationFromType(Type* type) {
|
|
if (type->Is(Type::UntaggedSigned()) || type->Is(Type::UntaggedUnsigned())) {
|
|
return Representation::Integer32();
|
|
}
|
|
|
|
if (type->Is(Type::TaggedSigned())) {
|
|
return Representation::Smi();
|
|
}
|
|
|
|
if (type->Is(Type::UntaggedPointer())) {
|
|
return Representation::External();
|
|
}
|
|
|
|
DCHECK(!type->Is(Type::Untagged()));
|
|
return Representation::Tagged();
|
|
}
|
|
} // namespace internal
|
|
} // namespace v8
|