LInstruction* LChunkBuilder::DoTransitionElementsKind(
HTransitionElementsKind* instr) {
- ElementsKind from_kind = instr->original_map()->elements_kind();
- ElementsKind to_kind = instr->transitioned_map()->elements_kind();
- if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
LOperand* object = UseRegister(instr->object());
LOperand* new_map_reg = TempRegister();
LTransitionElementsKind* result =
instr->arguments_count(),
instr->function(),
undefined,
- instr->call_kind(),
- instr->inlining_kind());
+ instr->inlining_kind(),
+ instr->undefined_receiver());
if (instr->arguments_var() != NULL) {
inner->Bind(instr->arguments_var(), graph()->GetArgumentsObject());
}
Handle<Map> original_map() { return hydrogen()->original_map(); }
Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
};
ASSERT(FLAG_deopt_every_n_times < 2); // Other values not supported on ARM.
- if (FLAG_deopt_every_n_times == 1 &&
- info_->shared_info()->opt_count() == id) {
+ if (FLAG_deopt_every_n_times == 1 && info_->opt_count() == id) {
__ Jump(entry, RelocInfo::RUNTIME_ENTRY);
return;
}
Handle<Map> from_map = instr->original_map();
Handle<Map> to_map = instr->transitioned_map();
- ElementsKind from_kind = from_map->elements_kind();
- ElementsKind to_kind = to_map->elements_kind();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
Label not_applicable;
__ ldr(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset));
deferred_handles_ = NULL;
code_stub_ = NULL;
prologue_offset_ = kPrologueOffsetNotSet;
+ opt_count_ = shared_info().is_null() ? 0 : shared_info()->opt_count();
if (mode == STUB) {
mode_ = STUB;
return;
// the optimizing compiler.
const int kMaxOptCount =
FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000;
- if (info()->shared_info()->opt_count() > kMaxOptCount) {
+ if (info()->opt_count() > kMaxOptCount) {
info()->set_bailout_reason("optimized too many times");
return AbortOptimization();
}
OptimizingCompiler::Status OptimizingCompiler::OptimizeGraph() {
AssertNoAllocation no_gc;
NoHandleAllocation no_handles;
+ NoHandleDereference no_deref;
ASSERT(last_status() == SUCCEEDED);
Timer t(this, &time_taken_to_optimize_);
ScriptDataImpl* pre_parse_data() const { return pre_parse_data_; }
Handle<Context> context() const { return context_; }
BailoutId osr_ast_id() const { return osr_ast_id_; }
+ int opt_count() const { return opt_count_; }
int num_parameters() const;
int num_heap_slots() const;
Code::Flags flags() const;
int prologue_offset_;
+ // A copy of shared_info()->opt_count() to avoid handle deref
+ // during graph optimization.
+ int opt_count_;
+
DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
};
inline T* Handle<T>::operator*() const {
ASSERT(location_ != NULL);
ASSERT(reinterpret_cast<Address>(*location_) != kHandleZapValue);
+ SLOW_ASSERT(ISOLATE->allow_handle_deref());
return *BitCast<T**>(location_);
}
+template <typename T>
+inline T** Handle<T>::location() const {
+ ASSERT(location_ == NULL ||
+ reinterpret_cast<Address>(*location_) != kZapValue);
+ SLOW_ASSERT(ISOLATE->allow_handle_deref());
+ return location_;
+}
+
HandleScope::HandleScope() {
Isolate* isolate = Isolate::Current();
data->level = level_;
}
}
+
+
+NoHandleDereference::NoHandleDereference() {
+ // The guard is set on a per-isolate basis, so it affects all threads.
+ // That's why we can only use it when running without parallel recompilation.
+ if (FLAG_parallel_recompilation) return;
+ Isolate* isolate = Isolate::Current();
+ old_state_ = isolate->allow_handle_deref();
+ isolate->set_allow_handle_deref(false);
+}
+
+
+NoHandleDereference::~NoHandleDereference() {
+ if (FLAG_parallel_recompilation) return;
+ Isolate::Current()->set_allow_handle_deref(old_state_);
+}
+
+
+AllowHandleDereference::AllowHandleDereference() {
+ // The guard is set on a per-isolate basis, so it affects all threads.
+ // That's why we can only use it when running without parallel recompilation.
+ if (FLAG_parallel_recompilation) return;
+ Isolate* isolate = Isolate::Current();
+ old_state_ = isolate->allow_handle_deref();
+ isolate->set_allow_handle_deref(true);
+}
+
+
+AllowHandleDereference::~AllowHandleDereference() {
+ if (FLAG_parallel_recompilation) return;
+ Isolate::Current()->set_allow_handle_deref(old_state_);
+}
#endif
a = b; // Fake assignment to enforce type checks.
USE(a);
#endif
- location_ = reinterpret_cast<T**>(handle.location());
+ location_ = reinterpret_cast<T**>(handle.location_);
}
INLINE(T* operator ->() const) { return operator*(); }
// Check if this handle refers to the exact same object as the other handle.
bool is_identical_to(const Handle<T> other) const {
- return operator*() == *other;
+ return *location_ == *other.location_;
}
// Provides the C++ dereference operator.
INLINE(T* operator*() const);
// Returns the address to where the raw pointer is stored.
- T** location() const {
- ASSERT(location_ == NULL ||
- reinterpret_cast<Address>(*location_) != kZapValue);
- return location_;
- }
+ INLINE(T** location() const);
template <class S> static Handle<T> cast(Handle<S> that) {
T::cast(*that);
private:
T** location_;
+
+ // Handles of different classes are allowed to access each other's location_.
+ template<class S> friend class Handle;
};
#endif
};
+
+class NoHandleDereference BASE_EMBEDDED {
+ public:
+#ifndef DEBUG
+ NoHandleDereference() {}
+ ~NoHandleDereference() {}
+#else
+ inline NoHandleDereference();
+ inline ~NoHandleDereference();
+ private:
+ bool old_state_;
+#endif
+};
+
+
+class AllowHandleDereference BASE_EMBEDDED {
+ public:
+#ifndef DEBUG
+ AllowHandleDereference() {}
+ ~AllowHandleDereference() {}
+#else
+ inline AllowHandleDereference();
+ inline ~AllowHandleDereference();
+ private:
+ bool old_state_;
+#endif
+};
+
} } // namespace v8::internal
#endif // V8_HANDLES_H_
HType HType::TypeFromValue(Handle<Object> value) {
+ // Handle dereferencing is safe here: an object's type as checked below
+ // never changes.
+ AllowHandleDereference allow_handle_deref;
+
HType result = HType::Tagged();
if (value->IsSmi()) {
result = HType::Smi();
value()->type().IsString()) {
return NULL;
}
- if (check_ == IS_SYMBOL &&
- value()->IsConstant() &&
- HConstant::cast(value())->handle()->IsSymbol()) {
- return NULL;
+
+ if (check_ == IS_SYMBOL && value()->IsConstant()) {
+ // Dereferencing is safe here: a symbol cannot become a non-symbol.
+ AllowHandleDereference allow_handle_deref;
+ if (HConstant::cast(value())->handle()->IsSymbol()) return NULL;
}
return this;
}
: handle_(handle),
has_int32_value_(false),
has_double_value_(false) {
+ // Dereferencing here is safe: the value of a number object does not change.
+ AllowHandleDereference allow_handle_deref;
SetFlag(kUseGVN);
if (handle_->IsNumber()) {
double n = handle_->Number();
double v = DoubleValue();
return v != 0 && !isnan(v);
}
- Handle<Object> literal = handle();
- if (literal->IsTrue()) return true;
- if (literal->IsFalse()) return false;
- if (literal->IsUndefined()) return false;
- if (literal->IsNull()) return false;
- if (literal->IsString() && String::cast(*literal)->length() == 0) {
+ // Dereferencing is safe: singletons do not change and strings are
+ // immutable.
+ AllowHandleDereference allow_handle_deref;
+ if (handle_->IsTrue()) return true;
+ if (handle_->IsFalse()) return false;
+ if (handle_->IsUndefined()) return false;
+ if (handle_->IsNull()) return false;
+ if (handle_->IsString() && String::cast(*handle_)->length() == 0) {
return false;
}
return true;
HEnterInlined(Handle<JSFunction> closure,
int arguments_count,
FunctionLiteral* function,
- CallKind call_kind,
InliningKind inlining_kind,
Variable* arguments_var,
- ZoneList<HValue*>* arguments_values)
+ ZoneList<HValue*>* arguments_values,
+ bool undefined_receiver)
: closure_(closure),
arguments_count_(arguments_count),
arguments_pushed_(false),
function_(function),
- call_kind_(call_kind),
inlining_kind_(inlining_kind),
arguments_var_(arguments_var),
- arguments_values_(arguments_values) {
+ arguments_values_(arguments_values),
+ undefined_receiver_(undefined_receiver) {
}
virtual void PrintDataTo(StringStream* stream);
bool arguments_pushed() const { return arguments_pushed_; }
void set_arguments_pushed() { arguments_pushed_ = true; }
FunctionLiteral* function() const { return function_; }
- CallKind call_kind() const { return call_kind_; }
InliningKind inlining_kind() const { return inlining_kind_; }
+ bool undefined_receiver() const { return undefined_receiver_; }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::None();
int arguments_count_;
bool arguments_pushed_;
FunctionLiteral* function_;
- CallKind call_kind_;
InliningKind inlining_kind_;
Variable* arguments_var_;
ZoneList<HValue*>* arguments_values_;
+ bool undefined_receiver_;
};
: HUnaryOperation(value), target_(function) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
+ target_in_new_space_ = Isolate::Current()->heap()->InNewSpace(*function);
}
virtual Representation RequiredInputRepresentation(int index) {
#endif
Handle<JSFunction> target() const { return target_; }
+ bool target_in_new_space() const { return target_in_new_space_; }
DECLARE_CONCRETE_INSTRUCTION(CheckFunction)
private:
Handle<JSFunction> target_;
+ bool target_in_new_space_;
};
virtual intptr_t Hashcode() {
ASSERT_ALLOCATION_DISABLED;
+ // Dereferencing to use the object's raw address for hashing is safe.
+ AllowHandleDereference allow_handle_deref;
intptr_t hash = 0;
for (int i = 0; i < prototypes_.length(); i++) {
hash = 17 * hash + reinterpret_cast<intptr_t>(*prototypes_[i]);
Heap* heap = HEAP;
// We should have handled minus_zero_value and nan_value in the
// has_double_value_ clause above.
+ // Dereferencing is safe to compare against singletons.
+ AllowHandleDereference allow_handle_deref;
ASSERT(*handle_ != heap->minus_zero_value());
ASSERT(*handle_ != heap->nan_value());
- if (*handle_ == heap->undefined_value()) return true;
- if (*handle_ == heap->null_value()) return true;
- if (*handle_ == heap->true_value()) return true;
- if (*handle_ == heap->false_value()) return true;
- if (*handle_ == heap->the_hole_value()) return true;
- if (*handle_ == heap->empty_string()) return true;
- return false;
+ return *handle_ == heap->undefined_value() ||
+ *handle_ == heap->null_value() ||
+ *handle_ == heap->true_value() ||
+ *handle_ == heap->false_value() ||
+ *handle_ == heap->the_hole_value() ||
+ *handle_ == heap->empty_string();
}
virtual Representation RequiredInputRepresentation(int index) {
hash = static_cast<intptr_t>(BitCast<int64_t>(double_value_));
} else {
ASSERT(!handle_.is_null());
+ // Dereferencing to use the object's raw address for hashing is safe.
+ AllowHandleDereference allow_handle_deref;
hash = reinterpret_cast<intptr_t>(*handle_);
}
} else {
ASSERT(!handle_.is_null());
return !other_constant->handle_.is_null() &&
- *handle_ == *other_constant->handle_;
+ handle_.is_identical_to(other_constant->handle_);
}
}
SetGVNFlag(kDependsOnGlobalVars);
}
- Handle<JSGlobalPropertyCell> cell() const { return cell_; }
+ Handle<JSGlobalPropertyCell> cell() const { return cell_; }
bool RequiresHoleCheck() const;
virtual void PrintDataTo(StringStream* stream);
virtual intptr_t Hashcode() {
ASSERT_ALLOCATION_DISABLED;
+ // Dereferencing to use the object's raw address for hashing is safe.
+ AllowHandleDereference allow_handle_deref;
return reinterpret_cast<intptr_t>(*cell_);
}
Handle<Map> original_map,
Handle<Map> transitioned_map)
: original_map_(original_map),
- transitioned_map_(transitioned_map) {
+ transitioned_map_(transitioned_map),
+ from_kind_(original_map->elements_kind()),
+ to_kind_(transitioned_map->elements_kind()) {
SetOperandAt(0, object);
SetFlag(kUseGVN);
SetGVNFlag(kChangesElementsKind);
HValue* object() { return OperandAt(0); }
Handle<Map> original_map() { return original_map_; }
Handle<Map> transitioned_map() { return transitioned_map_; }
+ ElementsKind from_kind() { return from_kind_; }
+ ElementsKind to_kind() { return to_kind_; }
virtual void PrintDataTo(StringStream* stream);
private:
Handle<Map> original_map_;
Handle<Map> transitioned_map_;
+ ElementsKind from_kind_;
+ ElementsKind to_kind_;
};
void HGraph::Verify(bool do_full_verify) const {
+ // Allow dereferencing for debug mode verification.
+ AllowHandleDereference allow_handle_deref;
for (int i = 0; i < blocks_.length(); i++) {
HBasicBlock* block = blocks_.at(i);
bool HGlobalValueNumberer::AllowCodeMotion() {
- return info()->shared_info()->opt_count() + 1 < FLAG_max_opt_count;
+ return info()->opt_count() + 1 < FLAG_max_opt_count;
}
this, &target_info, &target_oracle, inlining_kind);
HConstant* undefined = graph()->GetConstantUndefined();
+ bool undefined_receiver = HEnvironment::UseUndefinedReceiver(
+ target, function, call_kind, inlining_kind);
HEnvironment* inner_env =
environment()->CopyForInlining(target,
arguments_count,
function,
undefined,
- call_kind,
- function_state()->inlining_kind());
+ function_state()->inlining_kind(),
+ undefined_receiver);
#ifdef V8_TARGET_ARCH_IA32
// IA32 only, overwrite the caller's context in the deoptimization
// environment with the correct one.
new(zone()) HEnterInlined(target,
arguments_count,
function,
- call_kind,
function_state()->inlining_kind(),
function->scope()->arguments(),
- arguments_values);
+ arguments_values,
+ undefined_receiver);
function_state()->set_entry(enter_inlined);
AddInstruction(enter_inlined);
int arguments,
FunctionLiteral* function,
HConstant* undefined,
- CallKind call_kind,
- InliningKind inlining_kind) const {
+ InliningKind inlining_kind,
+ bool undefined_receiver) const {
ASSERT(frame_type() == JS_FUNCTION);
// Outer environment is a copy of this one without the arguments.
// If the function we are inlining is a strict mode function or a
// builtin function, pass undefined as the receiver for function
// calls (instead of the global receiver).
- if ((target->shared()->native() || !function->is_classic_mode()) &&
- call_kind == CALL_AS_FUNCTION && inlining_kind != CONSTRUCT_CALL_RETURN) {
+ if (undefined_receiver) {
inner->SetValueAt(0, undefined);
}
inner->SetValueAt(arity + 1, LookupContext());
int arguments,
FunctionLiteral* function,
HConstant* undefined,
- CallKind call_kind,
- InliningKind inlining_kind) const;
+ InliningKind inlining_kind,
+ bool undefined_receiver) const;
+
+ static bool UseUndefinedReceiver(Handle<JSFunction> closure,
+ FunctionLiteral* function,
+ CallKind call_kind,
+ InliningKind inlining_kind) {
+ return (closure->shared()->native() || !function->is_classic_mode()) &&
+ call_kind == CALL_AS_FUNCTION && inlining_kind != CONSTRUCT_CALL_RETURN;
+ }
HEnvironment* DiscardInlined(bool drop_extra) {
HEnvironment* outer = outer_;
Handle<Map> from_map = instr->original_map();
Handle<Map> to_map = instr->transitioned_map();
- ElementsKind from_kind = from_map->elements_kind();
- ElementsKind to_kind = to_map->elements_kind();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
Label not_applicable;
bool is_simple_map_transition =
void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
Handle<JSFunction> target = instr->hydrogen()->target();
- if (isolate()->heap()->InNewSpace(*target)) {
+ if (instr->hydrogen()->target_in_new_space()) {
Register reg = ToRegister(instr->value());
Handle<JSGlobalPropertyCell> cell =
isolate()->factory()->NewJSGlobalPropertyCell(target);
// want the value in a register. If the target gets promoted before we
// emit code, we will still get the register but will do an immediate
// compare instead of the cell compare. This is safe.
- LOperand* value = Isolate::Current()->heap()->InNewSpace(*instr->target())
- ? UseRegisterAtStart(instr->value())
- : UseAtStart(instr->value());
+ LOperand* value = instr->target_in_new_space()
+ ? UseRegisterAtStart(instr->value()) : UseAtStart(instr->value());
return AssignEnvironment(new(zone()) LCheckFunction(value));
}
LInstruction* LChunkBuilder::DoTransitionElementsKind(
HTransitionElementsKind* instr) {
- ElementsKind from_kind = instr->original_map()->elements_kind();
- ElementsKind to_kind = instr->transitioned_map()->elements_kind();
- if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
LOperand* object = UseRegister(instr->object());
LOperand* new_map_reg = TempRegister();
LOperand* temp_reg = TempRegister();
instr->arguments_count(),
instr->function(),
undefined,
- instr->call_kind(),
- instr->inlining_kind());
+ instr->inlining_kind(),
+ instr->undefined_receiver());
if (instr->arguments_var() != NULL) {
inner->Bind(instr->arguments_var(), graph()->GetArgumentsObject());
}
Handle<Map> original_map() { return hydrogen()->original_map(); }
Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
};
memset(&js_spill_information_, 0, sizeof(js_spill_information_));
memset(code_kind_statistics_, 0,
sizeof(code_kind_statistics_[0]) * Code::NUMBER_OF_KINDS);
+
+ allow_handle_deref_ = true;
#endif
#ifdef ENABLE_DEBUGGER_SUPPORT
}
int* code_kind_statistics() { return code_kind_statistics_; }
+
+ bool allow_handle_deref() { return allow_handle_deref_; }
+ void set_allow_handle_deref(bool allow) { allow_handle_deref_ = allow; }
#endif
#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
HistogramInfo heap_histograms_[LAST_TYPE + 1];
JSObject::SpillInformation js_spill_information_;
int code_kind_statistics_[Code::NUMBER_OF_KINDS];
+
+ bool allow_handle_deref_;
#endif
#ifdef ENABLE_DEBUGGER_SUPPORT
Handle<Map> from_map = instr->original_map();
Handle<Map> to_map = instr->transitioned_map();
- ElementsKind from_kind = from_map->elements_kind();
- ElementsKind to_kind = to_map->elements_kind();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
Label not_applicable;
__ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map);
LInstruction* LChunkBuilder::DoTransitionElementsKind(
HTransitionElementsKind* instr) {
- ElementsKind from_kind = instr->original_map()->elements_kind();
- ElementsKind to_kind = instr->transitioned_map()->elements_kind();
- if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
LOperand* object = UseRegister(instr->object());
LOperand* new_map_reg = TempRegister();
LOperand* temp_reg = TempRegister();
instr->arguments_count(),
instr->function(),
undefined,
- instr->call_kind(),
- instr->inlining_kind());
+ instr->inlining_kind(),
+ instr->undefined_receiver());
if (instr->arguments_var() != NULL) {
inner->Bind(instr->arguments_var(), graph()->GetArgumentsObject());
}
Handle<Map> original_map() { return hydrogen()->original_map(); }
Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
};