blob: 76f6469c7e3e1387fba999810605e46b5646f00e [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/flow_graph_builder.h"
#include "lib/invocation_mirror.h"
#include "vm/ast_printer.h"
#include "vm/code_descriptors.h"
#include "vm/dart_entry.h"
#include "vm/flags.h"
#include "vm/flow_graph_compiler.h"
#include "vm/il_printer.h"
#include "vm/intermediate_language.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/parser.h"
#include "vm/resolver.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
namespace dart {
DEFINE_FLAG(bool, eliminate_type_checks, true,
"Eliminate type checks when allowed by static type analysis.");
DEFINE_FLAG(bool, print_ast, false, "Print abstract syntax tree.");
DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph.");
DEFINE_FLAG(bool, print_flow_graph_optimized, false,
"Print the IR flow graph when optimizing.");
DEFINE_FLAG(bool, trace_type_check_elimination, false,
"Trace type check elimination at compile time.");
DECLARE_FLAG(bool, enable_type_checks);
static const String& PrivateCoreLibName(const String& str) {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const String& private_name = String::ZoneHandle(core_lib.PrivateName(str));
return private_name;
}
FlowGraphBuilder::FlowGraphBuilder(const ParsedFunction& parsed_function,
InlineExitCollector* exit_collector)
: parsed_function_(parsed_function),
num_copied_params_(parsed_function.num_copied_params()),
// All parameters are copied if any parameter is.
num_non_copied_params_((num_copied_params_ == 0)
? parsed_function.function().num_fixed_parameters()
: 0),
num_stack_locals_(parsed_function.num_stack_locals()),
exit_collector_(exit_collector),
last_used_block_id_(0), // 0 is used for the graph entry.
context_level_(0),
last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
try_index_(CatchClauseNode::kInvalidTryIndex),
graph_entry_(NULL) { }
void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) {
graph_entry_->AddCatchEntry(entry);
}
void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) {
ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id());
ASSERT(callee_graph->max_virtual_register_number() >
caller_graph_->max_virtual_register_number());
// Adjust the caller's maximum block id and current SSA temp index.
caller_graph_->set_max_block_id(callee_graph->max_block_id());
caller_graph_->set_current_ssa_temp_index(
callee_graph->max_virtual_register_number());
// Attach the outer environment on each instruction in the callee graph.
for (BlockIterator block_it = callee_graph->postorder_iterator();
!block_it.Done();
block_it.Advance()) {
for (ForwardInstructionIterator it(block_it.Current());
!it.Done();
it.Advance()) {
Instruction* instr = it.Current();
// TODO(zerny): Avoid creating unnecessary environments. Note that some
// optimizations need deoptimization info for non-deoptable instructions,
// eg, LICM on GOTOs.
if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr);
}
}
}
void InlineExitCollector::AddExit(ReturnInstr* exit) {
Data data = { NULL, exit };
exits_.Add(data);
}
int InlineExitCollector::LowestBlockIdFirst(const Data* a, const Data* b) {
return (a->exit_block->block_id() - b->exit_block->block_id());
}
void InlineExitCollector::SortExits() {
// Assign block entries here because we did not necessarily know them when
// the return exit was added to the array.
for (int i = 0; i < exits_.length(); ++i) {
exits_[i].exit_block = exits_[i].exit_return->GetBlock();
}
exits_.Sort(LowestBlockIdFirst);
}
Definition* InlineExitCollector::JoinReturns(BlockEntryInstr** exit_block,
Instruction** last_instruction) {
// First sort the list of exits by block id (caching return instruction
// block entries as a side effect).
SortExits();
intptr_t num_exits = exits_.length();
if (num_exits == 0) {
// TODO(zerny): Add support for non-local exits, such as throw.
UNREACHABLE();
return NULL;
} else if (num_exits == 1) {
ReturnAt(0)->UnuseAllInputs();
*exit_block = ExitBlockAt(0);
*last_instruction = LastInstructionAt(0);
return call_->HasUses() ? ValueAt(0)->definition() : NULL;
} else {
// Create a join of the returns.
intptr_t join_id = caller_graph_->max_block_id() + 1;
caller_graph_->set_max_block_id(join_id);
JoinEntryInstr* join =
new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex);
join->InheritDeoptTarget(call_);
// The dominator set of the join is the intersection of the dominator
// sets of all the predecessors. If we keep the dominator sets ordered
// by height in the dominator tree, we can also get the immediate
// dominator of the join node from the intersection.
//
// block_dominators is the dominator set for each block, ordered from
// the immediate dominator to the root of the dominator tree. This is
// the order we collect them in (adding at the end).
//
// join_dominators is the join's dominators ordered from the root of the
// dominator tree to the immediate dominator. This order supports
// removing during intersection by truncating the list.
GrowableArray<BlockEntryInstr*> block_dominators;
GrowableArray<BlockEntryInstr*> join_dominators;
for (intptr_t i = 0; i < num_exits; ++i) {
// Add the control-flow edge.
GotoInstr* goto_instr = new GotoInstr(join);
goto_instr->InheritDeoptTarget(ReturnAt(i));
LastInstructionAt(i)->LinkTo(goto_instr);
ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
join->predecessors_.Add(ExitBlockAt(i));
// Collect the block's dominators.
block_dominators.Clear();
BlockEntryInstr* dominator = ExitBlockAt(i)->dominator();
while (dominator != NULL) {
block_dominators.Add(dominator);
dominator = dominator->dominator();
}
if (i == 0) {
// The initial dominator set is the first predecessor's dominator
// set. Reverse it.
for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) {
join_dominators.Add(block_dominators[j]);
}
} else {
// Intersect the block's dominators with the join's dominators so far.
intptr_t last = block_dominators.length() - 1;
for (intptr_t j = 0; j < join_dominators.length(); ++j) {
intptr_t k = last - j; // Corresponding index in block_dominators.
if ((k < 0) || (join_dominators[j] != block_dominators[k])) {
// We either exhausted the dominators for this block before
// exhausting the current intersection, or else we found a block
// on the path from the root of the tree that is not in common.
ASSERT(j >= 2);
join_dominators.TruncateTo(j);
break;
}
}
}
}
// The immediate dominator of the join is the last one in the ordered
// intersection.
join->set_dominator(join_dominators.Last());
join_dominators.Last()->AddDominatedBlock(join);
*exit_block = join;
*last_instruction = join;
// If the call has uses, create a phi of the returns.
if (call_->HasUses()) {
// Add a phi of the return values.
PhiInstr* phi = new PhiInstr(join, num_exits);
phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index());
phi->mark_alive();
for (intptr_t i = 0; i < num_exits; ++i) {
ReturnAt(i)->RemoveEnvironment();
phi->SetInputAt(i, ValueAt(i));
}
join->InsertPhi(phi);
return phi;
} else {
// In the case that the result is unused, remove the return value uses
// from their definition's use list.
for (intptr_t i = 0; i < num_exits; ++i) {
ReturnAt(i)->UnuseAllInputs();
}
return NULL;
}
}
}
void InlineExitCollector::ReplaceCall(TargetEntryInstr* callee_entry) {
ASSERT(call_->previous() != NULL);
ASSERT(call_->next() != NULL);
BlockEntryInstr* call_block = call_->GetBlock();
// Insert the callee graph into the caller graph.
BlockEntryInstr* callee_exit = NULL;
Instruction* callee_last_instruction = NULL;
Definition* callee_result = JoinReturns(&callee_exit,
&callee_last_instruction);
if (callee_result != NULL) {
call_->ReplaceUsesWith(callee_result);
}
if (callee_last_instruction == callee_entry) {
// There are no instructions in the inlined function (e.g., it might be
// a return of a parameter or a return of a constant defined in the
// initial definitions).
call_->previous()->LinkTo(call_->next());
} else {
call_->previous()->LinkTo(callee_entry->next());
callee_last_instruction->LinkTo(call_->next());
}
if (callee_exit != callee_entry) {
// In case of control flow, locally update the predecessors, phis and
// dominator tree.
//
// Pictorially, the graph structure is:
//
// Bc : call_block Bi : callee_entry
// before_call inlined_head
// call ... other blocks ...
// after_call Be : callee_exit
// inlined_foot
// And becomes:
//
// Bc : call_block
// before_call
// inlined_head
// ... other blocks ...
// Be : callee_exit
// inlined_foot
// after_call
//
// For successors of 'after_call', the call block (Bc) is replaced as a
// predecessor by the callee exit (Be).
call_block->ReplaceAsPredecessorWith(callee_exit);
// For successors of 'inlined_head', the callee entry (Bi) is replaced
// as a predecessor by the call block (Bc).
callee_entry->ReplaceAsPredecessorWith(call_block);
// The callee exit is now the immediate dominator of blocks whose
// immediate dominator was the call block.
ASSERT(callee_exit->dominated_blocks().is_empty());
for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) {
BlockEntryInstr* block = call_block->dominated_blocks()[i];
block->set_dominator(callee_exit);
callee_exit->AddDominatedBlock(block);
}
// The call block is now the immediate dominator of blocks whose
// immediate dominator was the callee entry.
call_block->ClearDominatedBlocks();
for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
block->set_dominator(call_block);
call_block->AddDominatedBlock(block);
}
}
}
void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) {
ASSERT(is_open());
if (other_fragment.is_empty()) return;
if (is_empty()) {
entry_ = other_fragment.entry();
exit_ = other_fragment.exit();
} else {
exit()->LinkTo(other_fragment.entry());
exit_ = other_fragment.exit();
}
temp_index_ = other_fragment.temp_index();
}
Value* EffectGraphVisitor::Bind(Definition* definition) {
ASSERT(is_open());
DeallocateTempIndex(definition->InputCount());
definition->set_use_kind(Definition::kValue);
definition->set_temp_index(AllocateTempIndex());
if (is_empty()) {
entry_ = definition;
} else {
exit()->LinkTo(definition);
}
exit_ = definition;
return new Value(definition);
}
void EffectGraphVisitor::Do(Definition* definition) {
ASSERT(is_open());
DeallocateTempIndex(definition->InputCount());
definition->set_use_kind(Definition::kEffect);
if (is_empty()) {
entry_ = definition;
} else {
exit()->LinkTo(definition);
}
exit_ = definition;
}
void EffectGraphVisitor::AddInstruction(Instruction* instruction) {
ASSERT(is_open());
ASSERT(instruction->IsPushArgument() || !instruction->IsDefinition());
ASSERT(!instruction->IsBlockEntry());
DeallocateTempIndex(instruction->InputCount());
if (is_empty()) {
entry_ = exit_ = instruction;
} else {
exit()->LinkTo(instruction);
exit_ = instruction;
}
}
void EffectGraphVisitor::AddReturnExit(intptr_t token_pos, Value* value) {
ASSERT(is_open());
ReturnInstr* return_instr = new ReturnInstr(token_pos, value);
AddInstruction(return_instr);
InlineExitCollector* exit_collector = owner()->exit_collector();
if (exit_collector != NULL) {
exit_collector->AddExit(return_instr);
}
CloseFragment();
}
void EffectGraphVisitor::Goto(JoinEntryInstr* join) {
ASSERT(is_open());
if (is_empty()) {
entry_ = new GotoInstr(join);
} else {
exit()->Goto(join);
}
exit_ = NULL;
}
// Appends a graph fragment to a block entry instruction. Returns the entry
// instruction if the fragment was empty or else the exit of the fragment if
// it was non-empty (so NULL if the fragment is closed).
//
// Note that the fragment is no longer a valid fragment after calling this
// function -- the fragment is closed at its entry because the entry has a
// predecessor in the graph.
static Instruction* AppendFragment(BlockEntryInstr* entry,
const EffectGraphVisitor& fragment) {
if (fragment.is_empty()) return entry;
entry->LinkTo(fragment.entry());
return fragment.exit();
}
void EffectGraphVisitor::Join(const TestGraphVisitor& test_fragment,
const EffectGraphVisitor& true_fragment,
const EffectGraphVisitor& false_fragment) {
// We have: a test graph fragment with zero, one, or two available exits;
// and a pair of effect graph fragments with zero or one available exits.
// We want to append the branch and (if necessary) a join node to this
// graph fragment.
ASSERT(is_open());
// 1. Connect the test to this graph.
Append(test_fragment);
// 2. Connect the true and false bodies to the test and record their exits
// (if any).
BlockEntryInstr* true_entry = test_fragment.CreateTrueSuccessor();
Instruction* true_exit = AppendFragment(true_entry, true_fragment);
BlockEntryInstr* false_entry = test_fragment.CreateFalseSuccessor();
Instruction* false_exit = AppendFragment(false_entry, false_fragment);
// 3. Add a join or select one (or neither) of the arms as exit.
if (true_exit == NULL) {
exit_ = false_exit; // May be NULL.
if (false_exit != NULL) temp_index_ = false_fragment.temp_index();
} else if (false_exit == NULL) {
exit_ = true_exit;
temp_index_ = true_fragment.temp_index();
} else {
JoinEntryInstr* join =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
true_exit->Goto(join);
false_exit->Goto(join);
exit_ = join;
ASSERT(true_fragment.temp_index() == false_fragment.temp_index());
temp_index_ = true_fragment.temp_index();
}
}
void EffectGraphVisitor::TieLoop(const TestGraphVisitor& test_fragment,
const EffectGraphVisitor& body_fragment) {
// We have: a test graph fragment with zero, one, or two available exits;
// and an effect graph fragment with zero or one available exits. We want
// to append the 'while loop' consisting of the test graph fragment as
// condition and the effect graph fragment as body.
ASSERT(is_open());
// 1. Connect the body to the test if it is reachable, and if so record
// its exit (if any).
BlockEntryInstr* body_entry = test_fragment.CreateTrueSuccessor();
Instruction* body_exit = AppendFragment(body_entry, body_fragment);
// 2. Connect the test to this graph, including the body if reachable and
// using a fresh join node if the body is reachable and has an open exit.
if (body_exit == NULL) {
Append(test_fragment);
} else {
JoinEntryInstr* join =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
join->LinkTo(test_fragment.entry());
Goto(join);
body_exit->Goto(join);
}
// 3. Set the exit to the graph to be the false successor of the test, a
// fresh target node
exit_ = test_fragment.CreateFalseSuccessor();
}
PushArgumentInstr* EffectGraphVisitor::PushArgument(Value* value) {
PushArgumentInstr* result = new PushArgumentInstr(value);
AddInstruction(result);
return result;
}
Definition* EffectGraphVisitor::BuildStoreTemp(const LocalVariable& local,
Value* value) {
ASSERT(!local.is_captured());
return new StoreLocalInstr(local, value);
}
Definition* EffectGraphVisitor::BuildStoreExprTemp(Value* value) {
return BuildStoreTemp(*owner()->parsed_function().expression_temp_var(),
value);
}
Definition* EffectGraphVisitor::BuildLoadExprTemp() {
return BuildLoadLocal(*owner()->parsed_function().expression_temp_var());
}
Definition* EffectGraphVisitor::BuildStoreLocal(
const LocalVariable& local, Value* value, bool result_is_needed) {
if (local.is_captured()) {
InlineBailout("EffectGraphVisitor::BuildStoreLocal (context)");
if (result_is_needed) {
value = Bind(BuildStoreExprTemp(value));
}
intptr_t delta =
owner()->context_level() - local.owner()->context_level();
ASSERT(delta >= 0);
Value* context = Bind(new CurrentContextInstr());
while (delta-- > 0) {
context = Bind(new LoadFieldInstr(
context, Context::parent_offset(), Type::ZoneHandle()));
}
StoreVMFieldInstr* store =
new StoreVMFieldInstr(context,
Context::variable_offset(local.index()),
value,
local.type());
if (result_is_needed) {
Do(store);
return BuildLoadExprTemp();
} else {
return store;
}
} else {
return new StoreLocalInstr(local, value);
}
}
Definition* EffectGraphVisitor::BuildLoadLocal(const LocalVariable& local) {
if (local.is_captured()) {
InlineBailout("EffectGraphVisitor::BuildLoadLocal (context)");
intptr_t delta =
owner()->context_level() - local.owner()->context_level();
ASSERT(delta >= 0);
Value* context = Bind(new CurrentContextInstr());
while (delta-- > 0) {
context = Bind(new LoadFieldInstr(
context, Context::parent_offset(), Type::ZoneHandle()));
}
return new LoadFieldInstr(context,
Context::variable_offset(local.index()),
local.type());
} else {
return new LoadLocalInstr(local);
}
}
// Stores current context into the 'variable'
void EffectGraphVisitor::BuildStoreContext(const LocalVariable& variable) {
Value* context = Bind(new CurrentContextInstr());
Do(BuildStoreLocal(variable, context, kResultNotNeeded));
}
// Loads context saved in 'context_variable' into the current context.
void EffectGraphVisitor::BuildLoadContext(const LocalVariable& variable) {
Value* load_saved_context = Bind(BuildLoadLocal(variable));
AddInstruction(new StoreContextInstr(load_saved_context));
}
void TestGraphVisitor::ConnectBranchesTo(
const GrowableArray<TargetEntryInstr**>& branches,
JoinEntryInstr* join) const {
ASSERT(!branches.is_empty());
for (intptr_t i = 0; i < branches.length(); i++) {
TargetEntryInstr* target =
new TargetEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
*(branches[i]) = target;
target->Goto(join);
}
}
void TestGraphVisitor::IfTrueGoto(JoinEntryInstr* join) const {
ConnectBranchesTo(true_successor_addresses_, join);
}
void TestGraphVisitor::IfFalseGoto(JoinEntryInstr* join) const {
ConnectBranchesTo(false_successor_addresses_, join);
}
BlockEntryInstr* TestGraphVisitor::CreateSuccessorFor(
const GrowableArray<TargetEntryInstr**>& branches) const {
ASSERT(!branches.is_empty());
if (branches.length() == 1) {
TargetEntryInstr* target =
new TargetEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
*(branches[0]) = target;
return target;
}
JoinEntryInstr* join =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
ConnectBranchesTo(branches, join);
return join;
}
BlockEntryInstr* TestGraphVisitor::CreateTrueSuccessor() const {
return CreateSuccessorFor(true_successor_addresses_);
}
BlockEntryInstr* TestGraphVisitor::CreateFalseSuccessor() const {
return CreateSuccessorFor(false_successor_addresses_);
}
void TestGraphVisitor::ReturnValue(Value* value) {
if (FLAG_enable_type_checks) {
value = Bind(new AssertBooleanInstr(condition_token_pos(), value));
}
Value* constant_true = Bind(new ConstantInstr(Bool::True()));
StrictCompareInstr* comp =
new StrictCompareInstr(Token::kEQ_STRICT, value, constant_true);
BranchInstr* branch = new BranchInstr(comp);
AddInstruction(branch);
CloseFragment();
true_successor_addresses_.Add(branch->true_successor_address());
false_successor_addresses_.Add(branch->false_successor_address());
}
void TestGraphVisitor::MergeBranchWithComparison(ComparisonInstr* comp) {
ControlInstruction* branch;
if (Token::IsStrictEqualityOperator(comp->kind())) {
branch = new BranchInstr(new StrictCompareInstr(comp->kind(),
comp->left(),
comp->right()));
} else if (Token::IsEqualityOperator(comp->kind()) &&
(comp->left()->BindsToConstantNull() ||
comp->right()->BindsToConstantNull())) {
branch = new BranchInstr(new StrictCompareInstr(
(comp->kind() == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT,
comp->left(),
comp->right()));
} else {
branch = new BranchInstr(comp, FLAG_enable_type_checks);
}
AddInstruction(branch);
CloseFragment();
true_successor_addresses_.Add(branch->true_successor_address());
false_successor_addresses_.Add(branch->false_successor_address());
}
void TestGraphVisitor::MergeBranchWithNegate(BooleanNegateInstr* neg) {
ASSERT(!FLAG_enable_type_checks);
Value* constant_true = Bind(new ConstantInstr(Bool::True()));
BranchInstr* branch = new BranchInstr(
new StrictCompareInstr(Token::kNE_STRICT, neg->value(), constant_true));
AddInstruction(branch);
CloseFragment();
true_successor_addresses_.Add(branch->true_successor_address());
false_successor_addresses_.Add(branch->false_successor_address());
}
void TestGraphVisitor::ReturnDefinition(Definition* definition) {
ComparisonInstr* comp = definition->AsComparison();
if (comp != NULL) {
MergeBranchWithComparison(comp);
return;
}
if (!FLAG_enable_type_checks) {
BooleanNegateInstr* neg = definition->AsBooleanNegate();
if (neg != NULL) {
MergeBranchWithNegate(neg);
return;
}
}
ReturnValue(Bind(definition));
}
// Special handling for AND/OR.
void TestGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) {
// Operators "&&" and "||" cannot be overloaded therefore do not call
// operator.
if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
TestGraphVisitor for_left(owner(),
temp_index(),
node->left()->token_pos());
node->left()->Visit(&for_left);
TestGraphVisitor for_right(owner(),
temp_index(),
node->right()->token_pos());
node->right()->Visit(&for_right);
Append(for_left);
if (node->kind() == Token::kAND) {
AppendFragment(for_left.CreateTrueSuccessor(), for_right);
true_successor_addresses_.AddArray(for_right.true_successor_addresses_);
false_successor_addresses_.AddArray(for_left.false_successor_addresses_);
false_successor_addresses_.AddArray(for_right.false_successor_addresses_);
} else {
ASSERT(node->kind() == Token::kOR);
AppendFragment(for_left.CreateFalseSuccessor(), for_right);
false_successor_addresses_.AddArray(for_right.false_successor_addresses_);
true_successor_addresses_.AddArray(for_left.true_successor_addresses_);
true_successor_addresses_.AddArray(for_right.true_successor_addresses_);
}
CloseFragment();
return;
}
ValueGraphVisitor::VisitBinaryOpNode(node);
}
void EffectGraphVisitor::Bailout(const char* reason) {
owner()->Bailout(reason);
}
void EffectGraphVisitor::InlineBailout(const char* reason) {
owner()->parsed_function().function().set_is_inlinable(false);
if (owner()->IsInlining()) owner()->Bailout(reason);
}
// <Statement> ::= Return { value: <Expression>
// inlined_finally_list: <InlinedFinally>* }
void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) {
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
InlineBailout("EffectGraphVisitor::VisitReturnNode (exception)");
EffectGraphVisitor for_effect(owner(), temp_index());
node->InlinedFinallyNodeAt(i)->Visit(&for_effect);
Append(for_effect);
if (!is_open()) return;
}
Value* return_value = for_value.value();
if (FLAG_enable_type_checks) {
const Function& function = owner()->parsed_function().function();
const bool is_implicit_dynamic_getter =
(!function.is_static() &&
((function.kind() == RawFunction::kImplicitGetter) ||
(function.kind() == RawFunction::kConstImplicitGetter)));
// Implicit getters do not need a type check at return, unless they compute
// the initial value of a static field.
// The body of a constructor cannot modify the type of the
// constructed instance, which is passed in as an implicit parameter.
// However, factories may create an instance of the wrong type.
if (!is_implicit_dynamic_getter && !function.IsConstructor()) {
const AbstractType& dst_type =
AbstractType::ZoneHandle(
owner()->parsed_function().function().result_type());
return_value = BuildAssignableValue(node->value()->token_pos(),
return_value,
dst_type,
Symbols::FunctionResult());
}
}
intptr_t current_context_level = owner()->context_level();
ASSERT(current_context_level >= 0);
if (owner()->parsed_function().saved_entry_context_var() != NULL) {
// CTX on entry was saved, but not linked as context parent.
BuildLoadContext(*owner()->parsed_function().saved_entry_context_var());
} else {
while (current_context_level-- > 0) {
UnchainContext();
}
}
AddReturnExit(node->token_pos(), return_value);
}
// <Expression> ::= Literal { literal: Instance }
void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) {
return;
}
void ValueGraphVisitor::VisitLiteralNode(LiteralNode* node) {
ReturnDefinition(new ConstantInstr(node->literal()));
}
// Type nodes are used when a type is referenced as a literal. Type nodes
// can also be used for the right-hand side of instanceof comparisons,
// but they are handled specially in that context, not here.
void EffectGraphVisitor::VisitTypeNode(TypeNode* node) {
return;
}
void ValueGraphVisitor::VisitTypeNode(TypeNode* node) {
ReturnDefinition(new ConstantInstr(node->type()));
}
// Returns true if the type check can be skipped, for example, if the
// destination type is dynamic or if the compile type of the value is a subtype
// of the destination type.
bool EffectGraphVisitor::CanSkipTypeCheck(intptr_t token_pos,
Value* value,
const AbstractType& dst_type,
const String& dst_name) {
ASSERT(!dst_type.IsNull());
ASSERT(dst_type.IsFinalized());
// If the destination type is malformed, a dynamic type error must be thrown
// at run time.
if (dst_type.IsMalformed()) {
return false;
}
// Any type is more specific than the dynamic type and than the Object type.
if (dst_type.IsDynamicType() || dst_type.IsObjectType()) {
return true;
}
// Do not perform type check elimination if this optimization is turned off.
if (!FLAG_eliminate_type_checks) {
return false;
}
// If nothing is known about the value, as is the case for passed-in
// parameters, and since dst_type is not one of the tested cases above, then
// the type test cannot be eliminated.
if (value == NULL) {
return false;
}
const bool eliminated = value->Type()->IsAssignableTo(dst_type);
if (FLAG_trace_type_check_elimination) {
FlowGraphPrinter::PrintTypeCheck(owner()->parsed_function(),
token_pos,
value,
dst_type,
dst_name,
eliminated);
}
return eliminated;
}
// <Expression> :: Assignable { expr: <Expression>
// type: AbstractType
// dst_name: String }
void EffectGraphVisitor::VisitAssignableNode(AssignableNode* node) {
ValueGraphVisitor for_value(owner(), temp_index());
node->expr()->Visit(&for_value);
Append(for_value);
Definition* checked_value;
if (CanSkipTypeCheck(node->expr()->token_pos(),
for_value.value(),
node->type(),
node->dst_name())) {
checked_value = for_value.value()->definition(); // No check needed.
} else {
checked_value = BuildAssertAssignable(node->expr()->token_pos(),
for_value.value(),
node->type(),
node->dst_name());
}
ReturnDefinition(checked_value);
}
void ValueGraphVisitor::VisitAssignableNode(AssignableNode* node) {
ValueGraphVisitor for_value(owner(), temp_index());
node->expr()->Visit(&for_value);
Append(for_value);
ReturnValue(BuildAssignableValue(node->expr()->token_pos(),
for_value.value(),
node->type(),
node->dst_name()));
}
// <Expression> :: BinaryOp { kind: Token::Kind
// left: <Expression>
// right: <Expression> }
void EffectGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) {
// Operators "&&" and "||" cannot be overloaded therefore do not call
// operator.
if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
// See ValueGraphVisitor::VisitBinaryOpNode.
TestGraphVisitor for_left(owner(),
temp_index(),
node->left()->token_pos());
node->left()->Visit(&for_left);
EffectGraphVisitor for_right(owner(), temp_index());
node->right()->Visit(&for_right);
EffectGraphVisitor empty(owner(), temp_index());
if (node->kind() == Token::kAND) {
Join(for_left, for_right, empty);
} else {
Join(for_left, empty, for_right);
}
return;
}
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
PushArgumentInstr* push_left = PushArgument(for_left_value.value());
ValueGraphVisitor for_right_value(owner(), temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
PushArgumentInstr* push_right = PushArgument(for_right_value.value());
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
arguments->Add(push_left);
arguments->Add(push_right);
const String& name = String::ZoneHandle(Symbols::New(node->Name()));
InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(),
name,
node->kind(),
arguments,
Array::ZoneHandle(),
2);
ReturnDefinition(call);
}
// Special handling for AND/OR.
void ValueGraphVisitor::VisitBinaryOpNode(BinaryOpNode* node) {
// Operators "&&" and "||" cannot be overloaded therefore do not call
// operator.
if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
// Implement short-circuit logic: do not evaluate right if evaluation
// of left is sufficient.
// AND: left ? right === true : false;
// OR: left ? true : right === true;
TestGraphVisitor for_test(owner(),
temp_index(),
node->left()->token_pos());
node->left()->Visit(&for_test);
ValueGraphVisitor for_right(owner(), temp_index());
node->right()->Visit(&for_right);
Value* right_value = for_right.value();
if (FLAG_enable_type_checks) {
right_value =
for_right.Bind(new AssertBooleanInstr(node->right()->token_pos(),
right_value));
}
Value* constant_true = for_right.Bind(new ConstantInstr(Bool::True()));
Value* compare =
for_right.Bind(new StrictCompareInstr(Token::kEQ_STRICT,
right_value,
constant_true));
for_right.Do(BuildStoreExprTemp(compare));
if (node->kind() == Token::kAND) {
ValueGraphVisitor for_false(owner(), temp_index());
Value* constant_false = for_false.Bind(new ConstantInstr(Bool::False()));
for_false.Do(BuildStoreExprTemp(constant_false));
Join(for_test, for_right, for_false);
} else {
ASSERT(node->kind() == Token::kOR);
ValueGraphVisitor for_true(owner(), temp_index());
Value* constant_true = for_true.Bind(new ConstantInstr(Bool::True()));
for_true.Do(BuildStoreExprTemp(constant_true));
Join(for_test, for_true, for_right);
}
ReturnDefinition(BuildLoadExprTemp());
return;
}
EffectGraphVisitor::VisitBinaryOpNode(node);
}
void EffectGraphVisitor::BuildTypecheckPushArguments(
intptr_t token_pos,
PushArgumentInstr** push_instantiator_result,
PushArgumentInstr** push_instantiator_type_arguments_result) {
const Class& instantiator_class = Class::Handle(
owner()->parsed_function().function().Owner());
// Since called only when type tested against is not instantiated.
ASSERT(instantiator_class.NumTypeParameters() > 0);
Value* instantiator_type_arguments = NULL;
Value* instantiator = BuildInstantiator();
if (instantiator == NULL) {
// No instantiator when inside factory.
*push_instantiator_result = PushArgument(BuildNullValue());
instantiator_type_arguments =
BuildInstantiatorTypeArguments(token_pos, NULL);
} else {
instantiator = Bind(BuildStoreExprTemp(instantiator));
*push_instantiator_result = PushArgument(instantiator);
Value* loaded = Bind(BuildLoadExprTemp());
instantiator_type_arguments =
BuildInstantiatorTypeArguments(token_pos, loaded);
}
*push_instantiator_type_arguments_result =
PushArgument(instantiator_type_arguments);
}
void EffectGraphVisitor::BuildTypecheckArguments(
intptr_t token_pos,
Value** instantiator_result,
Value** instantiator_type_arguments_result) {
Value* instantiator = NULL;
Value* instantiator_type_arguments = NULL;
const Class& instantiator_class = Class::Handle(
owner()->parsed_function().function().Owner());
// Since called only when type tested against is not instantiated.
ASSERT(instantiator_class.NumTypeParameters() > 0);
instantiator = BuildInstantiator();
if (instantiator == NULL) {
// No instantiator when inside factory.
instantiator = BuildNullValue();
instantiator_type_arguments =
BuildInstantiatorTypeArguments(token_pos, NULL);
} else {
// Preserve instantiator.
instantiator = Bind(BuildStoreExprTemp(instantiator));
Value* loaded = Bind(BuildLoadExprTemp());
instantiator_type_arguments =
BuildInstantiatorTypeArguments(token_pos, loaded);
}
*instantiator_result = instantiator;
*instantiator_type_arguments_result = instantiator_type_arguments;
}
Value* EffectGraphVisitor::BuildNullValue() {
return Bind(new ConstantInstr(Object::ZoneHandle()));
}
// Used for testing incoming arguments.
AssertAssignableInstr* EffectGraphVisitor::BuildAssertAssignable(
intptr_t token_pos,
Value* value,
const AbstractType& dst_type,
const String& dst_name) {
// Build the type check computation.
Value* instantiator = NULL;
Value* instantiator_type_arguments = NULL;
if (dst_type.IsInstantiated()) {
instantiator = BuildNullValue();
instantiator_type_arguments = BuildNullValue();
} else {
BuildTypecheckArguments(token_pos,
&instantiator,
&instantiator_type_arguments);
}
return new AssertAssignableInstr(token_pos,
value,
instantiator,
instantiator_type_arguments,
dst_type,
dst_name);
}
// Used for type casts and to test assignments.
Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos,
Value* value,
const AbstractType& dst_type,
const String& dst_name) {
if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) {
return value;
}
return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name));
}
void EffectGraphVisitor::BuildTypeTest(ComparisonNode* node) {
ASSERT(Token::IsTypeTestOperator(node->kind()));
EffectGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
}
void ValueGraphVisitor::BuildTypeTest(ComparisonNode* node) {
ASSERT(Token::IsTypeTestOperator(node->kind()));
const AbstractType& type = node->right()->AsTypeNode()->type();
ASSERT(type.IsFinalized() && !type.IsMalformed());
const bool negate_result = (node->kind() == Token::kISNOT);
// All objects are instances of type T if Object type is a subtype of type T.
const Type& object_type = Type::Handle(Type::ObjectType());
if (type.IsInstantiated() && object_type.IsSubtypeOf(type, NULL)) {
// Must evaluate left side.
EffectGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ReturnDefinition(new ConstantInstr(negate_result ?
Bool::False() : Bool::True()));
return;
}
// Eliminate the test if it can be performed successfully at compile time.
if ((node->left() != NULL) &&
node->left()->IsLiteralNode() &&
type.IsInstantiated()) {
const Instance& literal_value = node->left()->AsLiteralNode()->literal();
const Class& cls = Class::Handle(literal_value.clazz());
ConstantInstr* result = NULL;
if (cls.IsNullClass()) {
// A null object is only an instance of Object and dynamic, which has
// already been checked above (if the type is instantiated). So we can
// return false here if the instance is null (and if the type is
// instantiated).
result = new ConstantInstr(negate_result ? Bool::True() : Bool::False());
} else {
Error& malformed_error = Error::Handle();
if (literal_value.IsInstanceOf(type,
TypeArguments::Handle(),
&malformed_error)) {
result = new ConstantInstr(negate_result ?
Bool::False() : Bool::True());
} else {
result = new ConstantInstr(negate_result ?
Bool::True() : Bool::False());
}
ASSERT(malformed_error.IsNull());
}
ReturnDefinition(result);
return;
}
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
PushArgumentInstr* push_left = PushArgument(for_left_value.value());
PushArgumentInstr* push_instantiator = NULL;
PushArgumentInstr* push_type_args = NULL;
if (type.IsInstantiated()) {
push_instantiator = PushArgument(BuildNullValue());
push_type_args = PushArgument(BuildNullValue());
} else {
BuildTypecheckPushArguments(node->token_pos(),
&push_instantiator,
&push_type_args);
}
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(5);
arguments->Add(push_left);
arguments->Add(push_instantiator);
arguments->Add(push_type_args);
ASSERT(!node->right()->AsTypeNode()->type().IsNull());
Value* type_arg = Bind(
new ConstantInstr(node->right()->AsTypeNode()->type()));
arguments->Add(PushArgument(type_arg));
const Bool& negate = (node->kind() == Token::kISNOT) ? Bool::True() :
Bool::False();
Value* negate_arg = Bind(new ConstantInstr(negate));
arguments->Add(PushArgument(negate_arg));
const intptr_t kNumArgsChecked = 1;
InstanceCallInstr* call = new InstanceCallInstr(
node->token_pos(),
PrivateCoreLibName(Symbols::_instanceOf()),
node->kind(),
arguments,
Array::ZoneHandle(), // No argument names.
kNumArgsChecked);
ReturnDefinition(call);
}
void EffectGraphVisitor::BuildTypeCast(ComparisonNode* node) {
ASSERT(Token::IsTypeCastOperator(node->kind()));
const AbstractType& type = node->right()->AsTypeNode()->type();
ASSERT(type.IsFinalized()); // The type in a type cast may be malformed.
ValueGraphVisitor for_value(owner(), temp_index());
node->left()->Visit(&for_value);
const String& dst_name = String::ZoneHandle(
Symbols::New(Exceptions::kCastErrorDstName));
if (!CanSkipTypeCheck(node->token_pos(), for_value.value(), type, dst_name)) {
Append(for_value);
Do(BuildAssertAssignable(
node->token_pos(), for_value.value(), type, dst_name));
}
}
void ValueGraphVisitor::BuildTypeCast(ComparisonNode* node) {
ASSERT(Token::IsTypeCastOperator(node->kind()));
ASSERT(!node->right()->AsTypeNode()->type().IsNull());
const AbstractType& type = node->right()->AsTypeNode()->type();
ASSERT(type.IsFinalized()); // The type in a type cast may be malformed.
ValueGraphVisitor for_value(owner(), temp_index());
node->left()->Visit(&for_value);
Append(for_value);
const String& dst_name = String::ZoneHandle(
Symbols::New(Exceptions::kCastErrorDstName));
if (type.IsMalformed()) {
ReturnValue(BuildAssignableValue(node->token_pos(),
for_value.value(),
type,
dst_name));
} else {
if (CanSkipTypeCheck(node->token_pos(),
for_value.value(),
type,
dst_name)) {
ReturnValue(for_value.value());
return;
}
PushArgumentInstr* push_left = PushArgument(for_value.value());
PushArgumentInstr* push_instantiator = NULL;
PushArgumentInstr* push_type_args = NULL;
if (type.IsInstantiated()) {
push_instantiator = PushArgument(BuildNullValue());
push_type_args = PushArgument(BuildNullValue());
} else {
BuildTypecheckPushArguments(node->token_pos(),
&push_instantiator,
&push_type_args);
}
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(4);
arguments->Add(push_left);
arguments->Add(push_instantiator);
arguments->Add(push_type_args);
Value* type_arg = Bind(new ConstantInstr(type));
arguments->Add(PushArgument(type_arg));
const intptr_t kNumArgsChecked = 1;
InstanceCallInstr* call = new InstanceCallInstr(
node->token_pos(),
PrivateCoreLibName(Symbols::_as()),
node->kind(),
arguments,
Array::ZoneHandle(), // No argument names.
kNumArgsChecked);
ReturnDefinition(call);
}
}
// <Expression> :: Comparison { kind: Token::Kind
// left: <Expression>
// right: <Expression> }
// TODO(srdjan): Implement new equality.
void EffectGraphVisitor::VisitComparisonNode(ComparisonNode* node) {
if (Token::IsTypeTestOperator(node->kind())) {
BuildTypeTest(node);
return;
}
if (Token::IsTypeCastOperator(node->kind())) {
BuildTypeCast(node);
return;
}
if ((node->kind() == Token::kEQ_STRICT) ||
(node->kind() == Token::kNE_STRICT)) {
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ValueGraphVisitor for_right_value(owner(), temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
StrictCompareInstr* comp = new StrictCompareInstr(
node->kind(), for_left_value.value(), for_right_value.value());
ReturnDefinition(comp);
return;
}
if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ValueGraphVisitor for_right_value(owner(), temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
if (FLAG_enable_type_checks) {
EqualityCompareInstr* comp = new EqualityCompareInstr(
node->token_pos(),
Token::kEQ,
for_left_value.value(),
for_right_value.value());
if (node->kind() == Token::kEQ) {
ReturnDefinition(comp);
} else {
Value* eq_result = Bind(comp);
eq_result = Bind(new AssertBooleanInstr(node->token_pos(), eq_result));
ReturnDefinition(new BooleanNegateInstr(eq_result));
}
} else {
EqualityCompareInstr* comp = new EqualityCompareInstr(
node->token_pos(),
node->kind(),
for_left_value.value(),
for_right_value.value());
ReturnDefinition(comp);
}
return;
}
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ValueGraphVisitor for_right_value(owner(), temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
RelationalOpInstr* comp = new RelationalOpInstr(node->token_pos(),
node->kind(),
for_left_value.value(),
for_right_value.value());
ReturnDefinition(comp);
}
void EffectGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) {
// "!" cannot be overloaded, therefore do not call operator.
if (node->kind() == Token::kNOT) {
ValueGraphVisitor for_value(owner(), temp_index());
node->operand()->Visit(&for_value);
Append(for_value);
Value* value = for_value.value();
if (FLAG_enable_type_checks) {
value =
Bind(new AssertBooleanInstr(node->operand()->token_pos(), value));
}
BooleanNegateInstr* negate = new BooleanNegateInstr(value);
ReturnDefinition(negate);
return;
}
ValueGraphVisitor for_value(owner(), temp_index());
node->operand()->Visit(&for_value);
Append(for_value);
PushArgumentInstr* push_value = PushArgument(for_value.value());
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(1);
arguments->Add(push_value);
InstanceCallInstr* call =
new InstanceCallInstr(node->token_pos(),
String::ZoneHandle(
Symbols::New(Token::Str(node->kind()))),
node->kind(),
arguments,
Array::ZoneHandle(),
1);
ReturnDefinition(call);
}
void EffectGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
// Translate the subexpressions for their effects.
EffectGraphVisitor for_true(owner(), temp_index());
node->true_expr()->Visit(&for_true);
EffectGraphVisitor for_false(owner(), temp_index());
node->false_expr()->Visit(&for_false);
Join(for_test, for_true, for_false);
}
void ValueGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
ValueGraphVisitor for_true(owner(), temp_index());
node->true_expr()->Visit(&for_true);
ASSERT(for_true.is_open());
for_true.Do(BuildStoreExprTemp(for_true.value()));
ValueGraphVisitor for_false(owner(), temp_index());
node->false_expr()->Visit(&for_false);
ASSERT(for_false.is_open());
for_false.Do(BuildStoreExprTemp(for_false.value()));
Join(for_test, for_true, for_false);
ReturnDefinition(BuildLoadExprTemp());
}
// <Statement> ::= If { condition: <Expression>
// true_branch: <Sequence>
// false_branch: <Sequence> }
void EffectGraphVisitor::VisitIfNode(IfNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
EffectGraphVisitor for_true(owner(), temp_index());
EffectGraphVisitor for_false(owner(), temp_index());
node->true_branch()->Visit(&for_true);
// The for_false graph fragment will be empty (default graph fragment) if
// we do not call Visit.
if (node->false_branch() != NULL) node->false_branch()->Visit(&for_false);
Join(for_test, for_true, for_false);
}
void EffectGraphVisitor::VisitSwitchNode(SwitchNode* node) {
EffectGraphVisitor switch_body(owner(), temp_index());
node->body()->Visit(&switch_body);
Append(switch_body);
if ((node->label() != NULL) && (node->label()->join_for_break() != NULL)) {
if (is_open()) Goto(node->label()->join_for_break());
exit_ = node->label()->join_for_break();
}
// No continue label allowed.
ASSERT((node->label() == NULL) ||
(node->label()->join_for_continue() == NULL));
}
// A case node contains zero or more case expressions, can contain default
// and a case statement body.
// Compose fragment as follows:
// - if no case expressions, must have default:
// a) target
// b) [ case-statements ]
//
// - if has 1 or more case statements
// a) target-0
// b) [ case-expression-0 ] -> (true-target-0, target-1)
// c) target-1
// d) [ case-expression-1 ] -> (true-target-1, exit-target)
// e) true-target-0 -> case-statements-join
// f) true-target-1 -> case-statements-join
// g) case-statements-join
// h) [ case-statements ] -> exit-join
// i) exit-target -> exit-join
// j) exit-join
//
// Note: The specification of switch/case is under discussion and may change
// drastically.
void EffectGraphVisitor::VisitCaseNode(CaseNode* node) {
const intptr_t len = node->case_expressions()->length();
// Create case statements instructions.
EffectGraphVisitor for_case_statements(owner(), temp_index());
// Compute start of statements fragment.
JoinEntryInstr* statement_start = NULL;
if ((node->label() != NULL) && node->label()->is_continue_target()) {
// Since a labeled jump continue statement occur in a different case node,
// allocate JoinNode here and use it as statement start.
statement_start = node->label()->join_for_continue();
if (statement_start == NULL) {
statement_start = new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
node->label()->set_join_for_continue(statement_start);
}
} else {
statement_start = new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
}
node->statements()->Visit(&for_case_statements);
Instruction* statement_exit =
AppendFragment(statement_start, for_case_statements);
if (is_open() && (len == 0)) {
ASSERT(node->contains_default());
// Default only case node.
Goto(statement_start);
exit_ = statement_exit;
return;
}
// Generate instructions for all case expressions.
TargetEntryInstr* next_target = NULL;
for (intptr_t i = 0; i < len; i++) {
AstNode* case_expr = node->case_expressions()->NodeAt(i);
TestGraphVisitor for_case_expression(owner(),
temp_index(),
case_expr->token_pos());
case_expr->Visit(&for_case_expression);
if (i == 0) {
// Append only the first one, everything else is connected from it.
Append(for_case_expression);
} else {
ASSERT(next_target != NULL);
AppendFragment(next_target, for_case_expression);
}
for_case_expression.IfTrueGoto(statement_start);
next_target = for_case_expression.CreateFalseSuccessor()->AsTargetEntry();
}
// Once a test fragment has been added, this fragment is closed.
ASSERT(!is_open());
Instruction* exit_instruction = NULL;
// Handle last (or only) case: false goes to exit or to statement if this
// node contains default.
if (len > 0) {
ASSERT(next_target != NULL);
if (node->contains_default()) {
// True and false go to statement start.
next_target->Goto(statement_start);
exit_instruction = statement_exit;
} else {
if (statement_exit != NULL) {
JoinEntryInstr* join = new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
statement_exit->Goto(join);
next_target->Goto(join);
exit_instruction = join;
} else {
exit_instruction = next_target;
}
}
} else {
// A CaseNode without case expressions must contain default.
ASSERT(node->contains_default());
Goto(statement_start);
exit_instruction = statement_exit;
}
ASSERT(!is_open());
exit_ = exit_instruction;
}
// <Statement> ::= While { label: SourceLabel
// condition: <Expression>
// body: <Sequence> }
// The fragment is composed as follows:
// a) loop-join
// b) [ test ] -> (body-entry-target, loop-exit-target)
// c) body-entry-target
// d) [ body ] -> (continue-join)
// e) continue-join -> (loop-join)
// f) loop-exit-target
// g) break-join (optional)
void EffectGraphVisitor::VisitWhileNode(WhileNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
ASSERT(!for_test.is_empty()); // Language spec.
EffectGraphVisitor for_body(owner(), temp_index());
for_body.AddInstruction(
new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
// Labels are set after body traversal.
SourceLabel* lbl = node->label();
ASSERT(lbl != NULL);
JoinEntryInstr* join = lbl->join_for_continue();
if (join != NULL) {
if (for_body.is_open()) for_body.Goto(join);
for_body.exit_ = join;
}
TieLoop(for_test, for_body);
join = lbl->join_for_break();
if (join != NULL) {
Goto(join);
exit_ = join;
}
}
// The fragment is composed as follows:
// a) body-entry-join
// b) [ body ]
// c) test-entry (continue-join or body-exit-target)
// d) [ test-entry ] -> (back-target, loop-exit-target)
// e) back-target -> (body-entry-join)
// f) loop-exit-target
// g) break-join
void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) {
// Traverse body first in order to generate continue and break labels.
EffectGraphVisitor for_body(owner(), temp_index());
for_body.AddInstruction(
new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
ASSERT(is_open());
// Tie do-while loop (test is after the body).
JoinEntryInstr* body_entry_join =
new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
Goto(body_entry_join);
Instruction* body_exit = AppendFragment(body_entry_join, for_body);
JoinEntryInstr* join = node->label()->join_for_continue();
if ((body_exit != NULL) || (join != NULL)) {
if (join == NULL) {
join = new JoinEntryInstr(owner()->AllocateBlockId(),
owner()->try_index());
}
join->LinkTo(for_test.entry());
if (body_exit != NULL) {
body_exit->Goto(join);
}
}
for_test.IfTrueGoto(body_entry_join);
join = node->label()->join_for_break();
if (join == NULL) {
exit_ = for_test.CreateFalseSuccessor();
} else {
for_test.IfFalseGoto(join);
exit_ = join;
}
}
// A ForNode can contain break and continue jumps. 'break' joins to
// ForNode exit, 'continue' joins at increment entry. The fragment is composed
// as follows:
// a) [ initializer ]
// b) loop-join
// c) [ test ] -> (body-entry-target, loop-exit-target)
// d) body-entry-target
// e) [ body ]
// f) continue-join (optional)
// g) [ increment ] -> (loop-join)
// h) loop-exit-target
// i) break-join
void EffectGraphVisitor::VisitForNode(ForNode* node) {
EffectGraphVisitor for_initializer(owner(), temp_index());
node->initializer()->Visit(&for_initializer);
Append(for_initializer);
ASSERT(is_open());
// Compose body to set any jump labels.
EffectGraphVisitor for_body(owner(), temp_index());
for_body.AddInstruction(
new CheckStackOverflowInstr(node->token_pos()));
node->body()->Visit(&for_body);
// Join loop body, increment and compute their end instruction.
ASSERT(!for_body.is_empty());
Instruction* loop_increment_end = NULL;
EffectGraphVisitor for_increment(owner(), temp_index());
node->increment()->Visit(&for_increment);
JoinEntryInstr* join = node->label()->join_for_continue();
if (join != NULL) {
// Insert the join between the body and increment.
if (for_body.is_open()) for_body.Goto(join);
loop_increment_end = AppendFragment(join, for_increment);
ASSERT(loop_increment_end != NULL);
} else if (for_body.is_open()) {
// Do not insert an extra basic block.
for_body.Append(for_increment);
loop_increment_end = for_body.exit();
// 'for_body' contains at least the stack check.
ASSERT(loop_increment_end != NULL);
} else {
loop_increment_end = NULL;
}
// 'loop_increment_end' is NULL only if there is no join for continue and the
// body is not open, i.e., no backward branch exists.
if (loop_increment_end != NULL) {
JoinEntryInstr* loop_start =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
Goto(loop_start);
loop_increment_end->Goto(loop_start);
exit_ = loop_start;
}
if (node->condition() == NULL) {
// Endless loop, no test.
JoinEntryInstr* body_entry =
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index());
AppendFragment(body_entry, for_body);
Goto(body_entry);
if (node->label()->join_for_break() != NULL) {
// Control flow of ForLoop continues into join_for_break.
exit_ = node->label()->join_for_break();
}
} else {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_pos());
node->condition()->Visit(&for_test);
Append(for_test);
BlockEntryInstr* body_entry = for_test.CreateTrueSuccessor();
AppendFragment(body_entry, for_body);
if (node->label()->join_for_break() == NULL) {
exit_ = for_test.CreateFalseSuccessor();
} else {
for_test.IfFalseGoto(node->label()->join_for_break());
exit_ = node->label()->join_for_break();
}
}
}
void EffectGraphVisitor::VisitJumpNode(JumpNode* node) {
for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
EffectGraphVisitor for_effect(owner(), temp_index());
node->InlinedFinallyNodeAt(i)->Visit(&for_effect);
Append(for_effect);
if (!is_open()) return;
}
// Unchain the context(s) up to the outer context level of the scope which
// contains the destination label.
SourceLabel* label = node->label();
ASSERT(label->owner() != NULL);
int target_context_level = 0;
LocalScope* target_scope = label->owner();
if (target_scope->num_context_variables() > 0) {
// The scope of the target label allocates a context, therefore its outer
// scope is at a lower context level.
target_context_level = target_scope->context_level() - 1;
} else {
// The scope of the target label does not allocate a context, so its outer
// scope is at the same context level. Find it.
while ((target_scope != NULL) &&
(target_scope->num_context_variables() == 0)) {
target_scope = target_scope->parent();
}
if (target_scope != NULL) {
target_context_level = target_scope->context_level();
}
}
ASSERT(target_context_level >= 0);
intptr_t current_context_level = owner()->context_level();
ASSERT(current_context_level >= target_context_level);
while (current_context_level-- > target_context_level) {
UnchainContext();
}
JoinEntryInstr* jump_target = NULL;
if (node->kind() == Token::kBREAK) {
if (node->label()->join_for_break() == NULL) {
node->label()->set_join_for_break(
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()));
}
jump_target = node->label()->join_for_break();
} else {
if (node->label()->join_for_continue() == NULL) {
node->label()->set_join_for_continue(
new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()));
}
jump_target = node->label()->join_for_continue();
}
Goto(jump_target);
}
void EffectGraphVisitor::VisitArgumentListNode(ArgumentListNode* node) {
UNREACHABLE();
}
void EffectGraphVisitor::VisitArgumentDefinitionTestNode(
ArgumentDefinitionTestNode* node) {
InlineBailout("EffectGraphVisitor::VisitArgumentDefinitionTestNode");
Definition* load = BuildLoadLocal(node->saved_arguments_descriptor());
Value* arguments_descriptor = Bind(load);
ArgumentDefinitionTestInstr* arg_def_test =
new ArgumentDefinitionTestInstr(node, arguments_descriptor);
ReturnDefinition(arg_def_test);
}
void EffectGraphVisitor::VisitArrayNode(ArrayNode* node) {
const AbstractTypeArguments& type_args =
AbstractTypeArguments::ZoneHandle(node->type().arguments());
Value* element_type = BuildInstantiatedTypeArguments(node->token_pos(),
type_args);
CreateArrayInstr* create = new CreateArrayInstr(node->token_pos(),
node->length(),
node->type(),
element_type);
Value* array_val = Bind(create);
Definition* store = BuildStoreTemp(node->temp_local(), array_val);
Do(store);
const intptr_t class_id = create->Type()->ToCid();
const intptr_t deopt_id = Isolate::kNoDeoptId;
for (int i = 0; i < node->length(); ++i) {
Value* array = Bind(
new LoadLocalInstr(node->temp_local()));
Value* index = Bind(new ConstantInstr(Smi::ZoneHandle(Smi::New(i))));
ValueGraphVisitor for_value(owner(), temp_index());
node->ElementAt(i)->Visit(&for_value);
Append(for_value);
// No store barrier needed for constants.
const StoreBarrierType emit_store_barrier =
for_value.value()->BindsToConstant()
? kNoStoreBarrier
: kEmitStoreBarrier;
intptr_t index_scale = FlowGraphCompiler::ElementSizeFor(class_id);
StoreIndexedInstr* store = new StoreIndexedInstr(
array, index, for_value.value(),
emit_store_barrier, index_scale, class_id, deopt_id);
Do(store);
}
ReturnDefinition(new LoadLocalInstr(node->temp_local()));
}
void EffectGraphVisitor::VisitClosureNode(ClosureNode* node) {
const Function& function = node->function();
if (function.IsImplicitStaticClosureFunction()) {
Instance& closure = Instance::ZoneHandle();
closure ^= function.implicit_static_closure();
if (closure.IsNull()) {
ObjectStore* object_store = Isolate::Current()->object_store();
const Context& context = Context::Handle(object_store->empty_context());
closure ^= Closure::New(function, context, Heap::kOld);
function.set_implicit_static_closure(closure);
}
ReturnDefinition(new ConstantInstr(closure));
return;
}
Value* receiver = NULL;
if (function.IsNonImplicitClosureFunction()) {
// The context scope may have already been set by the non-optimizing
// compiler. If it was not, set it here.
if (function.context_scope() == ContextScope::null()) {
// TODO(regis): Why are we not doing this in the parser?
const ContextScope& context_scope = ContextScope::ZoneHandle(
node->scope()->PreserveOuterScope(owner()->context_level()));
ASSERT(!function.HasCode());
ASSERT(function.context_scope() == ContextScope::null());
function.set_context_scope(context_scope);
}
receiver = BuildNullValue();
} else {
ASSERT(function.IsImplicitInstanceClosureFunction());
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
receiver = for_receiver.value();
}
PushArgumentInstr* push_receiver = PushArgument(receiver);
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
arguments->Add(push_receiver);
ASSERT(function.context_scope() != ContextScope::null());
// The function type of a closure may have type arguments. In that case, pass
// the type arguments of the instantiator. Otherwise, pass null object.
const Class& cls = Class::Handle(function.signature_class());
ASSERT(!cls.IsNull());
const bool requires_type_arguments = cls.HasTypeArguments();
Value* type_arguments = NULL;
if (requires_type_arguments) {
ASSERT(!function.IsImplicitStaticClosureFunction());
type_arguments = BuildInstantiatorTypeArguments(node->token_pos(), NULL);
} else {
type_arguments = BuildNullValue();
}
PushArgumentInstr* push_type_arguments = PushArgument(type_arguments);
arguments->Add(push_type_arguments);
ReturnDefinition(
new CreateClosureInstr(node->function(), arguments, node->token_pos()));
}
void EffectGraphVisitor::TranslateArgumentList(
const ArgumentListNode& node,
ZoneGrowableArray<Value*>* values) {
for (intptr_t i = 0; i < node.length(); ++i) {
ValueGraphVisitor for_argument(owner(), temp_index());
node.NodeAt(i)->Visit(&for_argument);
Append(for_argument);
values->Add(for_argument.value());
}
}
void EffectGraphVisitor::BuildPushArguments(
const ArgumentListNode& node,
ZoneGrowableArray<PushArgumentInstr*>* values) {
for (intptr_t i = 0; i < node.length(); ++i) {
ValueGraphVisitor for_argument(owner(), temp_index());
node.NodeAt(i)->Visit(&for_argument);
Append(for_argument);
PushArgumentInstr* push_arg = PushArgument(for_argument.value());
values->Add(push_arg);
}
}
void EffectGraphVisitor::VisitInstanceCallNode(InstanceCallNode* node) {
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
PushArgumentInstr* push_receiver = PushArgument(for_receiver.value());
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(
node->arguments()->length() + 1);
arguments->Add(push_receiver);
BuildPushArguments(*node->arguments(), arguments);
InstanceCallInstr* call = new InstanceCallInstr(
node->token_pos(),
node->function_name(), Token::kILLEGAL, arguments,
node->arguments()->names(), 1);
ReturnDefinition(call);
}
static intptr_t GetResultCidOfNative(const Function& function) {
const Class& function_class = Class::Handle(function.Owner());
if (function_class.library() == Library::TypedDataLibrary()) {
const String& function_name = String::Handle(function.name());
if (!String::EqualsIgnoringPrivateKey(function_name, Symbols::_New())) {
return kDynamicCid;
}
switch (function_class.id()) {
case kTypedDataInt8ArrayCid:
case kTypedDataUint8ArrayCid:
case kTypedDataUint8ClampedArrayCid:
case kTypedDataInt16ArrayCid:
case kTypedDataUint16ArrayCid:
case kTypedDataInt32ArrayCid:
case kTypedDataUint32ArrayCid:
case kTypedDataInt64ArrayCid:
case kTypedDataUint64ArrayCid:
case kTypedDataFloat32ArrayCid:
case kTypedDataFloat64ArrayCid:
case kTypedDataFloat32x4ArrayCid:
return function_class.id();
default:
return kDynamicCid; // Unknown.
}
}
return kDynamicCid;
}
// <Expression> ::= StaticCall { function: Function
// arguments: <ArgumentList> }
void EffectGraphVisitor::VisitStaticCallNode(StaticCallNode* node) {
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(node->arguments()->length());
BuildPushArguments(*node->arguments(), arguments);
StaticCallInstr* call =
new StaticCallInstr(node->token_pos(),
node->function(),
node->arguments()->names(),
arguments);
if (node->function().is_native()) {
const intptr_t result_cid = GetResultCidOfNative(node->function());
call->set_result_cid(result_cid);
}
ReturnDefinition(call);
}
ClosureCallInstr* EffectGraphVisitor::BuildClosureCall(
ClosureCallNode* node) {
ValueGraphVisitor for_closure(owner(), temp_index());
node->closure()->Visit(&for_closure);
Append(for_closure);
PushArgumentInstr* push_closure = PushArgument(for_closure.value());
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(node->arguments()->length());
arguments->Add(push_closure);
BuildPushArguments(*node->arguments(), arguments);
// Save context around the call.
ASSERT(owner()->parsed_function().saved_current_context_var() != NULL);
BuildStoreContext(*owner()->parsed_function().saved_current_context_var());
return new ClosureCallInstr(node, arguments);
}
void EffectGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) {
Do(BuildClosureCall(node));
// Restore context from saved location.
ASSERT(owner()->parsed_function().saved_current_context_var() != NULL);
BuildLoadContext(*owner()->parsed_function().saved_current_context_var());
}
void ValueGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) {
Value* result = Bind(BuildClosureCall(node));
// Restore context from temp.
ASSERT(owner()->parsed_function().saved_current_context_var() != NULL);
BuildLoadContext(*owner()->parsed_function().saved_current_context_var());
ReturnValue(result);
}
void EffectGraphVisitor::VisitCloneContextNode(CloneContextNode* node) {
InlineBailout("EffectGraphVisitor::VisitCloneContextNode (context)");
Value* context = Bind(new CurrentContextInstr());
Value* clone = Bind(new CloneContextInstr(node->token_pos(), context));
AddInstruction(new StoreContextInstr(clone));
}
Value* EffectGraphVisitor::BuildObjectAllocation(
ConstructorCallNode* node) {
const Class& cls = Class::ZoneHandle(node->constructor().Owner());
const bool requires_type_arguments = cls.HasTypeArguments();
// In checked mode, if the type arguments are uninstantiated, they may need to
// be checked against declared bounds at run time.
Definition* allocate_comp = NULL;
if (FLAG_enable_type_checks &&
requires_type_arguments &&
!node->type_arguments().IsNull() &&
!node->type_arguments().IsInstantiated() &&
node->type_arguments().IsBounded()) {
Value* type_arguments = NULL;
Value* instantiator = NULL;
BuildConstructorTypeArguments(node, &type_arguments, &instantiator, NULL);
// The uninstantiated type arguments cannot be verified to be within their
// bounds at compile time, so verify them at runtime.
allocate_comp = new AllocateObjectWithBoundsCheckInstr(node,
type_arguments,
instantiator);
} else {
ZoneGrowableArray<PushArgumentInstr*>* allocate_arguments =
new ZoneGrowableArray<PushArgumentInstr*>();
if (requires_type_arguments) {
BuildConstructorTypeArguments(node, NULL, NULL, allocate_arguments);
}
allocate_comp = new AllocateObjectInstr(node, allocate_arguments);
}
return Bind(allocate_comp);
}
void EffectGraphVisitor::BuildConstructorCall(
ConstructorCallNode* node,
PushArgumentInstr* push_alloc_value) {
Value* ctor_arg = Bind(
new ConstantInstr(Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll))));
PushArgumentInstr* push_ctor_arg = PushArgument(ctor_arg);
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
arguments->Add(push_alloc_value);
arguments->Add(push_ctor_arg);
BuildPushArguments(*node->arguments(), arguments);
Do(new StaticCallInstr(node->token_pos(),
node->constructor(),
node->arguments()->names(),
arguments));
}
// List of recognized list factories in core lib:
// (factory-name-symbol, result-cid, fingerprint).
// TODO(srdjan): Store the values in the snapshot instead.
#define RECOGNIZED_LIST_FACTORY_LIST(V) \
V(ObjectArrayFactory, kArrayCid, 97987288) \
V(GrowableObjectArrayWithData, kGrowableObjectArrayCid, 816132033) \
V(GrowableObjectArrayFactory, kGrowableObjectArrayCid, 552407276) \
V(Int8ListFactory, kTypedDataInt8ArrayCid, 1299195009) \
V(Uint8ListFactory, kTypedDataUint8ArrayCid, 1493118613) \
V(Uint8ClampedListFactory, kTypedDataUint8ClampedArrayCid, 642014193) \
V(Int16ListFactory, kTypedDataInt16ArrayCid, 1346619471) \
V(Uint16ListFactory, kTypedDataUint16ArrayCid, 1374024153) \
V(Int32ListFactory, kTypedDataInt32ArrayCid, 1583592980) \
V(Uint32ListFactory, kTypedDataUint32ArrayCid, 1940214615) \
V(Int64ListFactory, kTypedDataInt64ArrayCid, 108181413) \
V(Uint64ListFactory, kTypedDataUint64ArrayCid, 375587484) \
V(Float64ListFactory, kTypedDataFloat64ArrayCid, 919047725) \
V(Float32ListFactory, kTypedDataFloat32ArrayCid, 1038684997) \
V(Float32x4ListFactory, kTypedDataFloat32x4ArrayCid, 801641591) \
// Class that recognizes factories and returns corresponding result cid.
class FactoryRecognizer : public AllStatic {
public:
// Return kDynamicCid if factory is not recognized.
static intptr_t ResultCid(const Function& factory) {
ASSERT(factory.IsFactory());
const Class& function_class = Class::Handle(factory.Owner());
const Library& lib = Library::Handle(function_class.library());
ASSERT((lib.raw() == Library::CoreLibrary()) ||
(lib.raw() == Library::TypedDataLibrary()));
const String& factory_name = String::Handle(factory.name());
#define RECOGNIZE_FACTORY(test_factory_symbol, cid, fp) \
if (String::EqualsIgnoringPrivateKey( \
factory_name, Symbols::test_factory_symbol())) { \
ASSERT(factory.CheckSourceFingerprint(fp)); \
return cid; \
} \
RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY);
#undef RECOGNIZE_FACTORY
return kDynamicCid;
}
};
static intptr_t GetResultCidOfListFactory(ConstructorCallNode* node) {
const Function& function = node->constructor();
const Class& function_class = Class::Handle(function.Owner());
if ((function_class.library() != Library::CoreLibrary()) &&
(function_class.library() != Library::TypedDataLibrary())) {
return kDynamicCid;
}
if (node->constructor().IsFactory()) {
if ((function_class.Name() == Symbols::List().raw()) &&
(function.name() == Symbols::ListFactory().raw())) {
// Special recognition of 'new List()' vs 'new List(n)'.
if (node->arguments()->length() == 0) {
return kGrowableObjectArrayCid;
}
return kArrayCid;
}
return FactoryRecognizer::ResultCid(function);
}
return kDynamicCid; // Not a known list constructor.
}
void EffectGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) {
if (node->constructor().IsFactory()) {
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>();
PushArgumentInstr* push_type_arguments = PushArgument(
BuildInstantiatedTypeArguments(node->token_pos(),
node->type_arguments()));
arguments->Add(push_type_arguments);
ASSERT(arguments->length() == 1);
BuildPushArguments(*node->arguments(), arguments);
StaticCallInstr* call =
new StaticCallInstr(node->token_pos(),
node->constructor(),
node->arguments()->names(),
arguments);
const intptr_t result_cid = GetResultCidOfListFactory(node);
if (result_cid != kDynamicCid) {
call->set_result_cid(result_cid);
call->set_is_known_list_constructor(true);
// Recognized fixed length array factory must have two arguments:
// (0) type-arguments, (1) length.
ASSERT(!LoadFieldInstr::IsFixedLengthArrayCid(result_cid) ||
arguments->length() == 2);
}
ReturnDefinition(call);
return;
}
// t_n contains the allocated and initialized object.
// t_n <- AllocateObject(class)
// t_n+1 <- ctor-arg
// t_n+2... <- constructor arguments start here
// StaticCall(constructor, t_n+1, t_n+2, ...)
// No need to preserve allocated value (simpler than in ValueGraphVisitor).
Value* allocated_value = BuildObjectAllocation(node);
PushArgumentInstr* push_allocated_value = PushArgument(allocated_value);
BuildConstructorCall(node, push_allocated_value);
}
Value* EffectGraphVisitor::BuildInstantiator() {
const Class& instantiator_class = Class::Handle(
owner()->parsed_function().function().Owner());
if (instantiator_class.NumTypeParameters() == 0) {
return NULL;
}
Function& outer_function =
Function::Handle(owner()->parsed_function().function().raw());
while (outer_function.IsLocalFunction()) {
outer_function = outer_function.parent_function();
}
if (outer_function.IsFactory()) {
return NULL;
}
ASSERT(owner()->parsed_function().instantiator() != NULL);
ValueGraphVisitor for_instantiator(owner(), temp_index());
owner()->parsed_function().instantiator()->Visit(&for_instantiator);
Append(for_instantiator);
return for_instantiator.value();
}
// 'expression_temp_var' may not be used inside this method if 'instantiator'
// is not NULL.
Value* EffectGraphVisitor::BuildInstantiatorTypeArguments(
intptr_t token_pos, Value* instantiator) {
const Class& instantiator_class = Class::Handle(
owner()->parsed_function().function().Owner());
if (instantiator_class.NumTypeParameters() == 0) {
// The type arguments are compile time constants.
AbstractTypeArguments& type_arguments = AbstractTypeArguments::ZoneHandle();
// Type is temporary. Only its type arguments are preserved.
Type& type = Type::Handle(
Type::New(instantiator_class, type_arguments, token_pos, Heap::kNew));
type ^= ClassFinalizer::FinalizeType(
instantiator_class, type, ClassFinalizer::kFinalize);
ASSERT(!type.IsMalformed());
type_arguments = type.arguments();
type_arguments = type_arguments.Canonicalize();
return Bind(new ConstantInstr(type_arguments));
}
Function& outer_function =
Function::Handle(owner()->parsed_function().function().raw());
while (outer_function.IsLocalFunction()) {
outer_function = outer_function.parent_function();
}
if (outer_function.IsFactory()) {
// No instantiator for factories.
ASSERT(instantiator == NULL);
ASSERT(owner()->parsed_function().instantiator() != NULL);
ValueGraphVisitor for_instantiator(owner(), temp_index());
owner()->parsed_function().instantiator()->Visit(&for_instantiator);
Append(for_instantiator);
return for_instantiator.value();
}
if (instantiator == NULL) {
instantiator = BuildInstantiator();
}
// The instantiator is the receiver of the caller, which is not a factory.
// The receiver cannot be null; extract its AbstractTypeArguments object.
// Note that in the factory case, the instantiator is the first parameter
// of the factory, i.e. already an AbstractTypeArguments object.
intptr_t type_arguments_field_offset =
instantiator_class.type_arguments_field_offset();
ASSERT(type_arguments_field_offset != Class::kNoTypeArguments);
return Bind(new LoadFieldInstr(
instantiator,
type_arguments_field_offset,
Type::ZoneHandle())); // Not an instance, no type.
}
Value* EffectGraphVisitor::BuildInstantiatedTypeArguments(
intptr_t token_pos,
const AbstractTypeArguments& type_arguments) {
if (type_arguments.IsNull() || type_arguments.IsInstantiated()) {
return Bind(new ConstantInstr(type_arguments));
}
// The type arguments are uninstantiated.
Value* instantiator_value =
BuildInstantiatorTypeArguments(token_pos, NULL);
return Bind(new InstantiateTypeArgumentsInstr(token_pos,
type_arguments,
instantiator_value));
}
void EffectGraphVisitor::BuildConstructorTypeArguments(
ConstructorCallNode* node,
Value** type_arguments,
Value** instantiator,
ZoneGrowableArray<PushArgumentInstr*>* call_arguments) {
const Class& cls = Class::ZoneHandle(node->constructor().Owner());
ASSERT(cls.HasTypeArguments() && !node->constructor().IsFactory());
if (node->type_arguments().IsNull() ||
node->type_arguments().IsInstantiated()) {
Value* type_arguments_val = Bind(new ConstantInstr(node->type_arguments()));
if (call_arguments != NULL) {
ASSERT(type_arguments == NULL);
call_arguments->Add(PushArgument(type_arguments_val));
} else {
ASSERT(type_arguments != NULL);
*type_arguments = type_arguments_val;
}
// No instantiator required.
Value* instantiator_val = Bind(new ConstantInstr(
Smi::ZoneHandle(Smi::New(StubCode::kNoInstantiator))));
if (call_arguments != NULL) {
ASSERT(instantiator == NULL);
call_arguments->Add(PushArgument(instantiator_val));
} else {
ASSERT(instantiator != NULL);
*instantiator = instantiator_val;
}
return;
}
// The type arguments are uninstantiated. The generated pseudo code:
// t1 = InstantiatorTypeArguments();
// t2 = ExtractConstructorTypeArguments(t1);
// t1 = ExtractConstructorInstantiator(t1);
// t_n <- t2
// t_n+1 <- t1
// Use expression_temp_var and node->allocated_object_var() locals to keep
// intermediate results around (t1 and t2 above).
ASSERT(owner()->parsed_function().expression_temp_var() != NULL);
const LocalVariable& t1 = *owner()->parsed_function().expression_temp_var();
const LocalVariable& t2 = node->allocated_object_var();
Value* instantiator_type_arguments = BuildInstantiatorTypeArguments(
node->token_pos(), NULL);
Value* stored_instantiator =
Bind(BuildStoreTemp(t1, instantiator_type_arguments));
// t1: instantiator type arguments.
Value* extract_type_arguments = Bind(
new ExtractConstructorTypeArgumentsInstr(
node->token_pos(),
node->type_arguments(),
stored_instantiator));
Do(BuildStoreTemp(t2, extract_type_arguments));
// t2: extracted constructor type arguments.
Value* load_instantiator = Bind(BuildLoadLocal(t1));
Value* extract_instantiator =
Bind(new ExtractConstructorInstantiatorInstr(node, load_instantiator));
Do(BuildStoreTemp(t1, extract_instantiator));
// t2: extracted constructor type arguments.
// t1: extracted constructor instantiator.
Value* type_arguments_val = Bind(BuildLoadLocal(t2));
if (call_arguments != NULL) {
ASSERT(type_arguments == NULL);
call_arguments->Add(PushArgument(type_arguments_val));
} else {
ASSERT(type_arguments != NULL);
*type_arguments = type_arguments_val;
}
Value* instantiator_val = Bind(BuildLoadLocal(t1));
if (call_arguments != NULL) {
ASSERT(instantiator == NULL);
call_arguments->Add(PushArgument(instantiator_val));
} else {
ASSERT(instantiator != NULL);
*instantiator = instantiator_val;
}
}
void ValueGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) {
if (node->constructor().IsFactory()) {
EffectGraphVisitor::VisitConstructorCallNode(node);
return;
}
// t_n contains the allocated and initialized object.
// t_n <- AllocateObject(class)
// t_n <- StoreLocal(temp, t_n);
// t_n+1 <- ctor-arg
// t_n+2... <- constructor arguments start here
// StaticCall(constructor, t_n, t_n+1, ...)
// tn <- LoadLocal(temp)
Value* allocate = BuildObjectAllocation(node);
Value* allocated_value = Bind(BuildStoreTemp(
node->allocated_object_var(),
allocate));
PushArgumentInstr* push_allocated_value = PushArgument(allocated_value);
BuildConstructorCall(node, push_allocated_value);
Definition* load_allocated = BuildLoadLocal(
node->allocated_object_var());
allocated_value = Bind(load_allocated);
ReturnValue(allocated_value);
}
void EffectGraphVisitor::VisitInstanceGetterNode(InstanceGetterNode* node) {
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
PushArgumentInstr* push_receiver = PushArgument(for_receiver.value());
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(1);
arguments->Add(push_receiver);
const String& name =
String::ZoneHandle(Field::GetterSymbol(node->field_name()));
InstanceCallInstr* call = new InstanceCallInstr(
node->token_pos(), name, Token::kGET,
arguments, Array::ZoneHandle(), 1);
ReturnDefinition(call);
}
void EffectGraphVisitor::BuildInstanceSetterArguments(
InstanceSetterNode* node,
ZoneGrowableArray<PushArgumentInstr*>* arguments,
bool result_is_needed) {
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
arguments->Add(PushArgument(for_receiver.value()));
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* value = NULL;
if (result_is_needed) {
value = Bind(BuildStoreExprTemp(for_value.value()));
} else {
value = for_value.value();
}
arguments->Add(PushArgument(value));
}
void EffectGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) {
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
BuildInstanceSetterArguments(node, arguments, kResultNotNeeded);
const String& name =
String::ZoneHandle(Field::SetterSymbol(node->field_name()));
InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(),
name,
Token::kSET,
arguments,
Array::ZoneHandle(),
2); // Checked arg count.
ReturnDefinition(call);
}
void ValueGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) {
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
BuildInstanceSetterArguments(node, arguments, kResultNeeded);
const String& name =
String::ZoneHandle(Field::SetterSymbol(node->field_name()));
Do(new InstanceCallInstr(node->token_pos(),
name,
Token::kSET,
arguments,
Array::ZoneHandle(),
2)); // Checked argument count.
ReturnDefinition(BuildLoadExprTemp());
}
void EffectGraphVisitor::VisitStaticGetterNode(StaticGetterNode* node) {
const String& getter_name =
String::ZoneHandle(Field::GetterSymbol(node->field_name()));
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>();
Function& getter_function = Function::ZoneHandle();
if (node->is_super_getter()) {
// Statically resolved instance getter, i.e. "super getter".
ASSERT(node->receiver() != NULL);
getter_function = Resolver::ResolveDynamicAnyArgs(node->cls(), getter_name);
if (getter_function.IsNull()) {
// Resolve and call noSuchMethod.
ArgumentListNode* arguments = new ArgumentListNode(node->token_pos());
arguments->Add(node->receiver());
StaticCallInstr* call = BuildStaticNoSuchMethodCall(node->cls(),
node->receiver(),
getter_name,
arguments);
ReturnDefinition(call);
return;
} else {
ValueGraphVisitor receiver_value(owner(), temp_index());
node->receiver()->Visit(&receiver_value);
Append(receiver_value);
arguments->Add(PushArgument(receiver_value.value()));
}
} else {
getter_function = node->cls().LookupStaticFunction(getter_name);
if (getter_function.IsNull()) {
// When the parser encounters a reference to a static field materialized
// only by a static setter, but no corresponding static getter, it creates
// a StaticGetterNode ast node referring to the non-existing static getter
// for the case this field reference appears in a left hand side
// expression (the parser has not distinguished between left and right
// hand side yet at this stage). If the parser establishes later that the
// field access is part of a left hand side expression, the
// StaticGetterNode is transformed into a StaticSetterNode referring to
// the existing static setter.
// However, if the field reference appears in a right hand side
// expression, no such transformation occurs and we land here with a
// StaticGetterNode missing a getter function, so we throw a
// NoSuchMethodError.
// Throw a NoSuchMethodError.
StaticCallInstr* call = BuildThrowNoSuchMethodError(
node->token_pos(),
node->cls(),
getter_name,
InvocationMirror::EncodeType(
node->cls().IsTopLevel() ?
InvocationMirror::kTopLevel :
InvocationMirror::kStatic,
InvocationMirror::kGetter));
ReturnDefinition(call);
return;
}
}
ASSERT(!getter_function.IsNull());
StaticCallInstr* call = new StaticCallInstr(node->token_pos(),
getter_function,
Array::ZoneHandle(), // No names.
arguments);
ReturnDefinition(call);
}
void EffectGraphVisitor::BuildStaticSetter(StaticSetterNode* node,
bool result_is_needed) {
const String& setter_name =
String::ZoneHandle(Field::SetterSymbol(node->field_name()));
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(1);
// A super setter is an instance setter whose setter function is
// resolved at compile time (in the caller instance getter's super class).
// Unlike a static getter, a super getter has a receiver parameter.
const bool is_super_setter = (node->receiver() != NULL);
Function& setter_function =
Function::ZoneHandle(is_super_setter
? Resolver::ResolveDynamicAnyArgs(node->cls(), setter_name)
: node->cls().LookupStaticFunction(setter_name));
StaticCallInstr* call;
if (setter_function.IsNull()) {
if (is_super_setter) {
ASSERT(node->receiver() != NULL);
// Resolve and call noSuchMethod.
ArgumentListNode* arguments = new ArgumentListNode(node->token_pos());
arguments->Add(node->receiver());
arguments->Add(node->value());
call = BuildStaticNoSuchMethodCall(node->cls(),
node->receiver(),
setter_name,
arguments);
} else {
// Throw a NoSuchMethodError.
call = BuildThrowNoSuchMethodError(
node->token_pos(),
node->cls(),
setter_name,
InvocationMirror::EncodeType(
node->cls().IsTopLevel() ?
InvocationMirror::kTopLevel :
InvocationMirror::kStatic,
InvocationMirror::kGetter));
}
} else {
if (is_super_setter) {
// Add receiver of instance getter.
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
arguments->Add(PushArgument(for_receiver.value()));
}
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* value = NULL;
if (result_is_needed) {
value = Bind(BuildStoreExprTemp(for_value.value()));
} else {
value = for_value.value();
}
arguments->Add(PushArgument(value));
call = new StaticCallInstr(node->token_pos(),
setter_function,
Array::ZoneHandle(), // No names.
arguments);
}
if (result_is_needed) {
Do(call);
ReturnDefinition(BuildLoadExprTemp());
} else {
ReturnDefinition(call);
}
}
void EffectGraphVisitor::VisitStaticSetterNode(StaticSetterNode* node) {
BuildStaticSetter(node, false); // Result not needed.
}
void ValueGraphVisitor::VisitStaticSetterNode(StaticSetterNode* node) {
BuildStaticSetter(node, true); // Result needed.
}
void EffectGraphVisitor::VisitNativeBodyNode(NativeBodyNode* node) {
InlineBailout("EffectGraphVisitor::VisitNativeBodyNode");
NativeCallInstr* native_call = new NativeCallInstr(node);
ReturnDefinition(native_call);
}
void EffectGraphVisitor::VisitPrimaryNode(PrimaryNode* node) {
// PrimaryNodes are temporary during parsing.
UNREACHABLE();
}
// <Expression> ::= LoadLocal { local: LocalVariable }
void EffectGraphVisitor::VisitLoadLocalNode(LoadLocalNode* node) {
if (node->HasPseudo()) {
EffectGraphVisitor for_pseudo(owner(), temp_index());
node->pseudo()->Visit(&for_pseudo);
Append(for_pseudo);
}
}
void ValueGraphVisitor::VisitLoadLocalNode(LoadLocalNode* node) {
EffectGraphVisitor::VisitLoadLocalNode(node);
Definition* load = BuildLoadLocal(node->local());
ReturnDefinition(load);
}
// <Expression> ::= StoreLocal { local: LocalVariable
// value: <Expression> }
void EffectGraphVisitor::HandleStoreLocal(StoreLocalNode* node,
bool result_is_needed) {
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* store_value = for_value.value();
if (FLAG_enable_type_checks) {
store_value = BuildAssignableValue(node->value()->token_pos(),
store_value,
node->local().type(),
node->local().name());
}
Definition* store = BuildStoreLocal(node->local(),
store_value,
result_is_needed);
ReturnDefinition(store);
}
void EffectGraphVisitor::VisitStoreLocalNode(StoreLocalNode* node) {
HandleStoreLocal(node, kResultNotNeeded);
}
void ValueGraphVisitor::VisitStoreLocalNode(StoreLocalNode* node) {
HandleStoreLocal(node, kResultNeeded);
}
void EffectGraphVisitor::VisitLoadInstanceFieldNode(
LoadInstanceFieldNode* node) {
ValueGraphVisitor for_instance(owner(), temp_index());
node->instance()->Visit(&for_instance);
Append(for_instance);
LoadFieldInstr* load = new LoadFieldInstr(
for_instance.value(),
node->field().Offset(),
AbstractType::ZoneHandle(node->field().type()));
ReturnDefinition(load);
}
void EffectGraphVisitor::VisitStoreInstanceFieldNode(
StoreInstanceFieldNode* node) {
ValueGraphVisitor for_instance(owner(), temp_index());
node->instance()->Visit(&for_instance);
Append(for_instance);
ValueGraphVisitor for_value(owner(), for_instance.temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* store_value = for_value.value();
if (FLAG_enable_type_checks) {
const AbstractType& type = AbstractType::ZoneHandle(node->field().type());
const String& dst_name = String::ZoneHandle(node->field().name());
store_value = BuildAssignableValue(node->value()->token_pos(),
store_value,
type,
dst_name);
}
store_value = Bind(BuildStoreExprTemp(store_value));
GuardFieldInstr* guard =
new GuardFieldInstr(store_value,
node->field(),
Isolate::Current()->GetNextDeoptId());
AddInstruction(guard);
store_value = Bind(BuildLoadExprTemp());
StoreInstanceFieldInstr* store =
new StoreInstanceFieldInstr(node->field(),
for_instance.value(),
store_value,
kEmitStoreBarrier);
ReturnDefinition(store);
}
// StoreInstanceFieldNode does not return result.
void ValueGraphVisitor::VisitStoreInstanceFieldNode(
StoreInstanceFieldNode* node) {
UNIMPLEMENTED();
}
void EffectGraphVisitor::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
LoadStaticFieldInstr* load = new LoadStaticFieldInstr(node->field());
ReturnDefinition(load);
}
Definition* EffectGraphVisitor::BuildStoreStaticField(
StoreStaticFieldNode* node, bool result_is_needed) {
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* store_value = NULL;
if (result_is_needed) {
store_value = Bind(BuildStoreExprTemp(for_value.value()));
} else {
store_value = for_value.value();
}
if (FLAG_enable_type_checks) {
const AbstractType& type = AbstractType::ZoneHandle(node->field().type());
const String& dst_name = String::ZoneHandle(node->field().name());
store_value = BuildAssignableValue(node->value()->token_pos(),
store_value,
type,
dst_name);
}
StoreStaticFieldInstr* store =
new StoreStaticFieldInstr(node->field(), store_value);
if (result_is_needed) {
Do(store);
return BuildLoadExprTemp();
} else {
return store;
}
}
void EffectGraphVisitor::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
ReturnDefinition(BuildStoreStaticField(node, kResultNotNeeded));
}
void ValueGraphVisitor::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
ReturnDefinition(BuildStoreStaticField(node, kResultNeeded));
}
void EffectGraphVisitor::VisitLoadIndexedNode(LoadIndexedNode* node) {
Function* super_function = NULL;
if (node->IsSuperLoad()) {
// Resolve the load indexed operator in the super class.
super_function = &Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(node->super_class(),
Symbols::IndexToken()));
if (super_function->IsNull()) {
// Could not resolve super operator. Generate call noSuchMethod() of the
// super class instead.
ArgumentListNode* arguments = new ArgumentListNode(node->token_pos());
arguments->Add(node->array());
arguments->Add(node->index_expr());
StaticCallInstr* call =
BuildStaticNoSuchMethodCall(node->super_class(),
node->array(),
Symbols::IndexToken(),
arguments);
ReturnDefinition(call);
return;
}
}
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(2);
ValueGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);
arguments->Add(PushArgument(for_array.value()));
ValueGraphVisitor for_index(owner(), temp_index());
node->index_expr()->Visit(&for_index);
Append(for_index);
arguments->Add(PushArgument(for_index.value()));
if (super_function != NULL) {
// Generate static call to super operator.
StaticCallInstr* load = new StaticCallInstr(node->token_pos(),
*super_function,
Array::ZoneHandle(),
arguments);
ReturnDefinition(load);
} else {
// Generate dynamic call to index operator.
const intptr_t checked_argument_count = 1;
InstanceCallInstr* load = new InstanceCallInstr(node->token_pos(),
Symbols::IndexToken(),
Token::kINDEX,
arguments,
Array::ZoneHandle(),
checked_argument_count);
ReturnDefinition(load);
}
}
Definition* EffectGraphVisitor::BuildStoreIndexedValues(
StoreIndexedNode* node,
bool result_is_needed) {
Function* super_function = NULL;
if (node->IsSuperStore()) {
// Resolve the store indexed operator in the super class.
super_function = &Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(node->super_class(),
Symbols::AssignIndexToken()));
if (super_function->IsNull()) {
// Could not resolve super operator. Generate call noSuchMethod() of the
// super class instead.
ArgumentListNode* arguments = new ArgumentListNode(node->token_pos());
arguments->Add(node->array());
arguments->Add(node->index_expr());
// Even though noSuchMethod most likely does not return,
// we save the stored value if the result is needed.
if (result_is_needed) {
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Do(BuildStoreExprTemp(for_value.value()));
const LocalVariable* temp =
owner()->parsed_function().expression_temp_var();
AstNode* value = new LoadLocalNode(node->token_pos(), temp);
arguments->Add(value);
} else {
arguments->Add(node->value());
}
StaticCallInstr* call =
BuildStaticNoSuchMethodCall(node->super_class(),
node->array(),
Symbols::AssignIndexToken(),
arguments);
if (result_is_needed) {
Do(call);
return BuildLoadExprTemp();
} else {
return call;
}
}
}
ZoneGrowableArray<PushArgumentInstr*>* arguments =
new ZoneGrowableArray<PushArgumentInstr*>(3);
ValueGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);