[vm/compiler] Add support for more instructions to deserializer.

Adds support for:
  AllocateObject
  CheckNull
  LoadField
  StrictCompare

Results from compiling hello world program:

* Early round trip:
  * Contains unhandled instructions: 3065
  * Failed during deserialization: 6
  * Successful round trips: 1109
* Late round trip:
  * Contains unhandled instructions: 3085
  * Failed during deserialization: 7
  * Successful round trips: 1086

Bug: https://github.com/dart-lang/sdk/issues/36882
Change-Id: I09d25ca413a9facd27ba5a1b81e72a1fd126a3cc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114860
Commit-Queue: Teagan Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Teagan Strickland 2019-09-05 10:36:13 +00:00 committed by commit-bot@chromium.org
parent 7c8c197559
commit 4525105db9
8 changed files with 776 additions and 360 deletions

View file

@ -1409,6 +1409,8 @@ class BlockEntryInstr : public Instruction {
BitVector* block_marks);
private:
friend class FlowGraphDeserializer; // Access to AddPredecessor().
virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); }
virtual void ClearPredecessors() = 0;
@ -3812,6 +3814,7 @@ class StrictCompareInstr : public TemplateComparison<2, NoThrow, Pure> {
bool AttributesEqual(Instruction* other) const;
PRINT_OPERANDS_TO_SUPPORT
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT;
private:
// True if the comparison must check for double or Mint and
@ -7756,6 +7759,8 @@ class CheckNullInstr : public TemplateDefinition<1, Throws, Pure> {
virtual Value* RedefinedValue() const;
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
private:
const TokenPosition token_pos_;
const String& function_name_;

File diff suppressed because it is too large Load diff

View file

@ -48,9 +48,9 @@ class FlowGraphDeserializer : ValueObject {
root_sexp_(ASSERT_NOTNULL(root)),
parsed_function_(pf),
block_map_(zone_),
pushed_stack_map_(zone_),
definition_map_(zone_),
values_map_(zone_),
pushed_stack_(zone_, 2),
instance_class_(Class::Handle(zone)),
instance_field_(Field::Handle(zone)),
instance_object_(Object::Handle(zone)),
@ -85,17 +85,21 @@ class FlowGraphDeserializer : ValueObject {
M(TargetEntry)
#define FOR_EACH_HANDLED_INSTRUCTION_IN_DESERIALIZER(M) \
M(AllocateObject) \
M(Branch) \
M(CheckNull) \
M(CheckStackOverflow) \
M(Constant) \
M(DebugStepCheck) \
M(Goto) \
M(PushArgument) \
M(LoadField) \
M(Parameter) \
M(PushArgument) \
M(Return) \
M(SpecialParameter) \
M(StaticCall) \
M(StoreInstanceField)
M(StoreInstanceField) \
M(StrictCompare)
// Helper methods for AllUnhandledInstructions.
static bool IsHandledInstruction(Instruction* inst);
@ -125,37 +129,38 @@ 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;
};
using PushStack = ZoneGrowableArray<PushArgumentInstr*>;
using BlockWorklist = GrowableArray<intptr_t>;
#define HANDLER_DECL(name) \
name##Instr* Handle##name(SExpList* list, const CommonEntryInfo& info);
// Starts parsing the contents of [list], where the blocks begin at position
// [pos] and [worklist] contains the blocks whose body instructions should
// be parsed first.
bool ParseBlocks(SExpList* list, intptr_t pos, BlockWorklist* worklist);
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.
// The block is added to the [block_map_] as well as returned.
BlockEntryInstr* ParseBlockHeader(SExpList* list, SExpSymbol* tag);
// Block parsing is split into two passes. This pass adds function entries
// to the flow graph and also parses initial definitions found in the Entries
// list. The block is added to the [block_map_] before returning.
BlockEntryInstr* ParseBlockHeader(SExpList* list,
intptr_t block_id,
SExpSymbol* tag);
// 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);
// Attempts to parse Phi definitions until the first non-Phi instruction.
bool ParsePhis(SExpList* list);
// Parses the instructions in the body of a block. [current_block_] must be
// set before calling.
bool ParseBlockContents(SExpList* list);
// Expects [current_block_] to be set before calling.
// Returns the position of the first non-Phi instruction in a block.
intptr_t SkipPhis(SExpList* list);
// Parses the deopt environment, Phi definitions for JoinEntrys, and the
// instructions in the body of the block. Adds the IDs of the block successors
// to the worklist, if any. [current_block_] and [pushed_stack_] must be set
// before calling.
bool ParseBlockContents(SExpList* list, BlockWorklist* worklist);
// Helper function used by ParseConstantPool, ParsePhis, and ParseDefinition.
// This handles all the extra information stored in (def ...) expressions,
@ -166,7 +171,20 @@ class FlowGraphDeserializer : ValueObject {
Definition* ParseDefinition(SExpList* list);
Instruction* ParseInstruction(SExpList* list);
struct CommonInstrInfo {
struct EntryInfo {
intptr_t block_id;
intptr_t try_index;
intptr_t deopt_id;
};
#define HANDLER_DECL(name) \
name##Instr* Deserialize##name(SExpList* list, const EntryInfo& info);
FOR_EACH_HANDLED_BLOCK_TYPE_IN_DESERIALIZER(HANDLER_DECL);
#undef HANDLER_DECL
struct InstrInfo {
intptr_t deopt_id;
TokenPosition token_pos;
};
@ -190,14 +208,25 @@ class FlowGraphDeserializer : ValueObject {
#undef HANDLE_CASE
#define HANDLER_DECL(name) \
name##Instr* Handle##name(SExpList* list, const CommonInstrInfo& info);
name##Instr* Deserialize##name(SExpList* list, const InstrInfo& info);
FOR_EACH_HANDLED_INSTRUCTION_IN_DESERIALIZER(HANDLER_DECL);
#undef HANDLER_DECL
Value* ParseValue(SExpression* sexp);
// Parses [sexp] as a value form, that is, either the binding name for
// a definition as a symbol or the form (value <name> { ... }).
// If [allow_pending], then values for definitions not already in the
// [definition_map_] will be added to the [values_map_], otherwise,
// values for definitions not yet seen cause an error to be stored and
// nullptr to be returned.
Value* ParseValue(SExpression* sexp, bool allow_pending = true);
CompileType* ParseCompileType(SExpList* list);
// Parses [list] as an environment form: a list containing either binding
// names for definitions or a# for pushed arguments (where # is the depth
// of the argument from the top of the stack). Requires [pushed_stack_] to
// be set if any references to pushed arguments are found.
Environment* ParseEnvironment(SExpList* list);
// Parsing functions for which there are no good distinguished error
@ -243,6 +272,14 @@ class FlowGraphDeserializer : ValueObject {
// [block_map_]. Assumes all blocks have had their header parsed.
BlockEntryInstr* FetchBlock(SExpSymbol* sym);
// Checks that the pushed argument stacks for all predecessors of [succ_block]
// are the same as [curr_stack]. This check ensures that we can choose an
// arbitrary predecessor's pushed argument stack when parsing [succ_block]'s
// contents. [list] is used for error reporting.
bool AreStacksConsistent(SExpList* list,
PushStack* curr_stack,
BlockEntryInstr* succ_block);
// 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.
@ -280,6 +317,12 @@ class FlowGraphDeserializer : ValueObject {
// available via [flow_graph_].
IntMap<BlockEntryInstr*> block_map_;
// Map from block IDs to pushed argument stacks. Used for PushArgument
// instructions, environment parsing, and calls during block parsing. Also
// used to check that the final pushed argument stacks for predecessor blocks
// are consistent when parsing a JoinEntry.
IntMap<PushStack*> pushed_stack_map_;
// Map from variable indexes to definitions.
IntMap<Definition*> definition_map_;
@ -287,9 +330,6 @@ 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.

View file

@ -490,8 +490,9 @@ SExpression* FlowGraphSerializer::FunctionToSExp(const Function& func) {
AddExtraSymbol(sexp, "native_name", tmp_string_.ToCString());
}
}
if (FLAG_verbose_flow_graph_serialization) {
AddExtraSymbol(sexp, "kind", Function::KindToCString(func.kind()));
if (func.kind() != RawFunction::Kind::kRegularFunction ||
FLAG_verbose_flow_graph_serialization) {
AddExtraSymbol(sexp, "kind", RawFunction::KindToCString(func.kind()));
}
function_type_args_ = func.type_parameters();
if (auto const ta_sexp = NonEmptyTypeArgumentsToSExp(function_type_args_)) {
@ -514,6 +515,10 @@ SExpression* FlowGraphSerializer::ArrayToSExp(const Array& arr) {
array_elem = arr.At(i);
sexp->Add(DartValueToSExp(array_elem));
}
array_type_args_ = arr.GetTypeArguments();
if (auto const type_args_sexp = TypeArgumentsToSExp(array_type_args_)) {
sexp->AddExtra("type_args", type_args_sexp);
}
return sexp;
}
@ -840,6 +845,15 @@ void ComparisonInstr::AddOperandsToSExpression(SExpList* sexp,
Instruction::AddOperandsToSExpression(sexp, s);
}
void StrictCompareInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (needs_number_check_ || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "needs_check", needs_number_check_);
}
}
void DoubleTestOpInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
const bool negated = kind() != Token::kEQ;
@ -1081,6 +1095,14 @@ void CheckStackOverflowInstr::AddExtraInfoToSExpression(
}
}
void CheckNullInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (!function_name_.IsNull()) {
s->AddExtraString(sexp, "function_name", function_name_.ToCString());
}
}
SExpression* Value::ToSExpression(FlowGraphSerializer* s) const {
auto name = s->UseToSExp(definition());
if (reaching_type_ == nullptr || reaching_type_ == definition()->type_ ||

View file

@ -111,6 +111,7 @@ class FlowGraphSerializer : ValueObject {
: flow_graph_(ASSERT_NOTNULL(flow_graph)),
zone_(zone),
tmp_string_(String::Handle(zone_)),
array_type_args_((TypeArguments::Handle(zone_))),
closure_context_(Context::Handle(zone_)),
closure_function_(Function::Handle(zone_)),
closure_type_args_(TypeArguments::Handle(zone_)),
@ -156,6 +157,7 @@ class FlowGraphSerializer : ValueObject {
// DartValueToSExp with a sub-element of type Object, but any call to a
// FlowGraphSerializer method that may eventually enter one of the methods
// listed below should be examined with care.
TypeArguments& array_type_args_; // ArrayToSExp
Context& closure_context_; // ClosureToSExp
Function& closure_function_; // ClosureToSExp
TypeArguments& closure_type_args_; // ClosureToSExp

View file

@ -6211,66 +6211,7 @@ RawType* Function::RedirectionType() const {
}
const char* Function::KindToCString(RawFunction::Kind kind) {
switch (kind) {
case RawFunction::kRegularFunction:
return "RegularFunction";
break;
case RawFunction::kClosureFunction:
return "ClosureFunction";
break;
case RawFunction::kImplicitClosureFunction:
return "ImplicitClosureFunction";
break;
case RawFunction::kSignatureFunction:
return "SignatureFunction";
break;
case RawFunction::kGetterFunction:
return "GetterFunction";
break;
case RawFunction::kSetterFunction:
return "SetterFunction";
break;
case RawFunction::kConstructor:
return "Constructor";
break;
case RawFunction::kImplicitGetter:
return "ImplicitGetter";
break;
case RawFunction::kImplicitSetter:
return "ImplicitSetter";
break;
case RawFunction::kImplicitStaticGetter:
return "ImplicitStaticGetter";
break;
case RawFunction::kFieldInitializer:
return "FieldInitializer";
break;
case RawFunction::kMethodExtractor:
return "MethodExtractor";
break;
case RawFunction::kNoSuchMethodDispatcher:
return "NoSuchMethodDispatcher";
break;
case RawFunction::kInvokeFieldDispatcher:
return "InvokeFieldDispatcher";
break;
case RawFunction::kIrregexpFunction:
return "IrregexpFunction";
break;
case RawFunction::kDynamicInvocationForwarder:
return "DynamicInvocationForwarder";
break;
case RawFunction::kFfiTrampoline:
return "FfiTrampoline";
break;
}
// When you add a case to this switch, please also update the observatory.
// - runtime/observatory/lib/src/models/objects/function.dart (FunctionKind)
// - runtime/observatory/lib/src/elements/function_view.dart
// (_functionKindToString)
// - runtime/observatory/lib/src/service/object.dart (stringToFunctionKind)
UNREACHABLE();
return NULL;
return RawFunction::KindToCString(kind);
}
void Function::SetRedirectionType(const Type& type) const {

View file

@ -838,33 +838,78 @@ class RawPatchClass : public RawObject {
class RawFunction : public RawObject {
public:
// When you add a new kind, please also update the observatory to account
// for the new string returned by KindToCString().
// - runtime/observatory/lib/src/models/objects/function.dart (FunctionKind)
// - runtime/observatory/lib/src/elements/function_view.dart
// (_functionKindToString)
// - runtime/observatory/lib/src/service/object.dart (stringToFunctionKind)
#define FOR_EACH_RAW_FUNCTION_KIND(V) \
/* an ordinary or operator method */ \
V(RegularFunction) \
/* a user-declared closure function */ \
V(ClosureFunction) \
/* an implicit closure (i.e., tear-off) */ \
V(ImplicitClosureFunction) \
/* a signature only without actual code */ \
V(SignatureFunction) \
/* getter functions e.g: get foo() { .. } */ \
V(GetterFunction) \
/* setter functions e.g: set foo(..) { .. } */ \
V(SetterFunction) \
/* a generative (is_static=false) or factory (is_static=true) constructor */ \
V(Constructor) \
/* an implicit getter for instance fields */ \
V(ImplicitGetter) \
/* an implicit setter for instance fields */ \
V(ImplicitSetter) \
/* represents an implicit getter for static fields with initializers */ \
V(ImplicitStaticGetter) \
/* the initialization expression for a static or instance field */ \
V(FieldInitializer) \
/* return a closure on the receiver for tear-offs */ \
V(MethodExtractor) \
/* builds an Invocation and invokes noSuchMethod */ \
V(NoSuchMethodDispatcher) \
/* invokes a field as a closure (i.e., call-through-getter) */ \
V(InvokeFieldDispatcher) \
/* a generated irregexp matcher function. */ \
V(IrregexpFunction) \
/* a forwarder which performs type checks for arguments of a dynamic call */ \
/* (i.e., those checks omitted by the caller for interface calls). */ \
V(DynamicInvocationForwarder) \
V(FfiTrampoline)
enum Kind {
kRegularFunction, // an ordinary or operator method
kClosureFunction, // a user-declared closure function
kImplicitClosureFunction, // an implicit closure (i.e., tear-off)
kSignatureFunction, // a signature only without actual code
kGetterFunction, // getter functions e.g: get foo() { .. }
kSetterFunction, // setter functions e.g: set foo(..) { .. }
kConstructor, // a generative (is_static=false) or
// factory (is_static=true) constructor
kImplicitGetter, // an implicit getter for instance fields
kImplicitSetter, // an implicit setter for instance fields
kImplicitStaticGetter, // represents an implicit getter for static
// fields with initializers
kFieldInitializer, // the initialization expression for a static
// or instance field
kMethodExtractor, // return a closure on the receiver for tear-offs
kNoSuchMethodDispatcher, // builds an Invocation and invokes noSuchMethod
kInvokeFieldDispatcher, // invokes a field as a closure (i.e.,
// call-through-getter)
kIrregexpFunction, // a generated irregexp matcher function.
kDynamicInvocationForwarder, // a forwarder which performs type checks for
// arguments of a dynamic call (i.e., those
// checks omitted by the caller for interface
// calls).
kFfiTrampoline,
#define KIND_DEFN(Name) k##Name,
FOR_EACH_RAW_FUNCTION_KIND(KIND_DEFN)
#undef KIND_DEFN
};
static const char* KindToCString(Kind k) {
switch (k) {
#define KIND_CASE(Name) \
case Kind::k##Name: \
return #Name;
FOR_EACH_RAW_FUNCTION_KIND(KIND_CASE)
#undef KIND_CASE
default:
UNREACHABLE();
return nullptr;
}
}
static bool KindFromCString(const char* str, Kind* out) {
#define KIND_CASE(Name) \
if (strcmp(str, #Name) == 0) { \
*out = Kind::k##Name; \
return true; \
}
FOR_EACH_RAW_FUNCTION_KIND(KIND_CASE)
#undef KIND_CASE
return false;
}
enum AsyncModifier {
kNoModifier = 0x0,
kAsyncBit = 0x1,

View file

@ -259,6 +259,19 @@ class Token {
return tok_str_[tok];
}
static bool FromStr(const char* str, Kind* out) {
ASSERT(str != nullptr && out != nullptr);
#define TOK_CASE(t, s, p, a) \
if (strcmp(str, tok_str_[(t)]) == 0) { \
*out = (t); \
return true; \
}
DART_TOKEN_LIST(TOK_CASE)
DART_KEYWORD_LIST(TOK_CASE)
#undef TOK_CASE
return false;
}
static int Precedence(Kind tok) {
ASSERT(tok < kNumTokens);
return precedence_[tok];