Files
sdk/runtime/vm/compiler/assembler/disassembler.cc
T
Teagan Strickland e01457d138 Reland "[vm/aot] Remove object wrapping of bare instructions for AOT snapshots."
Now, when writing an AOT snapshot in bare instructions mode, only
the actual instructions in the RawInstructions payload are serialized
instead of the entire RawInstructions object.

Since there are no longer RawInstructions objects in these AOT
snapshots, we also change how Code objects are serialized. Instead
of just containing a reference to the RawInstructions object, we
serialize two pieces of information: where the instructions
payload for this Code object begins and whether there was a single
entry for the instructions payload. (To save space, the single
entry bit is serialized as the low order bit of the unchecked
offset, which was already being serialized).

While we also need the length of the instructions payload, we
approximate it for all but the last Code object by subtracting
the next Code object's payload start from this Code object's
payload start. For the last Code object, we assume it extends
to the end of the instructions image.

Changes on flutter gallery in release mode:
armv7: instructions size -2.70%, total size -1.73%
armv8: instructions size -6.04%, total size -3.63%

Fixes https://github.com/dart-lang/sdk/issues/38451.

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try
Change-Id: Ia0a5c4e5e47c956776dc62503da38ec55a143c04
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134325
Commit-Queue: Teagan Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2020-02-10 15:08:04 +00:00

444 lines
16 KiB
C++

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/assembler/disassembler.h"
#include "vm/code_patcher.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/deopt_instructions.h"
#include "vm/globals.h"
#include "vm/instructions.h"
#include "vm/json_stream.h"
#include "vm/log.h"
#include "vm/os.h"
namespace dart {
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
DECLARE_FLAG(bool, trace_inlining_intervals);
DEFINE_FLAG(bool, trace_source_positions, false, "Source position diagnostics");
void DisassembleToStdout::ConsumeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
Object* object,
uword pc) {
static const int kHexColumnWidth = 23;
#if defined(TARGET_ARCH_IS_32_BIT)
THR_Print("0x%" Px32 " %s", static_cast<uint32_t>(pc), hex_buffer);
#else
THR_Print("0x%" Px64 " %s", static_cast<uint64_t>(pc), hex_buffer);
#endif
int hex_length = strlen(hex_buffer);
if (hex_length < kHexColumnWidth) {
for (int i = kHexColumnWidth - hex_length; i > 0; i--) {
THR_Print(" ");
}
}
THR_Print("%s", human_buffer);
if (object != NULL) {
THR_Print(" %s", object->ToCString());
}
THR_Print("\n");
}
void DisassembleToStdout::Print(const char* format, ...) {
va_list args;
va_start(args, format);
THR_VPrint(format, args);
va_end(args);
}
void DisassembleToJSONStream::ConsumeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
Object* object,
uword pc) {
// Instructions are represented as four consecutive values in a JSON array.
// The first is the address of the instruction, the second is the hex string,
// of the code, and the third is a human readable string, and the fourth is
// the object loaded by the instruction.
jsarr_.AddValueF("%" Pp "", pc);
jsarr_.AddValue(hex_buffer);
jsarr_.AddValue(human_buffer);
if (object != NULL) {
jsarr_.AddValue(*object);
} else {
jsarr_.AddValueNull(); // Not a reference to null.
}
}
void DisassembleToJSONStream::Print(const char* format, ...) {
va_list args;
va_start(args, format);
intptr_t len = Utils::VSNPrint(NULL, 0, format, args);
va_end(args);
char* p = reinterpret_cast<char*>(malloc(len + 1));
va_start(args, format);
intptr_t len2 = Utils::VSNPrint(p, len, format, args);
va_end(args);
ASSERT(len == len2);
for (intptr_t i = 0; i < len; i++) {
if (p[i] == '\n' || p[i] == '\r') {
p[i] = ' ';
}
}
// Instructions are represented as four consecutive values in a JSON array.
// Comments only use the third slot. See above comment for more information.
jsarr_.AddValueNull();
jsarr_.AddValueNull();
jsarr_.AddValue(p);
jsarr_.AddValueNull();
free(p);
}
void DisassembleToMemory::ConsumeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
Object* object,
uword pc) {
if (overflowed_) {
return;
}
intptr_t len = strlen(human_buffer);
if (remaining_ < len + 100) {
*buffer_++ = '.';
*buffer_++ = '.';
*buffer_++ = '.';
*buffer_++ = '\n';
*buffer_++ = '\0';
overflowed_ = true;
return;
}
memmove(buffer_, human_buffer, len);
buffer_ += len;
remaining_ -= len;
*buffer_++ = '\n';
remaining_--;
*buffer_ = '\0';
}
void DisassembleToMemory::Print(const char* format, ...) {
if (overflowed_) {
return;
}
va_list args;
va_start(args, format);
intptr_t len = Utils::VSNPrint(NULL, 0, format, args);
va_end(args);
if (remaining_ < len + 100) {
*buffer_++ = '.';
*buffer_++ = '.';
*buffer_++ = '.';
*buffer_++ = '\n';
*buffer_++ = '\0';
overflowed_ = true;
return;
}
va_start(args, format);
intptr_t len2 = Utils::VSNPrint(buffer_, len, format, args);
va_end(args);
ASSERT(len == len2);
buffer_ += len;
remaining_ -= len;
*buffer_++ = '\n';
remaining_--;
*buffer_ = '\0';
}
void Disassembler::Disassemble(uword start,
uword end,
DisassemblyFormatter* formatter,
const Code& code,
const Code::Comments* comments) {
if (comments == nullptr) {
comments = code.IsNull() ? &Code::Comments::New(0) : &code.comments();
}
ASSERT(formatter != NULL);
char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
uword pc = start;
intptr_t comment_finger = 0;
GrowableArray<const Function*> inlined_functions;
GrowableArray<TokenPosition> token_positions;
while (pc < end) {
const intptr_t offset = pc - start;
const intptr_t old_comment_finger = comment_finger;
while (comment_finger < comments->Length() &&
comments->PCOffsetAt(comment_finger) <= offset) {
formatter->Print(
" ;; %s\n",
String::Handle(comments->CommentAt(comment_finger)).ToCString());
comment_finger++;
}
if (old_comment_finger != comment_finger && !code.IsNull()) {
char str[4000];
BufferFormatter f(str, sizeof(str));
// Comment emitted, emit inlining information.
code.GetInlinedFunctionsAtInstruction(offset, &inlined_functions,
&token_positions);
// Skip top scope function printing (last entry in 'inlined_functions').
bool first = true;
for (intptr_t i = 1; i < inlined_functions.length(); i++) {
const char* name = inlined_functions[i]->ToQualifiedCString();
if (first) {
f.Print(" ;; Inlined [%s", name);
first = false;
} else {
f.Print(" -> %s", name);
}
}
if (!first) {
f.Print("]\n");
formatter->Print("%s", str);
}
}
int instruction_length;
Object* object;
DecodeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
sizeof(human_buffer), &instruction_length, code, &object,
pc);
formatter->ConsumeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
sizeof(human_buffer), object,
FLAG_disassemble_relative ? offset : pc);
pc += instruction_length;
}
}
void Disassembler::DisassembleCodeHelper(const char* function_fullname,
const Code& code,
bool optimized) {
Zone* zone = Thread::Current()->zone();
LocalVarDescriptors& var_descriptors = LocalVarDescriptors::Handle(zone);
if (FLAG_print_variable_descriptors) {
var_descriptors = code.GetLocalVarDescriptors();
}
THR_Print("Code for %sfunction '%s' {\n", optimized ? "optimized " : "",
function_fullname);
code.Disassemble();
THR_Print("}\n");
#if defined(TARGET_ARCH_IA32)
THR_Print("Pointer offsets for function: {\n");
// Pointer offsets are stored in descending order.
Object& obj = Object::Handle(zone);
for (intptr_t i = code.pointer_offsets_length() - 1; i >= 0; i--) {
const uword addr = code.GetPointerOffsetAt(i) + code.PayloadStart();
obj = *reinterpret_cast<RawObject**>(addr);
THR_Print(" %d : %#" Px " '%s'\n", code.GetPointerOffsetAt(i), addr,
obj.ToCString());
}
THR_Print("}\n");
#else
ASSERT(code.pointer_offsets_length() == 0);
#endif
if (FLAG_use_bare_instructions) {
THR_Print("(No object pool for bare instructions.)\n");
} else {
const ObjectPool& object_pool =
ObjectPool::Handle(zone, code.GetObjectPool());
if (!object_pool.IsNull()) {
object_pool.DebugPrint();
}
}
code.DumpSourcePositions(/*relative_addresses=*/FLAG_disassemble_relative);
THR_Print("PC Descriptors for function '%s' {\n", function_fullname);
PcDescriptors::PrintHeaderString();
const PcDescriptors& descriptors =
PcDescriptors::Handle(zone, code.pc_descriptors());
THR_Print("%s}\n", descriptors.ToCString());
const uword start = code.PayloadStart();
const uword base = FLAG_disassemble_relative ? 0 : start;
#if !defined(DART_PRECOMPILED_RUNTIME)
const Array& deopt_table = Array::Handle(zone, code.deopt_info_array());
if (!deopt_table.IsNull()) {
intptr_t deopt_table_length = DeoptTable::GetLength(deopt_table);
if (deopt_table_length > 0) {
THR_Print("DeoptInfo: {\n");
Smi& offset = Smi::Handle(zone);
TypedData& info = TypedData::Handle(zone);
Smi& reason_and_flags = Smi::Handle(zone);
for (intptr_t i = 0; i < deopt_table_length; ++i) {
DeoptTable::GetEntry(deopt_table, i, &offset, &info, &reason_and_flags);
const intptr_t reason =
DeoptTable::ReasonField::decode(reason_and_flags.Value());
ASSERT((0 <= reason) && (reason < ICData::kDeoptNumReasons));
THR_Print(
"%4" Pd ": 0x%" Px " %s (%s)\n", i, base + offset.Value(),
DeoptInfo::ToCString(deopt_table, info),
DeoptReasonToCString(static_cast<ICData::DeoptReasonId>(reason)));
}
THR_Print("}\n");
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
THR_Print("StackMaps for function '%s' {\n", function_fullname);
if (code.compressed_stackmaps() != CompressedStackMaps::null()) {
const auto& stackmaps =
CompressedStackMaps::Handle(zone, code.compressed_stackmaps());
THR_Print("%s\n", stackmaps.ToCString());
}
THR_Print("}\n");
if (FLAG_print_variable_descriptors) {
THR_Print("Variable Descriptors for function '%s' {\n", function_fullname);
intptr_t var_desc_length =
var_descriptors.IsNull() ? 0 : var_descriptors.Length();
String& var_name = String::Handle(zone);
for (intptr_t i = 0; i < var_desc_length; i++) {
var_name = var_descriptors.GetName(i);
RawLocalVarDescriptors::VarInfo var_info;
var_descriptors.GetInfo(i, &var_info);
const int8_t kind = var_info.kind();
if (kind == RawLocalVarDescriptors::kSavedCurrentContext) {
THR_Print(" saved current CTX reg offset %d\n", var_info.index());
} else {
if (kind == RawLocalVarDescriptors::kContextLevel) {
THR_Print(" context level %d scope %d", var_info.index(),
var_info.scope_id);
} else if (kind == RawLocalVarDescriptors::kStackVar) {
THR_Print(" stack var '%s' offset %d", var_name.ToCString(),
var_info.index());
} else {
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
THR_Print(" context var '%s' level %d offset %d",
var_name.ToCString(), var_info.scope_id, var_info.index());
}
THR_Print(" (valid %s-%s)\n", var_info.begin_pos.ToCString(),
var_info.end_pos.ToCString());
}
}
THR_Print("}\n");
}
THR_Print("Exception Handlers for function '%s' {\n", function_fullname);
const ExceptionHandlers& handlers =
ExceptionHandlers::Handle(zone, code.exception_handlers());
THR_Print("%s}\n", handlers.ToCString());
{
THR_Print("Entry points for function '%s' {\n", function_fullname);
THR_Print(" [code+0x%02" Px "] %" Px " kNormal\n",
Code::entry_point_offset(CodeEntryKind::kNormal) - kHeapObjectTag,
code.EntryPoint() - start + base);
THR_Print(
" [code+0x%02" Px "] %" Px " kMonomorphic\n",
Code::entry_point_offset(CodeEntryKind::kMonomorphic) - kHeapObjectTag,
code.MonomorphicEntryPoint() - start + base);
THR_Print(
" [code+0x%02" Px "] %" Px " kUnchecked\n",
Code::entry_point_offset(CodeEntryKind::kUnchecked) - kHeapObjectTag,
code.UncheckedEntryPoint() - start + base);
THR_Print(" [code+0x%02" Px "] %" Px " kMonomorphicUnchecked\n",
Code::entry_point_offset(CodeEntryKind::kMonomorphicUnchecked) -
kHeapObjectTag,
code.MonomorphicUncheckedEntryPoint() - start + base);
THR_Print("}\n");
}
#if defined(DART_PRECOMPILED_RUNTIME)
THR_Print("(Cannot show static call target functions in AOT runtime.)\n");
#else
{
THR_Print("Static call target functions {\n");
const auto& table = Array::Handle(zone, code.static_calls_target_table());
auto& cls = Class::Handle(zone);
auto& kind_type_and_offset = Smi::Handle(zone);
auto& function = Function::Handle(zone);
auto& code = Code::Handle(zone);
if (!table.IsNull()) {
StaticCallsTable static_calls(table);
for (auto& call : static_calls) {
kind_type_and_offset = call.Get<Code::kSCallTableKindAndOffset>();
function = call.Get<Code::kSCallTableFunctionTarget>();
code = call.Get<Code::kSCallTableCodeTarget>();
auto kind = Code::KindField::decode(kind_type_and_offset.Value());
auto offset = Code::OffsetField::decode(kind_type_and_offset.Value());
auto entry_point =
Code::EntryPointField::decode(kind_type_and_offset.Value());
const char* s_entry_point =
entry_point == Code::kUncheckedEntry ? " <unchecked-entry>" : "";
const char* skind = nullptr;
switch (kind) {
case Code::kPcRelativeCall:
skind = "pc-relative-call";
break;
case Code::kCallViaCode:
skind = "call-via-code";
break;
default:
UNREACHABLE();
}
if (function.IsNull()) {
cls ^= code.owner();
if (cls.IsNull()) {
THR_Print(" 0x%" Px ": %s, (%s)%s\n", base + offset,
code.QualifiedName(), skind, s_entry_point);
} else {
THR_Print(" 0x%" Px ": allocation stub for %s, (%s)%s\n",
base + offset, cls.ToCString(), skind, s_entry_point);
}
} else {
THR_Print(" 0x%" Px ": %s, (%s)%s\n", base + offset,
function.ToFullyQualifiedCString(), skind, s_entry_point);
}
}
}
THR_Print("}\n");
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
if (optimized && FLAG_trace_inlining_intervals) {
code.DumpInlineIntervals();
}
if (FLAG_trace_source_positions) {
code.DumpSourcePositions();
}
}
void Disassembler::DisassembleCode(const Function& function,
const Code& code,
bool optimized) {
const char* function_fullname = function.ToFullyQualifiedCString();
LogBlock lb;
DisassembleCodeHelper(function_fullname, code, optimized);
}
void Disassembler::DisassembleStub(const char* name, const Code& code) {
LogBlock lb;
THR_Print("Code for stub '%s': {\n", name);
DisassembleToStdout formatter;
code.Disassemble(&formatter);
THR_Print("}\n");
const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
if (FLAG_use_bare_instructions) {
THR_Print("(No object pool for bare instructions.)\n");
} else if (!object_pool.IsNull()) {
object_pool.DebugPrint();
}
}
#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
void Disassembler::DisassembleCode(const Function& function,
const Code& code,
bool optimized) {}
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
} // namespace dart