blob: 46376dd6a84969678bc3633e69e794fa0d70bf51 [file] [log] [blame]
// Copyright 2016 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.
#ifndef V8_SRC_IC_ACCESSOR_ASSEMBLER_H_
#define V8_SRC_IC_ACCESSOR_ASSEMBLER_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
class CodeAssemblerState;
}
class ExitPoint;
class AccessorAssembler : public CodeStubAssembler {
public:
typedef compiler::Node Node;
explicit AccessorAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void GenerateLoadIC();
void GenerateLoadIC_Noninlined();
void GenerateLoadIC_Uninitialized();
void GenerateLoadField();
void GenerateLoadICTrampoline();
void GenerateKeyedLoadIC();
void GenerateKeyedLoadICTrampoline();
void GenerateKeyedLoadIC_Megamorphic();
void GenerateKeyedLoadIC_PolymorphicName();
void GenerateStoreIC();
void GenerateStoreICTrampoline();
void GenerateStoreGlobalIC();
void GenerateStoreGlobalICTrampoline();
void GenerateLoadGlobalIC(TypeofMode typeof_mode);
void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode);
void GenerateKeyedStoreIC();
void GenerateKeyedStoreICTrampoline();
void TryProbeStubCache(StubCache* stub_cache, Node* receiver, Node* name,
Label* if_handler, Variable* var_handler,
Label* if_miss);
Node* StubCachePrimaryOffsetForTesting(Node* name, Node* map) {
return StubCachePrimaryOffset(name, map);
}
Node* StubCacheSecondaryOffsetForTesting(Node* name, Node* map) {
return StubCacheSecondaryOffset(name, map);
}
struct LoadICParameters {
LoadICParameters(Node* context, Node* receiver, Node* name, Node* slot,
Node* vector, Node* holder = nullptr)
: context(context),
receiver(receiver),
name(name),
slot(slot),
vector(vector),
holder(holder ? holder : receiver) {}
Node* context;
Node* receiver;
Node* name;
Node* slot;
Node* vector;
Node* holder;
};
void LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot,
const LazyNode<Context>& lazy_context,
const LazyNode<Name>& lazy_name, TypeofMode typeof_mode,
ExitPoint* exit_point,
ParameterMode slot_mode = SMI_PARAMETERS);
// Specialized LoadIC for inlined bytecode handler, hand-tuned to omit frame
// construction on common paths.
void LoadIC_BytecodeHandler(const LoadICParameters* p, ExitPoint* exit_point);
// Loads dataX field from the DataHandler object.
Node* LoadHandlerDataField(Node* handler, int data_index);
protected:
struct StoreICParameters : public LoadICParameters {
StoreICParameters(Node* context, Node* receiver, Node* name, Node* value,
Node* slot, Node* vector)
: LoadICParameters(context, receiver, name, slot, vector),
value(value) {}
Node* value;
};
enum class ICMode { kNonGlobalIC, kGlobalIC };
enum ElementSupport { kOnlyProperties, kSupportElements };
void HandleStoreICHandlerCase(
const StoreICParameters* p, Node* handler, Label* miss, ICMode ic_mode,
ElementSupport support_elements = kOnlyProperties);
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
void BranchIfStrictMode(Node* vector, Node* slot, Label* if_strict);
void InvalidateValidityCellIfPrototype(Node* map, Node* bitfield2 = nullptr);
private:
// Stub generation entry points.
// LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains
// logic not inlined into Ignition bytecode handlers.
void LoadIC(const LoadICParameters* p);
void LoadIC_Noninlined(const LoadICParameters* p, Node* receiver_map,
Node* feedback, Variable* var_handler,
Label* if_handler, Label* miss, ExitPoint* exit_point);
Node* LoadDescriptorValue(Node* map, Node* descriptor);
void LoadIC_Uninitialized(const LoadICParameters* p);
void KeyedLoadIC(const LoadICParameters* p);
void KeyedLoadICGeneric(const LoadICParameters* p);
void KeyedLoadICPolymorphicName(const LoadICParameters* p);
void StoreIC(const StoreICParameters* p);
void StoreGlobalIC(const StoreICParameters* p);
void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value,
ExitPoint* exit_point, Label* miss);
void KeyedStoreIC(const StoreICParameters* p);
// IC dispatcher behavior.
// Checks monomorphic case. Returns {feedback} entry of the vector.
Node* TryMonomorphicCase(Node* slot, Node* vector, Node* receiver_map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
void HandlePolymorphicCase(Node* receiver_map, Node* feedback,
Label* if_handler, Variable* var_handler,
Label* if_miss, int min_feedback_capacity);
// LoadIC implementation.
enum class OnNonExistent { kThrowReferenceError, kReturnUndefined };
void HandleLoadICHandlerCase(
const LoadICParameters* p, Node* handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC,
OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined,
ElementSupport support_elements = kOnlyProperties);
void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder,
Node* smi_handler, Node* handler, Label* miss,
ExitPoint* exit_point,
OnNonExistent on_nonexistent,
ElementSupport support_elements);
void HandleLoadICProtoHandler(const LoadICParameters* p, Node* handler,
Variable* var_holder, Variable* var_smi_handler,
Label* if_smi_handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode);
void HandleLoadField(Node* holder, Node* handler_word,
Variable* var_double_value, Label* rebox_double,
ExitPoint* exit_point);
void EmitAccessCheck(Node* expected_native_context, Node* context,
Node* receiver, Label* can_access, Label* miss);
// LoadGlobalIC implementation.
void LoadGlobalIC_TryPropertyCellCase(
TNode<FeedbackVector> vector, Node* slot,
const LazyNode<Context>& lazy_context, ExitPoint* exit_point,
Label* try_handler, Label* miss,
ParameterMode slot_mode = SMI_PARAMETERS);
void LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector, Node* slot,
const LazyNode<Context>& lazy_context,
const LazyNode<Name>& lazy_name,
TypeofMode typeof_mode,
ExitPoint* exit_point, Label* miss,
ParameterMode slot_mode);
// StoreIC implementation.
void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler,
Label* miss, ICMode ic_mode,
ElementSupport support_elements);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder,
Node* value, Node* transition, Label* miss);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreFieldAndReturn(Node* handler_word, Node* holder,
Representation representation, Node* value,
Node* transition, Label* miss);
void HandleStoreICNativeDataProperty(const StoreICParameters* p, Node* holder,
Node* handler_word);
void HandleStoreToProxy(const StoreICParameters* p, Node* proxy, Label* miss,
ElementSupport support_elements);
void HandleStoreAccessor(const StoreICParameters* p, Node* holder,
Node* handler_word);
// KeyedLoadIC_Generic implementation.
void GenericElementLoad(Node* receiver, Node* receiver_map,
Node* instance_type, Node* index, Label* slow);
enum UseStubCache { kUseStubCache, kDontUseStubCache };
void GenericPropertyLoad(Node* receiver, Node* receiver_map,
Node* instance_type, const LoadICParameters* p,
Label* slow,
UseStubCache use_stub_cache = kUseStubCache);
// Low-level helpers.
typedef std::function<void(Node* code_handler)> OnCodeHandler;
typedef std::function<void(Node* properties, Node* name_index)>
OnFoundOnReceiver;
template <typename ICHandler, typename ICParameters>
Node* HandleProtoHandler(const ICParameters* p, Node* handler,
const OnCodeHandler& on_code_handler,
const OnFoundOnReceiver& on_found_on_receiver,
Label* miss, ICMode ic_mode);
Node* GetLanguageMode(Node* vector, Node* slot);
Node* PrepareValueForStore(Node* handler_word, Node* holder,
Representation representation, Node* transition,
Node* value, Label* bailout);
// Extends properties backing store by JSObject::kFieldsAdded elements.
void ExtendPropertiesBackingStore(Node* object, Node* handler_word);
void StoreNamedField(Node* handler_word, Node* object, bool is_inobject,
Representation representation, Node* value,
bool transition_to_field, Label* bailout);
void EmitFastElementsBoundsCheck(Node* object, Node* elements,
Node* intptr_index,
Node* is_jsarray_condition, Label* miss);
void EmitElementLoad(Node* object, Node* elements, Node* elements_kind,
Node* key, Node* is_jsarray_condition, Label* if_hole,
Label* rebox_double, Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds,
Label* miss, ExitPoint* exit_point);
void NameDictionaryNegativeLookup(Node* object, Node* name, Label* miss);
// Stub cache access helpers.
// This enum is used here as a replacement for StubCache::Table to avoid
// including stub cache header.
enum StubCacheTable : int;
Node* StubCachePrimaryOffset(Node* name, Node* map);
Node* StubCacheSecondaryOffset(Node* name, Node* seed);
void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id,
Node* entry_offset, Node* name, Node* map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
};
// Abstraction over direct and indirect exit points. Direct exits correspond to
// tailcalls and Return, while indirect exits store the result in a variable
// and then jump to an exit label.
class ExitPoint {
private:
typedef compiler::Node Node;
typedef compiler::CodeAssemblerLabel CodeAssemblerLabel;
typedef compiler::CodeAssemblerVariable CodeAssemblerVariable;
public:
explicit ExitPoint(CodeStubAssembler* assembler)
: ExitPoint(assembler, nullptr, nullptr) {}
ExitPoint(CodeStubAssembler* assembler, CodeAssemblerLabel* out,
CodeAssemblerVariable* var_result)
: out_(out), var_result_(var_result), asm_(assembler) {
DCHECK_EQ(out != nullptr, var_result != nullptr);
}
template <class... TArgs>
void ReturnCallRuntime(Runtime::FunctionId function, Node* context,
TArgs... args) {
if (IsDirect()) {
asm_->TailCallRuntime(function, context, args...);
} else {
IndirectReturn(asm_->CallRuntime(function, context, args...));
}
}
template <class... TArgs>
void ReturnCallStub(Callable const& callable, Node* context, TArgs... args) {
if (IsDirect()) {
asm_->TailCallStub(callable, context, args...);
} else {
IndirectReturn(asm_->CallStub(callable, context, args...));
}
}
template <class... TArgs>
void ReturnCallStub(const CallInterfaceDescriptor& descriptor, Node* target,
Node* context, TArgs... args) {
if (IsDirect()) {
asm_->TailCallStub(descriptor, target, context, args...);
} else {
IndirectReturn(asm_->CallStub(descriptor, target, context, args...));
}
}
void Return(Node* const result) {
if (IsDirect()) {
asm_->Return(result);
} else {
IndirectReturn(result);
}
}
bool IsDirect() const { return out_ == nullptr; }
private:
void IndirectReturn(Node* const result) {
var_result_->Bind(result);
asm_->Goto(out_);
}
CodeAssemblerLabel* const out_;
CodeAssemblerVariable* const var_result_;
CodeStubAssembler* const asm_;
};
} // namespace internal
} // namespace v8
#endif // V8_SRC_IC_ACCESSOR_ASSEMBLER_H_