755790f46f
Review URL: https://chromiumcodereview.appspot.com//10440099 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@8174 260f80e4-7a28-3924-810f-c04153c831b5
550 lines
15 KiB
C++
550 lines
15 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/intermediate_language.h"
|
|
|
|
#include "vm/bit_vector.h"
|
|
#include "vm/flow_graph_builder.h"
|
|
#include "vm/object.h"
|
|
#include "vm/os.h"
|
|
#include "vm/scopes.h"
|
|
|
|
namespace dart {
|
|
|
|
// ==== Support for visiting flow graphs.
|
|
#define DEFINE_ACCEPT(ShortName, ClassName) \
|
|
void ClassName::Accept(FlowGraphVisitor* visitor) { \
|
|
visitor->Visit##ShortName(this); \
|
|
}
|
|
|
|
FOR_EACH_COMPUTATION(DEFINE_ACCEPT)
|
|
|
|
#undef DEFINE_ACCEPT
|
|
|
|
|
|
#define DEFINE_ACCEPT(ShortName) \
|
|
Instruction* ShortName##Instr::Accept(FlowGraphVisitor* visitor) { \
|
|
visitor->Visit##ShortName(this); \
|
|
return StraightLineSuccessor(); \
|
|
}
|
|
|
|
FOR_EACH_INSTRUCTION(DEFINE_ACCEPT)
|
|
|
|
#undef DEFINE_ACCEPT
|
|
|
|
|
|
// Default implementation of visiting basic blocks. Can be overridden.
|
|
void FlowGraphVisitor::VisitBlocks() {
|
|
for (intptr_t i = 0; i < block_order_.length(); ++i) {
|
|
Instruction* current = block_order_[i]->Accept(this);
|
|
while ((current != NULL) && !current->IsBlockEntry()) {
|
|
current = current->Accept(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
intptr_t InstanceCallComp::InputCount() const {
|
|
return ArgumentCount();
|
|
}
|
|
|
|
|
|
intptr_t StaticCallComp::InputCount() const {
|
|
return ArgumentCount();
|
|
}
|
|
|
|
|
|
intptr_t ClosureCallComp::InputCount() const {
|
|
return ArgumentCount();
|
|
}
|
|
|
|
|
|
intptr_t AllocateObjectComp::InputCount() const {
|
|
return arguments().length();
|
|
}
|
|
|
|
|
|
intptr_t AllocateObjectWithBoundsCheckComp::InputCount() const {
|
|
return arguments().length();
|
|
}
|
|
|
|
|
|
intptr_t CreateArrayComp::InputCount() const {
|
|
return ElementCount() + 1;
|
|
}
|
|
|
|
|
|
intptr_t BranchInstr::InputCount() const {
|
|
return 1;
|
|
}
|
|
|
|
|
|
intptr_t ReThrowInstr::InputCount() const {
|
|
return 2;
|
|
}
|
|
|
|
|
|
intptr_t ThrowInstr::InputCount() const {
|
|
return 1;
|
|
}
|
|
|
|
|
|
intptr_t ReturnInstr::InputCount() const {
|
|
return 1;
|
|
}
|
|
|
|
|
|
intptr_t BindInstr::InputCount() const {
|
|
return computation()->InputCount();
|
|
}
|
|
|
|
|
|
intptr_t DoInstr::InputCount() const {
|
|
return computation()->InputCount();
|
|
}
|
|
|
|
|
|
intptr_t GraphEntryInstr::InputCount() const {
|
|
return 0;
|
|
}
|
|
|
|
|
|
intptr_t TargetEntryInstr::InputCount() const {
|
|
return 0;
|
|
}
|
|
|
|
|
|
intptr_t JoinEntryInstr::InputCount() const {
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ==== Recording assigned variables.
|
|
void Computation::RecordAssignedVars(BitVector* assigned_vars) {
|
|
// Nothing to do for the base class.
|
|
}
|
|
|
|
|
|
void StoreLocalComp::RecordAssignedVars(BitVector* assigned_vars) {
|
|
if (!local().is_captured()) {
|
|
assigned_vars->Add(local().BitIndexIn(assigned_vars));
|
|
}
|
|
}
|
|
|
|
|
|
void Instruction::RecordAssignedVars(BitVector* assigned_vars) {
|
|
// Nothing to do for the base class.
|
|
}
|
|
|
|
|
|
void DoInstr::RecordAssignedVars(BitVector* assigned_vars) {
|
|
computation()->RecordAssignedVars(assigned_vars);
|
|
}
|
|
|
|
|
|
void BindInstr::RecordAssignedVars(BitVector* assigned_vars) {
|
|
computation()->RecordAssignedVars(assigned_vars);
|
|
}
|
|
|
|
|
|
// ==== Postorder graph traversal.
|
|
void GraphEntryInstr::DiscoverBlocks(
|
|
BlockEntryInstr* current_block,
|
|
GrowableArray<BlockEntryInstr*>* preorder,
|
|
GrowableArray<BlockEntryInstr*>* postorder,
|
|
GrowableArray<intptr_t>* parent,
|
|
GrowableArray<BitVector*>* assigned_vars,
|
|
intptr_t variable_count) {
|
|
// We only visit this block once, first of all blocks.
|
|
ASSERT(preorder_number() == -1);
|
|
ASSERT(current_block == NULL);
|
|
ASSERT(preorder->is_empty());
|
|
ASSERT(postorder->is_empty());
|
|
ASSERT(parent->is_empty());
|
|
|
|
// This node has no parent, indicated by -1. The preorder number is 0.
|
|
parent->Add(-1);
|
|
set_preorder_number(0);
|
|
preorder->Add(this);
|
|
BitVector* vars =
|
|
(variable_count == 0) ? NULL : new BitVector(variable_count);
|
|
assigned_vars->Add(vars);
|
|
|
|
// Iteratively traverse all successors. In the unoptimized code, we will
|
|
// enter the function at the first successor in reverse postorder, so we
|
|
// must visit the normal entry last.
|
|
for (intptr_t i = catch_entries_.length() - 1; i >= 0; --i) {
|
|
catch_entries_[i]->DiscoverBlocks(this, preorder, postorder,
|
|
parent, assigned_vars, variable_count);
|
|
}
|
|
normal_entry_->DiscoverBlocks(this, preorder, postorder,
|
|
parent, assigned_vars, variable_count);
|
|
|
|
// Assign postorder number.
|
|
set_postorder_number(postorder->length());
|
|
postorder->Add(this);
|
|
}
|
|
|
|
|
|
// Base class implementation used for JoinEntry and TargetEntry.
|
|
void BlockEntryInstr::DiscoverBlocks(
|
|
BlockEntryInstr* current_block,
|
|
GrowableArray<BlockEntryInstr*>* preorder,
|
|
GrowableArray<BlockEntryInstr*>* postorder,
|
|
GrowableArray<intptr_t>* parent,
|
|
GrowableArray<BitVector*>* assigned_vars,
|
|
intptr_t variable_count) {
|
|
// We have already visited the graph entry, so we can assume current_block
|
|
// is non-null and preorder array is non-empty.
|
|
ASSERT(current_block != NULL);
|
|
ASSERT(!preorder->is_empty());
|
|
|
|
// 1. Record control-flow-graph basic-block predecessors.
|
|
AddPredecessor(current_block);
|
|
|
|
// 2. If the block has already been reached by the traversal, we are
|
|
// done. Blocks with a single predecessor cannot have been reached
|
|
// before.
|
|
ASSERT(!IsTargetEntry() || (preorder_number() == -1));
|
|
if (preorder_number() >= 0) return;
|
|
|
|
// 3. The last entry in the preorder array is the spanning-tree parent.
|
|
intptr_t parent_number = preorder->length() - 1;
|
|
parent->Add(parent_number);
|
|
|
|
// 4. Assign preorder number and add the block entry to the list.
|
|
// Allocate an empty set of assigned variables for the block.
|
|
set_preorder_number(parent_number + 1);
|
|
preorder->Add(this);
|
|
BitVector* vars =
|
|
(variable_count == 0) ? NULL : new BitVector(variable_count);
|
|
assigned_vars->Add(vars);
|
|
// The preorder, parent, and assigned_vars arrays are all indexed by
|
|
// preorder block number, so they should stay in lockstep.
|
|
ASSERT(preorder->length() == parent->length());
|
|
ASSERT(preorder->length() == assigned_vars->length());
|
|
|
|
// 5. Iterate straight-line successors until a branch instruction or
|
|
// another basic block entry instruction, and visit that instruction.
|
|
ASSERT(StraightLineSuccessor() != NULL);
|
|
Instruction* next = StraightLineSuccessor();
|
|
if (next->IsBlockEntry()) {
|
|
set_last_instruction(this);
|
|
} else {
|
|
while ((next != NULL) && !next->IsBlockEntry() && !next->IsBranch()) {
|
|
if (vars != NULL) next->RecordAssignedVars(vars);
|
|
set_last_instruction(next);
|
|
next = next->StraightLineSuccessor();
|
|
}
|
|
}
|
|
if (next != NULL) {
|
|
next->DiscoverBlocks(this, preorder, postorder,
|
|
parent, assigned_vars, variable_count);
|
|
}
|
|
|
|
// 6. Assign postorder number and add the block entry to the list.
|
|
set_postorder_number(postorder->length());
|
|
postorder->Add(this);
|
|
}
|
|
|
|
|
|
void BranchInstr::DiscoverBlocks(
|
|
BlockEntryInstr* current_block,
|
|
GrowableArray<BlockEntryInstr*>* preorder,
|
|
GrowableArray<BlockEntryInstr*>* postorder,
|
|
GrowableArray<intptr_t>* parent,
|
|
GrowableArray<BitVector*>* assigned_vars,
|
|
intptr_t variable_count) {
|
|
current_block->set_last_instruction(this);
|
|
// Visit the false successor before the true successor so they appear in
|
|
// true/false order in reverse postorder used as the block ordering in the
|
|
// nonoptimizing compiler.
|
|
ASSERT(true_successor_ != NULL);
|
|
ASSERT(false_successor_ != NULL);
|
|
false_successor_->DiscoverBlocks(current_block, preorder, postorder,
|
|
parent, assigned_vars, variable_count);
|
|
true_successor_->DiscoverBlocks(current_block, preorder, postorder,
|
|
parent, assigned_vars, variable_count);
|
|
}
|
|
|
|
|
|
// ==== Support for propagating static type.
|
|
RawAbstractType* ConstantVal::StaticType() const {
|
|
if (value().IsInstance()) {
|
|
Instance& instance = Instance::Handle();
|
|
instance ^= value().raw();
|
|
return instance.GetType();
|
|
} else {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
}
|
|
|
|
|
|
RawAbstractType* UseVal::StaticType() const {
|
|
return definition()->StaticType();
|
|
}
|
|
|
|
|
|
RawAbstractType* AssertAssignableComp::StaticType() const {
|
|
return dst_type().raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* AssertBooleanComp::StaticType() const {
|
|
return Type::BoolInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* CurrentContextComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreContextComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* ClosureCallComp::StaticType() const {
|
|
// The closure is the first argument to the call.
|
|
const AbstractType& function_type =
|
|
AbstractType::Handle(ArgumentAt(0)->StaticType());
|
|
if (function_type.IsDynamicType() || function_type.IsFunctionInterface()) {
|
|
// The function type is not statically known or simply Function.
|
|
return Type::DynamicType();
|
|
}
|
|
const Class& signature_class = Class::Handle(function_type.type_class());
|
|
ASSERT(signature_class.IsSignatureClass());
|
|
const Function& signature_function =
|
|
Function::Handle(signature_class.signature_function());
|
|
// TODO(regis): The result type may be generic. Consider upper bounds.
|
|
return signature_function.result_type();
|
|
}
|
|
|
|
|
|
RawAbstractType* InstanceCallComp::StaticType() const {
|
|
return Type::DynamicType();
|
|
}
|
|
|
|
|
|
RawAbstractType* StaticCallComp::StaticType() const {
|
|
return function().result_type();
|
|
}
|
|
|
|
|
|
RawAbstractType* LoadLocalComp::StaticType() const {
|
|
return local().type().raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreLocalComp::StaticType() const {
|
|
const AbstractType& assigned_value_type =
|
|
AbstractType::Handle(value()->StaticType());
|
|
if (assigned_value_type.IsDynamicType()) {
|
|
// Static type of assigned value is unknown, return static type of local.
|
|
return local().type().raw();
|
|
}
|
|
return assigned_value_type.raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* StrictCompareComp::StaticType() const {
|
|
return Type::BoolInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* EqualityCompareComp::StaticType() const {
|
|
return Type::BoolInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* NativeCallComp::StaticType() const {
|
|
// The result type of the native function is identical to the result type of
|
|
// the enclosing native Dart function. However, we prefer to check the type
|
|
// of the value returned from the native call.
|
|
return Type::DynamicType();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreIndexedComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* InstanceSetterComp::StaticType() const {
|
|
return value()->StaticType();
|
|
}
|
|
|
|
|
|
RawAbstractType* StaticSetterComp::StaticType() const {
|
|
const AbstractType& assigned_value_type =
|
|
AbstractType::Handle(value()->StaticType());
|
|
if (assigned_value_type.IsDynamicType()) {
|
|
// Static type of assigned value is unknown, return static type of setter
|
|
// value parameter.
|
|
return setter_function().ParameterTypeAt(0);
|
|
}
|
|
return assigned_value_type.raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* LoadInstanceFieldComp::StaticType() const {
|
|
return field().type();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreInstanceFieldComp::StaticType() const {
|
|
const AbstractType& assigned_value_type =
|
|
AbstractType::Handle(value()->StaticType());
|
|
if (assigned_value_type.IsDynamicType()) {
|
|
// Static type of assigned value is unknown, return static type of field.
|
|
return field().type();
|
|
}
|
|
return assigned_value_type.raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* LoadStaticFieldComp::StaticType() const {
|
|
return field().type();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreStaticFieldComp::StaticType() const {
|
|
const AbstractType& assigned_value_type =
|
|
AbstractType::Handle(value()->StaticType());
|
|
if (assigned_value_type.IsDynamicType()) {
|
|
// Static type of assigned value is unknown, return static type of field.
|
|
return field().type();
|
|
}
|
|
return assigned_value_type.raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* BooleanNegateComp::StaticType() const {
|
|
return Type::BoolInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* InstanceOfComp::StaticType() const {
|
|
return Type::BoolInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* CreateArrayComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* CreateClosureComp::StaticType() const {
|
|
const Function& fun = function();
|
|
const Class& signature_class = Class::Handle(fun.signature_class());
|
|
// TODO(regis): The signature type may be generic. Consider upper bounds.
|
|
// For now, we return Dynamic (no type test elimination) if the signature
|
|
// class is parameterized, or a non-parameterized finalized type otherwise.
|
|
if (signature_class.HasTypeArguments()) {
|
|
return Type::DynamicType();
|
|
}
|
|
// Make sure we use the canonical signature class.
|
|
const Type& type = Type::Handle(signature_class.SignatureType());
|
|
const Class& canonical_signature_class = Class::Handle(type.type_class());
|
|
return Type::NewNonParameterizedType(canonical_signature_class);
|
|
}
|
|
|
|
|
|
RawAbstractType* AllocateObjectComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* AllocateObjectWithBoundsCheckComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* LoadVMFieldComp::StaticType() const {
|
|
ASSERT(!type().IsNull());
|
|
return type().raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* StoreVMFieldComp::StaticType() const {
|
|
ASSERT(!type().IsNull());
|
|
const AbstractType& assigned_value_type =
|
|
AbstractType::Handle(value()->StaticType());
|
|
if (assigned_value_type.IsDynamicType()) {
|
|
// Static type of assigned value is unknown, return static type of field.
|
|
return type().raw();
|
|
}
|
|
return assigned_value_type.raw();
|
|
}
|
|
|
|
|
|
RawAbstractType* InstantiateTypeArgumentsComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* ExtractConstructorTypeArgumentsComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* ExtractConstructorInstantiatorComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* AllocateContextComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* ChainContextComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* CloneContextComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* CatchEntryComp::StaticType() const {
|
|
UNREACHABLE();
|
|
return AbstractType::null();
|
|
}
|
|
|
|
|
|
RawAbstractType* BinaryOpComp::StaticType() const {
|
|
// TODO(srdjan): Compute based on input types (ICData).
|
|
return Type::DynamicType();
|
|
}
|
|
|
|
|
|
RawAbstractType* UnarySmiOpComp::StaticType() const {
|
|
return Type::IntInterface();
|
|
}
|
|
|
|
|
|
RawAbstractType* NumberNegateComp::StaticType() const {
|
|
return Type::NumberInterface();
|
|
}
|
|
|
|
|
|
|
|
} // namespace dart
|