mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:29:47 +00:00
[vm/compiler] Continued work on the IL serializer.
Adds support for: Constant Goto JoinEntry PushArgument StaticCall Adds canonical name parsing, which allows us to parse Function and Field values. Adds ImmutableList, Instance, and TypeParameter parsing. Results from compiling hello world program: * Early round trip: * Contains unhandled instructions: 4070 * Failed during deserialization: 2 * Successful round trips: 106 * Late round trip: * Contains unhandled instructions: 3789 * Failed during deserialization: 4 * Successful round trips: 385 Bug: https://github.com/dart-lang/sdk/issues/36882 Change-Id: If9cb4c133e3ba8c62016e545f8471c67cc126290 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/113684 Commit-Queue: Teagan Strickland <sstrickl@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Aart Bik <ajcbik@google.com>
This commit is contained in:
parent
8fb0152af8
commit
d2e99c4935
|
@ -1910,10 +1910,7 @@ class Definition : public Instruction {
|
|||
explicit Definition(intptr_t deopt_id = DeoptId::kNone);
|
||||
|
||||
// Overridden by definitions that have call counts.
|
||||
virtual intptr_t CallCount() const {
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
}
|
||||
virtual intptr_t CallCount() const { return -1; }
|
||||
|
||||
intptr_t temp_index() const { return temp_index_; }
|
||||
void set_temp_index(intptr_t index) { temp_index_ = index; }
|
||||
|
@ -3441,6 +3438,7 @@ class ClosureCallInstr : public TemplateDartCall<1> {
|
|||
Code::EntryKind entry_kind() const { return entry_kind_; }
|
||||
|
||||
PRINT_OPERANDS_TO_SUPPORT
|
||||
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
|
||||
|
||||
private:
|
||||
const Code::EntryKind entry_kind_;
|
||||
|
@ -4110,6 +4108,7 @@ class StaticCallInstr : public TemplateDartCall<0> {
|
|||
|
||||
PRINT_OPERANDS_TO_SUPPORT
|
||||
ADD_OPERANDS_TO_S_EXPRESSION_SUPPORT
|
||||
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
|
||||
|
||||
private:
|
||||
const ICData* ic_data_;
|
||||
|
|
|
@ -24,18 +24,6 @@ DEFINE_FLAG(bool,
|
|||
|
||||
void FlowGraphDeserializer::RoundTripSerialization(CompilerPassState* state) {
|
||||
auto const flow_graph = state->flow_graph;
|
||||
auto const inst =
|
||||
FlowGraphDeserializer::FirstUnhandledInstruction(flow_graph);
|
||||
if (inst != nullptr) {
|
||||
if (FLAG_trace_round_trip_serialization_skips) {
|
||||
THR_Print("Cannot serialize graph due to instruction: %s\n",
|
||||
inst->DebugName());
|
||||
if (auto const const_inst = inst->AsConstant()) {
|
||||
THR_Print("Constant value: %s\n", const_inst->value().ToCString());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The deserialized flow graph must be in the same zone as the original flow
|
||||
// graph, to ensure it has the right lifetime. Thus, we leave an explicit
|
||||
|
@ -55,6 +43,30 @@ void FlowGraphDeserializer::RoundTripSerialization(CompilerPassState* state) {
|
|||
// that those VM parts mentioned can be passed an explicit zone.
|
||||
Zone* const zone = flow_graph->zone();
|
||||
|
||||
GrowableArray<Instruction*> unhandled(zone, 2);
|
||||
FlowGraphDeserializer::AllUnhandledInstructions(flow_graph, &unhandled);
|
||||
if (!unhandled.is_empty()) {
|
||||
if (FLAG_trace_round_trip_serialization_skips) {
|
||||
THR_Print("Cannot serialize graph due to instruction: %s\n",
|
||||
unhandled.At(0)->DebugName());
|
||||
if (unhandled.length() > 1) {
|
||||
CStringMap<intptr_t> count_map(zone);
|
||||
for (auto inst : unhandled) {
|
||||
auto const name = inst->DebugName();
|
||||
auto const old_count = count_map.LookupValue(name);
|
||||
count_map.Update({name, old_count + 1});
|
||||
}
|
||||
THR_Print("There are %" Pd " different unhandled instruction(s):\n",
|
||||
count_map.Length());
|
||||
auto count_it = count_map.GetIterator();
|
||||
while (auto kv = count_it.Next()) {
|
||||
THR_Print(" %s (%" Pd ")\n", kv->key, kv->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto const sexp = FlowGraphSerializer::SerializeToSExp(zone, flow_graph);
|
||||
if (FLAG_trace_round_trip_serialization) {
|
||||
THR_Print("----- Serialized flow graph:\n");
|
||||
|
@ -84,54 +96,49 @@ void FlowGraphDeserializer::RoundTripSerialization(CompilerPassState* state) {
|
|||
|
||||
#define HANDLED_CASE(name) \
|
||||
if (inst->Is##name()) return true;
|
||||
static bool IsHandledInstruction(Instruction* inst) {
|
||||
bool FlowGraphDeserializer::IsHandledInstruction(Instruction* inst) {
|
||||
if (auto const const_inst = inst->AsConstant()) {
|
||||
return IsHandledConstant(const_inst->value());
|
||||
}
|
||||
FOR_EACH_HANDLED_BLOCK_TYPE_IN_DESERIALIZER(HANDLED_CASE)
|
||||
FOR_EACH_HANDLED_INSTRUCTION_IN_DESERIALIZER(HANDLED_CASE)
|
||||
return false;
|
||||
}
|
||||
#undef HANDLED_CASE
|
||||
|
||||
Instruction* FlowGraphDeserializer::FirstUnhandledInstruction(
|
||||
const FlowGraph* graph) {
|
||||
void FlowGraphDeserializer::AllUnhandledInstructions(
|
||||
const FlowGraph* graph,
|
||||
GrowableArray<Instruction*>* unhandled) {
|
||||
ASSERT(graph != nullptr);
|
||||
ASSERT(unhandled != nullptr);
|
||||
for (auto block_it = graph->reverse_postorder_iterator(); !block_it.Done();
|
||||
block_it.Advance()) {
|
||||
auto const entry = block_it.Current();
|
||||
if (!IsHandledInstruction(entry)) return entry;
|
||||
// The constant pool (the initial definitions of the graph entry block) is
|
||||
// handled differently from other constant definitions, and there are no
|
||||
// body instructions for a graph entry block. We should still make sure the
|
||||
// values in the constant pool are serializable though.
|
||||
if (auto const graph_entry = entry->AsGraphEntry()) {
|
||||
auto const defs = graph_entry->initial_definitions();
|
||||
for (intptr_t i = 0; i < defs->length(); i++) {
|
||||
ASSERT(defs->At(i)->IsConstant());
|
||||
auto const current = defs->At(i)->AsConstant();
|
||||
if (!IsHandledConstant(current->value())) return current;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!IsHandledInstruction(entry)) unhandled->Add(entry);
|
||||
// Don't check the Phi definitions in JoinEntrys, as those are now handled
|
||||
// and also parsed differently from other definitions.
|
||||
if (auto const def_block = entry->AsBlockEntryWithInitialDefs()) {
|
||||
auto const defs = def_block->initial_definitions();
|
||||
for (intptr_t i = 0; i < defs->length(); i++) {
|
||||
auto const current = defs->At(i);
|
||||
if (!IsHandledInstruction(current)) return current;
|
||||
if (!IsHandledInstruction(current)) unhandled->Add(current);
|
||||
}
|
||||
}
|
||||
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
|
||||
auto current = it.Current();
|
||||
if (!IsHandledInstruction(current)) return current;
|
||||
// We handle branches, so we need to check the comparison instruction.
|
||||
if (current->IsBranch()) current = current->AsBranch()->comparison();
|
||||
if (!IsHandledInstruction(current)) unhandled->Add(current);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Keep in sync with work in ParseDartValue. Right now, this is just a shallow
|
||||
// check, not a deep one.
|
||||
bool FlowGraphDeserializer::IsHandledConstant(const Object& obj) {
|
||||
return obj.IsNull() || obj.IsBool() || obj.IsString() || obj.IsInteger() ||
|
||||
obj.IsDouble() || obj.IsClass() || obj.IsType() ||
|
||||
obj.IsTypeArguments();
|
||||
if (obj.IsArray()) return Array::Cast(obj).IsImmutable();
|
||||
if (obj.IsInstance()) return !obj.IsClosure();
|
||||
return obj.IsNull() || obj.IsClass() || obj.IsFunction() || obj.IsField();
|
||||
}
|
||||
|
||||
SExpression* FlowGraphDeserializer::Retrieve(SExpList* list, intptr_t index) {
|
||||
|
@ -164,25 +171,15 @@ FlowGraph* FlowGraphDeserializer::ParseFlowGraph() {
|
|||
auto const root = CheckTaggedList(root_sexp_, "FlowGraph");
|
||||
if (root == nullptr) return nullptr;
|
||||
|
||||
auto const name_sexp = CheckSymbol(Retrieve(root, 1));
|
||||
// TODO(sstrickl): If the FlowGraphDeserializer was constructed with a
|
||||
// non-null ParsedFunction, we should check that the name matches here.
|
||||
// If not, then we should create an appropriate ParsedFunction here.
|
||||
if (name_sexp == nullptr) return nullptr;
|
||||
|
||||
intptr_t osr_id = Compiler::kNoOSRDeoptId;
|
||||
if (auto const osr_id_sexp = CheckInteger(root->ExtraLookupValue("osr_id"))) {
|
||||
osr_id = osr_id_sexp->value();
|
||||
}
|
||||
|
||||
intptr_t deopt_id = DeoptId::kNone;
|
||||
if (auto const deopt_id_sexp =
|
||||
CheckInteger(root->ExtraLookupValue("deopt_id"))) {
|
||||
deopt_id = deopt_id_sexp->value();
|
||||
}
|
||||
CommonEntryInfo common_info = {0, kInvalidTryIndex, deopt_id};
|
||||
|
||||
auto const graph = HandleGraphEntry(root, common_info);
|
||||
|
||||
auto const graph =
|
||||
new (zone()) GraphEntryInstr(*parsed_function_, osr_id, deopt_id);
|
||||
PrologueInfo pi(-1, -1);
|
||||
flow_graph_ = new (zone()) FlowGraph(*parsed_function_, graph, 0, pi);
|
||||
flow_graph_->CreateCommonConstants();
|
||||
|
@ -307,36 +304,41 @@ BlockEntryInstr* FlowGraphDeserializer::ParseBlockHeader(SExpList* list,
|
|||
try_index = try_int->value();
|
||||
}
|
||||
|
||||
auto const old_block = block_map_.LookupValue(block_id);
|
||||
BlockEntryInstr* block = nullptr;
|
||||
CommonEntryInfo common_info = {block_id, try_index, deopt_id};
|
||||
switch (kind) {
|
||||
case FlowGraphSerializer::kTarget:
|
||||
block = new (zone()) TargetEntryInstr(block_id, try_index, deopt_id);
|
||||
block = HandleTargetEntry(list, common_info);
|
||||
break;
|
||||
case FlowGraphSerializer::kNormal:
|
||||
// The Normal and Unchecked cases are the same except for the
|
||||
// set_XXX_entry call, so combine them.
|
||||
FALL_THROUGH;
|
||||
case FlowGraphSerializer::kUnchecked: {
|
||||
block = block_map_.LookupValue(block_id);
|
||||
// These blocks were already created during ParseEntries, so just
|
||||
// return the created block.
|
||||
if (block != nullptr) {
|
||||
ASSERT(block_id == block->block_id());
|
||||
return block;
|
||||
if (old_block != nullptr) {
|
||||
ASSERT(old_block->block_id() == block_id);
|
||||
ASSERT(old_block->IsFunctionEntry());
|
||||
return old_block;
|
||||
}
|
||||
auto const graph = flow_graph_->graph_entry();
|
||||
block =
|
||||
new (zone()) FunctionEntryInstr(graph, block_id, try_index, deopt_id);
|
||||
if (kind == FlowGraphSerializer::kUnchecked) {
|
||||
graph->set_unchecked_entry(block->AsFunctionEntry());
|
||||
} else {
|
||||
block = HandleFunctionEntry(list, common_info);
|
||||
if (block != nullptr) {
|
||||
auto const graph = flow_graph_->graph_entry();
|
||||
graph->set_normal_entry(block->AsFunctionEntry());
|
||||
}
|
||||
graph->AddDominatedBlock(block);
|
||||
current_block_ = block;
|
||||
if (!ParseInitialDefinitions(list)) return nullptr;
|
||||
break;
|
||||
case FlowGraphSerializer::kUnchecked: {
|
||||
if (old_block != nullptr) {
|
||||
ASSERT(old_block->block_id() == block_id);
|
||||
ASSERT(old_block->IsFunctionEntry());
|
||||
return old_block;
|
||||
}
|
||||
block = HandleFunctionEntry(list, common_info);
|
||||
if (block != nullptr) {
|
||||
auto const graph = flow_graph_->graph_entry();
|
||||
graph->set_unchecked_entry(block->AsFunctionEntry());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FlowGraphSerializer::kJoin:
|
||||
block = HandleJoinEntry(list, common_info);
|
||||
break;
|
||||
case FlowGraphSerializer::kInvalid:
|
||||
StoreError(tag, "invalid block entry tag");
|
||||
return nullptr;
|
||||
|
@ -344,6 +346,12 @@ BlockEntryInstr* FlowGraphDeserializer::ParseBlockHeader(SExpList* list,
|
|||
StoreError(tag, "unhandled block type");
|
||||
return nullptr;
|
||||
}
|
||||
if (block == nullptr) return nullptr;
|
||||
if (old_block != nullptr) {
|
||||
// Any cases where this is not an error should have already returned.
|
||||
StoreError(id_sexp, "duplicate definition of block");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// For blocks with initial definitions, this needs to be done after those
|
||||
// are parsed.
|
||||
|
@ -353,24 +361,75 @@ BlockEntryInstr* FlowGraphDeserializer::ParseBlockHeader(SExpList* list,
|
|||
env->DeepCopyTo(zone(), block);
|
||||
}
|
||||
|
||||
if (block_map_.HasKey(block_id)) {
|
||||
StoreError(id_sexp, "duplicate definition of block");
|
||||
return nullptr;
|
||||
}
|
||||
block_map_.Insert(block_id, block);
|
||||
return block;
|
||||
}
|
||||
|
||||
intptr_t FlowGraphDeserializer::ParsePhis(SExpList* list, intptr_t pos) {
|
||||
ASSERT(current_block_ != nullptr && current_block_->IsJoinEntry());
|
||||
auto const join = current_block_->AsJoinEntry();
|
||||
|
||||
// All block S-expressions are of the form (Block B# inst...), so skip
|
||||
// the first two entries and check for Phi definitions.
|
||||
for (intptr_t i = 2, n = list->Length(); i < n; i++) {
|
||||
auto const def_sexp = CheckTaggedList(Retrieve(list, i), "def");
|
||||
if (def_sexp == nullptr) return i;
|
||||
auto const phi_sexp = CheckTaggedList(Retrieve(def_sexp, 2), "Phi");
|
||||
if (phi_sexp == nullptr) return i;
|
||||
|
||||
// Phi S-expressions are of the form (Phi value...). Since we use
|
||||
// FlowGraph::AddPhi to create the Phi node, which takes exactly two
|
||||
// definitions for the Phi inputs, error if we see more than two.
|
||||
// We can change AddPhi to take a variable number of definition arguments
|
||||
// if we ever run into the case where there are more than two.
|
||||
if (phi_sexp->Length() > 3) {
|
||||
StoreError(phi_sexp, "phi nodes with more than two inputs unhandled");
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr_t left_index;
|
||||
if (!ParseSSATemp(CheckSymbol(Retrieve(phi_sexp, 1)), &left_index)) {
|
||||
return -1;
|
||||
}
|
||||
bool has_pending_left = false;
|
||||
Definition* left_def = definition_map_.LookupValue(left_index);
|
||||
if (left_def == nullptr) {
|
||||
left_def = flow_graph_->constant_null();
|
||||
has_pending_left = true;
|
||||
}
|
||||
|
||||
intptr_t right_index;
|
||||
if (!ParseSSATemp(CheckSymbol(Retrieve(phi_sexp, 2)), &right_index)) {
|
||||
return -1;
|
||||
}
|
||||
bool has_pending_right = false;
|
||||
Definition* right_def = definition_map_.LookupValue(right_index);
|
||||
if (right_def == nullptr) {
|
||||
right_def = flow_graph_->constant_null();
|
||||
has_pending_right = true;
|
||||
}
|
||||
|
||||
auto const phi = flow_graph_->AddPhi(join, left_def, right_def);
|
||||
if (has_pending_left) AddPendingValue(left_index, phi->InputAt(0));
|
||||
if (has_pending_right) AddPendingValue(right_index, phi->InputAt(1));
|
||||
|
||||
if (!ParseDefinitionWithParsedBody(def_sexp, phi)) return -1;
|
||||
}
|
||||
|
||||
StoreError(list, "block is empty or contains only Phi definitions");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FlowGraphDeserializer::ParseBlockContents(SExpList* list) {
|
||||
ASSERT(current_block_ != nullptr);
|
||||
// All blocks are of the form (Block B# inst*), so the instructions start
|
||||
// at the second position of the S-expression.
|
||||
intptr_t pos = 2;
|
||||
|
||||
// TODO(sstrickl): Handle phis appropriately. Earlier attempts changed
|
||||
// the serialization to separate them from the other definitions, but
|
||||
// we can also just check for them since they always appear as the first
|
||||
// definitions in a serialized JoinEntry block.
|
||||
if (auto const join = current_block_->AsJoinEntry()) {
|
||||
pos = ParsePhis(list, pos);
|
||||
if (pos < 2) return false;
|
||||
}
|
||||
|
||||
for (intptr_t i = pos; i < list->Length(); i++) {
|
||||
auto const entry = CheckTaggedList(Retrieve(list, i));
|
||||
|
@ -474,6 +533,82 @@ Instruction* FlowGraphDeserializer::ParseInstruction(SExpList* list) {
|
|||
return inst;
|
||||
}
|
||||
|
||||
FunctionEntryInstr* FlowGraphDeserializer::HandleFunctionEntry(
|
||||
SExpList* sexp,
|
||||
const CommonEntryInfo& info) {
|
||||
ASSERT(flow_graph_ != nullptr);
|
||||
auto const graph = flow_graph_->graph_entry();
|
||||
auto const block = new (zone())
|
||||
FunctionEntryInstr(graph, info.block_id, info.try_index, info.deopt_id);
|
||||
graph->AddDominatedBlock(block);
|
||||
current_block_ = block;
|
||||
if (!ParseInitialDefinitions(sexp)) return nullptr;
|
||||
return block;
|
||||
}
|
||||
|
||||
GraphEntryInstr* FlowGraphDeserializer::HandleGraphEntry(
|
||||
SExpList* sexp,
|
||||
const CommonEntryInfo& info) {
|
||||
auto const name_sexp = CheckSymbol(Retrieve(sexp, 1));
|
||||
// TODO(sstrickl): If the FlowGraphDeserializer was constructed with a
|
||||
// non-null ParsedFunction, we should check that the name matches here.
|
||||
// If not, then we should create an appropriate ParsedFunction here.
|
||||
if (name_sexp == nullptr) return nullptr;
|
||||
|
||||
intptr_t osr_id = Compiler::kNoOSRDeoptId;
|
||||
if (auto const osr_id_sexp = CheckInteger(sexp->ExtraLookupValue("osr_id"))) {
|
||||
osr_id = osr_id_sexp->value();
|
||||
}
|
||||
|
||||
ASSERT(parsed_function_ != nullptr);
|
||||
return new (zone()) GraphEntryInstr(*parsed_function_, osr_id, info.deopt_id);
|
||||
}
|
||||
|
||||
JoinEntryInstr* FlowGraphDeserializer::HandleJoinEntry(
|
||||
SExpList* sexp,
|
||||
const CommonEntryInfo& info) {
|
||||
return new (zone())
|
||||
JoinEntryInstr(info.block_id, info.try_index, info.deopt_id);
|
||||
}
|
||||
|
||||
TargetEntryInstr* FlowGraphDeserializer::HandleTargetEntry(
|
||||
SExpList* sexp,
|
||||
const CommonEntryInfo& info) {
|
||||
return new (zone())
|
||||
TargetEntryInstr(info.block_id, info.try_index, info.deopt_id);
|
||||
}
|
||||
|
||||
BranchInstr* FlowGraphDeserializer::HandleBranch(SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
auto const comp_sexp = CheckTaggedList(Retrieve(sexp, 1));
|
||||
auto const comp_inst = ParseInstruction(comp_sexp);
|
||||
if (comp_inst == nullptr) return nullptr;
|
||||
if (!comp_inst->IsComparison()) {
|
||||
StoreError(sexp->At(1), "expected comparison instruction");
|
||||
return nullptr;
|
||||
}
|
||||
auto const comparison = comp_inst->AsComparison();
|
||||
|
||||
auto const true_block = FetchBlock(CheckSymbol(Retrieve(sexp, 2)));
|
||||
if (true_block == nullptr) return nullptr;
|
||||
if (!true_block->IsTargetEntry()) {
|
||||
StoreError(sexp->At(2), "true successor is not a target block");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const false_block = FetchBlock(CheckSymbol(Retrieve(sexp, 3)));
|
||||
if (false_block == nullptr) return nullptr;
|
||||
if (!false_block->IsTargetEntry()) {
|
||||
StoreError(sexp->At(3), "false successor is not a target block");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const branch = new (zone()) BranchInstr(comparison, info.deopt_id);
|
||||
*branch->true_successor_address() = true_block->AsTargetEntry();
|
||||
*branch->false_successor_address() = false_block->AsTargetEntry();
|
||||
return branch;
|
||||
}
|
||||
|
||||
CheckStackOverflowInstr* FlowGraphDeserializer::HandleCheckStackOverflow(
|
||||
SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
|
@ -499,6 +634,25 @@ CheckStackOverflowInstr* FlowGraphDeserializer::HandleCheckStackOverflow(
|
|||
loop_depth, info.deopt_id, kind);
|
||||
}
|
||||
|
||||
ConstantInstr* FlowGraphDeserializer::HandleConstant(
|
||||
SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
Object& obj = Object::ZoneHandle(zone());
|
||||
if (!ParseDartValue(Retrieve(sexp, 1), &obj)) return nullptr;
|
||||
return new (zone()) ConstantInstr(obj, info.token_pos);
|
||||
}
|
||||
|
||||
GotoInstr* FlowGraphDeserializer::HandleGoto(SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
auto const block = FetchBlock(CheckSymbol(Retrieve(sexp, 1)));
|
||||
if (block == nullptr) return nullptr;
|
||||
if (!block->IsJoinEntry()) {
|
||||
StoreError(sexp->At(1), "target of goto must be join entry");
|
||||
return nullptr;
|
||||
}
|
||||
return new (zone()) GotoInstr(block->AsJoinEntry(), info.deopt_id);
|
||||
}
|
||||
|
||||
ParameterInstr* FlowGraphDeserializer::HandleParameter(
|
||||
SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
|
@ -509,6 +663,16 @@ ParameterInstr* FlowGraphDeserializer::HandleParameter(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
PushArgumentInstr* FlowGraphDeserializer::HandlePushArgument(
|
||||
SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
auto const val = ParseValue(Retrieve(sexp, 1));
|
||||
if (val == nullptr) return nullptr;
|
||||
auto const push = new (zone()) PushArgumentInstr(val);
|
||||
pushed_stack_.Add(push);
|
||||
return push;
|
||||
}
|
||||
|
||||
ReturnInstr* FlowGraphDeserializer::HandleReturn(SExpList* list,
|
||||
const CommonInstrInfo& info) {
|
||||
Value* val = ParseValue(Retrieve(list, 1));
|
||||
|
@ -531,6 +695,59 @@ SpecialParameterInstr* FlowGraphDeserializer::HandleSpecialParameter(
|
|||
SpecialParameterInstr(kind, info.deopt_id, current_block_);
|
||||
}
|
||||
|
||||
StaticCallInstr* FlowGraphDeserializer::HandleStaticCall(
|
||||
SExpList* sexp,
|
||||
const CommonInstrInfo& info) {
|
||||
auto& function = Function::ZoneHandle(zone());
|
||||
auto const function_sexp = CheckTaggedList(Retrieve(sexp, 1), "Function");
|
||||
if (!ParseDartValue(function_sexp, &function)) return nullptr;
|
||||
|
||||
intptr_t type_args_len = 0;
|
||||
if (auto const type_args_len_sexp =
|
||||
CheckInteger(sexp->ExtraLookupValue("type_args_len"))) {
|
||||
type_args_len = type_args_len_sexp->value();
|
||||
}
|
||||
|
||||
Array& argument_names = Array::ZoneHandle(zone());
|
||||
if (auto const arg_names_sexp =
|
||||
CheckList(sexp->ExtraLookupValue("arg_names"))) {
|
||||
argument_names = Array::New(arg_names_sexp->Length(), Heap::kOld);
|
||||
for (intptr_t i = 0, n = arg_names_sexp->Length(); i < n; i++) {
|
||||
auto name_sexp = CheckString(Retrieve(arg_names_sexp, i));
|
||||
if (name_sexp == nullptr) return nullptr;
|
||||
tmp_string_ = String::New(name_sexp->value(), Heap::kOld);
|
||||
argument_names.SetAt(i, tmp_string_);
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t args_len = 0;
|
||||
if (auto const args_len_sexp =
|
||||
CheckInteger(sexp->ExtraLookupValue("args_len"))) {
|
||||
args_len = args_len_sexp->value();
|
||||
}
|
||||
auto const arguments = FetchPushedArguments(sexp, type_args_len + args_len);
|
||||
if (arguments == nullptr) return nullptr;
|
||||
|
||||
intptr_t call_count = 0;
|
||||
if (auto const call_count_sexp =
|
||||
CheckInteger(sexp->ExtraLookupValue("call_count"))) {
|
||||
call_count = call_count_sexp->value();
|
||||
}
|
||||
|
||||
auto rebind_rule = ICData::kInstance;
|
||||
if (auto const rebind_sexp =
|
||||
CheckSymbol(sexp->ExtraLookupValue("rebind_rule"))) {
|
||||
if (!ICData::RebindRuleFromCString(rebind_sexp->value(), &rebind_rule)) {
|
||||
StoreError(rebind_sexp, "unknown rebind rule value");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return new (zone())
|
||||
StaticCallInstr(info.token_pos, function, type_args_len, argument_names,
|
||||
arguments, info.deopt_id, call_count, rebind_rule);
|
||||
}
|
||||
|
||||
Value* FlowGraphDeserializer::ParseValue(SExpression* sexp) {
|
||||
auto name = sexp->AsSymbol();
|
||||
CompileType* type = nullptr;
|
||||
|
@ -549,7 +766,7 @@ Value* FlowGraphDeserializer::ParseValue(SExpression* sexp) {
|
|||
auto const def = definition_map_.LookupValue(index);
|
||||
Value* val;
|
||||
if (def == nullptr) {
|
||||
val = AddPendingValue(index);
|
||||
val = AddNewPendingValue(index);
|
||||
} else {
|
||||
val = new (zone()) Value(def);
|
||||
}
|
||||
|
@ -619,8 +836,13 @@ Environment* FlowGraphDeserializer::ParseEnvironment(SExpList* list) {
|
|||
StoreError(sym, "no definition found for environment use");
|
||||
return nullptr;
|
||||
}
|
||||
} else if (ParseSymbolAsPrefixedInt(sym, 'a', &index)) {
|
||||
if (index >= pushed_stack_.length()) {
|
||||
StoreError(sym, "out of range index for pushed argument");
|
||||
return nullptr;
|
||||
}
|
||||
def = pushed_stack_.At(index);
|
||||
} else {
|
||||
// TODO(sstrickl): Handle PushArgument references.
|
||||
StoreError(sym, "unexpected name in env list");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -635,10 +857,20 @@ bool FlowGraphDeserializer::ParseDartValue(SExpression* sexp, Object* out) {
|
|||
if (sexp == nullptr) return false;
|
||||
*out = Object::null();
|
||||
|
||||
// We'll use the null value in *out as a marker later, so go ahead and exit
|
||||
// early if we parse one.
|
||||
if (auto const sym = sexp->AsSymbol()) {
|
||||
// We'll use the null value in *out as a marker later, so go ahead and exit
|
||||
// early if we parse one.
|
||||
if (strcmp(sym->value(), "null") == 0) return true;
|
||||
|
||||
// The only other symbols that should appear in Dart value position are
|
||||
// names of constant definitions.
|
||||
if (auto const val = ParseValue(sym)) {
|
||||
if (!val->BindsToConstant()) {
|
||||
StoreError(sym, "not a reference to a constant definition");
|
||||
return false;
|
||||
}
|
||||
*out = val->BoundConstant().raw();
|
||||
}
|
||||
}
|
||||
|
||||
// Other instance values may need to be canonicalized, so do that before
|
||||
|
@ -649,8 +881,8 @@ bool FlowGraphDeserializer::ParseDartValue(SExpression* sexp, Object* out) {
|
|||
auto const cid_sexp = CheckInteger(Retrieve(list, 1));
|
||||
if (cid_sexp == nullptr) return false;
|
||||
ClassTable* table = thread()->isolate()->class_table();
|
||||
if (!table->IsValidIndex(cid_sexp->value())) {
|
||||
StoreError(cid_sexp, "no class found for cid");
|
||||
if (!table->HasValidClassAt(cid_sexp->value())) {
|
||||
StoreError(cid_sexp, "no valid class found for cid");
|
||||
return false;
|
||||
}
|
||||
*out = table->At(cid_sexp->value());
|
||||
|
@ -664,17 +896,52 @@ bool FlowGraphDeserializer::ParseDartValue(SExpression* sexp, Object* out) {
|
|||
if (!ParseDartValue(ta_sexp, &type_args)) return false;
|
||||
}
|
||||
*out = Type::New(cls, type_args, TokenPosition::kNoSource, Heap::kOld);
|
||||
// Need to set this for canonicalization.
|
||||
// Need to set this for canonicalization. We ensure in the serializer
|
||||
// that only finalized types are successfully serialized.
|
||||
Type::Cast(*out).SetIsFinalized();
|
||||
}
|
||||
// TODO(sstrickl): Handle types not derived from classes.
|
||||
} else if (strcmp(tag->value(), "TypeArguments") == 0) {
|
||||
*out = TypeArguments::New(list->Length() - 1, Heap::kOld);
|
||||
auto& typ = AbstractType::Handle(zone());
|
||||
for (intptr_t i = 1; i < list->Length(); i++) {
|
||||
if (!ParseDartValue(Retrieve(list, i), &typ)) return false;
|
||||
TypeArguments::Cast(*out).SetTypeAt(i - 1, typ);
|
||||
auto& type_args = TypeArguments::Cast(*out);
|
||||
for (intptr_t i = 1, n = list->Length(); i < n; i++) {
|
||||
if (!ParseDartValue(Retrieve(list, i), &value_type_)) return false;
|
||||
type_args.SetTypeAt(i - 1, value_type_);
|
||||
}
|
||||
} else if (strcmp(tag->value(), "Field") == 0 ||
|
||||
strcmp(tag->value(), "Function") == 0) {
|
||||
auto const name_sexp = CheckSymbol(Retrieve(list, 1));
|
||||
if (!ParseCanonicalName(name_sexp, out)) return false;
|
||||
} else if (strcmp(tag->value(), "TypeParameter") == 0) {
|
||||
ASSERT(parsed_function_ != nullptr);
|
||||
auto const name_sexp = CheckSymbol(Retrieve(list, 1));
|
||||
if (name_sexp == nullptr) return false;
|
||||
const auto& func = parsed_function_->function();
|
||||
tmp_string_ = String::New(name_sexp->value());
|
||||
*out = func.LookupTypeParameter(tmp_string_, nullptr);
|
||||
if (out->IsNull()) {
|
||||
// Check the owning class for the function as well.
|
||||
value_class_ = func.Owner();
|
||||
*out = value_class_.LookupTypeParameter(tmp_string_);
|
||||
}
|
||||
// We'll want a more specific error message than the generic unhandled
|
||||
// Dart value one if this failed.
|
||||
if (out->IsNull()) {
|
||||
StoreError(name_sexp, "no type parameter found for name");
|
||||
return false;
|
||||
}
|
||||
} else if (strcmp(tag->value(), "ImmutableList") == 0) {
|
||||
// Since arrays can contain arrays, we must allocate a new handle here.
|
||||
auto& arr =
|
||||
Array::Handle(zone(), Array::New(list->Length() - 1, Heap::kOld));
|
||||
for (intptr_t i = 1; i < list->Length(); i++) {
|
||||
if (!ParseDartValue(Retrieve(list, i), &value_object_)) return false;
|
||||
arr.SetAt(i - 1, value_object_);
|
||||
}
|
||||
arr.MakeImmutable();
|
||||
*out = arr.raw();
|
||||
} else if (strcmp(tag->value(), "Instance") == 0) {
|
||||
if (!ParseInstance(list, reinterpret_cast<Instance*>(out))) return false;
|
||||
}
|
||||
} else if (auto const b = sexp->AsBool()) {
|
||||
*out = Bool::Get(b->value()).raw();
|
||||
|
@ -712,6 +979,155 @@ bool FlowGraphDeserializer::ParseDartValue(SExpression* sexp, Object* out) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FlowGraphDeserializer::ParseInstance(SExpList* list, Instance* out) {
|
||||
auto const cid_sexp = CheckInteger(Retrieve(list, 1));
|
||||
if (cid_sexp == nullptr) return false;
|
||||
|
||||
auto const table = thread()->isolate()->class_table();
|
||||
if (!table->HasValidClassAt(cid_sexp->value())) {
|
||||
StoreError(cid_sexp, "cid is not valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
instance_class_ = table->At(cid_sexp->value());
|
||||
*out = Instance::New(instance_class_, Heap::kOld);
|
||||
|
||||
if (list->Length() > 2) {
|
||||
auto const fields_sexp = CheckTaggedList(Retrieve(list, 2), "Fields");
|
||||
if (fields_sexp == nullptr) return false;
|
||||
auto it = fields_sexp->ExtraIterator();
|
||||
while (auto kv = it.Next()) {
|
||||
tmp_string_ = String::New(kv->key);
|
||||
instance_field_ = instance_class_.LookupFieldAllowPrivate(
|
||||
tmp_string_, /*instance_only=*/true);
|
||||
if (instance_field_.IsNull()) {
|
||||
StoreError(list, "cannot find field %s", kv->key);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto const inst = CheckTaggedList(kv->value, "Instance")) {
|
||||
// Unsure if this will be necessary, so for now not doing fresh
|
||||
// Instance/Class handle allocations unless it is.
|
||||
StoreError(inst, "nested instances not handled yet");
|
||||
return false;
|
||||
}
|
||||
if (!ParseDartValue(kv->value, &instance_object_)) return false;
|
||||
out->SetField(instance_field_, instance_object_);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlowGraphDeserializer::ParseCanonicalName(SExpSymbol* sym, Object* obj) {
|
||||
if (sym == nullptr) return false;
|
||||
auto const name = sym->value();
|
||||
// TODO(sstrickl): No library URL, handle this better.
|
||||
if (*name == ':') {
|
||||
StoreError(sym, "expected non-empty library");
|
||||
return false;
|
||||
}
|
||||
const char* lib_end = nullptr;
|
||||
if (auto const first = strchr(name, ':')) {
|
||||
lib_end = strchr(first + 1, ':');
|
||||
if (lib_end == nullptr) lib_end = strchr(first + 1, '\0');
|
||||
} else {
|
||||
StoreError(sym, "malformed library");
|
||||
return false;
|
||||
}
|
||||
tmp_string_ =
|
||||
String::FromUTF8(reinterpret_cast<const uint8_t*>(name), lib_end - name);
|
||||
name_library_ = Library::LookupLibrary(thread(), tmp_string_);
|
||||
if (*lib_end == '\0') {
|
||||
*obj = name_library_.raw();
|
||||
return true;
|
||||
}
|
||||
const char* const class_start = lib_end + 1;
|
||||
if (*class_start == '\0') {
|
||||
StoreError(sym, "no class found after colon");
|
||||
return false;
|
||||
}
|
||||
// If classes are followed by another part, it's either a function
|
||||
// (separated by ':') or a field (separated by '.').
|
||||
const char* class_end = strchr(class_start, ':');
|
||||
if (class_end == nullptr) class_end = strchr(class_start, '.');
|
||||
if (class_end == nullptr) class_end = strchr(class_start, '\0');
|
||||
const bool empty_name = class_end == class_start;
|
||||
name_class_ = Class::null();
|
||||
if (empty_name) {
|
||||
name_class_ = name_library_.toplevel_class();
|
||||
} else {
|
||||
tmp_string_ = String::FromUTF8(
|
||||
reinterpret_cast<const uint8_t*>(class_start), class_end - class_start);
|
||||
name_class_ = name_library_.LookupClassAllowPrivate(tmp_string_);
|
||||
}
|
||||
if (name_class_.IsNull()) {
|
||||
StoreError(sym, "failure looking up class %s in library %s",
|
||||
empty_name ? "at top level" : tmp_string_.ToCString(),
|
||||
name_library_.ToCString());
|
||||
return false;
|
||||
}
|
||||
if (*class_end == '\0') {
|
||||
*obj = name_class_.raw();
|
||||
return true;
|
||||
}
|
||||
if (*class_end == '.') {
|
||||
if (class_end[1] == '\0') {
|
||||
StoreError(sym, "no field name found after period");
|
||||
return false;
|
||||
}
|
||||
const char* const field_start = class_end + 1;
|
||||
const char* field_end = strchr(field_start, '\0');
|
||||
tmp_string_ = String::FromUTF8(
|
||||
reinterpret_cast<const uint8_t*>(field_start), field_end - field_start);
|
||||
name_field_ = name_class_.LookupFieldAllowPrivate(tmp_string_);
|
||||
if (name_field_.IsNull()) {
|
||||
StoreError(sym, "failure looking up field %s in class %s",
|
||||
tmp_string_.ToCString(),
|
||||
empty_name ? "at top level" : name_class_.ToCString());
|
||||
return false;
|
||||
}
|
||||
*obj = name_field_.raw();
|
||||
return true;
|
||||
}
|
||||
if (class_end[1] == '\0') {
|
||||
StoreError(sym, "no function name found after final colon");
|
||||
return false;
|
||||
}
|
||||
const char* func_start = class_end + 1;
|
||||
name_function_ = Function::null();
|
||||
while (true) {
|
||||
const char* func_end = strchr(func_start, ':');
|
||||
// Special case for getters/setters, where they are prefixed with "get:"
|
||||
// or "set:", as those colons should not be used as separators.
|
||||
if ((func_end != nullptr) && (func_end - func_start == 3) &&
|
||||
(strncmp(func_start, "get", 3) == 0 ||
|
||||
strncmp(func_start, "set", 3) == 0)) {
|
||||
func_end = strchr(func_end + 1, ':');
|
||||
}
|
||||
if (func_end == nullptr) func_end = strchr(func_start, '\0');
|
||||
tmp_string_ = String::FromUTF8(reinterpret_cast<const uint8_t*>(func_start),
|
||||
func_end - func_start);
|
||||
if (!name_function_.IsNull()) {
|
||||
StoreError(sym, "no handling for local functions");
|
||||
return false;
|
||||
}
|
||||
name_function_ = name_class_.LookupFunctionAllowPrivate(tmp_string_);
|
||||
if (name_function_.IsNull()) {
|
||||
StoreError(sym, "failure looking up function %s in class %s",
|
||||
tmp_string_.ToCString(), name_class_.ToCString());
|
||||
return false;
|
||||
}
|
||||
if (func_end[0] == '\0') break;
|
||||
if (func_end[1] == '\0') {
|
||||
StoreError(sym, "no function name found after final colon");
|
||||
return false;
|
||||
}
|
||||
func_start = func_end + 1;
|
||||
}
|
||||
*obj = name_function_.raw();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlowGraphDeserializer::ParseBlockId(SExpSymbol* sym, intptr_t* out) {
|
||||
return ParseSymbolAsPrefixedInt(sym, 'B', out);
|
||||
}
|
||||
|
@ -744,17 +1160,21 @@ bool FlowGraphDeserializer::ParseSymbolAsPrefixedInt(SExpSymbol* sym,
|
|||
return true;
|
||||
}
|
||||
|
||||
Value* FlowGraphDeserializer::AddPendingValue(intptr_t index) {
|
||||
Value* FlowGraphDeserializer::AddNewPendingValue(intptr_t index) {
|
||||
ASSERT(flow_graph_ != nullptr);
|
||||
auto const val = new (zone()) Value(flow_graph_->constant_null());
|
||||
AddPendingValue(index, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void FlowGraphDeserializer::AddPendingValue(intptr_t index, Value* val) {
|
||||
ASSERT(!definition_map_.HasKey(index));
|
||||
auto value_list = values_map_.LookupValue(index);
|
||||
if (value_list == nullptr) {
|
||||
value_list = new (zone()) ZoneGrowableArray<Value*>(zone(), 2);
|
||||
values_map_.Insert(index, value_list);
|
||||
}
|
||||
auto const val = new (zone()) Value(flow_graph_->constant_null());
|
||||
value_list->Add(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void FlowGraphDeserializer::FixPendingValues(intptr_t index, Definition* def) {
|
||||
|
@ -767,6 +1187,32 @@ void FlowGraphDeserializer::FixPendingValues(intptr_t index, Definition* def) {
|
|||
}
|
||||
}
|
||||
|
||||
PushArgumentsArray* FlowGraphDeserializer::FetchPushedArguments(SExpList* list,
|
||||
intptr_t len) {
|
||||
auto const stack_len = pushed_stack_.length();
|
||||
if (len > stack_len) {
|
||||
StoreError(list, "expected %" Pd " pushed arguments, only %" Pd " on stack",
|
||||
len, stack_len);
|
||||
}
|
||||
auto const arr = new (zone()) PushArgumentsArray(zone(), len);
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
arr->InsertAt(0, pushed_stack_.RemoveLast());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
BlockEntryInstr* FlowGraphDeserializer::FetchBlock(SExpSymbol* sym) {
|
||||
if (sym == nullptr) return nullptr;
|
||||
intptr_t block_id;
|
||||
if (!ParseBlockId(sym, &block_id)) return nullptr;
|
||||
auto const entry = block_map_.LookupValue(block_id);
|
||||
if (entry == nullptr) {
|
||||
StoreError(sym, "reference to undefined block");
|
||||
return nullptr;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
#define BASE_CHECK_DEF(name, type) \
|
||||
SExp##name* FlowGraphDeserializer::Check##name(SExpression* sexp) { \
|
||||
if (sexp == nullptr) return nullptr; \
|
||||
|
|
|
@ -22,15 +22,17 @@ namespace dart {
|
|||
// Deserializes FlowGraphs from S-expressions.
|
||||
class FlowGraphDeserializer : ValueObject {
|
||||
public:
|
||||
// Returns the first instruction that is guaranteed not to be handled by
|
||||
// the current implementation of the FlowGraphDeserializer. This way,
|
||||
// we can filter out graphs that are guaranteed not to be deserializable
|
||||
// before going through the round-trip serialization process.
|
||||
// Adds to the given array all the instructions in the flow graph that are
|
||||
// guaranteed not to be handled by the current implementation of the
|
||||
// FlowGraphDeserializer. This way, we can filter out graphs that are
|
||||
// guaranteed not to be deserializable before going through the round-trip
|
||||
// serialization process.
|
||||
//
|
||||
// Note that there may be other reasons that the deserializer may fail on
|
||||
// a given flow graph, so getting back nullptr here is necessary, but not
|
||||
// a given flow graph, so no new members of the array is necessary, but not
|
||||
// sufficient, for a successful round-trip pass.
|
||||
static Instruction* FirstUnhandledInstruction(const FlowGraph* graph);
|
||||
static void AllUnhandledInstructions(const FlowGraph* graph,
|
||||
GrowableArray<Instruction*>* out);
|
||||
|
||||
// Takes the FlowGraph from [state] and runs it through the serializer
|
||||
// and deserializer. If the deserializer successfully deserializes the
|
||||
|
@ -47,7 +49,20 @@ class FlowGraphDeserializer : ValueObject {
|
|||
parsed_function_(pf),
|
||||
block_map_(zone_),
|
||||
definition_map_(zone_),
|
||||
values_map_(zone_) {
|
||||
values_map_(zone_),
|
||||
pushed_stack_(zone_, 2),
|
||||
instance_class_(Class::Handle(zone)),
|
||||
instance_field_(Field::Handle(zone)),
|
||||
instance_object_(Object::Handle(zone)),
|
||||
name_class_(Class::Handle(zone)),
|
||||
name_field_(Field::Handle(zone)),
|
||||
name_function_(Function::Handle(zone)),
|
||||
name_library_(Library::Handle(zone)),
|
||||
value_class_(Class::Handle(zone)),
|
||||
value_object_(Object::Handle(zone)),
|
||||
value_type_(AbstractType::Handle(zone)),
|
||||
value_type_args_(TypeArguments::Handle(zone)),
|
||||
tmp_string_(String::Handle(zone)) {
|
||||
// See canonicalization comment in ParseDartValue as to why this is
|
||||
// currently necessary.
|
||||
ASSERT(thread->zone() == zone);
|
||||
|
@ -66,17 +81,22 @@ class FlowGraphDeserializer : ValueObject {
|
|||
#define FOR_EACH_HANDLED_BLOCK_TYPE_IN_DESERIALIZER(M) \
|
||||
M(FunctionEntry) \
|
||||
M(GraphEntry) \
|
||||
M(JoinEntry) \
|
||||
M(TargetEntry)
|
||||
|
||||
#define FOR_EACH_HANDLED_INSTRUCTION_IN_DESERIALIZER(M) \
|
||||
M(Branch) \
|
||||
M(CheckStackOverflow) \
|
||||
M(Constant) \
|
||||
M(Goto) \
|
||||
M(PushArgument) \
|
||||
M(Parameter) \
|
||||
M(Return) \
|
||||
M(SpecialParameter)
|
||||
M(SpecialParameter) \
|
||||
M(StaticCall)
|
||||
|
||||
// Helper method for FirstUnhandledInstruction that returns whether a given
|
||||
// object should be (de)serializable. Any work done on ParseDartValue may
|
||||
// require changing this method.
|
||||
// Helper methods for AllUnhandledInstructions.
|
||||
static bool IsHandledInstruction(Instruction* inst);
|
||||
static bool IsHandledConstant(const Object& obj);
|
||||
|
||||
// **GENERAL DESIGN NOTES FOR PARSING METHODS**
|
||||
|
@ -103,6 +123,19 @@ class FlowGraphDeserializer : ValueObject {
|
|||
bool ParseConstantPool(SExpList* pool);
|
||||
bool ParseEntries(SExpList* list);
|
||||
|
||||
struct CommonEntryInfo {
|
||||
intptr_t block_id;
|
||||
intptr_t try_index;
|
||||
intptr_t deopt_id;
|
||||
};
|
||||
|
||||
#define HANDLER_DECL(name) \
|
||||
name##Instr* Handle##name(SExpList* list, const CommonEntryInfo& info);
|
||||
|
||||
FOR_EACH_HANDLED_BLOCK_TYPE_IN_DESERIALIZER(HANDLER_DECL);
|
||||
|
||||
#undef HANDLER_DECL
|
||||
|
||||
// Block parsing is split into two passes. This pass checks the
|
||||
// block ID and other extra information needed for certain block types.
|
||||
// In addition, it parses initial definitions found in the entry list.
|
||||
|
@ -112,6 +145,12 @@ class FlowGraphDeserializer : ValueObject {
|
|||
// Expects [current_block_] to be set before calling.
|
||||
bool ParseInitialDefinitions(SExpList* list);
|
||||
|
||||
// Expects [current_block_] to be set before calling.
|
||||
// Takes the tagged list to parse and the index where parsing should start.
|
||||
// Returns the index of the first non-Phi instruction or definition or -1 on
|
||||
// error.
|
||||
intptr_t ParsePhis(SExpList* list, intptr_t pos);
|
||||
|
||||
// Parses the instructions in the body of a block. [current_block_] must be
|
||||
// set before calling.
|
||||
bool ParseBlockContents(SExpList* list);
|
||||
|
@ -161,7 +200,16 @@ class FlowGraphDeserializer : ValueObject {
|
|||
|
||||
// Parsing functions for which there are no good distinguished error
|
||||
// values, so use out parameters and a boolean return instead.
|
||||
|
||||
// Parses a Dart value and returns a canonicalized result.
|
||||
bool ParseDartValue(SExpression* sexp, Object* out);
|
||||
|
||||
// Helper function for ParseDartValue for parsing instances.
|
||||
// Does not canonicalize (that is currently done in ParseDartValue), so
|
||||
// do not call this method directly.
|
||||
bool ParseInstance(SExpList* list, Instance* out);
|
||||
|
||||
bool ParseCanonicalName(SExpSymbol* sym, Object* out);
|
||||
bool ParseBlockId(SExpSymbol* sym, intptr_t* out);
|
||||
bool ParseSSATemp(SExpSymbol* sym, intptr_t* out);
|
||||
bool ParseUse(SExpSymbol* sym, intptr_t* out);
|
||||
|
@ -169,12 +217,26 @@ class FlowGraphDeserializer : ValueObject {
|
|||
|
||||
// Helper function for creating a placeholder value when the definition
|
||||
// has not yet been seen.
|
||||
Value* AddPendingValue(intptr_t index);
|
||||
Value* AddNewPendingValue(intptr_t index);
|
||||
|
||||
// Similar helper, but where we already have a created value.
|
||||
void AddPendingValue(intptr_t index, Value* val);
|
||||
|
||||
// Helper function for rebinding pending values once the definition has
|
||||
// been located.
|
||||
void FixPendingValues(intptr_t index, Definition* def);
|
||||
|
||||
// Creates a PushArgumentsArray of size [len] from [pushed_stack_] if there
|
||||
// are enough and pops the fetched arguments from the stack.
|
||||
//
|
||||
// The [sexp] argument should be the serialized form of the instruction that
|
||||
// needs the pushed arguments and is only used for error reporting.
|
||||
PushArgumentsArray* FetchPushedArguments(SExpList* sexp, intptr_t len);
|
||||
|
||||
// Retrieves the block corresponding to the given block ID symbol from
|
||||
// [block_map_]. Assumes all blocks have had their header parsed.
|
||||
BlockEntryInstr* FetchBlock(SExpSymbol* sym);
|
||||
|
||||
// Utility functions for checking the shape of an S-expression.
|
||||
// If these functions return nullptr for a non-null argument, they have the
|
||||
// side effect of setting the stored error message.
|
||||
|
@ -219,6 +281,26 @@ class FlowGraphDeserializer : ValueObject {
|
|||
// values that were parsed prior to the corresponding definition being found.
|
||||
IntMap<ZoneGrowableArray<Value*>*> values_map_;
|
||||
|
||||
// Stack of currently pushed arguments, used by environment parsing and calls.
|
||||
GrowableArray<PushArgumentInstr*> pushed_stack_;
|
||||
|
||||
// Temporary handles used by functions that are not re-entrant or where the
|
||||
// handle is not live after the re-entrant call. Comments show which handles
|
||||
// are expected to only be used within a single method.
|
||||
Class& instance_class_; // ParseInstance
|
||||
Field& instance_field_; // ParseInstance
|
||||
Object& instance_object_; // ParseInstance
|
||||
Class& name_class_; // ParseCanonicalName
|
||||
Field& name_field_; // ParseCanonicalName
|
||||
Function& name_function_; // ParseCanonicalName
|
||||
Library& name_library_; // ParseCanonicalName
|
||||
Class& value_class_; // ParseDartValue
|
||||
Object& value_object_; // ParseDartValue
|
||||
AbstractType& value_type_; // ParseDartValue
|
||||
TypeArguments& value_type_args_; // ParseDartValue
|
||||
// Uses of string handles tend to be immediate, so we only need one.
|
||||
String& tmp_string_;
|
||||
|
||||
// Stores a message appropriate to surfacing to the user when an error
|
||||
// occurs.
|
||||
const char* error_message_ = nullptr;
|
||||
|
|
|
@ -126,7 +126,9 @@ void FlowGraphSerializer::SerializeCanonicalName(TextBuffer* b,
|
|||
ASSERT(!obj.IsNull());
|
||||
if (obj.IsFunction()) {
|
||||
const auto& function = Function::Cast(obj);
|
||||
tmp_string_ = function.UserVisibleName();
|
||||
tmp_string_ = function.name();
|
||||
// We only want private keys removed, no other changes.
|
||||
tmp_string_ = String::RemovePrivateKey(tmp_string_);
|
||||
const char* function_name = tmp_string_.ToCString();
|
||||
// If this function is an inner closure then the parent points to its
|
||||
// containing function, which will also be part of the canonical name.
|
||||
|
@ -857,8 +859,8 @@ void NativeCallInstr::AddOperandsToSExpression(SExpList* sexp,
|
|||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void TemplateDartCall<0l>::AddExtraInfoToSExpression(
|
||||
template <intptr_t kInputCount>
|
||||
void TemplateDartCall<kInputCount>::AddExtraInfoToSExpression(
|
||||
SExpList* sexp,
|
||||
FlowGraphSerializer* s) const {
|
||||
Instruction::AddExtraInfoToSExpression(sexp, s);
|
||||
|
@ -866,17 +868,26 @@ void TemplateDartCall<0l>::AddExtraInfoToSExpression(
|
|||
s->AddExtraInteger(sexp, "type_args_len", type_args_len());
|
||||
}
|
||||
s->AddExtraInteger(sexp, "args_len", ArgumentCountWithoutTypeArgs());
|
||||
if (this->CallCount() > 0 || FLAG_verbose_flow_graph_serialization) {
|
||||
s->AddExtraInteger(sexp, "call_count", this->CallCount());
|
||||
}
|
||||
const auto& arg_names = argument_names();
|
||||
if (!arg_names.IsNull()) {
|
||||
auto arg_names_sexp = new (s->zone()) SExpList(s->zone());
|
||||
auto& str = String::Handle(s->zone());
|
||||
for (intptr_t i = 0; i < arg_names.Length(); i++) {
|
||||
str = String::RawCast(arg_names.At(i));
|
||||
arg_names_sexp->Add(s->ObjectToSExp(str));
|
||||
}
|
||||
sexp->AddExtra("arg_names", arg_names_sexp);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void TemplateDartCall<1l>::AddExtraInfoToSExpression(
|
||||
SExpList* sexp,
|
||||
FlowGraphSerializer* s) const {
|
||||
Instruction::AddExtraInfoToSExpression(sexp, s);
|
||||
if (type_args_len() > 0 || FLAG_verbose_flow_graph_serialization) {
|
||||
s->AddExtraInteger(sexp, "type_args_len", type_args_len());
|
||||
}
|
||||
s->AddExtraInteger(sexp, "args_len", ArgumentCountWithoutTypeArgs());
|
||||
void ClosureCallInstr::AddExtraInfoToSExpression(SExpList* sexp,
|
||||
FlowGraphSerializer* s) const {
|
||||
// For now, just here to ensure TemplateDartCall<1>::AddExtraInfoToSExpression
|
||||
// gets instantiated.
|
||||
TemplateDartCall<1>::AddExtraInfoToSExpression(sexp, s);
|
||||
}
|
||||
|
||||
void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp,
|
||||
|
@ -886,6 +897,18 @@ void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp,
|
|||
}
|
||||
}
|
||||
|
||||
void StaticCallInstr::AddExtraInfoToSExpression(SExpList* sexp,
|
||||
FlowGraphSerializer* s) const {
|
||||
TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s);
|
||||
|
||||
if (rebind_rule_ != ICData::kInstance ||
|
||||
FLAG_verbose_flow_graph_serialization) {
|
||||
auto const str = ICData::RebindRuleToCString(rebind_rule_);
|
||||
ASSERT(str != nullptr);
|
||||
s->AddExtraSymbol(sexp, "rebind_rule", str);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceCallInstr::AddOperandsToSExpression(SExpList* sexp,
|
||||
FlowGraphSerializer* s) const {
|
||||
if (auto const target = s->DartValueToSExp(interface_target())) {
|
||||
|
@ -1080,7 +1103,7 @@ SExpression* Environment::ToSExpression(FlowGraphSerializer* s) const {
|
|||
|
||||
for (intptr_t i = 0; i < values_.length(); ++i) {
|
||||
if (values_[i]->definition()->IsPushArgument()) {
|
||||
s->AddSymbol(sexp, OS::SCreate(s->zone(), "arg[%" Pd "]", arg_count++));
|
||||
s->AddSymbol(sexp, OS::SCreate(s->zone(), "a%" Pd "", arg_count++));
|
||||
} else {
|
||||
sexp->Add(values_[i]->ToSExpression(s));
|
||||
}
|
||||
|
|
|
@ -13624,6 +13624,29 @@ void ICData::AddDeoptReason(DeoptReasonId reason) const {
|
|||
}
|
||||
}
|
||||
|
||||
const char* ICData::RebindRuleToCString(RebindRule r) {
|
||||
switch (r) {
|
||||
#define RULE_CASE(Name) \
|
||||
case RebindRule::k##Name: \
|
||||
return #Name;
|
||||
FOR_EACH_REBIND_RULE(RULE_CASE)
|
||||
#undef RULE_CASE
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICData::RebindRuleFromCString(const char* str, RebindRule* out) {
|
||||
#define RULE_CASE(Name) \
|
||||
if (strcmp(str, #Name) == 0) { \
|
||||
*out = RebindRule::k##Name; \
|
||||
return true; \
|
||||
}
|
||||
FOR_EACH_REBIND_RULE(RULE_CASE)
|
||||
#undef RULE_CASE
|
||||
return false;
|
||||
}
|
||||
|
||||
ICData::RebindRule ICData::rebind_rule() const {
|
||||
return (ICData::RebindRule)RebindRuleBits::decode(raw_ptr()->state_bits_);
|
||||
}
|
||||
|
|
|
@ -1783,15 +1783,22 @@ class ICData : public Object {
|
|||
|
||||
// Call site classification that is helpful for hot-reload. Call sites with
|
||||
// different `RebindRule` have to be rebound differently.
|
||||
#define FOR_EACH_REBIND_RULE(V) \
|
||||
V(Instance) \
|
||||
V(NoRebind) \
|
||||
V(NSMDispatch) \
|
||||
V(Optimized) \
|
||||
V(Static) \
|
||||
V(Super)
|
||||
|
||||
enum RebindRule {
|
||||
kInstance,
|
||||
kNoRebind,
|
||||
kNSMDispatch,
|
||||
kOptimized,
|
||||
kStatic,
|
||||
kSuper,
|
||||
kNumRebindRules,
|
||||
#define REBIND_ENUM_DEF(name) k##name,
|
||||
FOR_EACH_REBIND_RULE(REBIND_ENUM_DEF)
|
||||
#undef REBIND_ENUM_DEF
|
||||
kNumRebindRules,
|
||||
};
|
||||
static const char* RebindRuleToCString(RebindRule r);
|
||||
static bool RebindRuleFromCString(const char* str, RebindRule* out);
|
||||
RebindRule rebind_rule() const;
|
||||
void set_rebind_rule(uint32_t rebind_rule) const;
|
||||
|
||||
|
|
Loading…
Reference in a new issue