Files
sdk/runtime/vm/flow_graph_builder.cc
T
2012-03-09 11:41:22 +00:00

1322 lines
45 KiB
C++

// 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 "vm/ast_printer.h"
#include "vm/flags.h"
#include "vm/intermediate_language.h"
#include "vm/longjump.h"
#include "vm/os.h"
#include "vm/parser.h"
namespace dart {
DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph.");
DECLARE_FLAG(bool, enable_type_checks);
DECLARE_FLAG(bool, print_ast);
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()->SetSuccessor(other_fragment.entry());
exit_ = other_fragment.exit();
}
}
void EffectGraphVisitor::AddInstruction(Instruction* instruction) {
ASSERT(is_open());
if (is_empty()) {
entry_ = exit_ = instruction;
} else {
exit()->SetSuccessor(instruction);
exit_ = instruction;
}
}
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 if they are reachable,
// and if so record their exits (if any).
Instruction* true_exit = NULL;
Instruction* false_exit = NULL;
if (test_fragment.can_be_true()) {
TargetEntryInstr* true_entry = new TargetEntryInstr();
*test_fragment.true_successor_address() = true_entry;
true_entry->SetSuccessor(true_fragment.entry());
true_exit = true_fragment.is_empty() ? true_entry : true_fragment.exit();
TargetEntryInstr* false_entry = new TargetEntryInstr();
*test_fragment.false_successor_address() = false_entry;
false_entry->SetSuccessor(false_fragment.entry());
false_exit =
false_fragment.is_empty() ? false_entry : false_fragment.exit();
}
// 3. Add a join or select one (or neither) of the arms as exit.
if (true_exit == NULL) {
exit_ = false_exit; // May be NULL.
} else if (false_exit == NULL) {
exit_ = true_exit;
} else {
exit_ = new JoinEntryInstr();
true_exit->SetSuccessor(exit_);
false_exit->SetSuccessor(exit_);
}
}
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).
Instruction* body_exit = NULL;
if (test_fragment.can_be_true()) {
TargetEntryInstr* body_entry = new TargetEntryInstr();
*test_fragment.true_successor_address() = body_entry;
body_entry->SetSuccessor(body_fragment.entry());
body_exit = body_fragment.is_empty() ? body_entry : body_fragment.exit();
}
// 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();
AddInstruction(join);
join->SetSuccessor(test_fragment.entry());
body_exit->SetSuccessor(join);
}
// 3. Set the exit to the graph to be empty or a fresh target node
// depending on whether the false branch of the test is reachable.
if (test_fragment.can_be_false()) {
exit_ = *test_fragment.false_successor_address() = new TargetEntryInstr();
} else {
exit_ = NULL;
}
}
void TestGraphVisitor::ReturnValue(Value* value) {
BranchInstr* branch = new BranchInstr(value);
AddInstruction(branch);
CloseFragment();
true_successor_address_ = branch->true_successor_address();
false_successor_address_ = branch->false_successor_address();
}
void ArgumentGraphVisitor::ReturnValue(Value* value) {
value_ = value;
if (value->IsConstant()) {
AddInstruction(new BindInstr(temp_index(), value));
value_ = new TempVal(AllocateTempIndex());
}
}
void EffectGraphVisitor::Bailout(const char* reason) {
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++) {
EffectGraphVisitor for_effect(owner(), for_value.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 RawFunction::Kind kind = owner()->parsed_function().function().kind();
// Implicit getters do not need a type check at return.
if ((kind != RawFunction::kImplicitGetter) &&
(kind != RawFunction::kConstImplicitGetter)) {
const AbstractType& type =
AbstractType::ZoneHandle(
owner()->parsed_function().function().result_type());
AssertAssignableComp* assert =
new AssertAssignableComp(return_value, type);
AddInstruction(new BindInstr(temp_index(), assert));
return_value = new TempVal(temp_index());
}
}
AddInstruction(new ReturnInstr(return_value, node->token_index()));
CloseFragment();
}
// <Expression> ::= Literal { literal: Instance }
void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) {
return;
}
void ValueGraphVisitor::VisitLiteralNode(LiteralNode* node) {
ReturnValue(new ConstantVal(node->literal()));
}
void TestGraphVisitor::VisitLiteralNode(LiteralNode* node) {
ReturnValue(new ConstantVal(node->literal()));
}
// Type nodes only occur as the right-hand side of instanceof comparisons,
// and they are handled specially in that context.
void EffectGraphVisitor::VisitTypeNode(TypeNode* node) { UNREACHABLE(); }
// <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);
AssertAssignableComp* assert =
new AssertAssignableComp(for_value.value(), node->type());
ReturnComputation(assert);
}
// <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)) {
// Implement short-circuit logic: do not evaluate right if evaluation
// of left is sufficient.
Bailout("EffectGraphVisitor::VisitBinaryOpNode AND/OR");
}
ArgumentGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ArgumentGraphVisitor for_right_value(owner(), for_left_value.temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(for_left_value.value());
arguments->Add(for_right_value.value());
const String& name = String::ZoneHandle(String::NewSymbol(node->Name()));
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 2);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitStringConcatNode(StringConcatNode* node) {
Bailout("EffectGraphVisitor::VisitStringConcatNode");
}
// <Expression> :: Comparison { kind: Token::Kind
// left: <Expression>
// right: <Expression> }
void EffectGraphVisitor::VisitComparisonNode(ComparisonNode* node) {
if (Token::IsInstanceofOperator(node->kind())) {
ArgumentGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
InstanceOfComp* instance_of = new InstanceOfComp(
node->id(),
node->token_index(),
for_left_value.value(),
node->right()->AsTypeNode()->type(),
(node->kind() == Token::kISNOT));
ReturnComputation(instance_of);
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(), for_left_value.temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
StrictCompareComp* comp = new StrictCompareComp(
node->kind(), for_left_value.value(), for_right_value.value());
ReturnComputation(comp);
return;
}
ArgumentGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ArgumentGraphVisitor for_right_value(owner(), for_left_value.temp_index());
node->right()->Visit(&for_right_value);
Append(for_right_value);
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(for_left_value.value());
arguments->Add(for_right_value.value());
// 'kNE' is not overloadable, must implement as kEQ and negation.
// Boolean negation '!' cannot be overloaded neither.
if (node->kind() == Token::kNE) {
const String& name = String::ZoneHandle(String::NewSymbol("=="));
InstanceCallComp* call_equal =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 2);
AddInstruction(new BindInstr(temp_index(), call_equal));
Value* eq_result = new TempVal(temp_index());
if (FLAG_enable_type_checks) {
Bailout("GenerateConditionTypeCheck in kNE");
}
BooleanNegateComp* negate = new BooleanNegateComp(eq_result);
ReturnComputation(negate);
} else {
const String& name = String::ZoneHandle(String::NewSymbol(node->Name()));
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 2);
ReturnComputation(call);
}
}
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);
if (FLAG_enable_type_checks) {
Bailout("GenerateConditionTypeCheck in kNOT");
}
BooleanNegateComp* negate = new BooleanNegateComp(for_value.value());
ReturnComputation(negate);
return;
}
ArgumentGraphVisitor for_value(owner(), temp_index());
node->operand()->Visit(&for_value);
Append(for_value);
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(1);
arguments->Add(for_value.value());
const String& name =
String::ZoneHandle(String::NewSymbol((node->kind() == Token::kSUB)
? Token::Str(Token::kNEGATE)
: node->Name()));
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
// In an effect context, treat postincrement as if it were preincrement
// because its value is not needed.
// 1. Load the value.
LoadLocalComp* load = new LoadLocalComp(node->local());
AddInstruction(new BindInstr(temp_index(), load));
// 2. Increment.
BuildIncrOpIncrement(node->kind(), node->id(), node->token_index(),
temp_index() + 1);
// 3. Perform the store, resulting in the new value.
StoreLocalComp* store =
new StoreLocalComp(node->local(), new TempVal(temp_index()));
ReturnComputation(store);
}
void ValueGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
if (node->prefix()) {
// Base class handles preincrement.
EffectGraphVisitor::VisitIncrOpLocalNode(node);
return;
}
// For postincrement, duplicate the original value to use one copy as the
// result.
//
// 1. Load the value.
LoadLocalComp* load = new LoadLocalComp(node->local());
AddInstruction(new BindInstr(temp_index(), load));
// 2. Duplicate it to increment.
AddInstruction(new PickTempInstr(temp_index() + 1, temp_index()));
// 3. Increment.
BuildIncrOpIncrement(node->kind(), node->id(), node->token_index(),
temp_index() + 2);
// 4. Perform the store and return the original value.
StoreLocalComp* store =
new StoreLocalComp(node->local(), new TempVal(temp_index() + 1));
AddInstruction(new DoInstr(store));
ReturnValue(new TempVal(AllocateTempIndex()));
}
int EffectGraphVisitor::BuildIncrOpFieldLoad(IncrOpInstanceFieldNode* node,
int start_index) {
// Evaluate the receiver and duplicate it (it has two uses).
// t_n <- ... receiver ...
// t_n+1 <- Pick(t_n)
ArgumentGraphVisitor for_receiver(owner(), start_index);
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
const int next_index = for_receiver.temp_index();
ASSERT(next_index == start_index + 1);
AddInstruction(new PickTempInstr(next_index, start_index));
// Load the value.
// t_n+1 <- InstanceCall(get:name, t_n+1)
const String& getter_name =
String::ZoneHandle(Field::GetterSymbol(node->field_name()));
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(1);
arguments->Add(new TempVal(next_index));
InstanceCallComp* load =
new InstanceCallComp(node->getter_id(), node->token_index(), getter_name,
arguments, Array::ZoneHandle(), 1);
AddInstruction(new BindInstr(next_index, load));
return next_index;
}
void EffectGraphVisitor::BuildIncrOpIncrement(Token::Kind kind,
intptr_t node_id,
intptr_t token_index,
int start_index) {
ASSERT((kind == Token::kINCR) || (kind == Token::kDECR));
// Assumed that t_n-1 (where n is start_index) is the field value.
// t_n <- #1
// t_n-1 <- InstanceCall(op, t_n-1, t_n)
const Smi& one = Smi::ZoneHandle(Smi::New(1));
AddInstruction(new BindInstr(start_index, new ConstantVal(one)));
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(new TempVal(start_index - 1));
arguments->Add(new TempVal(start_index));
const String& op_name =
String::ZoneHandle(String::NewSymbol((kind == Token::kINCR) ? "+" : "-"));
InstanceCallComp* add =
new InstanceCallComp(node_id, token_index, op_name,
arguments, Array::ZoneHandle(), 2);
AddInstruction(new BindInstr(start_index - 1, add));
}
void EffectGraphVisitor::VisitIncrOpInstanceFieldNode(
IncrOpInstanceFieldNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
// In an effect context, treat postincrement as if it were preincrement
// because its value is not needed.
// 1. Load the value.
const int value_index = BuildIncrOpFieldLoad(node, temp_index());
// 2. Increment.
BuildIncrOpIncrement(node->kind(), node->operator_id(), node->token_index(),
value_index + 1);
// 3. Perform the store, returning the stored value.
InstanceSetterComp* store =
new InstanceSetterComp(node->setter_id(), node->token_index(),
node->field_name(),
new TempVal(value_index - 1),
new TempVal(value_index));
ReturnComputation(store);
}
void ValueGraphVisitor::VisitIncrOpInstanceFieldNode(
IncrOpInstanceFieldNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
if (node->prefix()) {
// Base class handles preincrement.
EffectGraphVisitor::VisitIncrOpInstanceFieldNode(node);
return;
}
// For postincrement, preallocate a temporary to preserve the original
// value.
//
// 1. Name a placeholder.
const Smi& placeholder = Smi::ZoneHandle(Smi::New(0));
AddInstruction(new BindInstr(temp_index(), new ConstantVal(placeholder)));
// 2. Load the value.
const int value_index = BuildIncrOpFieldLoad(node, temp_index() + 1);
// 3. Preserve the original value.
AddInstruction(new TuckTempInstr(temp_index(), value_index));
// 4. Increment.
BuildIncrOpIncrement(node->kind(), node->operator_id(), node->token_index(),
value_index + 1);
// 5. Perform the store and return the original value.
const String& setter_name =
String::ZoneHandle(Field::SetterSymbol(node->field_name()));
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(new TempVal(value_index - 1));
arguments->Add(new TempVal(value_index));
InstanceCallComp* store =
new InstanceCallComp(node->setter_id(), node->token_index(),
setter_name, arguments, Array::ZoneHandle(), 1);
AddInstruction(new DoInstr(store));
ReturnValue(new TempVal(AllocateTempIndex()));
}
int EffectGraphVisitor::BuildIncrOpIndexedLoad(IncrOpIndexedNode* node,
int start_index) {
// Evaluate the receiver and index.
// t_n <- ... receiver ...
// t_n+1 <- ... index ...
ArgumentGraphVisitor for_receiver(owner(), start_index);
node->array()->Visit(&for_receiver);
Append(for_receiver);
ASSERT(for_receiver.temp_index() == start_index + 1);
ArgumentGraphVisitor for_index(owner(), start_index + 1);
node->index()->Visit(&for_index);
Append(for_index);
ASSERT(for_index.temp_index() == start_index + 2);
// Duplicate the receiver and index values, load the value.
// t_n+2 <- Pick(t_n)
// t_n+3 <- Pick(t_n+1)
// t_n+2 <- InstanceCall([], t_n+2, t_n+3)
const int next_index = start_index + 2;
AddInstruction(new PickTempInstr(next_index, start_index));
AddInstruction(new PickTempInstr(next_index + 1, start_index + 1));
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(new TempVal(next_index));
arguments->Add(new TempVal(next_index + 1));
const String& load_name =
String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX)));
InstanceCallComp* load =
new InstanceCallComp(node->load_id(), node->token_index(), load_name,
arguments, Array::ZoneHandle(), 1);
AddInstruction(new BindInstr(next_index, load));
return next_index;
}
void EffectGraphVisitor::VisitIncrOpIndexedNode(IncrOpIndexedNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
// In an effect context, treat postincrement as if it were preincrement
// because its value is not needed.
// 1. Load the value.
const int value_index = BuildIncrOpIndexedLoad(node, temp_index());
// 2. Increment.
BuildIncrOpIncrement(node->kind(), node->operator_id(), node->token_index(),
value_index + 1);
// 3. Perform the store, returning the stored value.
StoreIndexedComp* store = new StoreIndexedComp(node->store_id(),
node->token_index(),
new TempVal(value_index - 2),
new TempVal(value_index - 1),
new TempVal(value_index));
ReturnComputation(store);
}
void ValueGraphVisitor::VisitIncrOpIndexedNode(IncrOpIndexedNode* node) {
ASSERT((node->kind() == Token::kINCR) || (node->kind() == Token::kDECR));
if (node->prefix()) {
// Base class handles preincrement.
EffectGraphVisitor::VisitIncrOpIndexedNode(node);
return;
}
// For postincrement, preallocate a temporary to preserve the original
// value.
//
// 1. Name a placeholder.
const Smi& placeholder = Smi::ZoneHandle(Smi::New(0));
AddInstruction(new BindInstr(temp_index(), new ConstantVal(placeholder)));
// 2. Load the value.
const int value_index = BuildIncrOpIndexedLoad(node, temp_index() + 1);
// 3. Preserve the original value.
AddInstruction(new TuckTempInstr(temp_index(), value_index));
// 4. Increment.
BuildIncrOpIncrement(node->kind(), node->operator_id(), node->token_index(),
value_index + 1);
// 5. Perform the store and return the original value.
const String& store_name =
String::ZoneHandle(String::NewSymbol(Token::Str(Token::kASSIGN_INDEX)));
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(3);
arguments->Add(new TempVal(value_index - 2));
arguments->Add(new TempVal(value_index - 1));
arguments->Add(new TempVal(value_index));
InstanceCallComp* store =
new InstanceCallComp(node->store_id(), node->token_index(), store_name,
arguments, Array::ZoneHandle(), 1);
AddInstruction(new DoInstr(store));
ReturnValue(new TempVal(AllocateTempIndex()));
}
void EffectGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) {
TestGraphVisitor for_test(owner(), temp_index());
node->condition()->Visit(&for_test);
ASSERT(for_test.can_be_true() && for_test.can_be_false());
// 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()->Visit(&for_test);
ASSERT(for_test.can_be_true() && for_test.can_be_false());
// Ensure that the value of the true/false subexpressions are named with
// the same temporary name.
ValueGraphVisitor for_true(owner(), temp_index());
node->true_expr()->Visit(&for_true);
ASSERT(for_true.is_open());
if (for_true.value()->IsTemp()) {
ASSERT(for_true.value()->AsTemp()->index() == temp_index());
} else {
for_true.AddInstruction(new BindInstr(temp_index(), for_true.value()));
}
ValueGraphVisitor for_false(owner(), temp_index());
node->false_expr()->Visit(&for_false);
ASSERT(for_false.is_open());
if (for_false.value()->IsTemp()) {
ASSERT(for_false.value()->AsTemp()->index() == temp_index());
} else {
for_false.AddInstruction(new BindInstr(temp_index(), for_false.value()));
}
Join(for_test, for_true, for_false);
ReturnValue(new TempVal(AllocateTempIndex()));
}
// <Statement> ::= If { condition: <Expression>
// true_branch: <Sequence>
// false_branch: <Sequence> }
void EffectGraphVisitor::VisitIfNode(IfNode* node) {
TestGraphVisitor for_test(owner(), temp_index());
node->condition()->Visit(&for_test);
EffectGraphVisitor for_true(owner(), temp_index());
EffectGraphVisitor for_false(owner(), temp_index());
if (for_test.can_be_true()) {
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) {
Bailout("EffectGraphVisitor::VisitSwitchNode");
}
void EffectGraphVisitor::VisitCaseNode(CaseNode* node) {
Bailout("EffectGraphVisitor::VisitCaseNode");
}
// <Statement> ::= While { label: SourceLabel
// condition: <Expression>
// body: <Sequence> }
void EffectGraphVisitor::VisitWhileNode(WhileNode* node) {
TestGraphVisitor for_test(owner(), temp_index());
node->condition()->Visit(&for_test);
EffectGraphVisitor for_body(owner(), temp_index());
if (for_test.can_be_true()) node->body()->Visit(&for_body);
TieLoop(for_test, for_body);
}
void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) {
EffectGraphVisitor for_body(owner(), temp_index());
node->body()->Visit(&for_body);
TestGraphVisitor for_test(owner(), temp_index());
node->condition()->Visit(&for_test);
ASSERT(is_open());
// Tie do-while loop (test is after the body).
JoinEntryInstr* join = new JoinEntryInstr();
AddInstruction(join);
join->SetSuccessor(for_body.entry());
Instruction* body_exit = for_body.is_empty() ? join : for_body.exit();
if (body_exit != NULL) {
TargetEntryInstr* target_entry = new TargetEntryInstr();
target_entry->SetSuccessor(for_test.entry());
body_exit->SetSuccessor(target_entry);
}
*for_test.true_successor_address() = join;
exit_ = *for_test.false_successor_address() = new TargetEntryInstr();
}
void EffectGraphVisitor::VisitForNode(ForNode* node) {
EffectGraphVisitor for_initializer(owner(), temp_index());
node->initializer()->Visit(&for_initializer);
Append(for_initializer);
ASSERT(is_open());
EffectGraphVisitor for_body(owner(), temp_index());
node->body()->Visit(&for_body);
if (for_body.is_open()) {
EffectGraphVisitor for_increment(owner(), temp_index());
node->increment()->Visit(&for_increment);
for_body.Append(for_increment);
}
if (node->condition() != NULL) {
TestGraphVisitor for_test(owner(), temp_index());
node->condition()->Visit(&for_test);
TieLoop(for_test, for_body);
return;
}
// Degenerate cases. An absent condition is implicitly true. No
// normal exit from loop => no back edge.
if (!for_body.is_open()) {
Append(for_body);
return;
}
JoinEntryInstr* join = new JoinEntryInstr();
AddInstruction(join);
if (for_body.is_empty()) {
join->SetSuccessor(join);
} else {
join->SetSuccessor(for_body.entry());
for_body.exit()->SetSuccessor(join);
}
CloseFragment();
}
void EffectGraphVisitor::VisitJumpNode(JumpNode* node) {
Bailout("EffectGraphVisitor::VisitJumpNode");
}
void EffectGraphVisitor::VisitArgumentListNode(ArgumentListNode* node) {
UNREACHABLE();
}
void EffectGraphVisitor::VisitArrayNode(ArrayNode* node) {
// Translate the array elements and collect their values.
ZoneGrowableArray<Value*>* values =
new ZoneGrowableArray<Value*>(node->length());
int index = temp_index();
for (int i = 0; i < node->length(); ++i) {
ValueGraphVisitor for_value(owner(), index);
node->ElementAt(i)->Visit(&for_value);
Append(for_value);
values->Add(for_value.value());
index = for_value.temp_index();
}
CreateArrayComp* create = new CreateArrayComp(node, values);
ReturnComputation(create);
}
void EffectGraphVisitor::VisitClosureNode(ClosureNode* node) {
Bailout("EffectGraphVisitor::VisitClosureNode");
}
void EffectGraphVisitor::TranslateArgumentList(
const ArgumentListNode& node,
intptr_t next_temp_index,
ZoneGrowableArray<Value*>* values) {
for (intptr_t i = 0; i < node.length(); ++i) {
ArgumentGraphVisitor for_argument(owner(), next_temp_index);
node.NodeAt(i)->Visit(&for_argument);
Append(for_argument);
next_temp_index = for_argument.temp_index();
values->Add(for_argument.value());
}
}
void EffectGraphVisitor::VisitInstanceCallNode(InstanceCallNode* node) {
ArgumentListNode* arguments = node->arguments();
int length = arguments->length();
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>(length + 1);
ArgumentGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
values->Add(for_receiver.value());
TranslateArgumentList(*arguments, for_receiver.temp_index(), values);
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(),
node->function_name(), values,
arguments->names(), 1);
ReturnComputation(call);
}
// <Expression> ::= StaticCall { function: Function
// arguments: <ArgumentList> }
void EffectGraphVisitor::VisitStaticCallNode(StaticCallNode* node) {
int length = node->arguments()->length();
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>(length);
TranslateArgumentList(*node->arguments(), temp_index(), values);
StaticCallComp* call = new StaticCallComp(node, values);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) {
Bailout("EffectGraphVisitor::VisitClosureCallNode");
}
void EffectGraphVisitor::VisitCloneContextNode(CloneContextNode* node) {
Bailout("EffectGraphVisitor::VisitCloneContextNode");
}
void EffectGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) {
Bailout("EffectGraphVisitor::VisitConstructorCallNode");
}
void EffectGraphVisitor::VisitInstanceGetterNode(InstanceGetterNode* node) {
ArgumentGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(1);
arguments->Add(for_receiver.value());
const String& name =
String::ZoneHandle(Field::GetterSymbol(node->field_name()));
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) {
ArgumentGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
ArgumentGraphVisitor for_value(owner(), for_receiver.temp_index());
node->value()->Visit(&for_value);
Append(for_value);
InstanceSetterComp* setter = new InstanceSetterComp(node->id(),
node->token_index(),
node->field_name(),
for_receiver.value(),
for_value.value());
ReturnComputation(setter);
}
void EffectGraphVisitor::VisitStaticGetterNode(StaticGetterNode* node) {
Bailout("EffectGraphVisitor::VisitStaticGetterNode");
}
void EffectGraphVisitor::VisitStaticSetterNode(StaticSetterNode* node) {
Bailout("EffectGraphVisitor::VisitStaticSetterNode");
}
void EffectGraphVisitor::VisitNativeBodyNode(NativeBodyNode* node) {
NativeCallComp* native_call = new NativeCallComp(node);
ReturnComputation(native_call);
}
void EffectGraphVisitor::VisitPrimaryNode(PrimaryNode* node) {
Bailout("EffectGraphVisitor::VisitPrimaryNode");
}
// <Expression> ::= LoadLocal { local: LocalVariable }
void EffectGraphVisitor::VisitLoadLocalNode(LoadLocalNode* node) {
return;
}
void ValueGraphVisitor::VisitLoadLocalNode(LoadLocalNode* node) {
LoadLocalComp* load = new LoadLocalComp(node->local());
ReturnComputation(load);
}
void TestGraphVisitor::VisitLoadLocalNode(LoadLocalNode* node) {
LoadLocalComp* load = new LoadLocalComp(node->local());
ReturnComputation(load);
}
// <Expression> ::= StoreLocal { local: LocalVariable
// value: <Expression> }
void EffectGraphVisitor::VisitStoreLocalNode(StoreLocalNode* node) {
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
Value* value = for_value.value();
if (FLAG_enable_type_checks) {
AssertAssignableComp* assert =
new AssertAssignableComp(value, node->local().type());
AddInstruction(new BindInstr(temp_index(), assert));
value = new TempVal(temp_index());
}
StoreLocalComp* store = new StoreLocalComp(node->local(), value);
ReturnComputation(store);
}
void EffectGraphVisitor::VisitLoadInstanceFieldNode(
LoadInstanceFieldNode* node) {
ValueGraphVisitor for_instance(owner(), temp_index());
node->instance()->Visit(&for_instance);
Append(for_instance);
LoadInstanceFieldComp* load =
new LoadInstanceFieldComp(node, for_instance.value());
ReturnComputation(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());
AssertAssignableComp* assert = new AssertAssignableComp(store_value, type);
AddInstruction(new BindInstr(temp_index(), assert));
store_value = new TempVal(temp_index());
}
StoreInstanceFieldComp* store =
new StoreInstanceFieldComp(node, for_instance.value(), store_value);
ReturnComputation(store);
}
void EffectGraphVisitor::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
LoadStaticFieldComp* load = new LoadStaticFieldComp(node->field());
ReturnComputation(load);
}
void EffectGraphVisitor::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
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) {
const AbstractType& type = AbstractType::ZoneHandle(node->field().type());
AssertAssignableComp* assert = new AssertAssignableComp(store_value, type);
AddInstruction(new BindInstr(temp_index(), assert));
store_value = new TempVal(temp_index());
}
StoreStaticFieldComp* store =
new StoreStaticFieldComp(node->field(), store_value);
ReturnComputation(store);
}
void EffectGraphVisitor::VisitLoadIndexedNode(LoadIndexedNode* node) {
ArgumentGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);
ArgumentGraphVisitor for_index(owner(), for_array.temp_index());
node->index_expr()->Visit(&for_index);
Append(for_index);
ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2);
arguments->Add(for_array.value());
arguments->Add(for_index.value());
const String& name =
String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX)));
InstanceCallComp* call =
new InstanceCallComp(node->id(), node->token_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitStoreIndexedNode(StoreIndexedNode* node) {
ArgumentGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);
ArgumentGraphVisitor for_index(owner(), for_array.temp_index());
node->index_expr()->Visit(&for_index);
Append(for_index);
ArgumentGraphVisitor for_value(owner(), for_index.temp_index());
node->value()->Visit(&for_value);
Append(for_value);
StoreIndexedComp* store = new StoreIndexedComp(node->id(),
node->token_index(),
for_array.value(),
for_index.value(),
for_value.value());
ReturnComputation(store);
}
// <Statement> ::= Sequence { scope: LocalScope
// nodes: <Statement>*
// label: SourceLabel }
void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) {
if ((node->scope() != NULL) &&
(node->scope()->num_context_variables() != 0)) {
Bailout("Sequence needs a context. Gotta have a context.");
}
intptr_t i = 0;
while (is_open() && (i < node->length())) {
EffectGraphVisitor for_effect(owner(), temp_index());
node->NodeAt(i++)->Visit(&for_effect);
Append(for_effect);
}
}
void EffectGraphVisitor::VisitCatchClauseNode(CatchClauseNode* node) {
Bailout("EffectGraphVisitor::VisitCatchClauseNode");
}
void EffectGraphVisitor::VisitTryCatchNode(TryCatchNode* node) {
Bailout("EffectGraphVisitor::VisitTryCatchNode");
}
void EffectGraphVisitor::VisitThrowNode(ThrowNode* node) {
Bailout("EffectGraphVisitor::VisitThrowNode");
}
void EffectGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) {
Bailout("EffectGraphVisitor::VisitInlinedFinallyNode");
}
// Graph printing.
class FlowGraphPrinter : public FlowGraphVisitor {
public:
explicit FlowGraphPrinter(const Function& function) : function_(function) { }
virtual ~FlowGraphPrinter() {}
// Print the instructions in a block terminated by newlines. Add "goto N"
// to the end of the block if it ends with an unconditional jump to
// another block and that block is not next in reverse postorder.
void VisitBlocks(const GrowableArray<BlockEntryInstr*>& block_order);
// Visiting a computation prints it with no indentation or newline.
#define DECLARE_VISIT_COMPUTATION(ShortName, ClassName) \
virtual void Visit##ShortName(ClassName* comp);
// Visiting an instruction prints it with a four space indent and no
// trailing newline. Basic block entries are labeled with their block
// number.
#define DECLARE_VISIT_INSTRUCTION(ShortName) \
virtual void Visit##ShortName(ShortName##Instr* instr);
FOR_EACH_COMPUTATION(DECLARE_VISIT_COMPUTATION)
FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_COMPUTATION
#undef DECLARE_VISIT_INSTRUCTION
private:
const Function& function_;
DISALLOW_COPY_AND_ASSIGN(FlowGraphPrinter);
};
void FlowGraphPrinter::VisitBlocks(
const GrowableArray<BlockEntryInstr*>& block_order) {
OS::Print("==== %s\n", function_.ToFullyQualifiedCString());
for (intptr_t i = block_order.length() - 1; i >= 0; --i) {
// Print the block entry.
Instruction* current = block_order[i]->Accept(this);
// And all the successors until an exit, branch, or a block entry.
while ((current != NULL) && !current->IsBlockEntry()) {
OS::Print("\n");
current = current->Accept(this);
}
BlockEntryInstr* successor =
(current == NULL) ? NULL : current->AsBlockEntry();
if (successor != NULL) {
OS::Print(" goto %d", successor->block_number());
}
OS::Print("\n");
}
}
void FlowGraphPrinter::VisitTemp(TempVal* val) {
OS::Print("t%d", val->index());
}
void FlowGraphPrinter::VisitConstant(ConstantVal* val) {
OS::Print("#%s", val->instance().ToCString());
}
void FlowGraphPrinter::VisitAssertAssignable(AssertAssignableComp* comp) {
OS::Print("AssertAssignable(");
comp->value()->Accept(this);
OS::Print(", %s)", comp->type().ToCString());
}
void FlowGraphPrinter::VisitInstanceCall(InstanceCallComp* comp) {
OS::Print("InstanceCall(%s", comp->function_name().ToCString());
for (int i = 0; i < comp->ArgumentCount(); ++i) {
OS::Print(", ");
comp->ArgumentAt(i)->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitStrictCompare(StrictCompareComp* comp) {
OS::Print("StrictCompare(%s, ", Token::Str(comp->kind()));
comp->left()->Accept(this);
OS::Print(", ");
comp->right()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitStaticCall(StaticCallComp* comp) {
OS::Print("StaticCall(%s",
String::Handle(comp->function().name()).ToCString());
for (int i = 0; i < comp->ArgumentCount(); ++i) {
OS::Print(", ");
comp->ArgumentAt(i)->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitLoadLocal(LoadLocalComp* comp) {
OS::Print("LoadLocal(%s)", comp->local().name().ToCString());
}
void FlowGraphPrinter::VisitStoreLocal(StoreLocalComp* comp) {
OS::Print("StoreLocal(%s, ", comp->local().name().ToCString());
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitNativeCall(NativeCallComp* comp) {
OS::Print("NativeCall(%s)", comp->native_name().ToCString());
}
void FlowGraphPrinter::VisitLoadInstanceField(LoadInstanceFieldComp* comp) {
OS::Print("LoadInstanceField(%s, ",
String::Handle(comp->field().name()).ToCString());
comp->instance()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitStoreInstanceField(StoreInstanceFieldComp* comp) {
OS::Print("StoreInstanceField(%s, ",
String::Handle(comp->field().name()).ToCString());
comp->instance()->Accept(this);
OS::Print(", ");
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitLoadStaticField(LoadStaticFieldComp* comp) {
OS::Print("LoadStaticField(%s)",
String::Handle(comp->field().name()).ToCString());
}
void FlowGraphPrinter::VisitStoreStaticField(StoreStaticFieldComp* comp) {
OS::Print("StoreStaticField(%s, ",
String::Handle(comp->field().name()).ToCString());
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitStoreIndexed(StoreIndexedComp* comp) {
OS::Print("StoreIndexed(");
comp->array()->Accept(this);
OS::Print(", ");
comp->index()->Accept(this);
OS::Print(", ");
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitInstanceSetter(InstanceSetterComp* comp) {
OS::Print("InstanceSetter(");
comp->receiver()->Accept(this);
OS::Print(", ");
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitBooleanNegate(BooleanNegateComp* comp) {
OS::Print("! ");
comp->value()->Accept(this);
}
void FlowGraphPrinter::VisitInstanceOf(InstanceOfComp* comp) {
comp->value()->Accept(this);
OS::Print(" %s %s",
comp->negate_result() ? "ISNOT" : "IS",
String::Handle(comp->type().Name()).ToCString());
}
void FlowGraphPrinter::VisitCreateArray(CreateArrayComp* comp) {
OS::Print("CreateArray(");
for (int i = 0; i < comp->ElementCount(); ++i) {
if (i != 0) OS::Print(", ");
comp->ElementAt(i)->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitJoinEntry(JoinEntryInstr* instr) {
OS::Print("%2d: [join]", instr->block_number());
}
void FlowGraphPrinter::VisitTargetEntry(TargetEntryInstr* instr) {
OS::Print("%2d: [target]", instr->block_number());
}
void FlowGraphPrinter::VisitPickTemp(PickTempInstr* instr) {
OS::Print(" t%d <- Pick(t%d)", instr->destination(), instr->source());
}
void FlowGraphPrinter::VisitTuckTemp(TuckTempInstr* instr) {
OS::Print(" t%d := t%d", instr->destination(), instr->source());
}
void FlowGraphPrinter::VisitDo(DoInstr* instr) {
OS::Print(" ");
instr->computation()->Accept(this);
}
void FlowGraphPrinter::VisitBind(BindInstr* instr) {
OS::Print(" t%d <- ", instr->temp_index());
instr->computation()->Accept(this);
}
void FlowGraphPrinter::VisitReturn(ReturnInstr* instr) {
OS::Print(" return ");
instr->value()->Accept(this);
}
void FlowGraphPrinter::VisitBranch(BranchInstr* instr) {
OS::Print(" if ");
instr->value()->Accept(this);
OS::Print(" goto(%d, %d)", instr->true_successor()->block_number(),
instr->false_successor()->block_number());
}
void FlowGraphBuilder::BuildGraph() {
if (FLAG_print_ast) {
// Print the function ast before IL generation.
AstPrinter::PrintFunctionNodes(parsed_function_);
}
EffectGraphVisitor for_effect(this, 0);
for_effect.AddInstruction(new TargetEntryInstr());
parsed_function().node_sequence()->Visit(&for_effect);
// Check that the graph is properly terminated.
ASSERT(!for_effect.is_open());
if (for_effect.entry() != NULL) {
// Accumulate basic block entries via postorder traversal.
for_effect.entry()->Postorder(&postorder_block_entries_);
// Number the blocks in reverse postorder starting with 0.
intptr_t last_index = postorder_block_entries_.length() - 1;
for (intptr_t i = last_index; i >= 0; --i) {
postorder_block_entries_[i]->set_block_number(last_index - i);
}
}
if (FLAG_print_flow_graph) {
FlowGraphPrinter printer(parsed_function().function());
printer.VisitBlocks(postorder_block_entries_);
}
}
void FlowGraphBuilder::Bailout(const char* reason) {
const char* kFormat = "FlowGraphBuilder Bailout: %s %s";
const char* function_name = parsed_function_.function().ToCString();
intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
char* chars = reinterpret_cast<char*>(
Isolate::Current()->current_zone()->Allocate(len));
OS::SNPrint(chars, len, kFormat, function_name, reason);
const Error& error = Error::Handle(
LanguageError::New(String::Handle(String::New(chars))));
Isolate::Current()->long_jump_base()->Jump(1, error);
}
} // namespace dart