From 2e197ae4dec31172aa6ebfc8226a09768b1cedfe Mon Sep 17 00:00:00 2001 From: Teagan Strickland Date: Wed, 31 Jul 2019 11:52:44 +0000 Subject: [PATCH] [vm/compiler] Tweaks and additions to IL serializer/S-expressions. * Add a new `SExpDouble` atom and change `SExpNumber` to `SExpInteger`. * Allow for negative integers in deserialization. * Add support for `LocalVariable`s and related instructions. * Function objects are now represented by actual S-expressions generated with the new `FunctionToSExp` method. Previously, they were only represented by a symbol containing their canonical name. * The top-level tag for a serialized flow graph is now `FlowGraph`, not `function`. This avoids confusion between serialized flow graphs and serialized function references. Similarly, the old `FunctionToSExp` method is now called `FlowGraphToSExp`. * Made all SExpression* returning functions that take Object (or subclass) instances return nullptr if the passed in instance is the null object, except for ObjectToSExp, which returns the symbol `null`. * Factored out creating tags for the different kind of block/function entry and also created an `Entries` section to the top-level `FlowGraph` form that contains function entry points similar to the `Constants` one instead of inlining entries as separate elements in the `FlowGraph` form. * Additional extra information in verbose mode for some elements. Bug: https://github.com/dart-lang/sdk/issues/36882 Change-Id: Iede3865ec64f81955a87fd57b10e74d49ee8414c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/110917 Commit-Queue: Teagan Strickland Reviewed-by: Martin Kustermann --- runtime/vm/compiler/backend/il.h | 3 + .../vm/compiler/backend/il_deserializer.cc | 99 +++++- runtime/vm/compiler/backend/il_deserializer.h | 10 +- .../compiler/backend/il_deserializer_test.cc | 71 ++++ runtime/vm/compiler/backend/il_serializer.cc | 323 ++++++++++++------ runtime/vm/compiler/backend/il_serializer.h | 37 +- runtime/vm/compiler/backend/sexpression.cc | 17 +- runtime/vm/compiler/backend/sexpression.h | 1 + runtime/vm/double_conversion.cc | 34 +- runtime/vm/double_conversion.h | 7 + 10 files changed, 460 insertions(+), 142 deletions(-) diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index ded19366d0be..aefb07b75b9e 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -1295,6 +1295,7 @@ class BlockEntryInstr : public Instruction { TO_S_EXPRESSION_SUPPORT ADD_OPERANDS_TO_S_EXPRESSION_SUPPORT + ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT protected: BlockEntryInstr(intptr_t block_id, intptr_t try_index, intptr_t deopt_id) @@ -4148,6 +4149,7 @@ class LoadLocalInstr : public TemplateDefinition<0, NoThrow> { virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT + ADD_OPERANDS_TO_S_EXPRESSION_SUPPORT private: const LocalVariable& local_; @@ -4281,6 +4283,7 @@ class StoreLocalInstr : public TemplateDefinition<1, NoThrow> { virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT + ADD_OPERANDS_TO_S_EXPRESSION_SUPPORT private: const LocalVariable& local_; diff --git a/runtime/vm/compiler/backend/il_deserializer.cc b/runtime/vm/compiler/backend/il_deserializer.cc index 65301766f705..2e1ee9fb82db 100644 --- a/runtime/vm/compiler/backend/il_deserializer.cc +++ b/runtime/vm/compiler/backend/il_deserializer.cc @@ -8,10 +8,20 @@ #include #include "platform/utils.h" +#include "vm/double_conversion.h" #include "vm/object.h" namespace dart { +const char* const SExpParser::kBoolTrueSymbol = "true"; +const char* const SExpParser::kBoolFalseSymbol = "false"; +char const SExpParser::kDoubleExponentChar = + DoubleToStringConstants::kExponentChar; +const char* const SExpParser::kDoubleInfinitySymbol = + DoubleToStringConstants::kInfinitySymbol; +const char* const SExpParser::kDoubleNaNSymbol = + DoubleToStringConstants::kNaNSymbol; + const char* const SExpParser::ErrorStrings::kOpenString = "unterminated quoted string starting at position %" Pd ""; const char* const SExpParser::ErrorStrings::kBadUnicodeEscape = @@ -153,7 +163,8 @@ SExpression* SExpParser::Parse() { break; } case kBoolean: - case kNumber: + case kInteger: + case kDouble: case kQuotedString: { auto sexp = TokenToSExpression(token); // TokenToSExpression has already set the error info, so just return. @@ -190,15 +201,25 @@ SExpression* SExpParser::TokenToSExpression(Token* token) { switch (token->type()) { case kSymbol: return new (zone_) SExpSymbol(token->ToCString(zone_), start_pos); - case kNumber: { + case kInteger: { const char* cstr = token->ToCString(zone_); int64_t val; if (!OS::StringToInt64(cstr, &val)) return nullptr; return new (zone_) SExpInteger(val, start_pos); } case kBoolean: { - const char* cstr = token->ToCString(zone_); - return new (zone_) SExpBool(strcmp(cstr, "true") == 0, start_pos); + const bool is_true = + strncmp(token->cstr(), kBoolTrueSymbol, token->length()) == 0; + ASSERT(is_true || + strncmp(token->cstr(), kBoolFalseSymbol, token->length()) == 0); + return new (zone_) SExpBool(is_true, start_pos); + } + case kDouble: { + double val; + if (!CStringToDouble(token->cstr(), token->length(), &val)) { + return nullptr; + } + return new (zone_) SExpDouble(val, start_pos); } case kQuotedString: { const char* const cstr = token->cstr(); @@ -322,27 +343,69 @@ SExpParser::Token* SExpParser::GetNextToken() { default: break; } - if (isdigit(*start)) { - intptr_t len = 1; - while (start_pos + len < buffer_size_) { - if (!isdigit(start[len])) break; - len++; + intptr_t len = 0; + // Start number detection after possible negation sign. + if (start[len] == '-') { + len++; + if ((start_pos + len) >= buffer_size_) { + cur_pos_ = start_pos + len; + return new (zone_) Token(kSymbol, start, len); } - cur_pos_ = start_pos + len; - return new (zone_) Token(kNumber, start, len); } - intptr_t len = 1; - while (start_pos + len < buffer_size_) { + // Keep the currently detected token type. Start off by assuming we have + // an integer, then fall back to doubles if we see parts appropriate for + // those but not integers, and fall back to symbols otherwise. + TokenType type = kInteger; + bool saw_exponent = false; + while ((start_pos + len) < buffer_size_) { + // Both numbers and symbols cannot contain these values, so we are at the + // end of whichever one we're in. if (!IsSymbolContinue(start[len])) break; + if (type == kInteger && start[len] == '.') { + type = kDouble; + len++; + continue; + } + if (type != kSymbol && !saw_exponent && start[len] == kDoubleExponentChar) { + saw_exponent = true; + type = kDouble; + len++; + // Skip past negation in exponent if any. + if ((start_pos + len) < buffer_size_ && start[len] == '-') len++; + continue; + } + // If we find a character that can't appear in a number, then fall back + // to symbol-ness. + if (!isdigit(start[len])) type = kSymbol; len++; } cur_pos_ = start_pos + len; - if (len == 4 && strncmp(start, "true", 4) == 0) { - return new (zone_) Token(kBoolean, start, len); - } else if (len == 5 && strncmp(start, "false", 5) == 0) { - return new (zone_) Token(kBoolean, start, len); + // Skip special symbol detection if we don't have a symbol. + if (type != kSymbol) return new (zone_) Token(type, start, len); + // Check for special symbols used for booleans and certain Double values. + switch (len) { + case 3: + if (strncmp(start, kDoubleNaNSymbol, len) == 0) type = kDouble; + break; + case 4: + if (strncmp(start, kBoolTrueSymbol, len) == 0) type = kBoolean; + break; + case 5: + if (strncmp(start, kBoolFalseSymbol, len) == 0) type = kBoolean; + break; + case 8: + if (strncmp(start, kDoubleInfinitySymbol, len) == 0) type = kDouble; + break; + case 9: + if (start[0] == '-' && + strncmp(start + 1, kDoubleInfinitySymbol, len - 1) == 0) { + type = kDouble; + } + break; + default: + break; } - return new (zone_) Token(kSymbol, start, len); + return new (zone_) Token(type, start, len); } bool SExpParser::IsSymbolContinue(char c) { diff --git a/runtime/vm/compiler/backend/il_deserializer.h b/runtime/vm/compiler/backend/il_deserializer.h index 215339834ac7..9e1063f6442e 100644 --- a/runtime/vm/compiler/backend/il_deserializer.h +++ b/runtime/vm/compiler/backend/il_deserializer.h @@ -28,6 +28,13 @@ class SExpParser : public ValueObject { cur_label_stack_(zone, 2), error_message_(nullptr) {} + // Constants used in serializing and deserializing S-expressions. + static const char* const kBoolTrueSymbol; + static const char* const kBoolFalseSymbol; + static char const kDoubleExponentChar; + static const char* const kDoubleInfinitySymbol; + static const char* const kDoubleNaNSymbol; + struct ErrorStrings : AllStatic { static const char* const kOpenString; static const char* const kBadUnicodeEscape; @@ -59,7 +66,8 @@ class SExpParser : public ValueObject { M(LeftCurly) \ M(RightCurly) \ M(QuotedString) \ - M(Number) \ + M(Integer) \ + M(Double) \ M(Boolean) \ M(Symbol) diff --git a/runtime/vm/compiler/backend/il_deserializer_test.cc b/runtime/vm/compiler/backend/il_deserializer_test.cc index e317cbce2c97..16e6d0452c38 100644 --- a/runtime/vm/compiler/backend/il_deserializer_test.cc +++ b/runtime/vm/compiler/backend/il_deserializer_test.cc @@ -5,6 +5,7 @@ #include "vm/compiler/backend/il_deserializer.h" #include "vm/compiler/backend/il_serializer.h" +#include #include "platform/assert.h" #include "vm/unit_test.h" @@ -80,6 +81,76 @@ ISOLATE_UNIT_TEST_CASE(DeserializeSExp) { } } +ISOLATE_UNIT_TEST_CASE(DeserializeSExpNumbers) { + Zone* const zone = Thread::Current()->zone(); + + // Negative integers are handled. + { + const char* const cstr = "(-4 -50 -1414243)"; + SExpParser parser(zone, cstr, strlen(cstr)); + SExpression* const sexp = parser.Parse(); + EXPECT_NOTNULL(sexp); + EXPECT(sexp->IsList()); + auto list = sexp->AsList(); + EXPECT_EQ(3, list->Length()); + EXPECT_EQ(0, list->ExtraLength()); + for (intptr_t i = 0; i < list->Length(); i++) { + EXPECT(list->At(i)->IsInteger()); + EXPECT(list->At(i)->AsInteger()->value() < 0); + } + } + + // Various decimal/exponent Doubles are appropriately handled. + { + const char* const cstr = "(1.05 0.05 .03 1e100 1e-100)"; + SExpParser parser(zone, cstr, strlen(cstr)); + SExpression* const sexp = parser.Parse(); + EXPECT_NOTNULL(sexp); + EXPECT(sexp->IsList()); + auto list = sexp->AsList(); + EXPECT_EQ(5, list->Length()); + EXPECT_EQ(0, list->ExtraLength()); + EXPECT(list->At(0)->IsDouble()); + double val = list->At(0)->AsDouble()->value(); + EXPECT(val > 1.04 && val < 1.06); + EXPECT(list->At(1)->IsDouble()); + val = list->At(1)->AsDouble()->value(); + EXPECT(val > 0.04 && val < 0.06); + EXPECT(list->At(2)->IsDouble()); + val = list->At(2)->AsDouble()->value(); + EXPECT(val > 0.02 && val < 0.04); + EXPECT(list->At(3)->IsDouble()); + val = list->At(3)->AsDouble()->value(); + EXPECT(val > 0.9e100 && val < 1.1e100); + EXPECT(list->At(4)->IsDouble()); + val = list->At(4)->AsDouble()->value(); + EXPECT(val > 0.9e-100 && val < 1.1e-100); + } + + // Special Double symbols are appropriately handled. + { + const char* const cstr = "(NaN Infinity -Infinity)"; + SExpParser parser(zone, cstr, strlen(cstr)); + SExpression* const sexp = parser.Parse(); + EXPECT_NOTNULL(sexp); + EXPECT(sexp->IsList()); + auto list = sexp->AsList(); + EXPECT_EQ(3, list->Length()); + EXPECT_EQ(0, list->ExtraLength()); + EXPECT(list->At(0)->IsDouble()); + double val = list->At(0)->AsDouble()->value(); + EXPECT(isnan(val)); + EXPECT(list->At(1)->IsDouble()); + val = list->At(1)->AsDouble()->value(); + EXPECT(val > 0.0); + EXPECT(isinf(val)); + EXPECT(list->At(2)->IsDouble()); + val = list->At(2)->AsDouble()->value(); + EXPECT(val < 0.0); + EXPECT(isinf(val)); + } +} + ISOLATE_UNIT_TEST_CASE(DeserializeSExpRoundTrip) { Zone* const zone = Thread::Current()->zone(); SExpression* sexp = SExpression::FromCString(zone, shared_sexp_cstr); diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc index bca102440d61..c22a89840bd6 100644 --- a/runtime/vm/compiler/backend/il_serializer.cc +++ b/runtime/vm/compiler/backend/il_serializer.cc @@ -8,6 +8,7 @@ #include "vm/compiler/backend/flow_graph.h" #include "vm/compiler/backend/il.h" +#include "vm/compiler/method_recognizer.h" #include "vm/os.h" namespace dart { @@ -42,7 +43,7 @@ void FlowGraphSerializer::SerializeToBuffer(Zone* zone, TextBuffer* buffer) { ASSERT(buffer != nullptr); FlowGraphSerializer serializer(zone, flow_graph); - auto sexp = serializer.FunctionToSExp(); + auto const sexp = serializer.FlowGraphToSExp(); if (FLAG_pretty_print_serialization) { sexp->SerializeTo(serializer.zone(), buffer, initial_indent); } else { @@ -97,6 +98,7 @@ SExpression* FlowGraphSerializer::BlockIdToSExp(intptr_t block_id) { void FlowGraphSerializer::SerializeCanonicalName(TextBuffer* b, const Object& obj) { + ASSERT(!obj.IsNull()); if (obj.IsFunction()) { const auto& function = Function::Cast(obj); tmp_string_ = function.UserVisibleName(); @@ -141,59 +143,102 @@ void FlowGraphSerializer::SerializeCanonicalName(TextBuffer* b, } SExpression* FlowGraphSerializer::CanonicalNameToSExp(const Object& obj) { + ASSERT(!obj.IsNull()); TextBuffer b(100); SerializeCanonicalName(&b, obj); return new (zone()) SExpSymbol(OS::SCreate(zone(), "%s", b.buf())); } -SExpression* FlowGraphSerializer::BlockEntryToSExp(const char* entry_name, - BlockEntryInstr* entry) { +SExpSymbol* FlowGraphSerializer::BlockEntryTag(const BlockEntryInstr* entry) { + if (entry == nullptr) return nullptr; + BlockEntryInstr* const to_test = const_cast(entry); + if (to_test->IsGraphEntry()) { + return new (zone()) SExpSymbol("Graph"); + } + if (to_test->IsOsrEntry()) { + return new (zone()) SExpSymbol("OSR"); + } + if (to_test->IsCatchBlockEntry()) { + return new (zone()) SExpSymbol("Catch"); + } + if (to_test->IsIndirectEntry()) { + return new (zone()) SExpSymbol("Indirect"); + } + if (to_test->IsFunctionEntry()) { + if (entry == flow_graph()->graph_entry()->normal_entry()) { + return new (zone()) SExpSymbol("Normal"); + } + if (entry == flow_graph()->graph_entry()->unchecked_entry()) { + return new (zone()) SExpSymbol("Unchecked"); + } + } + if (to_test->IsJoinEntry()) { + return new (zone()) SExpSymbol("Join"); + } + return nullptr; +} + +SExpression* FlowGraphSerializer::FunctionEntryToSExp(BlockEntryInstr* entry) { + if (entry == nullptr) return nullptr; auto sexp = new (zone()) SExpList(zone()); - const auto tag_cstr = OS::SCreate(zone(), "%s-entry", entry_name); - sexp->Add(new (zone()) SExpSymbol(tag_cstr)); + sexp->Add(BlockEntryTag(entry)); sexp->Add(BlockIdToSExp(entry->block_id())); if (auto with_defs = entry->AsBlockEntryWithInitialDefs()) { auto initial_defs = with_defs->initial_definitions(); for (intptr_t i = 0; i < initial_defs->length(); i++) { sexp->Add(initial_defs->At(i)->ToSExpression(this)); } - } else if (auto join = entry->AsJoinEntry()) { - if (auto phi_list = join->phis()) { - for (intptr_t i = 0; i < phi_list->length(); i++) { - sexp->Add(phi_list->At(i)->ToSExpression(this)); - } - } } return sexp; } -SExpression* FlowGraphSerializer::FunctionToSExp() { - auto start = flow_graph()->graph_entry(); +SExpression* FlowGraphSerializer::EntriesToSExp(GraphEntryInstr* start) { auto sexp = new (zone()) SExpList(zone()); - AddSymbol(sexp, "function"); - sexp->Add(CanonicalNameToSExp(flow_graph()->function())); - AddExtraInteger(sexp, "deopt_id", start->deopt_id()); - AddConstantPool(sexp); - if (start->normal_entry()) { - sexp->Add(BlockEntryToSExp("normal", start->normal_entry())); + AddSymbol(sexp, "Entries"); + if (auto const normal = FunctionEntryToSExp(start->normal_entry())) { + sexp->Add(normal); } - if (start->unchecked_entry()) { - sexp->Add(BlockEntryToSExp("unchecked", start->unchecked_entry())); + if (auto const unchecked = FunctionEntryToSExp(start->unchecked_entry())) { + sexp->Add(unchecked); } - if (start->osr_entry()) { - sexp->Add(BlockEntryToSExp("osr", start->osr_entry())); + if (auto const osr = FunctionEntryToSExp(start->osr_entry())) { + sexp->Add(osr); } for (intptr_t i = 0; i < start->catch_entries().length(); i++) { - sexp->Add(BlockEntryToSExp("catch", start->catch_entries().At(i))); + sexp->Add(FunctionEntryToSExp(start->catch_entries().At(i))); } for (intptr_t i = 0; i < start->indirect_entries().length(); i++) { - sexp->Add(BlockEntryToSExp("indirect", start->indirect_entries().At(i))); + sexp->Add(FunctionEntryToSExp(start->indirect_entries().At(i))); + } + return sexp; +} + +SExpression* FlowGraphSerializer::FlowGraphToSExp() { + auto const start = flow_graph()->graph_entry(); + auto const sexp = new (zone()) SExpList(zone()); + AddSymbol(sexp, "FlowGraph"); + sexp->Add(CanonicalNameToSExp(flow_graph()->function())); + AddExtraInteger(sexp, "deopt_id", start->deopt_id()); + if (start->IsCompiledForOsr()) { + AddExtraInteger(sexp, "osr_id", start->osr_id()); + } + if (auto const constants = ConstantPoolToSExp(start)) { + sexp->Add(constants); + } + sexp->Add(EntriesToSExp(start)); + auto& block_order = flow_graph()->reverse_postorder(); + // Skip the first block, which will be the graph entry block (B0). We + // output all its information as part of the function expression, so it'll + // just show up as an empty block here. + ASSERT(block_order[0]->IsGraphEntry()); + for (intptr_t i = 1; i < block_order.length(); ++i) { + sexp->Add(block_order[i]->ToSExpression(this)); } - AddBlocks(sexp); return sexp; } SExpression* FlowGraphSerializer::UseToSExp(const Definition* definition) { + ASSERT(definition != nullptr); ASSERT(definition->HasSSATemp() || definition->HasTemp()); if (definition->HasSSATemp()) { const intptr_t temp_index = definition->ssa_temp_index(); @@ -209,12 +254,12 @@ SExpression* FlowGraphSerializer::UseToSExp(const Definition* definition) { } else if (definition->HasTemp()) { const intptr_t temp_index = definition->temp_index(); return new (zone()) SExpSymbol(OS::SCreate(zone(), "t%" Pd "", temp_index)); - } else { - UNREACHABLE(); } + UNREACHABLE(); } SExpression* FlowGraphSerializer::ClassToSExp(const Class& cls) { + if (cls.IsNull()) return nullptr; auto sexp = new (zone()) SExpList(zone()); AddSymbol(sexp, "Class"); AddInteger(sexp, cls.id()); @@ -232,6 +277,7 @@ static bool ShouldSerializeType(CompileType* type) { } SExpression* FlowGraphSerializer::FieldToSExp(const Field& field) { + if (field.IsNull()) return nullptr; auto sexp = new (zone()) SExpList(zone()); AddSymbol(sexp, "Field"); sexp->Add(CanonicalNameToSExp(field)); @@ -243,6 +289,7 @@ SExpression* FlowGraphSerializer::FieldToSExp(const Field& field) { } SExpression* FlowGraphSerializer::AbstractTypeToSExp(const AbstractType& t) { + if (t.IsNull()) return nullptr; auto sexp = new (zone()) SExpList(zone()); if (t.IsTypeParameter()) { const auto& param = TypeParameter::Cast(t); @@ -289,6 +336,7 @@ SExpression* FlowGraphSerializer::AbstractTypeToSExp(const AbstractType& t) { } SExpression* FlowGraphSerializer::CodeToSExp(const Code& code) { + if (code.IsNull()) return nullptr; auto sexp = new (zone()) SExpList(zone()); AddSymbol(sexp, "Code"); if (code.IsStubCode()) { @@ -299,27 +347,22 @@ SExpression* FlowGraphSerializer::CodeToSExp(const Code& code) { return sexp; } tmp_object_ = code.owner(); - if (tmp_object_.IsClass()) { - sexp->Add(ClassToSExp(Class::Cast(tmp_object_))); - if (FLAG_verbose_flow_graph_serialization) { + if (!tmp_object_.IsNull() && FLAG_verbose_flow_graph_serialization) { + if (tmp_object_.IsClass()) { AddExtraSymbol(sexp, "kind", "allocate"); - } - } else if (tmp_object_.IsAbstractType()) { - sexp->Add(AbstractTypeToSExp(AbstractType::Cast(tmp_object_))); - if (FLAG_verbose_flow_graph_serialization) { + } else if (tmp_object_.IsAbstractType()) { AddExtraSymbol(sexp, "kind", "type_test"); - } - } else { - ASSERT(tmp_object_.IsFunction()); - sexp->Add(CanonicalNameToSExp(tmp_object_)); - if (FLAG_verbose_flow_graph_serialization) { + } else { + ASSERT(tmp_object_.IsFunction()); AddExtraSymbol(sexp, "kind", "function"); } } + sexp->Add(ObjectToSExp(tmp_object_)); return sexp; } SExpression* FlowGraphSerializer::TypeArgumentsToSExp(const TypeArguments& ta) { + if (ta.IsNull()) return nullptr; auto sexp = new (zone()) SExpList(zone()); AddSymbol(sexp, "TypeArguments"); for (intptr_t i = 0; i < ta.Length(); i++) { @@ -333,7 +376,50 @@ SExpression* FlowGraphSerializer::TypeArgumentsToSExp(const TypeArguments& ta) { return sexp; } -SExpression* FlowGraphSerializer::DartValueToSExp(const Object& dartval) { +SExpression* FlowGraphSerializer::InstanceToSExp(const Instance& inst) { + if (inst.IsNull()) return nullptr; + auto const sexp = new (zone()) SExpList(zone()); + AddSymbol(sexp, "Instance"); + tmp_class_ = inst.clazz(); + AddInteger(sexp, tmp_class_.id()); + if (FLAG_verbose_flow_graph_serialization) { + AddExtraInteger(sexp, "size", inst.InstanceSize()); + if (auto const cls = ClassToSExp(tmp_class_)) { + sexp->AddExtra("class", cls); + } + } + return sexp; +} + +SExpression* FlowGraphSerializer::FunctionToSExp(const Function& func) { + if (func.IsNull()) return nullptr; + auto const sexp = new (zone()) SExpList(zone()); + AddSymbol(sexp, "Function"); + sexp->Add(CanonicalNameToSExp(func)); + if (func.IsRecognized()) { + AddExtraSymbol(sexp, "recognized", + MethodRecognizer::KindToCString(func.recognized_kind())); + } + if (func.is_native()) { + tmp_string_ = func.native_name(); + if (!tmp_string_.IsNull()) { + AddExtraSymbol(sexp, "native_name", tmp_string_.ToCString()); + } + } + if (FLAG_verbose_flow_graph_serialization) { + AddExtraSymbol(sexp, "kind", Function::KindToCString(func.kind())); + } + tmp_type_args_ = func.type_parameters(); + if (auto const ta_sexp = TypeArgumentsToSExp(tmp_type_args_)) { + sexp->AddExtra("type_params", ta_sexp); + } + return sexp; +} + +SExpression* FlowGraphSerializer::ObjectToSExp(const Object& dartval) { + if (dartval.IsNull()) { + return new (zone()) SExpSymbol("null"); + } if (dartval.IsString()) { return new (zone()) SExpString(dartval.ToCString()); } @@ -343,8 +429,8 @@ SExpression* FlowGraphSerializer::DartValueToSExp(const Object& dartval) { if (dartval.IsBool()) { return new (zone()) SExpBool(Bool::Cast(dartval).value()); } - if (dartval.IsNull()) { - return new (zone()) SExpSymbol("null"); + if (dartval.IsDouble()) { + return new (zone()) SExpDouble(Double::Cast(dartval).value()); } if (dartval.IsField()) { return FieldToSExp(Field::Cast(dartval)); @@ -365,51 +451,36 @@ SExpression* FlowGraphSerializer::DartValueToSExp(const Object& dartval) { auto& elem = Object::Handle(zone()); for (intptr_t i = 0; i < arr.Length(); i++) { elem = arr.At(i); - sexp->Add(DartValueToSExp(elem)); + sexp->Add(ObjectToSExp(elem)); } return sexp; } - tmp_class_ = dartval.clazz(); - auto sexp = new (zone()) SExpList(zone()); - AddSymbol(sexp, "Instance"); - AddInteger(sexp, tmp_class_.id()); - if (FLAG_verbose_flow_graph_serialization) { - AddExtraInteger(sexp, "size", dartval.InstanceSize()); - sexp->AddExtra("class", ClassToSExp(tmp_class_)); + if (dartval.IsFunction()) { + return FunctionToSExp(Function::Cast(dartval)); } - return sexp; + ASSERT(dartval.IsInstance()); + return InstanceToSExp(Instance::Cast(dartval)); } -void FlowGraphSerializer::AddConstantPool(SExpList* sexp) { - auto initial_defs = flow_graph()->graph_entry()->initial_definitions(); - if (initial_defs->is_empty()) return; +SExpression* FlowGraphSerializer::ConstantPoolToSExp(GraphEntryInstr* start) { + auto const initial_defs = start->initial_definitions(); + if (initial_defs == nullptr || initial_defs->is_empty()) return nullptr; auto constant_list = new (zone()) SExpList(zone()); - AddSymbol(constant_list, "constants"); + AddSymbol(constant_list, "Constants"); for (intptr_t i = 0; i < initial_defs->length(); i++) { ASSERT(initial_defs->At(i)->IsConstant()); ConstantInstr* value = initial_defs->At(i)->AsConstant(); auto elem = new (zone()) SExpList(zone()); AddSymbol(elem, "def"); elem->Add(UseToSExp(value->AsDefinition())); - elem->Add(DartValueToSExp(value->value())); + elem->Add(ObjectToSExp(value->value())); if (ShouldSerializeType(value->AsDefinition()->Type())) { - auto val = value->AsDefinition()->Type()->ToSExpression(this); + auto const val = value->AsDefinition()->Type()->ToSExpression(this); elem->AddExtra("type", val); } constant_list->Add(elem); } - sexp->Add(constant_list); -} - -void FlowGraphSerializer::AddBlocks(SExpList* sexp) { - auto& block_order = flow_graph()->reverse_postorder(); - // Skip the first block, which will be the graph entry block (B0). We - // output all its information as part of the function expression, so it'll - // just show up as an empty block here. - ASSERT(block_order[0]->IsGraphEntry()); - for (intptr_t i = 1; i < block_order.length(); ++i) { - sexp->Add(block_order[i]->ToSExpression(this)); - } + return constant_list; } SExpression* Instruction::ToSExpression(FlowGraphSerializer* s) const { @@ -422,13 +493,30 @@ SExpression* Instruction::ToSExpression(FlowGraphSerializer* s) const { SExpression* BlockEntryInstr::ToSExpression(FlowGraphSerializer* s) const { auto sexp = new (s->zone()) SExpList(s->zone()); - s->AddSymbol(sexp, "block"); + s->AddSymbol(sexp, "Block"); sexp->Add(s->BlockIdToSExp(block_id())); AddOperandsToSExpression(sexp, s); AddExtraInfoToSExpression(sexp, s); return sexp; } +void BlockEntryInstr::AddExtraInfoToSExpression(SExpList* sexp, + FlowGraphSerializer* s) const { + Instruction::AddExtraInfoToSExpression(sexp, s); + if (FLAG_verbose_flow_graph_serialization) { + if (auto const entry_tag = s->BlockEntryTag(this)) { + sexp->AddExtra("block_type", entry_tag); + } + if (PredecessorCount() > 0) { + auto const preds = new (s->zone()) SExpList(s->zone()); + for (intptr_t i = 0; i < PredecessorCount(); i++) { + preds->Add(s->BlockIdToSExp(PredecessorAt(i)->block_id())); + } + sexp->AddExtra("predecessors", preds); + } + } +} + void BlockEntryInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { // We don't use RemoveCurrentFromGraph(), so this cast is safe. @@ -482,7 +570,7 @@ SExpression* Definition::ToSExpression(FlowGraphSerializer* s) const { void ConstantInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { - sexp->Add(s->DartValueToSExp(value())); + sexp->Add(s->ObjectToSExp(value())); } void BranchInstr::AddOperandsToSExpression(SExpList* sexp, @@ -503,22 +591,50 @@ void SpecialParameterInstr::AddOperandsToSExpression( s->AddSymbol(sexp, KindToCString(kind())); } +SExpression* FlowGraphSerializer::LocalVariableToSExp(const LocalVariable& v) { + auto const sexp = new (zone()) SExpList(zone()); + AddSymbol(sexp, "LocalVariable"); + if (!v.name().IsNull()) { + AddSymbol(sexp, v.name().ToCString()); + } + if (v.index().IsValid()) { + AddExtraInteger(sexp, "index", v.index().value()); + } + return sexp; +} + +void LoadLocalInstr::AddOperandsToSExpression(SExpList* sexp, + FlowGraphSerializer* s) const { + sexp->Add(s->LocalVariableToSExp(local())); +} + +void StoreLocalInstr::AddOperandsToSExpression(SExpList* sexp, + FlowGraphSerializer* s) const { + sexp->Add(s->LocalVariableToSExp(local())); +} + +static const char* SlotKindToCString(Slot::Kind kind) { + switch (kind) { + case Slot::Kind::kDartField: + return "DartField"; + case Slot::Kind::kCapturedVariable: + return "CapturedVariable"; + case Slot::Kind::kTypeArguments: + return "TypeArguments"; + default: + return "NativeSlot"; + } +} + SExpression* FlowGraphSerializer::SlotToSExp(const Slot& slot) { auto sexp = new (zone()) SExpList(zone()); AddSymbol(sexp, "Slot"); AddInteger(sexp, slot.offset_in_bytes()); if (FLAG_verbose_flow_graph_serialization) { + AddExtraSymbol(sexp, "kind", SlotKindToCString(slot.kind())); if (slot.IsDartField()) { - AddExtraSymbol(sexp, "kind", "kDartField"); sexp->AddExtra("field", FieldToSExp(slot.field())); - } else if (slot.IsLocalVariable()) { - AddExtraSymbol(sexp, "kind", "kCapturedVariable"); - AddExtraString(sexp, "name", slot.Name()); - } else if (slot.IsTypeArguments()) { - AddExtraSymbol(sexp, "kind", "kTypeArguments"); - AddExtraString(sexp, "name", slot.Name()); } else { - AddExtraSymbol(sexp, "kind", "kNativeSlot"); AddExtraString(sexp, "name", slot.Name()); } } @@ -586,14 +702,17 @@ void GotoInstr::AddOperandsToSExpression(SExpList* sexp, void TailCallInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { - sexp->Add(s->CodeToSExp(code_)); + if (auto const code = s->CodeToSExp(code_)) { + sexp->Add(code); + } Instruction::AddOperandsToSExpression(sexp, s); } void NativeCallInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { - sexp->Add(s->CanonicalNameToSExp(function())); - s->AddSymbol(sexp, native_name().ToCString()); + if (auto const func = s->FunctionToSExp(function())) { + sexp->Add(func); + } } template <> @@ -620,13 +739,15 @@ void TemplateDartCall<1l>::AddExtraInfoToSExpression( void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { - sexp->Add(s->CanonicalNameToSExp(function())); + if (auto const func = s->FunctionToSExp(function())) { + sexp->Add(func); + } } void InstanceCallInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { - if (!interface_target().IsNull()) { - sexp->Add(s->CanonicalNameToSExp(interface_target())); + if (auto const target = s->FunctionToSExp(interface_target())) { + sexp->Add(target); } } @@ -654,7 +775,9 @@ void PolymorphicInstanceCallInstr::AddExtraInfoToSExpression( s->AddInteger(range, ti->cid_end); elem->Add(range); } - elem->Add(s->CanonicalNameToSExp(*ti->target)); + if (auto const target = s->FunctionToSExp(*ti->target)) { + elem->Add(target); + } elem_list->Add(elem); } sexp->AddExtra("targets", elem_list); @@ -664,7 +787,9 @@ void PolymorphicInstanceCallInstr::AddExtraInfoToSExpression( void AllocateObjectInstr::AddOperandsToSExpression( SExpList* sexp, FlowGraphSerializer* s) const { - sexp->Add(s->ClassToSExp(cls())); + if (auto const sexp_cls = s->ClassToSExp(cls())) { + sexp->Add(sexp_cls); + } } void AllocateObjectInstr::AddExtraInfoToSExpression( @@ -675,12 +800,8 @@ void AllocateObjectInstr::AddExtraInfoToSExpression( if (ArgumentCount() > 0 || FLAG_verbose_flow_graph_serialization) { s->AddExtraInteger(sexp, "args_len", ArgumentCount()); } - if (FLAG_verbose_flow_graph_serialization) { - if (closure_function().IsNull()) { - s->AddSymbol(sexp, "null"); - } else { - sexp->Add(s->CanonicalNameToSExp(closure_function())); - } + if (auto const closure = s->FunctionToSExp(closure_function())) { + sexp->AddExtra("closure_function", closure); } } @@ -699,11 +820,14 @@ void CheckedSmiOpInstr::AddOperandsToSExpression(SExpList* sexp, sexp->Add(right()->ToSExpression(s)); } +// clang-format off static const char* simd_op_kind_string[] = { #define CASE(Arity, Mask, Name, ...) #Name, - SIMD_OP_LIST(CASE, CASE) + SIMD_OP_LIST(CASE, CASE) #undef CASE + "IllegalSimdOp", }; +// clang-format on void SimdOpInstr::AddOperandsToSExpression(SExpList* sexp, FlowGraphSerializer* s) const { @@ -792,8 +916,13 @@ void CompileType::AddExtraInfoToSExpression(SExpList* sexp, if (!is_nullable() || FLAG_verbose_flow_graph_serialization) { s->AddExtraBool(sexp, "nullable", is_nullable()); } - if (FLAG_verbose_flow_graph_serialization) { - s->AddExtraString(sexp, "name", ToCString()); + if (cid_ == kIllegalCid || cid_ == kDynamicCid || + FLAG_verbose_flow_graph_serialization) { + if (type_ != nullptr) { + sexp->AddExtra("type", s->AbstractTypeToSExp(*type_)); + } else { + s->AddExtraString(sexp, "name", ToCString()); + } } } diff --git a/runtime/vm/compiler/backend/il_serializer.h b/runtime/vm/compiler/backend/il_serializer.h index 1f32403074dc..bcd676cbb31e 100644 --- a/runtime/vm/compiler/backend/il_serializer.h +++ b/runtime/vm/compiler/backend/il_serializer.h @@ -5,6 +5,7 @@ #ifndef RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_ #define RUNTIME_VM_COMPILER_BACKEND_IL_SERIALIZER_H_ +#include "platform/assert.h" #include "platform/text_buffer.h" #include "vm/allocation.h" @@ -28,23 +29,34 @@ class FlowGraphSerializer : ValueObject { const FlowGraph* flow_graph() const { return flow_graph_; } Zone* zone() const { return zone_; } - SExpression* FunctionToSExp(); + SExpression* FlowGraphToSExp(); + SExpSymbol* BlockEntryTag(const BlockEntryInstr* entry); SExpression* BlockIdToSExp(intptr_t block_id); - SExpression* BlockEntryToSExp(const char* entry_name, BlockEntryInstr* entry); SExpression* CanonicalNameToSExp(const Object& obj); SExpression* UseToSExp(const Definition* definition); + // Helper method for creating canonical names. void SerializeCanonicalName(TextBuffer* b, const Object& obj); - // Methods for serializing Dart values. + // Methods for serializing Dart values. If the argument + // value is the null object, the null pointer is returned. SExpression* AbstractTypeToSExp(const AbstractType& typ); SExpression* ClassToSExp(const Class& cls); SExpression* CodeToSExp(const Code& c); SExpression* FieldToSExp(const Field& f); - SExpression* SlotToSExp(const Slot& s); + SExpression* FunctionToSExp(const Function& f); + SExpression* InstanceToSExp(const Instance& obj); SExpression* TypeArgumentsToSExp(const TypeArguments& ta); - SExpression* DartValueToSExp(const Object& obj); + + // A method for serializing a Dart value of arbitrary type. + // Unlike the type-specific methods, this returns the symbol + // "null" for the null object. + SExpression* ObjectToSExp(const Object& obj); + + // Methods for serializing IL-specific values. + SExpression* LocalVariableToSExp(const LocalVariable& v); + SExpression* SlotToSExp(const Slot& s); // Helper methods for adding atoms to S-expression lists void AddBool(SExpList* sexp, bool b); @@ -58,22 +70,26 @@ class FlowGraphSerializer : ValueObject { private: FlowGraphSerializer(Zone* zone, const FlowGraph* flow_graph) - : flow_graph_(flow_graph), + : flow_graph_(ASSERT_NOTNULL(flow_graph)), zone_(zone), tmp_type_(AbstractType::Handle(zone_)), tmp_class_(Class::Handle(zone_)), tmp_function_(Function::Handle(zone_)), tmp_library_(Library::Handle(zone_)), tmp_object_(Object::Handle(zone_)), + tmp_type_args_(TypeArguments::Handle(zone_)), tmp_string_(String::Handle(zone_)) {} static const char* const initial_indent; - void AddConstantPool(SExpList* sexp); - void AddBlocks(SExpList* sexp); + // Helper methods for the function level that are not used by any + // instruction serialization methods. + SExpression* FunctionEntryToSExp(BlockEntryInstr* entry); + SExpression* EntriesToSExp(GraphEntryInstr* start); + SExpression* ConstantPoolToSExp(GraphEntryInstr* start); - const FlowGraph* flow_graph_; - Zone* zone_; + const FlowGraph* const flow_graph_; + Zone* const zone_; // Handles for temporary use. AbstractType& tmp_type_; @@ -81,6 +97,7 @@ class FlowGraphSerializer : ValueObject { Function& tmp_function_; Library& tmp_library_; Object& tmp_object_; + TypeArguments& tmp_type_args_; String& tmp_string_; }; diff --git a/runtime/vm/compiler/backend/sexpression.cc b/runtime/vm/compiler/backend/sexpression.cc index c98ca71ab1cd..b33fa256a981 100644 --- a/runtime/vm/compiler/backend/sexpression.cc +++ b/runtime/vm/compiler/backend/sexpression.cc @@ -7,6 +7,7 @@ #include "vm/compiler/backend/sexpression.h" #include "vm/compiler/backend/il_deserializer.h" +#include "vm/double_conversion.h" namespace dart { @@ -23,7 +24,21 @@ bool SExpBool::Equals(SExpression* sexp) const { } void SExpBool::SerializeToLine(TextBuffer* buffer) const { - buffer->AddString(value() ? "true" : "false"); + buffer->AddString(value() ? SExpParser::kBoolTrueSymbol + : SExpParser::kBoolFalseSymbol); +} + +bool SExpDouble::Equals(SExpression* sexp) const { + if (!sexp->IsDouble()) return false; + return this->value() == sexp->AsDouble()->value(); +} + +void SExpDouble::SerializeToLine(TextBuffer* buffer) const { + // Use existing Dart serialization for Doubles. + const intptr_t kBufSize = 128; + char strbuf[kBufSize]; + DoubleToCString(value(), strbuf, kBufSize); + buffer->Printf("%s", strbuf); } bool SExpInteger::Equals(SExpression* sexp) const { diff --git a/runtime/vm/compiler/backend/sexpression.h b/runtime/vm/compiler/backend/sexpression.h index 1fd0c559d2ff..5062c2e35d3c 100644 --- a/runtime/vm/compiler/backend/sexpression.h +++ b/runtime/vm/compiler/backend/sexpression.h @@ -17,6 +17,7 @@ namespace dart { #define FOR_EACH_S_EXPRESSION_ATOM(M) \ M(Bool, bool) \ + M(Double, double) \ M(Integer, intptr_t) \ M(String, const char*) \ M(Symbol, const char*) diff --git a/runtime/vm/double_conversion.cc b/runtime/vm/double_conversion.cc index 1cd64596ac71..b3784abe579f 100644 --- a/runtime/vm/double_conversion.cc +++ b/runtime/vm/double_conversion.cc @@ -12,9 +12,9 @@ namespace dart { -static const char kDoubleToStringCommonExponentChar = 'e'; -static const char* kDoubleToStringCommonInfinitySymbol = "Infinity"; -static const char* kDoubleToStringCommonNaNSymbol = "NaN"; +char const DoubleToStringConstants::kExponentChar = 'e'; +const char* const DoubleToStringConstants::kInfinitySymbol = "Infinity"; +const char* const DoubleToStringConstants::kNaNSymbol = "NaN"; void DoubleToCString(double d, char* buffer, int buffer_size) { static const int kDecimalLow = -6; @@ -38,9 +38,9 @@ void DoubleToCString(double d, char* buffer, int buffer_size) { EMIT_TRAILING_ZERO_AFTER_POINT; const double_conversion::DoubleToStringConverter converter( - kConversionFlags, kDoubleToStringCommonInfinitySymbol, - kDoubleToStringCommonNaNSymbol, kDoubleToStringCommonExponentChar, - kDecimalLow, kDecimalHigh, 0, + kConversionFlags, DoubleToStringConstants::kInfinitySymbol, + DoubleToStringConstants::kNaNSymbol, + DoubleToStringConstants::kExponentChar, kDecimalLow, kDecimalHigh, 0, 0); // Last two values are ignored in shortest mode. double_conversion::StringBuilder builder(buffer, buffer_size); @@ -78,9 +78,10 @@ RawString* DoubleToStringAsFixed(double d, int fraction_digits) { fraction_digits <= kMaxFractionDigits); const double_conversion::DoubleToStringConverter converter( - kConversionFlags, kDoubleToStringCommonInfinitySymbol, - kDoubleToStringCommonNaNSymbol, kDoubleToStringCommonExponentChar, 0, 0, - 0, 0); // Last four values are ignored in fixed mode. + kConversionFlags, DoubleToStringConstants::kInfinitySymbol, + DoubleToStringConstants::kNaNSymbol, + DoubleToStringConstants::kExponentChar, 0, 0, 0, + 0); // Last four values are ignored in fixed mode. char* buffer = Thread::Current()->zone()->Alloc(kBufferSize); buffer[kBufferSize - 1] = '\0'; @@ -108,9 +109,10 @@ RawString* DoubleToStringAsExponential(double d, int fraction_digits) { fraction_digits <= kMaxFractionDigits); const double_conversion::DoubleToStringConverter converter( - kConversionFlags, kDoubleToStringCommonInfinitySymbol, - kDoubleToStringCommonNaNSymbol, kDoubleToStringCommonExponentChar, 0, 0, - 0, 0); // Last four values are ignored in exponential mode. + kConversionFlags, DoubleToStringConstants::kInfinitySymbol, + DoubleToStringConstants::kNaNSymbol, + DoubleToStringConstants::kExponentChar, 0, 0, 0, + 0); // Last four values are ignored in exponential mode. char* buffer = Thread::Current()->zone()->Alloc(kBufferSize); buffer[kBufferSize - 1] = '\0'; @@ -143,8 +145,9 @@ RawString* DoubleToStringAsPrecision(double d, int precision) { ASSERT(kMinPrecisionDigits <= precision && precision <= kMaxPrecisionDigits); const double_conversion::DoubleToStringConverter converter( - kConversionFlags, kDoubleToStringCommonInfinitySymbol, - kDoubleToStringCommonNaNSymbol, kDoubleToStringCommonExponentChar, 0, + kConversionFlags, DoubleToStringConstants::kInfinitySymbol, + DoubleToStringConstants::kNaNSymbol, + DoubleToStringConstants::kExponentChar, 0, 0, // Ignored in precision mode. kMaxLeadingPaddingZeroes, kMaxTrailingPaddingZeroes); @@ -163,7 +166,8 @@ bool CStringToDouble(const char* str, intptr_t length, double* result) { double_conversion::StringToDoubleConverter converter( double_conversion::StringToDoubleConverter::NO_FLAGS, 0.0, 0.0, - kDoubleToStringCommonInfinitySymbol, kDoubleToStringCommonNaNSymbol); + DoubleToStringConstants::kInfinitySymbol, + DoubleToStringConstants::kNaNSymbol); int parsed_count = 0; *result = diff --git a/runtime/vm/double_conversion.h b/runtime/vm/double_conversion.h index dd6e2136542f..87936492b0cf 100644 --- a/runtime/vm/double_conversion.h +++ b/runtime/vm/double_conversion.h @@ -5,11 +5,18 @@ #ifndef RUNTIME_VM_DOUBLE_CONVERSION_H_ #define RUNTIME_VM_DOUBLE_CONVERSION_H_ +#include "vm/allocation.h" #include "vm/globals.h" #include "vm/object.h" namespace dart { +struct DoubleToStringConstants : AllStatic { + static char const kExponentChar; + static const char* const kInfinitySymbol; + static const char* const kNaNSymbol; +}; + void DoubleToCString(double d, char* buffer, int buffer_size); RawString* DoubleToStringAsFixed(double d, int fraction_digits); RawString* DoubleToStringAsExponential(double d, int fraction_digits);