Files
sdk/runtime/vm/compiler/backend/block_builder.h
T
Martin Kustermann dec7b4f5b6 [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>
2021-03-23 12:22:16 +00:00

176 lines
6.1 KiB
C++

// Copyright (c) 2019, 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.
#ifndef RUNTIME_VM_COMPILER_BACKEND_BLOCK_BUILDER_H_
#define RUNTIME_VM_COMPILER_BACKEND_BLOCK_BUILDER_H_
#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
namespace dart {
namespace compiler {
// Helper class for building basic blocks in SSA form.
class BlockBuilder : public ValueObject {
public:
BlockBuilder(FlowGraph* flow_graph, BlockEntryInstr* entry)
: flow_graph_(flow_graph),
source_(InstructionSource(flow_graph_->function().token_pos(),
flow_graph->inlining_id())),
entry_(entry),
current_(entry),
dummy_env_(
new Environment(0, 0, 0, flow_graph->parsed_function(), nullptr)) {
// Some graph transformations use environments from block entries.
entry->SetEnvironment(dummy_env_);
}
Definition* AddToInitialDefinitions(Definition* def) {
def->set_ssa_temp_index(flow_graph_->alloc_ssa_temp_index());
if (FlowGraph::NeedsPairLocation(def->representation())) {
flow_graph_->alloc_ssa_temp_index();
}
auto normal_entry = flow_graph_->graph_entry()->normal_entry();
flow_graph_->AddToInitialDefinitions(normal_entry, def);
return def;
}
template <typename T>
T* AddDefinition(T* def) {
flow_graph_->AllocateSSAIndexes(def);
AddInstruction(def);
return def;
}
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.
instr->SetEnvironment(dummy_env_);
}
current_ = current_->AppendInstruction(instr);
return instr;
}
const Function& function() const { return flow_graph_->function(); }
ReturnInstr* AddReturn(Value* value) {
const auto& function = flow_graph_->function();
const auto representation = FlowGraph::ReturnRepresentationOf(function);
ReturnInstr* instr = new ReturnInstr(
Source(), value, CompilerState::Current().GetNextDeoptId(),
UntaggedPcDescriptors::kInvalidYieldIndex, representation);
AddInstruction(instr);
entry_->set_last_instruction(instr);
return instr;
}
Definition* AddParameter(intptr_t index, bool with_frame) {
const auto& function = flow_graph_->function();
const intptr_t param_offset = FlowGraph::ParameterOffsetAt(function, index);
const auto representation =
FlowGraph::ParameterRepresentationAt(function, index);
return AddParameter(index, param_offset, with_frame, representation);
}
Definition* AddParameter(intptr_t index,
intptr_t param_offset,
bool with_frame,
Representation representation) {
auto normal_entry = flow_graph_->graph_entry()->normal_entry();
return AddToInitialDefinitions(
new ParameterInstr(index, param_offset, normal_entry, representation,
with_frame ? FPREG : SPREG));
}
TokenPosition TokenPos() const { return source_.token_pos; }
const InstructionSource& Source() const { return source_; }
Definition* AddNullDefinition() {
return flow_graph_->GetConstant(Object::ZoneHandle());
}
Definition* AddUnboxInstr(Representation rep, Value* value, bool is_checked) {
Definition* unboxed_value =
AddDefinition(UnboxInstr::Create(rep, value, DeoptId::kNone));
if (is_checked) {
// The type of |value| has already been checked and it is safe to
// adjust reaching type. This is done manually because there is no type
// propagation when building intrinsics.
unboxed_value->AsUnbox()->value()->SetReachingType(
TypeForRepresentation(rep));
}
return unboxed_value;
}
Definition* AddUnboxInstr(Representation rep,
Definition* boxed,
bool is_checked) {
return AddUnboxInstr(rep, new Value(boxed), is_checked);
}
BranchInstr* AddBranch(ComparisonInstr* comp,
TargetEntryInstr* true_successor,
TargetEntryInstr* false_successor) {
auto branch =
new BranchInstr(comp, CompilerState::Current().GetNextDeoptId());
// Some graph transformations use environments from branches.
branch->SetEnvironment(dummy_env_);
current_->AppendInstruction(branch);
current_ = nullptr;
*branch->true_successor_address() = true_successor;
*branch->false_successor_address() = false_successor;
return branch;
}
void AddPhi(PhiInstr* phi) {
phi->set_ssa_temp_index(flow_graph_->alloc_ssa_temp_index());
if (FlowGraph::NeedsPairLocation(phi->representation())) {
flow_graph_->alloc_ssa_temp_index();
}
phi->mark_alive();
entry_->AsJoinEntry()->InsertPhi(phi);
}
private:
static CompileType* TypeForRepresentation(Representation rep) {
switch (rep) {
case kUnboxedDouble:
return new CompileType(CompileType::FromCid(kDoubleCid));
case kUnboxedFloat32x4:
return new CompileType(CompileType::FromCid(kFloat32x4Cid));
case kUnboxedInt32x4:
return new CompileType(CompileType::FromCid(kInt32x4Cid));
case kUnboxedFloat64x2:
return new CompileType(CompileType::FromCid(kFloat64x2Cid));
case kUnboxedUint32:
case kUnboxedInt64:
return new CompileType(CompileType::Int());
default:
UNREACHABLE();
return nullptr;
}
}
FlowGraph* const flow_graph_;
const InstructionSource source_;
BlockEntryInstr* entry_;
Instruction* current_;
Environment* dummy_env_;
};
} // namespace compiler
} // namespace dart
#endif // RUNTIME_VM_COMPILER_BACKEND_BLOCK_BUILDER_H_