[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 <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Teagan Strickland 2019-07-31 11:52:44 +00:00 committed by commit-bot@chromium.org
parent 2875b1bcce
commit 2e197ae4de
10 changed files with 460 additions and 142 deletions

View file

@ -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_;

View file

@ -8,10 +8,20 @@
#include <ctype.h>
#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) {

View file

@ -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)

View file

@ -5,6 +5,7 @@
#include "vm/compiler/backend/il_deserializer.h"
#include "vm/compiler/backend/il_serializer.h"
#include <cmath>
#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);

View file

@ -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<BlockEntryInstr*>(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());
}
}
}

View file

@ -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_;
};

View file

@ -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 {

View file

@ -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*)

View file

@ -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<char>(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<char>(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 =

View file

@ -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);