mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:35:05 +00:00
[vm/compiler] keep stack depth per block in stack IR
Rationale: This simplifies finding the proper stack depth while traversing the entries in the DOM tree during OSR. Also enables asserting stack integrity on all paths (OSR and non-OSR) more rigidly. https://github.com/dart-lang/sdk/issues/38459 https://github.com/dart-lang/sdk/issues/38436 https://github.com/dart-lang/sdk/issues/38434 Change-Id: I3bc43def081013357e9a765e9e03fffe4c72dc6b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118473 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Aart Bik <ajcbik@google.com>
This commit is contained in:
parent
acba8cf7a1
commit
78bd9f17a1
|
@ -1029,14 +1029,15 @@ void FlowGraph::PopulateEnvironmentFromFunctionEntry(
|
|||
VariableLivenessAnalysis* variable_liveness,
|
||||
ZoneGrowableArray<Definition*>* inlining_parameters) {
|
||||
ASSERT(!IsCompiledForOsr());
|
||||
const intptr_t parameter_count = num_direct_parameters_;
|
||||
const intptr_t direct_parameter_count = num_direct_parameters_;
|
||||
|
||||
// Check if inlining_parameters include a type argument vector parameter.
|
||||
const intptr_t inlined_type_args_param =
|
||||
((inlining_parameters != NULL) && function().IsGeneric()) ? 1 : 0;
|
||||
|
||||
ASSERT(parameter_count <= env->length());
|
||||
for (intptr_t i = 0; i < parameter_count; i++) {
|
||||
ASSERT(variable_count() == env->length());
|
||||
ASSERT(direct_parameter_count <= env->length());
|
||||
for (intptr_t i = 0; i < direct_parameter_count; i++) {
|
||||
ParameterInstr* param = new (zone()) ParameterInstr(i, function_entry);
|
||||
param->set_ssa_temp_index(alloc_ssa_temp_index());
|
||||
AddToInitialDefinitions(function_entry, param);
|
||||
|
@ -1097,8 +1098,7 @@ void FlowGraph::PopulateEnvironmentFromOsrEntry(
|
|||
// passed as parameters. The latter mimics the incoming expression
|
||||
// stack that was set up prior to triggering OSR.
|
||||
const intptr_t parameter_count = osr_variable_count();
|
||||
ASSERT(env->length() == (parameter_count - osr_entry->stack_depth()));
|
||||
env->EnsureLength(parameter_count, constant_dead());
|
||||
ASSERT(parameter_count == env->length());
|
||||
for (intptr_t i = 0; i < parameter_count; i++) {
|
||||
ParameterInstr* param = new (zone()) ParameterInstr(i, osr_entry);
|
||||
param->set_ssa_temp_index(alloc_ssa_temp_index());
|
||||
|
@ -1120,7 +1120,7 @@ void FlowGraph::PopulateEnvironmentFromCatchEntry(
|
|||
: -1;
|
||||
|
||||
// Add real definitions for all locals and parameters.
|
||||
ASSERT(variable_count() <= env->length());
|
||||
ASSERT(variable_count() == env->length());
|
||||
for (intptr_t i = 0, n = variable_count(); i < n; ++i) {
|
||||
// Replace usages of the raw exception/stacktrace variables with
|
||||
// [SpecialParameterInstr]s.
|
||||
|
@ -1288,7 +1288,7 @@ void FlowGraph::RenameRecursive(
|
|||
PushArgumentInstr* push_arg = current->PushArgumentAt(i);
|
||||
ASSERT(push_arg->IsPushArgument());
|
||||
ASSERT(reaching_defn->ssa_temp_index() != -1);
|
||||
ASSERT(reaching_defn->IsPhi());
|
||||
ASSERT(reaching_defn->IsPhi() || reaching_defn == constant_dead());
|
||||
push_arg->ReplaceUsesWith(push_arg->InputAt(0)->definition());
|
||||
push_arg->UnuseAllInputs();
|
||||
push_arg->previous()->LinkTo(push_arg->next());
|
||||
|
@ -1416,29 +1416,30 @@ void FlowGraph::RenameRecursive(
|
|||
// Update expression stack and remove current instruction from the graph.
|
||||
Definition* definition = current->Cast<Definition>();
|
||||
if (definition->HasTemp()) {
|
||||
ASSERT(result != NULL);
|
||||
ASSERT(result != nullptr);
|
||||
env->Add(result);
|
||||
}
|
||||
it.RemoveCurrentFromGraph();
|
||||
}
|
||||
|
||||
// 3. Process dominated blocks.
|
||||
BlockEntryInstr* osr_succ =
|
||||
(block_entry == graph_entry() && IsCompiledForOsr())
|
||||
? graph_entry()->osr_entry()->last_instruction()->SuccessorAt(0)
|
||||
: nullptr;
|
||||
const bool set_stack = (block_entry == graph_entry()) && IsCompiledForOsr();
|
||||
for (intptr_t i = 0; i < block_entry->dominated_blocks().length(); ++i) {
|
||||
BlockEntryInstr* block = block_entry->dominated_blocks()[i];
|
||||
GrowableArray<Definition*> new_env(env->length());
|
||||
new_env.AddArray(*env);
|
||||
ASSERT(block != nullptr);
|
||||
if (block == osr_succ) {
|
||||
// During OSR, when visiting the successor block of the OSR entry from
|
||||
// the graph entry (rather than going through the OSR entry first), we
|
||||
// must adjust the environment to mimic a non-empty incoming expression
|
||||
// stack to ensure temporaries refer to the right stack items.
|
||||
new_env.FillWith(constant_dead(), new_env.length(),
|
||||
graph_entry()->osr_entry()->stack_depth());
|
||||
// During OSR, when traversing from the graph entry directly any block
|
||||
// (which may be a non-entry), we must adjust the environment to mimic
|
||||
// a non-empty incoming expression stack to ensure temporaries refer to
|
||||
// the right stack items.
|
||||
const intptr_t stack_depth = block->stack_depth();
|
||||
ASSERT(stack_depth >= 0);
|
||||
if (set_stack) {
|
||||
ASSERT(variable_count() == new_env.length());
|
||||
new_env.FillWith(constant_dead(), variable_count(), stack_depth);
|
||||
} else if (!block->last_instruction()->IsTailCall()) {
|
||||
// Assert environment integrity otherwise.
|
||||
ASSERT((variable_count() + stack_depth) == new_env.length());
|
||||
}
|
||||
RenameRecursive(block, &new_env, live_phis, variable_liveness,
|
||||
inlining_parameters);
|
||||
|
|
|
@ -1129,7 +1129,10 @@ GraphEntryInstr::GraphEntryInstr(const ParsedFunction& parsed_function,
|
|||
GraphEntryInstr::GraphEntryInstr(const ParsedFunction& parsed_function,
|
||||
intptr_t osr_id,
|
||||
intptr_t deopt_id)
|
||||
: BlockEntryWithInitialDefs(0, kInvalidTryIndex, deopt_id),
|
||||
: BlockEntryWithInitialDefs(0,
|
||||
kInvalidTryIndex,
|
||||
deopt_id,
|
||||
/*stack_depth*/ 0),
|
||||
parsed_function_(parsed_function),
|
||||
catch_entries_(),
|
||||
indirect_entries_(),
|
||||
|
@ -1656,11 +1659,11 @@ bool BlockEntryInstr::FindOsrEntryAndRelink(GraphEntryInstr* graph_entry,
|
|||
// we can simply jump to the beginning of the block.
|
||||
ASSERT(instr->previous() == this);
|
||||
|
||||
const intptr_t stack_depth = instr->AsCheckStackOverflow()->stack_depth();
|
||||
ASSERT(stack_depth() == instr->AsCheckStackOverflow()->stack_depth());
|
||||
auto normal_entry = graph_entry->normal_entry();
|
||||
auto osr_entry = new OsrEntryInstr(graph_entry, normal_entry->block_id(),
|
||||
normal_entry->try_index(),
|
||||
normal_entry->deopt_id(), stack_depth);
|
||||
auto osr_entry = new OsrEntryInstr(
|
||||
graph_entry, normal_entry->block_id(), normal_entry->try_index(),
|
||||
normal_entry->deopt_id(), stack_depth());
|
||||
|
||||
auto goto_join = new GotoInstr(AsJoinEntry(),
|
||||
CompilerState::Current().GetNextDeoptId());
|
||||
|
|
|
@ -1384,6 +1384,10 @@ class BlockEntryInstr : public Instruction {
|
|||
intptr_t offset() const { return offset_; }
|
||||
void set_offset(intptr_t offset) { offset_ = offset; }
|
||||
|
||||
// Stack-based IR bookkeeping.
|
||||
intptr_t stack_depth() const { return stack_depth_; }
|
||||
void set_stack_depth(intptr_t s) { stack_depth_ = s; }
|
||||
|
||||
// For all instruction in this block: Remove all inputs (including in the
|
||||
// environment) from their definition's use lists for all instructions.
|
||||
void ClearAllInstructions();
|
||||
|
@ -1395,12 +1399,16 @@ class BlockEntryInstr : public Instruction {
|
|||
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
|
||||
|
||||
protected:
|
||||
BlockEntryInstr(intptr_t block_id, intptr_t try_index, intptr_t deopt_id)
|
||||
BlockEntryInstr(intptr_t block_id,
|
||||
intptr_t try_index,
|
||||
intptr_t deopt_id,
|
||||
intptr_t stack_depth)
|
||||
: Instruction(deopt_id),
|
||||
block_id_(block_id),
|
||||
try_index_(try_index),
|
||||
preorder_number_(-1),
|
||||
postorder_number_(-1),
|
||||
stack_depth_(stack_depth),
|
||||
dominator_(nullptr),
|
||||
dominated_blocks_(1),
|
||||
last_instruction_(NULL),
|
||||
|
@ -1428,6 +1436,8 @@ class BlockEntryInstr : public Instruction {
|
|||
intptr_t try_index_;
|
||||
intptr_t preorder_number_;
|
||||
intptr_t postorder_number_;
|
||||
// Expected stack depth on entry (for stack-based IR only).
|
||||
intptr_t stack_depth_;
|
||||
// Starting and ending lifetime positions for this block. Used by
|
||||
// the linear scan register allocator.
|
||||
intptr_t start_pos_;
|
||||
|
@ -1512,8 +1522,9 @@ class BlockEntryWithInitialDefs : public BlockEntryInstr {
|
|||
public:
|
||||
BlockEntryWithInitialDefs(intptr_t block_id,
|
||||
intptr_t try_index,
|
||||
intptr_t deopt_id)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id) {}
|
||||
intptr_t deopt_id,
|
||||
intptr_t stack_depth)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id, stack_depth) {}
|
||||
|
||||
GrowableArray<Definition*>* initial_definitions() {
|
||||
return &initial_definitions_;
|
||||
|
@ -1630,8 +1641,11 @@ class GraphEntryInstr : public BlockEntryWithInitialDefs {
|
|||
|
||||
class JoinEntryInstr : public BlockEntryInstr {
|
||||
public:
|
||||
JoinEntryInstr(intptr_t block_id, intptr_t try_index, intptr_t deopt_id)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id),
|
||||
JoinEntryInstr(intptr_t block_id,
|
||||
intptr_t try_index,
|
||||
intptr_t deopt_id,
|
||||
intptr_t stack_depth = 0)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id, stack_depth),
|
||||
predecessors_(2), // Two is the assumed to be the common case.
|
||||
phis_(NULL) {}
|
||||
|
||||
|
@ -1698,8 +1712,11 @@ class PhiIterator : public ValueObject {
|
|||
|
||||
class TargetEntryInstr : public BlockEntryInstr {
|
||||
public:
|
||||
TargetEntryInstr(intptr_t block_id, intptr_t try_index, intptr_t deopt_id)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id),
|
||||
TargetEntryInstr(intptr_t block_id,
|
||||
intptr_t try_index,
|
||||
intptr_t deopt_id,
|
||||
intptr_t stack_depth = 0)
|
||||
: BlockEntryInstr(block_id, try_index, deopt_id, stack_depth),
|
||||
predecessor_(NULL),
|
||||
edge_weight_(0.0) {}
|
||||
|
||||
|
@ -1749,7 +1766,10 @@ class FunctionEntryInstr : public BlockEntryWithInitialDefs {
|
|||
intptr_t block_id,
|
||||
intptr_t try_index,
|
||||
intptr_t deopt_id)
|
||||
: BlockEntryWithInitialDefs(block_id, try_index, deopt_id),
|
||||
: BlockEntryWithInitialDefs(block_id,
|
||||
try_index,
|
||||
deopt_id,
|
||||
/*stack_depth=*/0),
|
||||
graph_entry_(graph_entry) {}
|
||||
|
||||
DECLARE_INSTRUCTION(FunctionEntry)
|
||||
|
@ -1815,8 +1835,7 @@ class OsrEntryInstr : public BlockEntryWithInitialDefs {
|
|||
intptr_t try_index,
|
||||
intptr_t deopt_id,
|
||||
intptr_t stack_depth)
|
||||
: BlockEntryWithInitialDefs(block_id, try_index, deopt_id),
|
||||
stack_depth_(stack_depth),
|
||||
: BlockEntryWithInitialDefs(block_id, try_index, deopt_id, stack_depth),
|
||||
graph_entry_(graph_entry) {}
|
||||
|
||||
DECLARE_INSTRUCTION(OsrEntry)
|
||||
|
@ -1829,7 +1848,6 @@ class OsrEntryInstr : public BlockEntryWithInitialDefs {
|
|||
return graph_entry_;
|
||||
}
|
||||
|
||||
intptr_t stack_depth() const { return stack_depth_; }
|
||||
GraphEntryInstr* graph_entry() const { return graph_entry_; }
|
||||
|
||||
PRINT_TO_SUPPORT
|
||||
|
@ -1841,7 +1859,6 @@ class OsrEntryInstr : public BlockEntryWithInitialDefs {
|
|||
graph_entry_ = predecessor->AsGraphEntry();
|
||||
}
|
||||
|
||||
const intptr_t stack_depth_;
|
||||
GraphEntryInstr* graph_entry_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OsrEntryInstr);
|
||||
|
@ -1880,7 +1897,10 @@ class CatchBlockEntryInstr : public BlockEntryWithInitialDefs {
|
|||
const LocalVariable* stacktrace_var,
|
||||
const LocalVariable* raw_exception_var,
|
||||
const LocalVariable* raw_stacktrace_var)
|
||||
: BlockEntryWithInitialDefs(block_id, try_index, deopt_id),
|
||||
: BlockEntryWithInitialDefs(block_id,
|
||||
try_index,
|
||||
deopt_id,
|
||||
/*stack_depth=*/0),
|
||||
graph_entry_(graph_entry),
|
||||
predecessor_(NULL),
|
||||
catch_handler_types_(Array::ZoneHandle(handler_types.raw())),
|
||||
|
|
|
@ -661,8 +661,8 @@ Fragment BaseFlowGraphBuilder::MakeTemp() {
|
|||
}
|
||||
|
||||
TargetEntryInstr* BaseFlowGraphBuilder::BuildTargetEntry() {
|
||||
return new (Z)
|
||||
TargetEntryInstr(AllocateBlockId(), CurrentTryIndex(), GetNextDeoptId());
|
||||
return new (Z) TargetEntryInstr(AllocateBlockId(), CurrentTryIndex(),
|
||||
GetNextDeoptId(), GetStackDepth());
|
||||
}
|
||||
|
||||
FunctionEntryInstr* BaseFlowGraphBuilder::BuildFunctionEntry(
|
||||
|
@ -672,12 +672,13 @@ FunctionEntryInstr* BaseFlowGraphBuilder::BuildFunctionEntry(
|
|||
}
|
||||
|
||||
JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) {
|
||||
return new (Z) JoinEntryInstr(AllocateBlockId(), try_index, GetNextDeoptId());
|
||||
return new (Z) JoinEntryInstr(AllocateBlockId(), try_index, GetNextDeoptId(),
|
||||
GetStackDepth());
|
||||
}
|
||||
|
||||
JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry() {
|
||||
return new (Z)
|
||||
JoinEntryInstr(AllocateBlockId(), CurrentTryIndex(), GetNextDeoptId());
|
||||
return new (Z) JoinEntryInstr(AllocateBlockId(), CurrentTryIndex(),
|
||||
GetNextDeoptId(), GetStackDepth());
|
||||
}
|
||||
|
||||
ArgumentArray BaseFlowGraphBuilder::GetArguments(int count) {
|
||||
|
|
|
@ -2177,6 +2177,7 @@ FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() {
|
|||
B->stack_ = stack_state;
|
||||
}
|
||||
code_ = Fragment(join);
|
||||
join->set_stack_depth(B->GetStackDepth());
|
||||
B->SetCurrentTryIndex(join->try_index());
|
||||
} else {
|
||||
// Unreachable bytecode is not allowed.
|
||||
|
|
|
@ -267,8 +267,6 @@ Fragment PrologueBuilder::BuildOptionalParameterHandling(
|
|||
copy_args_prologue += Drop();
|
||||
|
||||
for (intptr_t i = 0; param < num_params; ++param, ++i) {
|
||||
JoinEntryInstr* join = BuildJoinEntry();
|
||||
|
||||
copy_args_prologue += IntConstant(
|
||||
compiler::target::ArgumentsDescriptor::named_entry_size() /
|
||||
compiler::target::kWordSize);
|
||||
|
@ -299,6 +297,9 @@ Fragment PrologueBuilder::BuildOptionalParameterHandling(
|
|||
TargetEntryInstr *supplied, *missing;
|
||||
copy_args_prologue += BranchIfStrictEqual(&supplied, &missing);
|
||||
|
||||
// Join good/not_good.
|
||||
JoinEntryInstr* join = BuildJoinEntry();
|
||||
|
||||
// Let's load position from arg descriptor (to see which parameter is the
|
||||
// name) and move kEntrySize forward in ArgDescriptopr names array.
|
||||
Fragment good(supplied);
|
||||
|
|
2362
tests/language_2/vm/regression_38436.dart
Normal file
2362
tests/language_2/vm/regression_38436.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue