Files
sdk/runtime/vm/flow_graph_builder.cc
T
regis@google.com 5e99edcea7 Properly set the element type of literal lists.
Add test.
Keep element type consistent between growable array and backing array.
Fix snapshot reader to set the element type in growable array
Remove run time call checking rest argument.
Review URL: https://chromiumcodereview.appspot.com//10368004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7337 260f80e4-7a28-3924-810f-c04153c831b5
2012-05-04 16:36:27 +00:00

2598 lines
93 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/code_descriptors.h"
#include "vm/dart_entry.h"
#include "vm/flags.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"
namespace dart {
DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph.");
DECLARE_FLAG(bool, enable_type_checks);
DEFINE_FLAG(bool, print_ast, false, "Print abstract syntax tree.");
FlowGraphBuilder::FlowGraphBuilder(const ParsedFunction& parsed_function)
: parsed_function_(parsed_function),
preorder_block_entries_(),
postorder_block_entries_(),
context_level_(0),
last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
try_index_(CatchClauseNode::kInvalidTryIndex),
catch_entries_() {}
void FlowGraphBuilder::AddCatchEntry(intptr_t try_index, Instruction* entry) {
catch_entries_.Add(entry);
}
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();
}
temp_index_ = other_fragment.temp_index();
}
void EffectGraphVisitor::AddInstruction(Instruction* instruction) {
ASSERT(is_open());
DeallocateTempIndex(instruction->InputCount());
if (instruction->IsDefinition()) {
instruction->AsDefinition()->set_temp_index(AllocateTempIndex());
}
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 and record their exits
// (if any).
Instruction* true_exit = NULL;
Instruction* false_exit = NULL;
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.
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 {
exit_ = new JoinEntryInstr();
true_exit->SetSuccessor(exit_);
false_exit->SetSuccessor(exit_);
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).
Instruction* body_exit = NULL;
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 the false successor of the test, a
// fresh target node
exit_ = *test_fragment.false_successor_address() = new TargetEntryInstr();
}
// Stores current context into the 'variable'
void EffectGraphVisitor::BuildStoreContext(const LocalVariable& variable) {
BindInstr* context = new BindInstr(new CurrentContextComp());
AddInstruction(context);
StoreLocalComp* store_context =
new StoreLocalComp(variable, new UseVal(context),
owner()->context_level());
AddInstruction(new DoInstr(store_context));
}
// Loads context saved in 'context_variable' into the current context.
void EffectGraphVisitor::BuildLoadContext(const LocalVariable& variable) {
BindInstr* load_saved_context =
new BindInstr(new LoadLocalComp(variable, owner()->context_level()));
AddInstruction(load_saved_context);
DoInstr* store_context =
new DoInstr(new StoreContextComp(new UseVal(load_saved_context)));
AddInstruction(store_context);
}
void TestGraphVisitor::ReturnValue(Value* value) {
if (FLAG_enable_type_checks) {
BindInstr* assert_boolean =
new BindInstr(new AssertBooleanComp(condition_token_index(),
owner()->try_index(),
value));
AddInstruction(assert_boolean);
value = new UseVal(assert_boolean);
}
BranchInstr* branch = new BranchInstr(value);
AddInstruction(branch);
CloseFragment();
true_successor_address_ = branch->true_successor_address();
false_successor_address_ = branch->false_successor_address();
}
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(), 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();
const bool is_implicit_getter =
(kind == RawFunction::kImplicitGetter) ||
(kind == RawFunction::kConstImplicitGetter);
const bool is_static = owner()->parsed_function().function().is_static();
// Implicit getters do not need a type check at return, unless they compute
// the initial value of a static field.
if (is_static || !is_implicit_getter) {
const AbstractType& dst_type =
AbstractType::ZoneHandle(
owner()->parsed_function().function().result_type());
const String& dst_name =
String::ZoneHandle(String::NewSymbol("function result"));
return_value = BuildAssignableValue(node->value(),
return_value,
dst_type,
dst_name);
}
}
intptr_t current_context_level = owner()->context_level();
ASSERT(current_context_level >= 0);
if (owner()->parsed_function().saved_context_var() != NULL) {
// CTX on entry was saved, but not linked as context parent.
BuildLoadContext(*owner()->parsed_function().saved_context_var());
} else {
while (current_context_level-- > 0) {
UnchainContext();
}
}
AddInstruction(
new ReturnInstr(node->token_index(), return_value));
CloseFragment();
}
// <Expression> ::= Literal { literal: Instance }
void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) {
return;
}
void ValueGraphVisitor::VisitLiteralNode(LiteralNode* node) {
ReturnComputation(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(); }
// Returns true if the type check can be skipped, for example, if the type is
// Dynamic or if the value is a compile time constant and an instance of type.
static bool CanSkipTypeCheck(AstNode* value, const AbstractType& dst_type) {
ASSERT(FLAG_enable_type_checks);
ASSERT(!dst_type.IsNull());
ASSERT(dst_type.IsFinalized());
// Any expression is assignable to the Dynamic type and to the Object type.
// Skip the test.
if (!dst_type.IsMalformed() &&
(dst_type.IsDynamicType() || dst_type.IsObjectType())) {
return true;
}
// It is a compile-time error to explicitly return a value (including null)
// from a void function. However, functions that do not explicitly return a
// value, implicitly return null. This includes void functions. Therefore, we
// skip the type test here and trust the parser to only return null in void
// function.
if (dst_type.IsVoidType()) {
return true;
}
// Eliminate the test if it can be performed successfully at compile time.
if ((value != NULL) && value->IsLiteralNode()) {
const Instance& literal_value = value->AsLiteralNode()->literal();
const Class& cls = Class::Handle(literal_value.clazz());
if (cls.IsNullClass()) {
// There are only three instances that can be of Class Null:
// Object::null(), Object::sentinel(), and Object::transition_sentinel().
// The inline code and run time code performing the type check will never
// encounter the 2 sentinel values. The type check of a sentinel value
// will always be eliminated here, because these sentinel values can only
// be encountered as constants, never as actual value of an heap object
// being type checked.
ASSERT(literal_value.IsNull() ||
(literal_value.raw() == Object::sentinel()) ||
(literal_value.raw() == Object::transition_sentinel()));
return true;
}
Error& malformed_error = Error::Handle();
if (!dst_type.IsMalformed() &&
dst_type.IsInstantiated() &&
literal_value.IsInstanceOf(dst_type,
TypeArguments::Handle(),
&malformed_error)) {
return true;
}
}
return false;
}
// <Expression> :: Assignable { expr: <Expression>
// type: AbstractType
// dst_name: String }
void EffectGraphVisitor::VisitAssignableNode(AssignableNode* node) {
UNREACHABLE();
}
void ValueGraphVisitor::VisitAssignableNode(AssignableNode* node) {
ValueGraphVisitor for_value(owner(), temp_index());
node->expr()->Visit(&for_value);
Append(for_value);
ReturnValue(BuildAssignableValue(node->expr(),
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_index());
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);
ValueGraphVisitor for_right_value(owner(), 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->token_index(),
owner()->try_index(),
name,
arguments,
Array::ZoneHandle(),
2);
ReturnComputation(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;
const Bool& bool_true = Bool::ZoneHandle(Bool::True());
const Bool& bool_false = Bool::ZoneHandle(Bool::False());
TestGraphVisitor for_test(owner(),
temp_index(),
node->left()->token_index());
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) {
BindInstr* assert_boolean =
new BindInstr(new AssertBooleanComp(node->right()->token_index(),
owner()->try_index(),
right_value));
for_right.AddInstruction(assert_boolean);
right_value = new UseVal(assert_boolean);
}
BindInstr* constant_true = new BindInstr(new ConstantVal(bool_true));
for_right.AddInstruction(constant_true);
StrictCompareComp* comp = new StrictCompareComp(Token::kEQ_STRICT,
right_value, new UseVal(constant_true));
for_right.AddInstruction(new BindInstr(comp));
if (node->kind() == Token::kAND) {
ValueGraphVisitor for_false(owner(), temp_index());
for_false.ReturnComputation(new ConstantVal(bool_false));
Join(for_test, for_right, for_false);
} else {
ASSERT(node->kind() == Token::kOR);
ValueGraphVisitor for_true(owner(), temp_index());
for_true.ReturnComputation(new ConstantVal(bool_true));
Join(for_test, for_true, for_right);
}
ReturnValue(new TempVal(temp_index() - 1));
return;
}
EffectGraphVisitor::VisitBinaryOpNode(node);
}
void EffectGraphVisitor::CompiletimeStringInterpolation(
const Function& interpol_func, const Array& literals) {
// Do nothing.
}
void ValueGraphVisitor::CompiletimeStringInterpolation(
const Function& interpol_func, const Array& literals) {
// Build argument array to pass to the interpolation function.
GrowableArray<const Object*> interpolate_arg;
interpolate_arg.Add(&literals);
const Array& kNoArgumentNames = Array::Handle();
// Call the interpolation function.
String& concatenated = String::ZoneHandle();
concatenated ^= DartEntry::InvokeStatic(interpol_func,
interpolate_arg,
kNoArgumentNames);
if (concatenated.IsUnhandledException()) {
// TODO(srdjan): Remove this node and this UNREACHABLE.
UNREACHABLE();
}
ASSERT(!concatenated.IsNull());
concatenated = String::NewSymbol(concatenated);
ReturnComputation(new ConstantVal(concatenated));
}
// TODO(srdjan): Remove this node once the "+" string operator has been
// eliminated.
void EffectGraphVisitor::VisitStringConcatNode(StringConcatNode* node) {
const String& cls_name = String::Handle(String::NewSymbol("StringBase"));
const Library& core_lib = Library::Handle(
Isolate::Current()->object_store()->core_library());
const Class& cls = Class::Handle(core_lib.LookupClass(cls_name));
ASSERT(!cls.IsNull());
const String& func_name = String::Handle(String::NewSymbol("_interpolate"));
const int number_of_parameters = 1;
const Function& interpol_func = Function::ZoneHandle(
Resolver::ResolveStatic(cls, func_name,
number_of_parameters,
Array::Handle(),
Resolver::kIsQualified));
ASSERT(!interpol_func.IsNull());
// First try to concatenate and canonicalize the values at compile time.
bool compile_time_interpolation = true;
Array& literals = Array::Handle(Array::New(node->values()->length()));
for (int i = 0; i < node->values()->length(); i++) {
if (node->values()->ElementAt(i)->IsLiteralNode()) {
LiteralNode* lit = node->values()->ElementAt(i)->AsLiteralNode();
literals.SetAt(i, lit->literal());
} else {
compile_time_interpolation = false;
break;
}
}
if (compile_time_interpolation) {
// Not needed for effect, only for value
CompiletimeStringInterpolation(interpol_func, literals);
return;
}
// Runtime string interpolation.
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>();
ArgumentListNode* interpol_arg = new ArgumentListNode(node->token_index());
interpol_arg->Add(node->values());
TranslateArgumentList(*interpol_arg, values);
StaticCallComp* call =
new StaticCallComp(node->token_index(),
owner()->try_index(),
interpol_func,
interpol_arg->names(),
values);
ReturnComputation(call);
}
void EffectGraphVisitor::BuildAssertAssignable(intptr_t token_index,
Value* value,
const AbstractType& dst_type,
const String& dst_name) {
// Build the type check computation.
Value* instantiator_type_arguments = NULL;
if (!dst_type.IsInstantiated()) {
instantiator_type_arguments =
BuildInstantiatorTypeArguments(token_index);
}
AssertAssignableComp* assert_assignable =
new AssertAssignableComp(token_index,
owner()->try_index(),
value,
instantiator_type_arguments,
dst_type,
dst_name);
AddInstruction(new DoInstr(assert_assignable));
}
Value* EffectGraphVisitor::BuildAssignableValue(AstNode* value_node,
Value* value,
const AbstractType& dst_type,
const String& dst_name) {
if (CanSkipTypeCheck(value_node, dst_type)) {
return value;
}
// Build the type check computation.
Value* instantiator_type_arguments = NULL;
if (!dst_type.IsInstantiated()) {
instantiator_type_arguments =
BuildInstantiatorTypeArguments(value_node->token_index());
}
BindInstr* assert_assignable =
new BindInstr(new AssertAssignableComp(value_node->token_index(),
owner()->try_index(),
value,
instantiator_type_arguments,
dst_type,
dst_name));
AddInstruction(assert_assignable);
return new UseVal(assert_assignable);
}
void EffectGraphVisitor::BuildInstanceOf(ComparisonNode* node) {
ASSERT(Token::IsInstanceofOperator(node->kind()));
EffectGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
}
void ValueGraphVisitor::BuildInstanceOf(ComparisonNode* node) {
ASSERT(Token::IsInstanceofOperator(node->kind()));
const Bool& bool_true = Bool::ZoneHandle(Bool::True());
const Bool& bool_false = Bool::ZoneHandle(Bool::False());
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(Isolate::Current()->object_store()->object_type());
Error& malformed_error = Error::Handle();
if (type.IsInstantiated() &&
object_type.IsSubtypeOf(type, &malformed_error)) {
// Must evaluate left side.
EffectGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
ReturnComputation(new ConstantVal(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());
ConstantVal* 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 ConstantVal(negate_result ? bool_true : bool_false);
} else {
Error& malformed_error = Error::Handle();
if (literal_value.IsInstanceOf(type,
TypeArguments::Handle(),
&malformed_error)) {
result = new ConstantVal(negate_result ? bool_false : bool_true);
} else {
ASSERT(malformed_error.IsNull());
result = new ConstantVal(negate_result ? bool_true : bool_false);
}
}
ReturnComputation(result);
return;
}
ValueGraphVisitor for_left_value(owner(), temp_index());
node->left()->Visit(&for_left_value);
Append(for_left_value);
Value* type_arguments = NULL;
if (!type.IsInstantiated()) {
type_arguments =
BuildInstantiatorTypeArguments(node->token_index());
}
InstanceOfComp* instance_of =
new InstanceOfComp(node->token_index(),
owner()->try_index(),
for_left_value.value(),
type_arguments,
node->right()->AsTypeNode()->type(),
(node->kind() == Token::kISNOT));
ReturnComputation(instance_of);
}
// <Expression> :: Comparison { kind: Token::Kind
// left: <Expression>
// right: <Expression> }
// TODO(srdjan): Implement new equality.
void EffectGraphVisitor::VisitComparisonNode(ComparisonNode* node) {
if (Token::IsInstanceofOperator(node->kind())) {
BuildInstanceOf(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);
StrictCompareComp* comp = new StrictCompareComp(
node->kind(), for_left_value.value(), for_right_value.value());
ReturnComputation(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);
EqualityCompareComp* comp = new EqualityCompareComp(
node->token_index(), owner()->try_index(),
for_left_value.value(), for_right_value.value());
if (node->kind() == Token::kEQ) {
ReturnComputation(comp);
} else {
Definition* eq_result = new BindInstr(comp);
AddInstruction(eq_result);
if (FLAG_enable_type_checks) {
eq_result =
new BindInstr(new AssertBooleanComp(node->token_index(),
owner()->try_index(),
new UseVal(eq_result)));
AddInstruction(eq_result);
}
BooleanNegateComp* negate = new BooleanNegateComp(new UseVal(eq_result));
ReturnComputation(negate);
}
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);
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->token_index(), owner()->try_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);
Value* value = for_value.value();
if (FLAG_enable_type_checks) {
BindInstr* assert_boolean =
new BindInstr(new AssertBooleanComp(node->operand()->token_index(),
owner()->try_index(),
value));
AddInstruction(assert_boolean);
value = new UseVal(assert_boolean);
}
BooleanNegateComp* negate = new BooleanNegateComp(value);
ReturnComputation(negate);
return;
}
ValueGraphVisitor 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->token_index(), owner()->try_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitConditionalExprNode(ConditionalExprNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_index());
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_index());
node->condition()->Visit(&for_test);
// 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());
ASSERT(for_true.value()->IsTemp() || for_true.value()->IsUse());
ValueGraphVisitor for_false(owner(), temp_index());
node->false_expr()->Visit(&for_false);
ASSERT(for_false.is_open());
ASSERT(for_false.value()->IsTemp() || for_false.value()->IsUse());
Join(for_test, for_true, for_false);
ReturnValue(new TempVal(temp_index() - 1));
}
// <Statement> ::= If { condition: <Expression>
// true_branch: <Sequence>
// false_branch: <Sequence> }
void EffectGraphVisitor::VisitIfNode(IfNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_index());
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()) {
AddInstruction(node->label()->join_for_break());
} else {
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.
const bool needs_join_at_statement_entry =
(len > 1) || ((len > 0) && (node->contains_default()));
EffectGraphVisitor for_case_statements(owner(), temp_index());
// Compute start of statements fragment.
BlockEntryInstr* 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.
if (node->label()->join_for_continue() == NULL) {
node->label()->set_join_for_continue(new JoinEntryInstr());
}
statement_start = node->label()->join_for_continue();
} else if (needs_join_at_statement_entry) {
statement_start = new JoinEntryInstr();
} else {
statement_start = new TargetEntryInstr();
}
for_case_statements.AddInstruction(statement_start);
node->statements()->Visit(&for_case_statements);
if (is_open() && (len == 0)) {
ASSERT(node->contains_default());
// Default only case node.
Append(for_case_statements);
return;
}
// Generate instructions for all case expressions and collect data to
// connect them.
GrowableArray<TargetEntryInstr**> case_true_addresses;
GrowableArray<TargetEntryInstr**> case_false_addresses;
GrowableArray<TargetEntryInstr*> case_entries;
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_index());
if (i == 0) {
case_entries.Add(NULL); // Not to be used
case_expr->Visit(&for_case_expression);
// Append only the first one, everything else is connected from it.
Append(for_case_expression);
} else {
TargetEntryInstr* case_entry_target = new TargetEntryInstr();
case_entries.Add(case_entry_target);
for_case_expression.AddInstruction(case_entry_target);
case_expr->Visit(&for_case_expression);
}
case_true_addresses.Add(for_case_expression.true_successor_address());
case_false_addresses.Add(for_case_expression.false_successor_address());
}
// Once a test fragment has been added, this fragment is closed.
ASSERT(!is_open());
// Connect all test cases except the last one.
for (intptr_t i = 0; i < (len - 1); i++) {
ASSERT(needs_join_at_statement_entry);
*case_false_addresses[i] = case_entries[i + 1];
TargetEntryInstr* true_target = new TargetEntryInstr();
*case_true_addresses[i] = true_target;
true_target->SetSuccessor(statement_start);
}
BlockEntryInstr* exit_instruction = NULL;
// Handle last (or only) case: false goes to exit or to statement if this
// node contains default.
if (len > 0) {
if (statement_start->IsTargetEntry()) {
*case_true_addresses[len - 1] = statement_start->AsTargetEntry();
} else {
TargetEntryInstr* true_target = new TargetEntryInstr();
*case_true_addresses[len - 1] = true_target;
true_target->SetSuccessor(statement_start);
}
TargetEntryInstr* false_target = new TargetEntryInstr();
*case_false_addresses[len - 1] = false_target;
if (node->contains_default()) {
// True and false go to statement start.
false_target->SetSuccessor(statement_start);
if (for_case_statements.is_open()) {
exit_instruction = new TargetEntryInstr();
for_case_statements.exit()->SetSuccessor(exit_instruction);
}
} else {
if (for_case_statements.is_open()) {
exit_instruction = new JoinEntryInstr();
for_case_statements.exit()->SetSuccessor(exit_instruction);
} else {
exit_instruction = new TargetEntryInstr();
}
false_target->SetSuccessor(exit_instruction);
}
} else {
// A CaseNode without case expressions must contain default.
ASSERT(node->contains_default());
AddInstruction(statement_start);
}
ASSERT(!is_open());
exit_ = exit_instruction;
}
// <Statement> ::= While { label: SourceLabel
// condition: <Expression>
// body: <Sequence> }
// The fragment is composed as follows:
// a) continue-join (optional)
// b) loop-join
// c) [ test ] -> (body-entry-target, loop-exit-target)
// d) body-entry-target
// e) [ body ] -> (loop-join)
// f) loop-exit-target
// g) break-join (optional)
void EffectGraphVisitor::VisitWhileNode(WhileNode* node) {
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_index());
node->condition()->Visit(&for_test);
ASSERT(!for_test.is_empty()); // Language spec.
EffectGraphVisitor for_body(owner(), temp_index());
node->body()->Visit(&for_body);
// Labels are set after body traversal.
SourceLabel* lbl = node->label();
ASSERT(lbl != NULL);
if (lbl->join_for_continue() != NULL) {
AddInstruction(lbl->join_for_continue());
}
TieLoop(for_test, for_body);
if (lbl->join_for_break() != NULL) {
AddInstruction(lbl->join_for_break());
}
}
// 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());
node->body()->Visit(&for_body);
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_index());
node->condition()->Visit(&for_test);
ASSERT(is_open());
// Tie do-while loop (test is after the body).
JoinEntryInstr* body_entry_join = new JoinEntryInstr();
AddInstruction(body_entry_join);
body_entry_join->SetSuccessor(for_body.entry());
Instruction* body_exit =
for_body.is_empty() ? body_entry_join : for_body.exit();
if (for_body.is_open() || (node->label()->join_for_continue() != NULL)) {
BlockEntryInstr* test_entry = NULL;
if (node->label()->join_for_continue() == NULL) {
test_entry = new TargetEntryInstr();
} else {
test_entry = node->label()->join_for_continue();
}
test_entry->SetSuccessor(for_test.entry());
if (body_exit != NULL) {
body_exit->SetSuccessor(test_entry);
}
}
TargetEntryInstr* back_target_entry = new TargetEntryInstr();
*for_test.true_successor_address() = back_target_entry;
back_target_entry->SetSuccessor(body_entry_join);
TargetEntryInstr* loop_exit_target = new TargetEntryInstr();
*for_test.false_successor_address() = loop_exit_target;
if (node->label()->join_for_break() == NULL) {
exit_ = loop_exit_target;
} else {
loop_exit_target->SetSuccessor(node->label()->join_for_break());
exit_ = node->label()->join_for_break();
}
}
// 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());
TargetEntryInstr* body_entry = new TargetEntryInstr();
for_body.AddInstruction(body_entry);
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());
if ((node->label()->join_for_continue() == NULL) && for_body.is_open()) {
// Do not insert an extra basic block.
node->increment()->Visit(&for_increment);
for_body.Append(for_increment);
loop_increment_end = for_body.exit();
// 'for_body' contains at least the TargetInstruction 'body_entry'.
ASSERT(loop_increment_end != NULL);
} else if (node->label()->join_for_continue() != NULL) {
// Insert join between body and increment.
if (for_body.is_open()) {
for_body.exit()->SetSuccessor(node->label()->join_for_continue());
}
for_increment.AddInstruction(node->label()->join_for_continue());
node->increment()->Visit(&for_increment);
loop_increment_end = for_increment.exit();
ASSERT(loop_increment_end != NULL);
} else {
loop_increment_end = NULL;
ASSERT(!for_body.is_open() && node->label()->join_for_continue() == 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();
AddInstruction(loop_start);
loop_increment_end->SetSuccessor(loop_start);
}
if (node->condition() == NULL) {
// Endless loop, no test.
Append(for_body);
if (node->label()->join_for_break() == NULL) {
CloseFragment();
} else {
// Control flow of ForLoop continues into join_for_break.
exit_ = node->label()->join_for_break();
}
} else {
TargetEntryInstr* loop_exit = new TargetEntryInstr();
TestGraphVisitor for_test(owner(),
temp_index(),
node->condition()->token_index());
node->condition()->Visit(&for_test);
Append(for_test);
*for_test.true_successor_address() = body_entry;
*for_test.false_successor_address() = loop_exit;
if (node->label()->join_for_break() == NULL) {
exit_ = loop_exit;
} else {
loop_exit->SetSuccessor(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();
}
Instruction* jump_target = NULL;
if (node->kind() == Token::kBREAK) {
if (node->label()->join_for_break() == NULL) {
node->label()->set_join_for_break(new JoinEntryInstr());
}
jump_target = node->label()->join_for_break();
} else {
if (node->label()->join_for_continue() == NULL) {
node->label()->set_join_for_continue(new JoinEntryInstr());
}
jump_target = node->label()->join_for_continue();
}
AddInstruction(jump_target);
CloseFragment();
}
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());
for (int i = 0; i < node->length(); ++i) {
ValueGraphVisitor for_value(owner(), temp_index());
node->ElementAt(i)->Visit(&for_value);
Append(for_value);
values->Add(for_value.value());
}
Value* element_type = new UseVal(
BuildInstantiatedTypeArguments(node->token_index(),
node->type_arguments()));
CreateArrayComp* create = new CreateArrayComp(node->token_index(),
owner()->try_index(),
values,
element_type);
ReturnComputation(create);
}
void EffectGraphVisitor::VisitClosureNode(ClosureNode* node) {
const Function& function = node->function();
if (function.IsNonImplicitClosureFunction()) {
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);
} else if (function.IsImplicitInstanceClosureFunction()) {
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_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.
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_index());
}
CreateClosureComp* create =
new CreateClosureComp(node, owner()->try_index(), type_arguments);
ReturnComputation(create);
}
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::VisitInstanceCallNode(InstanceCallNode* node) {
ArgumentListNode* arguments = node->arguments();
int length = arguments->length();
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>(length + 1);
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
values->Add(for_receiver.value());
TranslateArgumentList(*arguments, values);
InstanceCallComp* call = new InstanceCallComp(
node->token_index(), owner()->try_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(), values);
StaticCallComp* call =
new StaticCallComp(node->token_index(),
owner()->try_index(),
node->function(),
node->arguments()->names(),
values);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitClosureCallNode(ClosureCallNode* node) {
// Context is saved around the call, it's treated as an extra operand
// consumed by the call (but not an argument).
BindInstr* context = new BindInstr(new CurrentContextComp());
AddInstruction(context);
ValueGraphVisitor for_closure(owner(), temp_index());
node->closure()->Visit(&for_closure);
Append(for_closure);
ZoneGrowableArray<Value*>* arguments =
new ZoneGrowableArray<Value*>(node->arguments()->length());
arguments->Add(for_closure.value());
TranslateArgumentList(*node->arguments(), arguments);
// First operand is the saved context, consumed by the call.
ClosureCallComp* call = new ClosureCallComp(node,
owner()->try_index(),
new UseVal(context),
arguments);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitCloneContextNode(CloneContextNode* node) {
BindInstr* context = new BindInstr(new CurrentContextComp());
AddInstruction(context);
BindInstr* clone =
new BindInstr(new CloneContextComp(node->token_index(),
owner()->try_index(),
new UseVal(context)));
AddInstruction(clone);
ReturnComputation(new StoreContextComp(new UseVal(clone)));
}
Definition* EffectGraphVisitor::BuildObjectAllocation(
ConstructorCallNode* node) {
const Class& cls = Class::ZoneHandle(node->constructor().owner());
const bool requires_type_arguments = cls.HasTypeArguments();
ZoneGrowableArray<Value*>* allocate_arguments =
new ZoneGrowableArray<Value*>();
if (requires_type_arguments) {
BuildConstructorTypeArguments(node, allocate_arguments);
}
// In checked mode, if the type arguments are uninstantiated, they may need to
// be checked against declared bounds at run time.
Computation* allocate_comp = NULL;
Error& malformed_error = Error::Handle();
if (FLAG_enable_type_checks &&
requires_type_arguments &&
!node->type_arguments().IsNull() &&
!node->type_arguments().IsInstantiated() &&
!node->type_arguments().IsWithinBoundsOf(cls,
node->type_arguments(),
&malformed_error)) {
// The uninstantiated type arguments cannot be verified to be within their
// bounds at compile time, so verify them at runtime.
// Although the type arguments may be uninstantiated at compile time, they
// may represent the identity vector and may be replaced by the instantiated
// type arguments of the instantiator at run time.
allocate_comp = new AllocateObjectWithBoundsCheckComp(node,
owner()->try_index(),
allocate_arguments);
} else {
allocate_comp = new AllocateObjectComp(node,
owner()->try_index(),
allocate_arguments);
}
BindInstr* allocate = new BindInstr(allocate_comp);
AddInstruction(allocate);
return allocate;
}
void EffectGraphVisitor::BuildConstructorCall(ConstructorCallNode* node,
Value* alloc_value) {
BindInstr* ctor_arg =
new BindInstr(new ConstantVal(
Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll))));
AddInstruction(ctor_arg);
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>();
values->Add(alloc_value);
values->Add(new UseVal(ctor_arg));
TranslateArgumentList(*node->arguments(), values);
StaticCallComp* call =
new StaticCallComp(node->token_index(),
owner()->try_index(),
node->constructor(),
node->arguments()->names(),
values);
AddInstruction(new DoInstr(call));
}
void EffectGraphVisitor::VisitConstructorCallNode(ConstructorCallNode* node) {
if (node->constructor().IsFactory()) {
ZoneGrowableArray<Value*>* factory_arguments =
new ZoneGrowableArray<Value*>();
factory_arguments->Add(
new UseVal(BuildInstantiatedTypeArguments(node->token_index(),
node->type_arguments())));
ASSERT(factory_arguments->length() == 1);
TranslateArgumentList(*node->arguments(), factory_arguments);
StaticCallComp* call =
new StaticCallComp(node->token_index(),
owner()->try_index(),
node->constructor(),
node->arguments()->names(),
factory_arguments);
ReturnComputation(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).
Definition* allocate = BuildObjectAllocation(node);
BuildConstructorCall(node, new UseVal(allocate));
}
Value* EffectGraphVisitor::BuildInstantiatorTypeArguments(
intptr_t token_index) {
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();
// TODO(regis): Temporary type should be allocated in new gen heap.
Type& type = Type::Handle(
Type::New(instantiator_class, type_arguments, token_index));
type ^= ClassFinalizer::FinalizeType(
instantiator_class, type, ClassFinalizer::kFinalizeWellFormed);
type_arguments = type.arguments();
BindInstr* args = new BindInstr(new ConstantVal(type_arguments));
AddInstruction(args);
return new UseVal(args);
}
ASSERT(owner()->parsed_function().instantiator() != NULL);
ValueGraphVisitor for_instantiator(owner(), temp_index());
owner()->parsed_function().instantiator()->Visit(&for_instantiator);
Append(for_instantiator);
Function& outer_function =
Function::Handle(owner()->parsed_function().function().raw());
while (outer_function.IsLocalFunction()) {
outer_function = outer_function.parent_function();
}
if (outer_function.IsFactory()) {
// All OK.
return for_instantiator.value();
}
// 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_instance_field_offset =
instantiator_class.type_arguments_instance_field_offset();
ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments);
BindInstr* load =
new BindInstr(new NativeLoadFieldComp(
for_instantiator.value(),
type_arguments_instance_field_offset));
AddInstruction(load);
return new UseVal(load);
}
Definition* EffectGraphVisitor::BuildInstantiatedTypeArguments(
intptr_t token_index,
const AbstractTypeArguments& type_arguments) {
if (type_arguments.IsNull() || type_arguments.IsInstantiated()) {
BindInstr* type_args =
new BindInstr(new ConstantVal(type_arguments));
AddInstruction(type_args);
return type_args;
}
// The type arguments are uninstantiated.
Value* instantiator_value = BuildInstantiatorTypeArguments(token_index);
BindInstr* instantiate =
new BindInstr(new InstantiateTypeArgumentsComp(token_index,
owner()->try_index(),
type_arguments,
instantiator_value));
AddInstruction(instantiate);
return instantiate;
}
void EffectGraphVisitor::BuildConstructorTypeArguments(
ConstructorCallNode* node,
ZoneGrowableArray<Value*>* args) {
const Class& cls = Class::ZoneHandle(node->constructor().owner());
ASSERT(cls.HasTypeArguments() && !node->constructor().IsFactory());
if (node->type_arguments().IsNull() ||
node->type_arguments().IsInstantiated()) {
BindInstr* type_args =
new BindInstr(new ConstantVal(node->type_arguments()));
AddInstruction(type_args);
// No instantiator required.
BindInstr* no_instantiator =
new BindInstr(new ConstantVal(
Smi::ZoneHandle(Smi::New(
StubCode::kNoInstantiator))));
AddInstruction(no_instantiator);
args->Add(new UseVal(type_args));
args->Add(new UseVal(no_instantiator));
return;
}
// The type arguments are uninstantiated.
// Place holder to hold uninstantiated constructor type arguments.
BindInstr* placeholder =
new BindInstr(new ConstantVal(Object::ZoneHandle()));
AddInstruction(placeholder);
Value* instantiator =
BuildInstantiatorTypeArguments(node->token_index());
ASSERT(instantiator->IsUse());
PickTempInstr* duplicate_instantiator =
new PickTempInstr(instantiator->AsUse()->definition()->temp_index());
AddInstruction(duplicate_instantiator);
BindInstr* extract_type_arguments = new BindInstr(
new ExtractConstructorTypeArgumentsComp(
node->token_index(),
owner()->try_index(),
node->type_arguments(),
new UseVal(duplicate_instantiator)));
AddInstruction(extract_type_arguments);
AddInstruction(new TuckTempInstr(placeholder->temp_index(),
extract_type_arguments->temp_index()));
BindInstr* extract_instantiator =
new BindInstr(new ExtractConstructorInstantiatorComp(
node,
instantiator,
new UseVal(extract_type_arguments)));
AddInstruction(extract_instantiator);
args->Add(new UseVal(placeholder));
args->Add(new UseVal(extract_instantiator));
}
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+1 <- Pick(t_n)
// t_n+2 <- ctor-arg
// t_n+3... <- constructor arguments start here
// StaticCall(constructor, t_n+1, t_n+2, ...)
Definition* allocate = BuildObjectAllocation(node);
PickTempInstr* duplicate = new PickTempInstr(allocate->temp_index());
AddInstruction(duplicate);
BuildConstructorCall(node, new UseVal(duplicate));
ReturnValue(new UseVal(allocate));
}
void EffectGraphVisitor::VisitInstanceGetterNode(InstanceGetterNode* node) {
ValueGraphVisitor 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->token_index(), owner()->try_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitInstanceSetterNode(InstanceSetterNode* node) {
ValueGraphVisitor for_receiver(owner(), temp_index());
node->receiver()->Visit(&for_receiver);
Append(for_receiver);
ValueGraphVisitor for_value(owner(), for_receiver.temp_index());
node->value()->Visit(&for_value);
Append(for_value);
InstanceSetterComp* setter =
new InstanceSetterComp(node->token_index(),
owner()->try_index(),
node->field_name(),
for_receiver.value(),
for_value.value());
ReturnComputation(setter);
}
void EffectGraphVisitor::VisitStaticGetterNode(StaticGetterNode* node) {
const String& getter_name =
String::Handle(Field::GetterName(node->field_name()));
const Function& getter_function =
Function::ZoneHandle(node->cls().LookupStaticFunction(getter_name));
ASSERT(!getter_function.IsNull());
ZoneGrowableArray<Value*>* values = new ZoneGrowableArray<Value*>();
StaticCallComp* call = new StaticCallComp(node->token_index(),
owner()->try_index(),
getter_function,
Array::ZoneHandle(), // No names.
values);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitStaticSetterNode(StaticSetterNode* node) {
const String& setter_name =
String::Handle(Field::SetterName(node->field_name()));
const Function& setter_function =
Function::ZoneHandle(node->cls().LookupStaticFunction(setter_name));
ASSERT(!setter_function.IsNull());
ValueGraphVisitor for_value(owner(), temp_index());
node->value()->Visit(&for_value);
Append(for_value);
StaticSetterComp* call = new StaticSetterComp(node->token_index(),
owner()->try_index(),
setter_function,
for_value.value());
ReturnComputation(call);
}
void EffectGraphVisitor::VisitNativeBodyNode(NativeBodyNode* node) {
NativeCallComp* native_call =
new NativeCallComp(node, owner()->try_index());
ReturnComputation(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);
LoadLocalComp* load = new LoadLocalComp(node->local(),
owner()->context_level());
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* store_value = for_value.value();
if (FLAG_enable_type_checks) {
store_value = BuildAssignableValue(node->value(),
store_value,
node->local().type(),
node->local().name());
}
StoreLocalComp* store =
new StoreLocalComp(node->local(), store_value, owner()->context_level());
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());
const String& dst_name = String::ZoneHandle(node->field().name());
store_value = BuildAssignableValue(node->value(),
store_value,
type,
dst_name);
}
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());
const String& dst_name = String::ZoneHandle(node->field().name());
store_value = BuildAssignableValue(node->value(),
store_value,
type,
dst_name);
}
StoreStaticFieldComp* store =
new StoreStaticFieldComp(node->field(), store_value);
ReturnComputation(store);
}
void EffectGraphVisitor::VisitLoadIndexedNode(LoadIndexedNode* node) {
ValueGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);
ValueGraphVisitor 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->token_index(), owner()->try_index(), name,
arguments, Array::ZoneHandle(), 1);
ReturnComputation(call);
}
void EffectGraphVisitor::VisitStoreIndexedNode(StoreIndexedNode* node) {
ValueGraphVisitor for_array(owner(), temp_index());
node->array()->Visit(&for_array);
Append(for_array);
ValueGraphVisitor for_index(owner(), for_array.temp_index());
node->index_expr()->Visit(&for_index);
Append(for_index);
ValueGraphVisitor for_value(owner(), for_index.temp_index());
node->value()->Visit(&for_value);
Append(for_value);
StoreIndexedComp* store = new StoreIndexedComp(node->token_index(),
owner()->try_index(),
for_array.value(),
for_index.value(),
for_value.value());
ReturnComputation(store);
}
bool EffectGraphVisitor::MustSaveRestoreContext(SequenceNode* node) const {
return (node == owner()->parsed_function().node_sequence()) &&
(owner()->parsed_function().saved_context_var() != NULL);
}
void EffectGraphVisitor::UnchainContext() {
BindInstr* context = new BindInstr(new CurrentContextComp());
AddInstruction(context);
BindInstr* parent =
new BindInstr(new NativeLoadFieldComp(
new UseVal(context), Context::parent_offset()));
AddInstruction(parent);
AddInstruction(new DoInstr(new StoreContextComp(new UseVal(parent))));
}
// <Statement> ::= Sequence { scope: LocalScope
// nodes: <Statement>*
// label: SourceLabel }
void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) {
LocalScope* scope = node->scope();
const intptr_t num_context_variables =
(scope != NULL) ? scope->num_context_variables() : 0;
int previous_context_level = owner()->context_level();
if (num_context_variables > 0) {
// The loop local scope declares variables that are captured.
// Allocate and chain a new context.
// Allocate context computation (uses current CTX)
BindInstr* allocated_context =
new BindInstr(new AllocateContextComp(node->token_index(),
owner()->try_index(),
num_context_variables));
AddInstruction(allocated_context);
// If this node_sequence is the body of the function being compiled, and if
// this function is not a closure, do not link the current context as the
// parent of the newly allocated context, as it is not accessible. Instead,
// save it in a pre-allocated variable and restore it on exit.
if (MustSaveRestoreContext(node)) {
BindInstr* current_context = new BindInstr(new CurrentContextComp());
AddInstruction(current_context);
StoreLocalComp* store_local = new StoreLocalComp(
*owner()->parsed_function().saved_context_var(),
new UseVal(current_context),
0);
AddInstruction(new DoInstr(store_local));
BindInstr* null_context =
new BindInstr(new ConstantVal(Object::ZoneHandle()));
AddInstruction(null_context);
StoreContextComp* store_context =
new StoreContextComp(new UseVal(null_context));
AddInstruction(new DoInstr(store_context));
}
ChainContextComp* chain_context =
new ChainContextComp(new UseVal(allocated_context));
AddInstruction(new DoInstr(chain_context));
owner()->set_context_level(scope->context_level());
// If this node_sequence is the body of the function being compiled, copy
// the captured parameters from the frame into the context.
if (node == owner()->parsed_function().node_sequence()) {
ASSERT(scope->context_level() == 1);
const Function& function = owner()->parsed_function().function();
const int num_params = function.NumberOfParameters();
int param_frame_index =
(num_params == function.num_fixed_parameters()) ? 1 + num_params : -1;
for (int pos = 0; pos < num_params; param_frame_index--, pos++) {
const LocalVariable& parameter = *scope->VariableAt(pos);
ASSERT(parameter.owner() == scope);
if (parameter.is_captured()) {
// Create a temporary local describing the original position.
const String& temp_name = String::ZoneHandle(String::Concat(
parameter.name(), String::Handle(String::NewSymbol("-orig"))));
LocalVariable* temp_local = new LocalVariable(
0, // Token index.
temp_name,
Type::ZoneHandle(Type::DynamicType())); // Type.
temp_local->set_index(param_frame_index);
// Copy parameter from local frame to current context.
BindInstr* load =
new BindInstr(new LoadLocalComp(*temp_local,
owner()->context_level()));
AddInstruction(load);
StoreLocalComp* store_local = new StoreLocalComp(
parameter,
new UseVal(load),
owner()->context_level());
AddInstruction(new DoInstr(store_local));
// Write NULL to the source location to detect buggy accesses and
// allow GC of passed value if it gets overwritten by a new value in
// the function.
BindInstr* null_constant =
new BindInstr(new ConstantVal(Object::ZoneHandle()));
AddInstruction(null_constant);
StoreLocalComp* clear_local = new StoreLocalComp(
*temp_local,
new UseVal(null_constant),
owner()->context_level());
AddInstruction(new DoInstr(clear_local));
}
}
}
}
if (FLAG_enable_type_checks &&
(node == owner()->parsed_function().node_sequence())) {
const int num_params =
owner()->parsed_function().function().NumberOfParameters();
for (int pos = 0; pos < num_params; pos++) {
const LocalVariable& parameter = *scope->VariableAt(pos);
ASSERT(parameter.owner() == scope);
if (!CanSkipTypeCheck(NULL, parameter.type())) {
BindInstr* load =
new BindInstr(new LoadLocalComp(parameter,
owner()->context_level()));
AddInstruction(load);
BuildAssertAssignable(parameter.token_index(),
new UseVal(load),
parameter.type(),
parameter.name());
}
}
}
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);
if (!is_open()) {
// E.g., because of a JumpNode.
break;
}
}
if (is_open()) {
if (MustSaveRestoreContext(node)) {
ASSERT(num_context_variables > 0);
BuildLoadContext(*owner()->parsed_function().saved_context_var());
} else if (num_context_variables > 0) {
UnchainContext();
}
}
// No continue on sequence allowed.
ASSERT((node->label() == NULL) ||
(node->label()->join_for_continue() == NULL));
// If this node sequence is labeled, a break out of the sequence will have
// taken care of unchaining the context.
if ((node->label() != NULL) &&
(node->label()->join_for_break() != NULL)) {
if (is_open()) {
AddInstruction(node->label()->join_for_break());
} else {
exit_ = node->label()->join_for_break();
}
}
// The outermost function sequence cannot contain a label.
ASSERT((node->label() == NULL) ||
(node != owner()->parsed_function().node_sequence()));
owner()->set_context_level(previous_context_level);
}
void EffectGraphVisitor::VisitCatchClauseNode(CatchClauseNode* node) {
// NOTE: The implicit variables ':saved_context', ':exception_var'
// and ':stacktrace_var' can never be captured variables.
// Restores CTX from local variable ':saved_context'.
CatchEntryComp* catch_entry = new CatchEntryComp(node->exception_var(),
node->stacktrace_var());
AddInstruction(new DoInstr(catch_entry));
BuildLoadContext(node->context_var());
EffectGraphVisitor for_catch(owner(), temp_index());
node->VisitChildren(&for_catch);
Append(for_catch);
}
void EffectGraphVisitor::VisitTryCatchNode(TryCatchNode* node) {
intptr_t old_try_index = owner()->try_index();
intptr_t try_index = owner()->AllocateTryIndex();
owner()->set_try_index(try_index);
// Preserve CTX into local variable '%saved_context'.
BuildStoreContext(node->context_var());
EffectGraphVisitor for_try_block(owner(), temp_index());
node->try_block()->Visit(&for_try_block);
Append(for_try_block);
// We are done generating code for the try block.
owner()->set_try_index(old_try_index);
CatchClauseNode* catch_block = node->catch_block();
if (catch_block != NULL) {
// Set the corresponding try index for this catch block so
// that we can set the appropriate handler pc when we generate
// code for this catch block.
catch_block->set_try_index(try_index);
EffectGraphVisitor for_catch_block(owner(), temp_index());
for_catch_block.AddInstruction(new TargetEntryInstr(try_index));
catch_block->Visit(&for_catch_block);
owner()->AddCatchEntry(try_index, for_catch_block.entry());
ASSERT(!for_catch_block.is_open());
if ((node->end_catch_label() != NULL) &&
(node->end_catch_label()->join_for_continue() != NULL)) {
if (is_open()) {
AddInstruction(node->end_catch_label()->join_for_continue());
} else {
exit_ = node->end_catch_label()->join_for_continue();
}
}
}
// Generate code for the finally block if one exists.
if ((node->finally_block() != NULL) && is_open()) {
EffectGraphVisitor for_finally_block(owner(), temp_index());
node->finally_block()->Visit(&for_finally_block);
Append(for_finally_block);
}
}
void EffectGraphVisitor::BuildThrowNode(ThrowNode* node) {
ValueGraphVisitor for_exception(owner(), temp_index());
node->exception()->Visit(&for_exception);
Append(for_exception);
Instruction* instr = NULL;
if (node->stacktrace() == NULL) {
instr = new ThrowInstr(node->token_index(),
owner()->try_index(),
for_exception.value());
} else {
ValueGraphVisitor for_stack_trace(owner(), temp_index() + 1);
node->stacktrace()->Visit(&for_stack_trace);
Append(for_stack_trace);
instr = new ReThrowInstr(node->token_index(),
owner()->try_index(),
for_exception.value(),
for_stack_trace.value());
}
AddInstruction(instr);
}
void EffectGraphVisitor::VisitThrowNode(ThrowNode* node) {
BuildThrowNode(node);
CloseFragment();
}
// A throw cannot be part of an expression, however, the parser may replace
// certain expression nodes with a throw. In that case generate a literal null
// so that the fragment is not closed in the middle of an expression.
void ValueGraphVisitor::VisitThrowNode(ThrowNode* node) {
BuildThrowNode(node);
ReturnComputation(new ConstantVal(Instance::ZoneHandle()));
}
void EffectGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) {
const intptr_t try_index = owner()->try_index();
if (try_index >= 0) {
// We are about to generate code for an inlined finally block. Exceptions
// thrown in this block of code should be treated as though they are
// thrown not from the current try block but the outer try block if any.
owner()->set_try_index((try_index - 1));
}
BuildLoadContext(node->context_var());
EffectGraphVisitor for_finally_block(owner(), temp_index());
node->finally_block()->Visit(&for_finally_block);
Append(for_finally_block);
if (try_index >= 0) {
owner()->set_try_index(try_index);
}
}
// Graph printing.
class FlowGraphPrinter : public FlowGraphVisitor {
public:
FlowGraphPrinter(const Function& function,
const GrowableArray<BlockEntryInstr*>& block_order)
: FlowGraphVisitor(block_order), 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();
// 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() {
OS::Print("==== %s\n", function_.ToFullyQualifiedCString());
for (intptr_t i = 0; i < block_order_.length(); ++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) {
// For readability label blocks with their reverse postorder index,
// not their postorder block number, so the first block is 0 (not
// n-1).
OS::Print(" goto %d", reverse_index(successor->postorder_number()));
}
OS::Print("\n");
}
}
void FlowGraphPrinter::VisitTemp(TempVal* val) {
OS::Print("t%d", val->index());
}
void FlowGraphPrinter::VisitUse(UseVal* val) {
OS::Print("s%d", val->definition()->temp_index());
}
void FlowGraphPrinter::VisitConstant(ConstantVal* val) {
OS::Print("#%s", val->value().ToCString());
}
void FlowGraphPrinter::VisitAssertAssignable(AssertAssignableComp* comp) {
OS::Print("AssertAssignable(");
comp->value()->Accept(this);
OS::Print(", %s, '%s'",
String::Handle(comp->dst_type().Name()).ToCString(),
comp->dst_name().ToCString());
if (comp->instantiator_type_arguments() != NULL) {
OS::Print(" (instantiator:");
comp->instantiator_type_arguments()->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitAssertBoolean(AssertBooleanComp* comp) {
OS::Print("AssertBoolean(");
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitCurrentContext(CurrentContextComp* comp) {
OS::Print("CurrentContext");
}
void FlowGraphPrinter::VisitClosureCall(ClosureCallComp* comp) {
OS::Print("ClosureCall(");
comp->context()->Accept(this);
for (intptr_t i = 0; i < comp->ArgumentCount(); ++i) {
OS::Print(", ");
comp->ArgumentAt(i)->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitInstanceCall(InstanceCallComp* comp) {
OS::Print("InstanceCall(%s", comp->function_name().ToCString());
for (intptr_t 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::VisitEqualityCompare(EqualityCompareComp* comp) {
comp->left()->Accept(this);
OS::Print(" == ");
comp->right()->Accept(this);
}
void FlowGraphPrinter::VisitStaticCall(StaticCallComp* comp) {
OS::Print("StaticCall(%s",
String::Handle(comp->function().name()).ToCString());
for (intptr_t i = 0; i < comp->ArgumentCount(); ++i) {
OS::Print(", ");
comp->ArgumentAt(i)->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitLoadLocal(LoadLocalComp* comp) {
OS::Print("LoadLocal(%s lvl:%d)",
comp->local().name().ToCString(), comp->context_level());
}
void FlowGraphPrinter::VisitStoreLocal(StoreLocalComp* comp) {
OS::Print("StoreLocal(%s, ", comp->local().name().ToCString());
comp->value()->Accept(this);
OS::Print(", lvl: %d)", comp->context_level());
}
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::VisitStaticSetter(StaticSetterComp* comp) {
OS::Print("StaticSetter(");
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());
if (comp->type_arguments() != NULL) {
OS::Print(" (type-arg:");
comp->type_arguments()->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitAllocateObject(AllocateObjectComp* comp) {
OS::Print("AllocateObject(%s",
Class::Handle(comp->constructor().owner()).ToCString());
for (intptr_t i = 0; i < comp->arguments().length(); i++) {
OS::Print(", ");
comp->arguments()[i]->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitAllocateObjectWithBoundsCheck(
AllocateObjectWithBoundsCheckComp* comp) {
OS::Print("AllocateObjectWithBoundsCheck(%s",
Class::Handle(comp->constructor().owner()).ToCString());
for (intptr_t i = 0; i < comp->arguments().length(); i++) {
OS::Print(", ");
comp->arguments()[i]->Accept(this);
}
OS::Print(")");
}
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);
}
if (comp->ElementCount() > 0) OS::Print(", ");
comp->element_type()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitCreateClosure(CreateClosureComp* comp) {
OS::Print("CreateClosure(%s", comp->function().ToCString());
if (comp->type_arguments() != NULL) {
OS::Print(", ");
comp->type_arguments()->Accept(this);
}
OS::Print(")");
}
void FlowGraphPrinter::VisitNativeLoadField(NativeLoadFieldComp* comp) {
OS::Print("NativeLoadField(");
comp->value()->Accept(this);
OS::Print(", %d)", comp->offset_in_bytes());
}
void FlowGraphPrinter::VisitInstantiateTypeArguments(
InstantiateTypeArgumentsComp* comp) {
const String& type_args = String::Handle(comp->type_arguments().Name());
OS::Print("InstantiateTypeArguments(%s, ", type_args.ToCString());
comp->instantiator()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitExtractConstructorTypeArguments(
ExtractConstructorTypeArgumentsComp* comp) {
const String& type_args = String::Handle(comp->type_arguments().Name());
OS::Print("ExtractConstructorTypeArguments(%s, ", type_args.ToCString());
comp->instantiator()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitExtractConstructorInstantiator(
ExtractConstructorInstantiatorComp* comp) {
OS::Print("ExtractConstructorInstantiator(");
comp->instantiator()->Accept(this);
OS::Print(", ");
comp->discard_value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitAllocateContext(AllocateContextComp* comp) {
OS::Print("AllocateContext(%d)", comp->num_context_variables());
}
void FlowGraphPrinter::VisitChainContext(ChainContextComp* comp) {
OS::Print("ChainContext(");
comp->context_value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitCloneContext(CloneContextComp* comp) {
OS::Print("CloneContext(");
comp->context_value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitCatchEntry(CatchEntryComp* comp) {
OS::Print("CatchEntry(%s, %s)",
comp->exception_var().name().ToCString(),
comp->stacktrace_var().name().ToCString());
}
void FlowGraphPrinter::VisitStoreContext(StoreContextComp* comp) {
OS::Print("StoreContext(");
comp->value()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitJoinEntry(JoinEntryInstr* instr) {
OS::Print("%2d: [join]", reverse_index(instr->postorder_number()));
}
void FlowGraphPrinter::VisitTargetEntry(TargetEntryInstr* instr) {
OS::Print("%2d: [target", reverse_index(instr->postorder_number()));
if (instr->HasTryIndex()) {
OS::Print(" catch %d]", instr->try_index());
} else {
OS::Print("]");
}
}
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::VisitPickTemp(PickTempInstr* instr) {
OS::Print(" t%d <- Pick(t%d)", instr->temp_index(), instr->source());
}
void FlowGraphPrinter::VisitTuckTemp(TuckTempInstr* instr) {
OS::Print(" t%d := t%d", instr->destination(), instr->source());
}
void FlowGraphPrinter::VisitReturn(ReturnInstr* instr) {
OS::Print(" return ");
instr->value()->Accept(this);
}
void FlowGraphPrinter::VisitThrow(ThrowInstr* instr) {
OS::Print("Throw(");
instr->exception()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitReThrow(ReThrowInstr* instr) {
OS::Print("ReThrow(");
instr->exception()->Accept(this);
OS::Print(", ");
instr->stack_trace()->Accept(this);
OS::Print(")");
}
void FlowGraphPrinter::VisitBranch(BranchInstr* instr) {
OS::Print(" if ");
instr->value()->Accept(this);
OS::Print(" goto(%d, %d)",
reverse_index(instr->true_successor()->postorder_number()),
reverse_index(instr->false_successor()->postorder_number()));
}
void FlowGraphBuilder::BuildGraph() {
if (FLAG_print_ast) {
// Print the function ast before IL generation.
AstPrinter::PrintFunctionNodes(parsed_function());
}
TimerScope timer(FLAG_compiler_stats, &CompilerStats::graphbuilder_timer);
// Compilation can be nested, preserve the computation-id.
Isolate* isolate = Isolate::Current();
const intptr_t prev_cid = isolate->computation_id();
isolate->set_computation_id(0);
const Function& function = parsed_function().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());
GrowableArray<intptr_t> parent;
for (intptr_t i = 0; i < catch_entries_.length(); i++) {
Instruction* entry = catch_entries_[i];
entry->DiscoverBlocks(NULL, // Entry block predecessor.
&preorder_block_entries_,
&postorder_block_entries_,
&parent);
ComputeDominators(&preorder_block_entries_, &parent);
}
if (for_effect.entry() != NULL) {
// Perform a depth-first traversal of the graph to build preorder and
// postorder block orders.
for_effect.entry()->DiscoverBlocks(NULL, // Entry block predecessor.
&preorder_block_entries_,
&postorder_block_entries_,
&parent);
ComputeDominators(&preorder_block_entries_, &parent);
}
isolate->set_computation_id(prev_cid);
if (FLAG_print_flow_graph) {
intptr_t length = postorder_block_entries_.length();
GrowableArray<BlockEntryInstr*> reverse_postorder(length);
for (intptr_t i = length - 1; i >= 0; --i) {
reverse_postorder.Add(postorder_block_entries_[i]);
}
FlowGraphPrinter printer(function, reverse_postorder);
printer.VisitBlocks();
}
}
void FlowGraphBuilder::ComputeDominators(
GrowableArray<BlockEntryInstr*>* preorder,
GrowableArray<intptr_t>* parent) {
// Use the SEMI-NCA algorithm to compute dominators. This is a two-pass
// version of the Lengauer-Tarjan algorithm (LT is normally three passes)
// that eliminates a pass by using nearest-common ancestor (NCA) to
// compute immediate dominators from semidominators. It also removes a
// level of indirection in the link-eval forest data structure.
//
// The algorithm is described in Georgiadis, Tarjan, and Werneck's
// "Finding Dominators in Practice".
// See http://www.cs.princeton.edu/~rwerneck/dominators/ .
// All arrays are maps between preorder basic-block numbers.
intptr_t size = parent->length();
GrowableArray<intptr_t> idom(size); // Immediate dominator.
GrowableArray<intptr_t> semi(size); // Semidominator.
GrowableArray<intptr_t> label(size); // Label for link-eval forest.
// 1. First pass: compute semidominators as in Lengauer-Tarjan.
// Semidominators are computed from a depth-first spanning tree and are an
// approximation of immediate dominators.
// Use a link-eval data structure with path compression. Implement path
// compression in place by mutating the parent array. Each block has a
// label, which is the minimum block number on the compressed path.
// Initialize idom, semi, and label.
for (intptr_t i = 0; i < size; ++i) {
idom.Add((*parent)[i]);
semi.Add(i);
label.Add(i);
}
// Loop over the blocks in reverse preorder (not including the graph
// entry).
for (intptr_t block_index = size - 1; block_index >= 1; --block_index) {
// Loop over the predecessors.
BlockEntryInstr* block = (*preorder)[block_index];
for (intptr_t i = 0; i < block->PredecessorCount(); ++i) {
BlockEntryInstr* pred = block->PredecessorAt(i);
ASSERT(pred != NULL);
// Look for the semidominator by ascending the semidominator path
// starting from pred.
intptr_t pred_index = pred->preorder_number();
intptr_t best = pred_index;
if (pred_index > block_index) {
CompressPath(block_index, pred_index, parent, &label);
best = label[pred_index];
}
// Update the semidominator if we've found a better one.
semi[block_index] = Utils::Minimum(semi[block_index], semi[best]);
}
// Now use label for the semidominator.
label[block_index] = semi[block_index];
}
// 2. Compute the immediate dominators as the nearest common ancestor of
// spanning tree parent and semidominator, for all nodes except the entry.
for (intptr_t block_index = 1; block_index < size; ++block_index) {
intptr_t dom_index = idom[block_index];
while (dom_index > semi[block_index]) {
dom_index = idom[dom_index];
}
idom[block_index] = dom_index;
(*preorder)[block_index]->set_dominator((*preorder)[dom_index]);
}
}
void FlowGraphBuilder::CompressPath(intptr_t start_index,
intptr_t current_index,
GrowableArray<intptr_t>* parent,
GrowableArray<intptr_t>* label) {
intptr_t next_index = (*parent)[current_index];
if (next_index > start_index) {
CompressPath(start_index, next_index, parent, label);
(*label)[current_index] =
Utils::Minimum((*label)[current_index], (*label)[next_index]);
(*parent)[current_index] = (*parent)[next_index];
}
}
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