unsigned output_offset =
output->GetOffsetFromSlotIndex(this, output_index);
if (FLAG_trace_osr) {
- PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d]\n",
+ PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ",
output_offset,
input_value,
*input_offset);
+ reinterpret_cast<Object*>(input_value)->ShortPrint();
+ PrintF("\n");
}
output->SetFrameSlot(output_offset, input_value);
break;
}
-void HBasicBlock::Goto(HBasicBlock* block) {
+void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) {
if (block->IsInlineReturnTarget()) {
AddInstruction(new(zone()) HLeaveInlined);
last_environment_ = last_environment()->outer();
+ if (drop_extra) last_environment_->Drop(1);
}
AddSimulate(AstNode::kNoNumber);
HGoto* instr = new(zone()) HGoto(block);
}
-void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) {
+void HBasicBlock::AddLeaveInlined(HValue* return_value,
+ HBasicBlock* target,
+ bool drop_extra) {
ASSERT(target->IsInlineReturnTarget());
ASSERT(return_value != NULL);
AddInstruction(new(zone()) HLeaveInlined);
last_environment_ = last_environment()->outer();
+ if (drop_extra) last_environment_->Drop(1);
last_environment()->Push(return_value);
AddSimulate(AstNode::kNoNumber);
HGoto* instr = new(zone()) HGoto(target);
HGraphBuilder::HGraphBuilder(CompilationInfo* info,
TypeFeedbackOracle* oracle)
: function_state_(NULL),
- initial_function_state_(this, info, oracle),
+ initial_function_state_(this, info, oracle, false),
ast_context_(NULL),
break_scope_(NULL),
graph_(NULL),
// a (possibly inlined) function.
FunctionState::FunctionState(HGraphBuilder* owner,
CompilationInfo* info,
- TypeFeedbackOracle* oracle)
+ TypeFeedbackOracle* oracle,
+ bool drop_extra)
: owner_(owner),
compilation_info_(info),
oracle_(oracle),
call_context_(NULL),
+ drop_extra_(drop_extra),
function_return_(NULL),
test_context_(NULL),
outer_(owner->function_state()) {
instr->SetSuccessorAt(0, empty_true);
instr->SetSuccessorAt(1, empty_false);
owner()->current_block()->Finish(instr);
- empty_true->Goto(if_true());
- empty_false->Goto(if_false());
+ empty_true->Goto(if_true(), owner()->function_state()->drop_extra());
+ empty_false->Goto(if_false(), owner()->function_state()->drop_extra());
owner()->set_current_block(NULL);
}
HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected);
builder->current_block()->Finish(test);
- empty_true->Goto(if_true());
- empty_false->Goto(if_false());
+ empty_true->Goto(if_true(), owner()->function_state()->drop_extra());
+ empty_false->Goto(if_false(), owner()->function_state()->drop_extra());
builder->set_current_block(NULL);
}
test->if_false());
} else if (context->IsEffect()) {
CHECK_ALIVE(VisitForEffect(stmt->expression()));
- current_block()->Goto(function_return());
+ current_block()->Goto(function_return(), function_state()->drop_extra());
} else {
ASSERT(context->IsValue());
CHECK_ALIVE(VisitForValue(stmt->expression()));
HValue* return_value = environment()->Pop();
- current_block()->AddLeaveInlined(return_value, function_return());
+ current_block()->AddLeaveInlined(return_value,
+ function_return(),
+ function_state()->drop_extra());
}
set_current_block(NULL);
}
}
-bool HGraphBuilder::TryInline(Call* expr) {
+bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
if (!FLAG_use_inlining) return false;
// The function call we are inlining is a method call if the call
return false;
}
- CompilationInfo* outer_info = info();
#if !defined(V8_TARGET_ARCH_IA32)
// Target must be able to use caller's context.
+ CompilationInfo* outer_info = info();
if (target->context() != outer_info->closure()->context() ||
outer_info->scope()->contains_with() ||
outer_info->scope()->num_heap_slots() > 0) {
}
// Don't inline recursive functions.
- if (*target_shared == outer_info->closure()->shared()) {
- TraceInline(target, caller, "target is recursive");
- return false;
+ for (FunctionState* state = function_state();
+ state != NULL;
+ state = state->outer()) {
+ if (state->compilation_info()->closure()->shared() == *target_shared) {
+ TraceInline(target, caller, "target is recursive");
+ return false;
+ }
}
// We don't want to add more than a certain number of nodes from inlining.
Handle<Code>(target_shared->code()),
Handle<Context>(target->context()->global_context()),
isolate());
- FunctionState target_state(this, &target_info, &target_oracle);
+ FunctionState target_state(this, &target_info, &target_oracle, drop_extra);
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner_env =
ASSERT(function_return() != NULL);
ASSERT(call_context()->IsEffect() || call_context()->IsValue());
if (call_context()->IsEffect()) {
- current_block()->Goto(function_return());
+ current_block()->Goto(function_return(), drop_extra);
} else {
- current_block()->AddLeaveInlined(undefined, function_return());
+ current_block()->AddLeaveInlined(undefined,
+ function_return(),
+ drop_extra);
}
} else {
// The graph builder assumes control can reach both branches of a
// simply jumping to the false target.
//
// TODO(3168478): refactor to avoid this.
+ ASSERT(call_context()->IsTest());
HBasicBlock* empty_true = graph()->CreateBasicBlock();
HBasicBlock* empty_false = graph()->CreateBasicBlock();
HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
current_block()->Finish(test);
- empty_true->Goto(inlined_test_context()->if_true());
- empty_false->Goto(inlined_test_context()->if_false());
+ empty_true->Goto(inlined_test_context()->if_true(), drop_extra);
+ empty_false->Goto(inlined_test_context()->if_false(), drop_extra);
}
}
if (if_true->HasPredecessor()) {
if_true->SetJoinId(expr->id());
HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
- if_true->Goto(true_target);
+ if_true->Goto(true_target, drop_extra);
}
if (if_false->HasPredecessor()) {
if_false->SetJoinId(expr->id());
HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
- if_false->Goto(false_target);
+ if_false->Goto(false_target, drop_extra);
}
set_current_block(NULL);
PushAndAdd(receiver);
CHECK_ALIVE(VisitExpressions(expr->arguments()));
AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
- if (TryInline(expr)) {
- // The function is lingering in the deoptimization environment.
- // Handle it by case analysis on the AST context.
- if (ast_context()->IsEffect()) {
- Drop(1);
- } else if (ast_context()->IsValue()) {
- HValue* result = Pop();
- Drop(1);
- Push(result);
- } else if (ast_context()->IsTest()) {
- TestContext* context = TestContext::cast(ast_context());
- if (context->if_true()->HasPredecessor()) {
- context->if_true()->last_environment()->Drop(1);
- }
- if (context->if_false()->HasPredecessor()) {
- context->if_true()->last_environment()->Drop(1);
- }
- } else {
- UNREACHABLE();
- }
+ if (TryInline(expr, true)) { // Drop function from environment.
return;
} else {
call = PreProcessCall(new(zone()) HInvokeFunction(context,
void HGraphBuilder::VisitNot(UnaryOperation* expr) {
- // TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
VisitForControl(expr->expression(),
void Finish(HControlInstruction* last);
void FinishExit(HControlInstruction* instruction);
- void Goto(HBasicBlock* block);
+ void Goto(HBasicBlock* block, bool drop_extra = false);
int PredecessorIndexOf(HBasicBlock* predecessor) const;
void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); }
// Add the inlined function exit sequence, adding an HLeaveInlined
// instruction and updating the bailout environment.
- void AddLeaveInlined(HValue* return_value, HBasicBlock* target);
+ void AddLeaveInlined(HValue* return_value,
+ HBasicBlock* target,
+ bool drop_extra = false);
// If a target block is tagged as an inline function return, all
// predecessors should contain the inlined exit sequence:
public:
FunctionState(HGraphBuilder* owner,
CompilationInfo* info,
- TypeFeedbackOracle* oracle);
+ TypeFeedbackOracle* oracle,
+ bool drop_extra);
~FunctionState();
CompilationInfo* compilation_info() { return compilation_info_; }
TypeFeedbackOracle* oracle() { return oracle_; }
AstContext* call_context() { return call_context_; }
+ bool drop_extra() { return drop_extra_; }
HBasicBlock* function_return() { return function_return_; }
TestContext* test_context() { return test_context_; }
void ClearInlinedTestContext() {
// inlined. NULL when not inlining.
AstContext* call_context_;
+ // Indicate if we have to drop an extra value from the environment on
+ // return from inlined functions.
+ bool drop_extra_;
+
// When inlining in an effect of value context, this is the return block.
// It is NULL otherwise. When inlining in a test context, there are a
// pair of return blocks in the context. When not inlining, there is no
TypeFeedbackOracle* oracle() const { return function_state()->oracle(); }
+ FunctionState* function_state() const { return function_state_; }
+
private:
// Type of a member function that generates inline code for a native function.
typedef void (HGraphBuilder::*InlineFunctionGenerator)(CallRuntime* call);
static const int kMaxSourceSize = 600;
// Simple accessors.
- FunctionState* function_state() const { return function_state_; }
void set_function_state(FunctionState* state) { function_state_ = state; }
AstContext* ast_context() const { return ast_context_; }
// Try to optimize fun.apply(receiver, arguments) pattern.
bool TryCallApply(Call* expr);
- bool TryInline(Call* expr);
+ bool TryInline(Call* expr, bool drop_extra = false);
bool TryInlineBuiltinFunction(Call* expr,
HValue* receiver,
Handle<Map> receiver_map,
flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
}
CallFunctionStub stub(arg_count, flags);
- __ CallStub(&stub);
+ __ CallStub(&stub, expr->id());
if (record_call_target) {
// There is a one element cache in the instruction stream.
#ifdef DEBUG
void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
for (int i = 0; i < infos->length(); i++) {
- Address target_address = (*infos)[i].target_address();
+ RelocInfo reloc_entry = (*infos)[i];
+ Address target_address = reloc_entry.target_address();
unsigned ast_id = static_cast<unsigned>((*infos)[i].data());
- ProcessTargetAt(target_address, ast_id);
- }
-}
-
+ 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();
+ SetInfo(ast_id, map == NULL ? static_cast<Object*>(target) : map);
+ }
+ } else if (target->ic_state() == MEGAMORPHIC) {
+ SetInfo(ast_id, target);
+ }
+ break;
-void TypeFeedbackOracle::ProcessTargetAt(Address target_address,
- unsigned ast_id) {
- 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();
- SetInfo(ast_id, map == NULL ? static_cast<Object*>(target) : map);
+ case Code::KEYED_LOAD_IC:
+ case Code::KEYED_STORE_IC:
+ if (target->ic_state() == MONOMORPHIC ||
+ target->ic_state() == MEGAMORPHIC) {
+ SetInfo(ast_id, target);
}
- } else if (target->ic_state() == MEGAMORPHIC) {
- SetInfo(ast_id, target);
- }
- break;
+ break;
- case Code::KEYED_LOAD_IC:
- case Code::KEYED_STORE_IC:
- if (target->ic_state() == MONOMORPHIC ||
- target->ic_state() == MEGAMORPHIC) {
+ case Code::UNARY_OP_IC:
+ case Code::BINARY_OP_IC:
+ case Code::COMPARE_IC:
+ case Code::TO_BOOLEAN_IC:
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;
-
- case Code::STUB:
- if (target->major_key() == CodeStub::CallFunction &&
- target->has_function_cache()) {
- Object* value = CallFunctionStub::GetCachedValue(target_address);
- if (value->IsJSFunction()) {
- SetInfo(ast_id, value);
+ break;
+
+ case Code::STUB:
+ if (target->major_key() == CodeStub::CallFunction &&
+ target->has_function_cache()) {
+ Object* value = CallFunctionStub::GetCachedValue(reloc_entry.pc());
+ if (value->IsJSFunction()) {
+ SetInfo(ast_id, value);
+ }
}
- }
- break;
+ break;
- default:
- break;
+ default:
+ break;
+ }
}
}
byte* old_start,
byte* new_start);
void ProcessRelocInfos(ZoneList<RelocInfo>* infos);
- void ProcessTargetAt(Address target_address, unsigned ast_id);
// Returns an element from the backing store. Returns undefined if
// there is no information.
--- /dev/null
+// 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.
+
+// Test deoptimization after inlined call.
+
+function bar(a, b) {try { return a; } finally { } }
+
+function test_context() {
+ function foo(x) { return 42; }
+ var s, t;
+ for (var i = 0x7ff00000; i < 0x80000000; i++) {
+ bar(t = foo(i) ? bar(42 + i - i) : bar(0), s = i + t);
+ }
+ return s;
+}
+assertEquals(0x7fffffff + 42, test_context());
+
+
+function value_context() {
+ function foo(x) { return 42; }
+ var s, t;
+ for (var i = 0x7ff00000; i < 0x80000000; i++) {
+ bar(t = foo(i), s = i + t);
+ }
+ return s;
+}
+assertEquals(0x7fffffff + 42, value_context());
+
+
+function effect_context() {
+ function foo(x) { return 42; }
+ var s, t;
+ for (var i = 0x7ff00000; i < 0x80000000; i++) {
+ bar(foo(i), s = i + 42);
+ }
+ return s;
+}
+assertEquals(0x7fffffff + 42, effect_context());