Inline recognized List factory in the flow graph optimizer.

The call to the native List factory is lowered into IL instructions if
the length is known to be a valid smi.

This is mostly performance neutral. The acutal allocation now takes place
in the array allocation stub instead of the intrinsic code (and the runtime
List_allocate in case the fast case path fails).

This is a preparation for enabling allocation sinking for arrays and will
be extended to handle type list allocation as well. This way the allocation
site is explicitly represented as a CreateArrayInstr, instead of just being
a static call.

Another benefit is that this allows to simplify the special handling of
recognized factory calls in the optimizer once all array types are handled
this way.

R=johnmccutchan@google.com, srdjan@google.com

Review URL: https://codereview.chromium.org//138523004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@32194 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
fschneider@google.com 2014-01-31 13:26:48 +00:00
parent d0651286c6
commit bb4869e3bb
11 changed files with 103 additions and 67 deletions

View file

@ -2173,14 +2173,15 @@ void EffectGraphVisitor::VisitArrayNode(ArrayNode* node) {
AbstractTypeArguments::ZoneHandle(node->type().arguments());
Value* element_type = BuildInstantiatedTypeArguments(node->token_pos(),
type_args);
Value* num_elements =
Bind(new ConstantInstr(Smi::ZoneHandle(Smi::New(node->length()))));
CreateArrayInstr* create = new CreateArrayInstr(node->token_pos(),
node->length(),
node->type(),
element_type);
element_type,
num_elements);
Value* array_val = Bind(create);
{ LocalVariable* tmp_var = EnterTempLocalScope(array_val);
const intptr_t class_id = create->Type()->ToCid();
const intptr_t class_id = kArrayCid;
const intptr_t deopt_id = Isolate::kNoDeoptId;
for (int i = 0; i < node->length(); ++i) {
Value* array = Bind(new LoadLocalInstr(*tmp_var));
@ -2553,32 +2554,6 @@ void EffectGraphVisitor::BuildConstructorCall(
}
// Class that recognizes factories and returns corresponding result cid.
class FactoryRecognizer : public AllStatic {
public:
// Return kDynamicCid if factory is not recognized.
static intptr_t ResultCid(const Function& factory) {
ASSERT(factory.IsFactory());
const Class& function_class = Class::Handle(factory.Owner());
const Library& lib = Library::Handle(function_class.library());
ASSERT((lib.raw() == Library::CoreLibrary()) ||
(lib.raw() == Library::TypedDataLibrary()));
const String& factory_name = String::Handle(factory.name());
#define RECOGNIZE_FACTORY(test_factory_symbol, cid, fp) \
if (String::EqualsIgnoringPrivateKey( \
factory_name, Symbols::test_factory_symbol())) { \
ASSERT(factory.CheckSourceFingerprint(fp)); \
return cid; \
} \
RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY);
#undef RECOGNIZE_FACTORY
return kDynamicCid;
}
};
static intptr_t GetResultCidOfListFactory(ConstructorCallNode* node) {
const Function& function = node->constructor();
const Class& function_class = Class::Handle(function.Owner());

View file

@ -49,6 +49,32 @@ class TestGraphVisitor;
V(_Float32x4ArrayFactory, kTypedDataFloat32x4ArrayCid, 879975401) \
// Class that recognizes factories and returns corresponding result cid.
class FactoryRecognizer : public AllStatic {
public:
// Return kDynamicCid if factory is not recognized.
static intptr_t ResultCid(const Function& factory) {
ASSERT(factory.IsFactory());
const Class& function_class = Class::Handle(factory.Owner());
const Library& lib = Library::Handle(function_class.library());
ASSERT((lib.raw() == Library::CoreLibrary()) ||
(lib.raw() == Library::TypedDataLibrary()));
const String& factory_name = String::Handle(factory.name());
#define RECOGNIZE_FACTORY(test_factory_symbol, cid, fp) \
if (String::EqualsIgnoringPrivateKey( \
factory_name, Symbols::test_factory_symbol())) { \
ASSERT(factory.CheckSourceFingerprint(fp)); \
return cid; \
} \
RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY);
#undef RECOGNIZE_FACTORY
return kDynamicCid;
}
};
// A class to collect the exits from an inlined function during graph
// construction so they can be plugged into the caller's flow graph.
class InlineExitCollector: public ZoneAllocated {

View file

@ -271,9 +271,7 @@ class CallSites : public ValueObject {
}
StaticCallInstr* static_call = current->AsStaticCall();
if (static_call != NULL) {
if (static_call->function().IsInlineable()) {
static_calls_.Add(StaticCallInfo(static_call));
}
static_calls_.Add(StaticCallInfo(static_call));
continue;
}
PolymorphicInstanceCallInstr* instance_call =

View file

@ -3680,6 +3680,31 @@ void FlowGraphOptimizer::VisitStaticCall(StaticCallInstr* call) {
ConstantInstr* cid_instr = new ConstantInstr(Smi::Handle(Smi::New(cid)));
ReplaceCall(call, cid_instr);
}
if (call->function().IsFactory()) {
const Class& function_class = Class::Handle(call->function().Owner());
if ((function_class.library() == Library::CoreLibrary()) ||
(function_class.library() == Library::TypedDataLibrary())) {
intptr_t cid = FactoryRecognizer::ResultCid(call->function());
switch (cid) {
case kArrayCid: {
Value* type = new Value(call->ArgumentAt(0));
Value* num_elements = new Value(call->ArgumentAt(1));
if (num_elements->BindsToConstant() &&
num_elements->BoundConstant().IsSmi()) {
intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
if (length >= 0 && length <= Array::kMaxElements) {
CreateArrayInstr* create_array =
new CreateArrayInstr(call->token_pos(), type, num_elements);
ReplaceCall(call, create_array);
}
}
}
default:
break;
}
}
}
}
@ -7119,11 +7144,15 @@ void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) {
void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) {
if ((instr->recognized_kind() == MethodRecognizer::kObjectArrayLength) &&
(instr->instance()->definition()->IsCreateArray())) {
const intptr_t length =
Value* num_elements =
instr->instance()->definition()->AsCreateArray()->num_elements();
const Object& result = Smi::ZoneHandle(Smi::New(length));
SetValue(instr, result);
return;
if (num_elements->BindsToConstant() &&
num_elements->BoundConstant().IsSmi()) {
intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
const Object& result = Smi::ZoneHandle(Smi::New(length));
SetValue(instr, result);
return;
}
}
if (instr->IsImmutableLengthLoad()) {

View file

@ -922,7 +922,8 @@ CompileType* StoreStaticFieldInstr::ComputeInitialType() const {
CompileType CreateArrayInstr::ComputeType() const {
return CompileType::FromAbstractType(type(), CompileType::kNonNullable);
// TODO(fschneider): Add abstract type and type arguments to the compile type.
return CompileType::FromCid(kArrayCid);
}

View file

@ -2917,8 +2917,14 @@ Definition* StringInterpolateInstr::Canonicalize(FlowGraph* flow_graph) {
CreateArrayInstr* create_array = value()->definition()->AsCreateArray();
ASSERT(create_array != NULL);
// Check if the string interpolation has only constant inputs.
GrowableArray<ConstantInstr*> constants(create_array->num_elements());
for (intptr_t i = 0; i < create_array->num_elements(); i++) {
Value* num_elements = create_array->num_elements();
if (!num_elements->BindsToConstant() ||
!num_elements->BoundConstant().IsSmi()) {
return this;
}
intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
GrowableArray<ConstantInstr*> constants(length);
for (intptr_t i = 0; i < length; i++) {
constants.Add(NULL);
}
for (Value::Iterator it(create_array->input_use_list());
@ -2944,7 +2950,7 @@ Definition* StringInterpolateInstr::Canonicalize(FlowGraph* flow_graph) {
}
// Interpolate string at compile time.
const Array& array_argument =
Array::Handle(Array::New(create_array->num_elements()));
Array::Handle(Array::New(length));
for (intptr_t i = 0; i < constants.length(); i++) {
array_argument.SetAt(i, constants[i]->value());
}

View file

@ -4095,29 +4095,28 @@ class AllocateObjectWithBoundsCheckInstr : public TemplateDefinition<0> {
};
class CreateArrayInstr : public TemplateDefinition<1> {
class CreateArrayInstr : public TemplateDefinition<2> {
public:
CreateArrayInstr(intptr_t token_pos,
intptr_t num_elements,
const AbstractType& type,
Value* element_type)
: token_pos_(token_pos),
num_elements_(num_elements),
type_(type) {
ASSERT(type_.IsZoneHandle());
ASSERT(!type_.IsNull());
ASSERT(type_.IsFinalized());
SetInputAt(0, element_type);
Value* element_type,
Value* num_elements)
: token_pos_(token_pos) {
SetInputAt(kElementTypePos, element_type);
SetInputAt(kLengthPos, num_elements);
}
enum {
kElementTypePos = 0,
kLengthPos = 1
};
DECLARE_INSTRUCTION(CreateArray)
virtual CompileType ComputeType() const;
intptr_t num_elements() const { return num_elements_; }
intptr_t token_pos() const { return token_pos_; }
const AbstractType& type() const { return type_; }
Value* element_type() const { return inputs_[0]; }
Value* element_type() const { return inputs_[kElementTypePos]; }
Value* num_elements() const { return inputs_[kLengthPos]; }
virtual void PrintOperandsTo(BufferFormatter* f) const;
@ -4129,8 +4128,6 @@ class CreateArrayInstr : public TemplateDefinition<1> {
private:
const intptr_t token_pos_;
const intptr_t num_elements_;
const AbstractType& type_;
DISALLOW_COPY_AND_ASSIGN(CreateArrayInstr);
};

View file

@ -1822,11 +1822,12 @@ void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
const intptr_t kNumInputs = 1;
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = 0;
LocationSummary* locs =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
locs->set_in(0, Location::RegisterLocation(R1));
locs->set_in(kElementTypePos, Location::RegisterLocation(R1));
locs->set_in(kLengthPos, Location::RegisterLocation(R2));
locs->set_out(Location::RegisterLocation(R0));
return locs;
}
@ -1834,8 +1835,8 @@ LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Allocate the array. R2 = length, R1 = element type.
ASSERT(locs()->in(0).reg() == R1);
__ LoadImmediate(R2, Smi::RawValue(num_elements()));
ASSERT(locs()->in(kElementTypePos).reg() == R1);
ASSERT(locs()->in(kLengthPos).reg() == R2);
compiler->GenerateCall(token_pos(),
&StubCode::AllocateArrayLabel(),
PcDescriptors::kOther,

View file

@ -1835,11 +1835,12 @@ void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
const intptr_t kNumInputs = 1;
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = 0;
LocationSummary* locs =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
locs->set_in(0, Location::RegisterLocation(ECX));
locs->set_in(1, Location::RegisterLocation(EDX));
locs->set_out(Location::RegisterLocation(EAX));
return locs;
}
@ -1848,7 +1849,7 @@ LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Allocate the array. EDX = length, ECX = element type.
ASSERT(locs()->in(0).reg() == ECX);
__ movl(EDX, Immediate(Smi::RawValue(num_elements())));
ASSERT(locs()->in(1).reg() == EDX);
compiler->GenerateCall(token_pos(),
&StubCode::AllocateArrayLabel(),
PcDescriptors::kOther,

View file

@ -1897,11 +1897,12 @@ void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
const intptr_t kNumInputs = 1;
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = 0;
LocationSummary* locs =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
locs->set_in(0, Location::RegisterLocation(A0));
locs->set_in(1, Location::RegisterLocation(A1));
locs->set_out(Location::RegisterLocation(V0));
return locs;
}
@ -1911,7 +1912,7 @@ void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("CreateArrayInstr");
// Allocate the array. A1 = length, A0 = element type.
ASSERT(locs()->in(0).reg() == A0);
__ LoadImmediate(A1, Smi::RawValue(num_elements()));
ASSERT(locs()->in(1).reg() == A1);
compiler->GenerateCall(token_pos(),
&StubCode::AllocateArrayLabel(),
PcDescriptors::kOther,

View file

@ -1728,11 +1728,12 @@ void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
const intptr_t kNumInputs = 1;
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = 0;
LocationSummary* locs =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
locs->set_in(0, Location::RegisterLocation(RBX));
locs->set_in(1, Location::RegisterLocation(R10));
locs->set_out(Location::RegisterLocation(RAX));
return locs;
}
@ -1741,7 +1742,7 @@ LocationSummary* CreateArrayInstr::MakeLocationSummary(bool opt) const {
void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Allocate the array. R10 = length, RBX = element type.
ASSERT(locs()->in(0).reg() == RBX);
__ LoadImmediate(R10, Immediate(Smi::RawValue(num_elements())), PP);
ASSERT(locs()->in(1).reg() == R10);
compiler->GenerateCall(token_pos(),
&StubCode::AllocateArrayLabel(),
PcDescriptors::kOther,