[vm/compiler] Avoid pruning deopt environments during SSA construction
Generally speaking, the optimizing compiler should be able to use (deopt-id, deopt-environ) of any IR instruction and use it as an eager deopt target (e.g. optimizing compiler might insert TestCidsInstr with eager (deopt-id, deopt-environ) from AssertAssignable). Currently we prune the environment eagerly during SSA construction for some instructions This pruning breaks the above mechanism, since the deopt-environ isn't usable as eager deopt target. (It is effectively changing the environment on the IR instruction to be a lazy-deopt environment). This CL makes the [Environment] represent both the eager deopt target as well as the lazy deopt target. It distinguishes the two by remembering how many slots the eager deopt target needs to be pruned to come to the lazy deopt target. The SSA construction will populate this information. Effectively we move the deopt env pruning from SSA construction to the place when we need it (e.g. inlining, emitting after-call metadata). Issue https://github.com/dart-lang/sdk/issues/45213 TEST=Refactoring of existing code. Change-Id: I6c2a117b33f35764e556372484e4beaa294b708d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192141 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
e7d3a6027c
commit
dec7b4f5b6
@@ -25,7 +25,7 @@ class BlockBuilder : public ValueObject {
|
||||
entry_(entry),
|
||||
current_(entry),
|
||||
dummy_env_(
|
||||
new Environment(0, 0, flow_graph->parsed_function(), nullptr)) {
|
||||
new Environment(0, 0, 0, flow_graph->parsed_function(), nullptr)) {
|
||||
// Some graph transformations use environments from block entries.
|
||||
entry->SetEnvironment(dummy_env_);
|
||||
}
|
||||
@@ -50,6 +50,7 @@ class BlockBuilder : public ValueObject {
|
||||
template <typename T>
|
||||
T* AddInstruction(T* instr) {
|
||||
if (instr->ComputeCanDeoptimize() ||
|
||||
instr->ComputeCanDeoptimizeAfterCall() ||
|
||||
instr->CanBecomeDeoptimizationTarget()) {
|
||||
// All instructions that can deoptimize must have an environment attached
|
||||
// to them.
|
||||
|
||||
@@ -1304,24 +1304,9 @@ void FlowGraph::PopulateEnvironmentFromCatchEntry(
|
||||
|
||||
void FlowGraph::AttachEnvironment(Instruction* instr,
|
||||
GrowableArray<Definition*>* env) {
|
||||
Environment* deopt_env =
|
||||
Environment::From(zone(), *env, num_direct_parameters_, parsed_function_);
|
||||
if (instr->IsClosureCall() || instr->IsLoadField()) {
|
||||
// Trim extra inputs of ClosureCall and LoadField instructions from
|
||||
// the environment. Inputs of those instructions are not pushed onto
|
||||
// the stack at the point where deoptimization can occur.
|
||||
// Note that in case of LoadField there can be two possible situations,
|
||||
// the code here handles LoadField to LoadField lazy deoptimization in
|
||||
// which we are transitioning from position after the call to initialization
|
||||
// stub in optimized code to a similar position after the call to
|
||||
// initialization stub in unoptimized code. There is another variant
|
||||
// (LoadField deoptimizing into a position after a getter call) which is
|
||||
// handled in a different way (see
|
||||
// CallSpecializer::InlineImplicitInstanceGetter).
|
||||
deopt_env =
|
||||
deopt_env->DeepCopy(zone(), deopt_env->Length() - instr->InputCount() +
|
||||
instr->ArgumentCount());
|
||||
}
|
||||
auto deopt_env = Environment::From(zone(), *env, num_direct_parameters_,
|
||||
instr->NumberOfInputsConsumedBeforeCall(),
|
||||
parsed_function_);
|
||||
instr->SetEnvironment(deopt_env);
|
||||
for (Environment::DeepIterator it(deopt_env); !it.Done(); it.Advance()) {
|
||||
Value* use = it.CurrentValue();
|
||||
@@ -2308,6 +2293,7 @@ void FlowGraph::EliminateEnvironments() {
|
||||
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
|
||||
Instruction* current = it.Current();
|
||||
if (!current->ComputeCanDeoptimize() &&
|
||||
!current->ComputeCanDeoptimizeAfterCall() &&
|
||||
(!current->MayThrow() || !current->GetBlock()->InsideTryBlock())) {
|
||||
// Instructions that can throw need an environment for optimized
|
||||
// try-catch.
|
||||
|
||||
@@ -118,8 +118,11 @@ static void AssertArgumentsInEnv(FlowGraph* flow_graph, Definition* call) {
|
||||
// correspond directly with the arguments.
|
||||
const intptr_t env_count = env->Length();
|
||||
const intptr_t arg_count = call->ArgumentCount();
|
||||
ASSERT(arg_count <= env_count);
|
||||
const intptr_t env_base = env_count - arg_count;
|
||||
// Some calls (e.g. closure calls) have more inputs than actual arguments.
|
||||
// Those extra inputs will be consumed from the stack before the call.
|
||||
const intptr_t after_args_input_count = call->env()->LazyDeoptPruneCount();
|
||||
ASSERT((arg_count + after_args_input_count) <= env_count);
|
||||
const intptr_t env_base = env_count - arg_count - after_args_input_count;
|
||||
for (intptr_t i = 0; i < arg_count; i++) {
|
||||
if (call->HasPushArguments()) {
|
||||
ASSERT(call->ArgumentAt(i) == env->ValueAt(env_base + i)
|
||||
|
||||
@@ -910,6 +910,9 @@ CompilerDeoptInfo* FlowGraphCompiler::AddDeoptIndexAtCall(intptr_t deopt_id,
|
||||
if (env == nullptr) {
|
||||
env = pending_deoptimization_env_;
|
||||
}
|
||||
if (env != nullptr) {
|
||||
env = env->GetLazyDeoptEnv(zone());
|
||||
}
|
||||
CompilerDeoptInfo* info =
|
||||
new (zone()) CompilerDeoptInfo(deopt_id, ICData::kDeoptAtCall,
|
||||
0, // No flags.
|
||||
@@ -1108,7 +1111,8 @@ Environment* FlowGraphCompiler::SlowPathEnvironmentFor(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Environment* slow_path_env = env->DeepCopy(zone());
|
||||
Environment* slow_path_env =
|
||||
env->DeepCopy(zone(), env->Length() - env->LazyDeoptPruneCount());
|
||||
// 1. Iterate the registers in the order they will be spilled to compute
|
||||
// the slots they will be spilled to.
|
||||
intptr_t next_slot = StackSize() + slow_path_env->CountArgsPushed();
|
||||
@@ -2938,21 +2942,7 @@ void FlowGraphCompiler::GenerateTTSCall(const InstructionSource& source,
|
||||
GenerateIndirectTTSCall(assembler(), reg_with_type, sub_type_cache_index);
|
||||
}
|
||||
|
||||
// Lazy deopt to after the call should not have the inputs to AssertAssignable
|
||||
// because those are poped before doing the call.
|
||||
auto pruned_env = pending_deoptimization_env_;
|
||||
if (pruned_env != nullptr) {
|
||||
// If the AssertAssignable was licm hoisted, a lazy-deopt will not continue
|
||||
// after the TTS call inside the assert assignable. Rather the lazy-deopt
|
||||
// will continue at the instruction it was hoisted above (e.g. continue at a
|
||||
// Goto branch) and will later on re-do the AssertAssignable (again).
|
||||
if (!was_licm_hoisted) {
|
||||
pruned_env = pruned_env->DeepCopy(
|
||||
zone(), pruned_env->Length() - AssertAssignableInstr::kNumInputs);
|
||||
}
|
||||
}
|
||||
EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs,
|
||||
pruned_env);
|
||||
EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs);
|
||||
}
|
||||
|
||||
// Optimize assignable type check by adding inlined tests for:
|
||||
|
||||
@@ -143,7 +143,7 @@ TypedDataPtr CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
|
||||
// For any outer environment the deopt id is that of the call instruction
|
||||
// which is recorded in the outer environment.
|
||||
builder->AddReturnAddress(current->function(),
|
||||
DeoptId::ToDeoptAfter(current->deopt_id()),
|
||||
DeoptId::ToDeoptAfter(current->GetDeoptId()),
|
||||
slot_ix++);
|
||||
|
||||
// The values of outgoing arguments can be changed from the inlined call so
|
||||
|
||||
@@ -137,7 +137,7 @@ TypedDataPtr CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
|
||||
// For any outer environment the deopt id is that of the call instruction
|
||||
// which is recorded in the outer environment.
|
||||
builder->AddReturnAddress(current->function(),
|
||||
DeoptId::ToDeoptAfter(current->deopt_id()),
|
||||
DeoptId::ToDeoptAfter(current->GetDeoptId()),
|
||||
slot_ix++);
|
||||
|
||||
// The values of outgoing arguments can be changed from the inlined call so
|
||||
|
||||
@@ -113,7 +113,7 @@ TypedDataPtr CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
|
||||
// For any outer environment the deopt id is that of the call instruction
|
||||
// which is recorded in the outer environment.
|
||||
builder->AddReturnAddress(current->function(),
|
||||
DeoptId::ToDeoptAfter(current->deopt_id()),
|
||||
DeoptId::ToDeoptAfter(current->GetDeoptId()),
|
||||
slot_ix++);
|
||||
|
||||
// The values of outgoing arguments can be changed from the inlined call so
|
||||
|
||||
@@ -138,7 +138,7 @@ TypedDataPtr CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
|
||||
// For any outer environment the deopt id is that of the call instruction
|
||||
// which is recorded in the outer environment.
|
||||
builder->AddReturnAddress(current->function(),
|
||||
DeoptId::ToDeoptAfter(current->deopt_id()),
|
||||
DeoptId::ToDeoptAfter(current->GetDeoptId()),
|
||||
slot_ix++);
|
||||
|
||||
// The values of outgoing arguments can be changed from the inlined call so
|
||||
|
||||
@@ -1580,11 +1580,15 @@ void Instruction::UnuseAllInputs() {
|
||||
}
|
||||
|
||||
void Instruction::RepairPushArgsInEnvironment() const {
|
||||
// Some calls (e.g. closure calls) have more inputs than actual arguments.
|
||||
// Those extra inputs will be consumed from the stack before the call.
|
||||
const intptr_t after_args_input_count = env()->LazyDeoptPruneCount();
|
||||
PushArgumentsArray* push_arguments = GetPushArguments();
|
||||
ASSERT(push_arguments != nullptr);
|
||||
const intptr_t arg_count = ArgumentCount();
|
||||
ASSERT(arg_count <= env()->Length());
|
||||
const intptr_t env_base = env()->Length() - arg_count;
|
||||
ASSERT((arg_count + after_args_input_count) <= env()->Length());
|
||||
const intptr_t env_base =
|
||||
env()->Length() - arg_count - after_args_input_count;
|
||||
for (intptr_t i = 0; i < arg_count; ++i) {
|
||||
env()->ValueAt(env_base + i)->BindToEnvironment(push_arguments->At(i));
|
||||
}
|
||||
@@ -4429,9 +4433,6 @@ void LoadFieldInstr::EmitNativeCodeForInitializerCall(
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Instruction inputs are popped from the stack at this point,
|
||||
// so deoptimization environment has to be adjusted.
|
||||
// This adjustment is done in FlowGraph::AttachEnvironment.
|
||||
compiler->GenerateStubCall(source(), stub,
|
||||
/*kind=*/UntaggedPcDescriptors::kOther, locs(),
|
||||
deopt_id());
|
||||
@@ -5707,9 +5708,11 @@ void UnboxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
Environment* Environment::From(Zone* zone,
|
||||
const GrowableArray<Definition*>& definitions,
|
||||
intptr_t fixed_parameter_count,
|
||||
intptr_t lazy_deopt_pruning_count,
|
||||
const ParsedFunction& parsed_function) {
|
||||
Environment* env = new (zone) Environment(
|
||||
definitions.length(), fixed_parameter_count, parsed_function, NULL);
|
||||
Environment* env =
|
||||
new (zone) Environment(definitions.length(), fixed_parameter_count,
|
||||
lazy_deopt_pruning_count, parsed_function, NULL);
|
||||
for (intptr_t i = 0; i < definitions.length(); ++i) {
|
||||
env->values_.Add(new (zone) Value(definitions[i]));
|
||||
}
|
||||
@@ -5722,10 +5725,10 @@ void Environment::PushValue(Value* value) {
|
||||
|
||||
Environment* Environment::DeepCopy(Zone* zone, intptr_t length) const {
|
||||
ASSERT(length <= values_.length());
|
||||
Environment* copy =
|
||||
new (zone) Environment(length, fixed_parameter_count_, parsed_function_,
|
||||
(outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
|
||||
copy->deopt_id_ = this->deopt_id_;
|
||||
Environment* copy = new (zone) Environment(
|
||||
length, fixed_parameter_count_, LazyDeoptPruneCount(), parsed_function_,
|
||||
(outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
|
||||
copy->SetDeoptId(DeoptIdBits::decode(bitfield_));
|
||||
if (locations_ != NULL) {
|
||||
Location* new_locations = zone->Alloc<Location>(length);
|
||||
copy->set_locations(new_locations);
|
||||
@@ -5762,7 +5765,9 @@ void Environment::DeepCopyAfterTo(Zone* zone,
|
||||
it.CurrentValue()->RemoveFromUseList();
|
||||
}
|
||||
|
||||
Environment* copy = DeepCopy(zone, values_.length() - argc);
|
||||
Environment* copy =
|
||||
DeepCopy(zone, values_.length() - argc - LazyDeoptPruneCount());
|
||||
copy->SetLazyDeoptPruneCount(0);
|
||||
for (intptr_t i = 0; i < argc; i++) {
|
||||
copy->values_.Add(new (zone) Value(dead));
|
||||
}
|
||||
@@ -5784,11 +5789,13 @@ void Environment::DeepCopyToOuter(Zone* zone,
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(instr->env()->outer() == NULL);
|
||||
intptr_t argument_count = instr->env()->fixed_parameter_count();
|
||||
Environment* copy = DeepCopy(zone, values_.length() - argument_count);
|
||||
copy->deopt_id_ = outer_deopt_id;
|
||||
instr->env()->outer_ = copy;
|
||||
Environment* outer =
|
||||
DeepCopy(zone, values_.length() - argument_count - LazyDeoptPruneCount());
|
||||
outer->SetDeoptId(outer_deopt_id);
|
||||
outer->SetLazyDeoptPruneCount(0);
|
||||
instr->env()->outer_ = outer;
|
||||
intptr_t use_index = instr->env()->Length(); // Start index after inner.
|
||||
for (Environment::DeepIterator it(copy); !it.Done(); it.Advance()) {
|
||||
for (Environment::DeepIterator it(outer); !it.Done(); it.Advance()) {
|
||||
Value* value = it.CurrentValue();
|
||||
value->set_instruction(instr);
|
||||
value->set_use_index(use_index++);
|
||||
|
||||
@@ -502,7 +502,7 @@ struct InstrAttrs {
|
||||
M(BoxInt32, _) \
|
||||
M(UnboxInt32, kNoGC) \
|
||||
M(BoxUint8, kNoGC) \
|
||||
M(IntConverter, _) \
|
||||
M(IntConverter, kNoGC) \
|
||||
M(BitCast, kNoGC) \
|
||||
M(Deoptimize, kNoGC) \
|
||||
M(SimdOp, kNoGC)
|
||||
@@ -775,10 +775,6 @@ class Instruction : public ZoneAllocated {
|
||||
explicit Instruction(const InstructionSource& source,
|
||||
intptr_t deopt_id = DeoptId::kNone)
|
||||
: deopt_id_(deopt_id),
|
||||
previous_(NULL),
|
||||
next_(NULL),
|
||||
env_(NULL),
|
||||
locs_(NULL),
|
||||
inlining_id_(source.inlining_id) {}
|
||||
|
||||
explicit Instruction(intptr_t deopt_id = DeoptId::kNone)
|
||||
@@ -791,7 +787,8 @@ class Instruction : public ZoneAllocated {
|
||||
virtual intptr_t statistics_tag() const { return tag(); }
|
||||
|
||||
intptr_t deopt_id() const {
|
||||
ASSERT(ComputeCanDeoptimize() || CanBecomeDeoptimizationTarget() ||
|
||||
ASSERT(ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall() ||
|
||||
CanBecomeDeoptimizationTarget() ||
|
||||
CompilerState::Current().is_aot());
|
||||
return GetDeoptId();
|
||||
}
|
||||
@@ -851,9 +848,19 @@ class Instruction : public ZoneAllocated {
|
||||
// the type or the range of input operands during compilation.
|
||||
virtual bool ComputeCanDeoptimize() const = 0;
|
||||
|
||||
virtual bool ComputeCanDeoptimizeAfterCall() const {
|
||||
// TODO(dartbug.com/45213): Incrementally migrate IR instructions from using
|
||||
// [ComputeCanDeoptimze] to either [ComputeCanDeoptimizeAfterCall] if they
|
||||
// can only lazy deoptimize.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Once we removed the deopt environment, we assume that this
|
||||
// instruction can't deoptimize.
|
||||
bool CanDeoptimize() const { return env() != NULL && ComputeCanDeoptimize(); }
|
||||
bool CanDeoptimize() const {
|
||||
return env() != nullptr &&
|
||||
(ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall());
|
||||
}
|
||||
|
||||
// Visiting support.
|
||||
virtual void Accept(FlowGraphVisitor* visitor) = 0;
|
||||
@@ -972,6 +979,8 @@ class Instruction : public ZoneAllocated {
|
||||
void RemoveEnvironment();
|
||||
void ReplaceInEnvironment(Definition* current, Definition* replacement);
|
||||
|
||||
virtual intptr_t NumberOfInputsConsumedBeforeCall() const { return 0; }
|
||||
|
||||
// Different compiler passes can assign pass specific ids to the instruction.
|
||||
// Only one id can be stored at a time.
|
||||
intptr_t GetPassSpecificId(CompilerPass::Id pass) const {
|
||||
@@ -1089,8 +1098,8 @@ class Instruction : public ZoneAllocated {
|
||||
virtual void InheritDeoptTarget(Zone* zone, Instruction* other);
|
||||
|
||||
bool NeedsEnvironment() const {
|
||||
return ComputeCanDeoptimize() || CanBecomeDeoptimizationTarget() ||
|
||||
MayThrow();
|
||||
return ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall() ||
|
||||
CanBecomeDeoptimizationTarget() || MayThrow();
|
||||
}
|
||||
|
||||
virtual bool CanBecomeDeoptimizationTarget() const { return false; }
|
||||
@@ -1179,12 +1188,12 @@ class Instruction : public ZoneAllocated {
|
||||
"Pass Id does not fit into the bit field");
|
||||
};
|
||||
|
||||
intptr_t deopt_id_;
|
||||
intptr_t deopt_id_ = DeoptId::kNone;
|
||||
intptr_t pass_specific_id_ = PassSpecificId::kNoId;
|
||||
Instruction* previous_;
|
||||
Instruction* next_;
|
||||
Environment* env_;
|
||||
LocationSummary* locs_;
|
||||
Instruction* previous_ = nullptr;
|
||||
Instruction* next_ = nullptr;
|
||||
Environment* env_ = nullptr;
|
||||
LocationSummary* locs_ = nullptr;
|
||||
intptr_t inlining_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Instruction);
|
||||
@@ -3704,9 +3713,21 @@ class AssertAssignableInstr : public TemplateDefinition<4, Throws, Pure> {
|
||||
virtual TokenPosition token_pos() const { return token_pos_; }
|
||||
const String& dst_name() const { return dst_name_; }
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const {
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
virtual bool ComputeCanDeoptimizeAfterCall() const {
|
||||
return !CompilerState::Current().is_aot();
|
||||
}
|
||||
virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
|
||||
#if !defined(TARGET_ARCH_IA32)
|
||||
return InputCount();
|
||||
#else
|
||||
// The ia32 implementation calls the stub by pushing the input registers
|
||||
// in the same order onto the stack thereby making the deopt-env correct.
|
||||
// (Due to lack of registers we cannot use all-argument calling convention
|
||||
// as in other architectures.)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool CanBecomeDeoptimizationTarget() const {
|
||||
// AssertAssignable instructions that are specialized by the optimizer
|
||||
@@ -3875,6 +3896,13 @@ class TemplateDartCall : public Definition {
|
||||
|
||||
virtual intptr_t InputCount() const { return inputs_->length(); }
|
||||
virtual Value* InputAt(intptr_t i) const { return inputs_->At(i); }
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
virtual bool ComputeCanDeoptimizeAfterCall() const {
|
||||
return !CompilerState::Current().is_aot();
|
||||
}
|
||||
virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
|
||||
return kExtraInputs;
|
||||
}
|
||||
|
||||
intptr_t FirstArgIndex() const { return type_args_len_ > 0 ? 1 : 0; }
|
||||
Value* Receiver() const { return this->ArgumentValueAt(FirstArgIndex()); }
|
||||
@@ -3958,10 +3986,6 @@ class ClosureCallInstr : public TemplateDartCall<1> {
|
||||
// TODO(kmillikin): implement exact call counts for closure calls.
|
||||
virtual intptr_t CallCount() const { return 1; }
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const {
|
||||
return !CompilerState::Current().is_aot();
|
||||
}
|
||||
|
||||
virtual bool HasUnknownSideEffects() const { return true; }
|
||||
|
||||
Code::EntryKind entry_kind() const { return entry_kind_; }
|
||||
@@ -4034,10 +4058,6 @@ class InstanceCallBaseInstr : public TemplateDartCall<0> {
|
||||
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const {
|
||||
return !CompilerState::Current().is_aot();
|
||||
}
|
||||
|
||||
virtual bool CanBecomeDeoptimizationTarget() const {
|
||||
// Instance calls that are specialized by the optimizer need a
|
||||
// deoptimization descriptor before the call.
|
||||
@@ -4331,8 +4351,6 @@ class DispatchTableCallInstr : public TemplateDartCall<1> {
|
||||
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
|
||||
virtual Definition* Canonicalize(FlowGraph* flow_graph);
|
||||
|
||||
virtual bool CanBecomeDeoptimizationTarget() const { return false; }
|
||||
@@ -6546,9 +6564,13 @@ class LoadFieldInstr : public TemplateDefinition<1, Throws> {
|
||||
DECLARE_INSTRUCTION(LoadField)
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const {
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
virtual bool ComputeCanDeoptimizeAfterCall() const {
|
||||
return calls_initializer() && !CompilerState::Current().is_aot();
|
||||
}
|
||||
virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
|
||||
return InputCount();
|
||||
}
|
||||
|
||||
virtual bool HasUnknownSideEffects() const {
|
||||
return calls_initializer() && !throw_exception_on_initialization();
|
||||
@@ -9413,6 +9435,7 @@ class Environment : public ZoneAllocated {
|
||||
static Environment* From(Zone* zone,
|
||||
const GrowableArray<Definition*>& definitions,
|
||||
intptr_t fixed_parameter_count,
|
||||
intptr_t lazy_deopt_pruning_count,
|
||||
const ParsedFunction& parsed_function);
|
||||
|
||||
void set_locations(Location* locations) {
|
||||
@@ -9423,9 +9446,19 @@ class Environment : public ZoneAllocated {
|
||||
// Get deopt_id associated with this environment.
|
||||
// Note that only outer environments have deopt id associated with
|
||||
// them (set by DeepCopyToOuter).
|
||||
intptr_t deopt_id() const {
|
||||
ASSERT(deopt_id_ != DeoptId::kNone);
|
||||
return deopt_id_;
|
||||
intptr_t GetDeoptId() const {
|
||||
ASSERT(DeoptIdBits::decode(bitfield_) != DeoptId::kNone);
|
||||
return DeoptIdBits::decode(bitfield_);
|
||||
}
|
||||
|
||||
intptr_t LazyDeoptPruneCount() const {
|
||||
return LazyDeoptPruningBits::decode(bitfield_);
|
||||
}
|
||||
|
||||
Environment* GetLazyDeoptEnv(Zone* zone) {
|
||||
const intptr_t num_args_to_prune = LazyDeoptPruneCount();
|
||||
if (num_args_to_prune == 0) return this;
|
||||
return DeepCopy(zone, Length() - num_args_to_prune);
|
||||
}
|
||||
|
||||
Environment* outer() const { return outer_; }
|
||||
@@ -9499,21 +9532,39 @@ class Environment : public ZoneAllocated {
|
||||
friend class compiler::BlockBuilder; // For Environment constructor.
|
||||
friend class FlowGraphDeserializer; // For constructor and deopt_id_.
|
||||
|
||||
class LazyDeoptPruningBits : public BitField<uintptr_t, uintptr_t, 0, 8> {};
|
||||
class DeoptIdBits
|
||||
: public BitField<uintptr_t,
|
||||
intptr_t,
|
||||
LazyDeoptPruningBits::kNextBit,
|
||||
kBitsPerWord - LazyDeoptPruningBits::kNextBit,
|
||||
/*sign_extend=*/true> {};
|
||||
|
||||
Environment(intptr_t length,
|
||||
intptr_t fixed_parameter_count,
|
||||
intptr_t lazy_deopt_pruning_count,
|
||||
const ParsedFunction& parsed_function,
|
||||
Environment* outer)
|
||||
: values_(length),
|
||||
fixed_parameter_count_(fixed_parameter_count),
|
||||
bitfield_(DeoptIdBits::encode(DeoptId::kNone) |
|
||||
LazyDeoptPruningBits::encode(lazy_deopt_pruning_count)),
|
||||
parsed_function_(parsed_function),
|
||||
outer_(outer) {}
|
||||
|
||||
void SetDeoptId(intptr_t deopt_id) {
|
||||
bitfield_ = DeoptIdBits::update(deopt_id, bitfield_);
|
||||
}
|
||||
void SetLazyDeoptPruneCount(intptr_t value) {
|
||||
bitfield_ = LazyDeoptPruningBits::update(value, bitfield_);
|
||||
}
|
||||
|
||||
GrowableArray<Value*> values_;
|
||||
Location* locations_ = nullptr;
|
||||
const intptr_t fixed_parameter_count_;
|
||||
// Deoptimization id associated with this environment. Only set for
|
||||
// outer environments.
|
||||
intptr_t deopt_id_ = DeoptId::kNone;
|
||||
uintptr_t bitfield_;
|
||||
const ParsedFunction& parsed_function_;
|
||||
Environment* outer_;
|
||||
|
||||
|
||||
@@ -1351,8 +1351,6 @@ void LICM::Hoist(ForwardInstructionIterator* it,
|
||||
USE(check);
|
||||
} else if (auto check = current->AsTestCids()) {
|
||||
check->set_licm_hoisted(true);
|
||||
} else if (auto check = current->AsAssertAssignable()) {
|
||||
check->set_licm_hoisted(true);
|
||||
}
|
||||
if (FLAG_trace_optimization) {
|
||||
THR_Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
|
||||
|
||||
@@ -50,6 +50,10 @@ void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) {
|
||||
// Attach the outer environment on each instruction in the callee graph.
|
||||
ASSERT(call_->env() != NULL);
|
||||
ASSERT(call_->deopt_id() != DeoptId::kNone);
|
||||
|
||||
auto zone = callee_graph->zone();
|
||||
auto env = call_->env();
|
||||
|
||||
const intptr_t outer_deopt_id = call_->deopt_id();
|
||||
// Scale the edge weights by the call count for the inlined function.
|
||||
double scale_factor = 1.0;
|
||||
@@ -65,18 +69,16 @@ void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) {
|
||||
block->AsTargetEntry()->adjust_edge_weight(scale_factor);
|
||||
}
|
||||
Instruction* instr = block;
|
||||
if (block->env() != NULL) {
|
||||
call_->env()->DeepCopyToOuter(callee_graph->zone(), block,
|
||||
outer_deopt_id);
|
||||
if (block->env() != nullptr) {
|
||||
env->DeepCopyToOuter(zone, block, outer_deopt_id);
|
||||
}
|
||||
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
|
||||
instr = it.Current();
|
||||
// TODO(zerny): Avoid creating unnecessary environments. Note that some
|
||||
// optimizations need deoptimization info for non-deoptable instructions,
|
||||
// eg, LICM on GOTOs.
|
||||
if (instr->env() != NULL) {
|
||||
call_->env()->DeepCopyToOuter(callee_graph->zone(), instr,
|
||||
outer_deopt_id);
|
||||
if (instr->env() != nullptr) {
|
||||
env->DeepCopyToOuter(zone, instr, outer_deopt_id);
|
||||
}
|
||||
}
|
||||
if (instr->IsGoto()) {
|
||||
|
||||
Reference in New Issue
Block a user