[VM runtime] Initial version of Kernel Bytecode interpreter in VM runtime.
Not fully working yet, only x64, no gc, no frame walking, etc... Change-Id: I4d8357f6d46371bf21c3d54266cfe26163e3c8dc Reviewed-on: https://dart-review.googlesource.com/50021 Commit-Queue: Régis Crelier <regis@google.com> Reviewed-by: Zach Anderson <zra@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
a4772ea629
commit
41fcbd097c
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2018, 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.
|
||||
|
||||
# Script for generating bytecode in a kernel file using Dart 2 pipeline and
|
||||
# interpreting the resulting bytecode.
|
||||
|
||||
# Usage
|
||||
# pkg/vm/tool/test_bytecode ~/foo.dart
|
||||
|
||||
set -e
|
||||
|
||||
# Pick the architecture and mode to build and test.
|
||||
BUILD_FLAGS="-m debug -a x64"
|
||||
BUILD_SUBDIR="DebugX64"
|
||||
|
||||
function follow_links() {
|
||||
file="$1"
|
||||
while [ -h "$file" ]; do
|
||||
# On Mac OS, readlink -f doesn't work.
|
||||
file="$(readlink "$file")"
|
||||
done
|
||||
echo "$file"
|
||||
}
|
||||
|
||||
# Unlike $0, $BASH_SOURCE points to the absolute path of this file.
|
||||
PROG_NAME="$(follow_links "$BASH_SOURCE")"
|
||||
|
||||
# Handle the case where dart-sdk/bin has been symlinked to.
|
||||
CUR_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
|
||||
|
||||
SDK_DIR="$CUR_DIR/../../.."
|
||||
BUILD_DIR="$SDK_DIR/out/$BUILD_SUBDIR"
|
||||
|
||||
# Verify that the VM supports the interpreter, if not, rebuild it.
|
||||
REBUILD=0
|
||||
if [ -f $BUILD_DIR/dart ]
|
||||
then
|
||||
$BUILD_DIR/dart --trace-interpreter-after=-1 \
|
||||
$SDK_DIR/runtime/tests/vm/dart/hello_world_test.dart > /dev/null 2>&1 \
|
||||
|| REBUILD=1
|
||||
else
|
||||
REBUILD=1
|
||||
fi
|
||||
if [ $REBUILD -ne 0 ]
|
||||
then
|
||||
echo "Rebuilding VM to support interpreter"
|
||||
rm -rf $BUILD_DIR
|
||||
$SDK_DIR/tools/gn.py $BUILD_FLAGS --gn-args=dart_use_interpreter=true
|
||||
$SDK_DIR/tools/build.py $BUILD_FLAGS runtime
|
||||
fi
|
||||
|
||||
# Generate dill file containing bytecode for input dart source.
|
||||
$CUR_DIR/gen_kernel --platform $BUILD_DIR/vm_platform_strong.dill \
|
||||
--gen-bytecode $@ -o $BUILD_DIR/test_bytecode.dill
|
||||
|
||||
# Required flags.
|
||||
DART_VM_FLAGS="--preview-dart-2 --optimization-counter-threshold=-1 $DART_VM_FLAGS"
|
||||
|
||||
# Optional flags.
|
||||
# DART_VM_FLAGS="--force-log-flush --dump-kernel-bytecode --trace-interpreter-after=0 $DART_VM_FLAGS"
|
||||
|
||||
# Execute dill file.
|
||||
exec $BUILD_DIR/dart $DART_VM_FLAGS $BUILD_DIR/test_bytecode.dill
|
||||
|
||||
@@ -143,6 +143,10 @@ config("dart_config") {
|
||||
]
|
||||
}
|
||||
|
||||
if (dart_use_interpreter) {
|
||||
defines += [ "DART_USE_INTERPRETER" ]
|
||||
}
|
||||
|
||||
if (!is_win) {
|
||||
cflags = [
|
||||
"-Werror",
|
||||
|
||||
@@ -140,6 +140,11 @@
|
||||
#error DART_PRECOMPILED_RUNTIME and DART_NOSNAPSHOT are mutually exclusive
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME) && defined(DART_NOSNAPSHOT)
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
// TODO(zra): Fix GN build file not to define DART_USE_INTERPRETER in this case.
|
||||
#undef DART_USE_INTERPRETER
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#define NOT_IN_PRECOMPILED(code)
|
||||
#else
|
||||
|
||||
@@ -77,4 +77,8 @@ declare_args() {
|
||||
} else {
|
||||
dart_component_kind = "static_library"
|
||||
}
|
||||
|
||||
# Whether the runtime should interpret called functions for which bytecode
|
||||
# is provided by kernel, rather than compile them before execution.
|
||||
dart_use_interpreter = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
// Copyright (c) 2018, 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/globals.h"
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
|
||||
#include "vm/compiler/assembler/disassembler_kbc.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
#include "vm/cpu.h"
|
||||
#include "vm/instructions.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
static const char* kOpcodeNames[] = {
|
||||
#define BYTECODE_NAME(name, encoding, op1, op2, op3) #name,
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_NAME)
|
||||
#undef BYTECODE_NAME
|
||||
};
|
||||
|
||||
static const size_t kOpcodeCount =
|
||||
sizeof(kOpcodeNames) / sizeof(kOpcodeNames[0]);
|
||||
|
||||
typedef void (*BytecodeFormatter)(char* buffer,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t bc);
|
||||
typedef void (*Fmt)(char** buf, intptr_t* size, uword pc, int32_t value);
|
||||
|
||||
template <typename ValueType>
|
||||
void FormatOperand(char** buf,
|
||||
intptr_t* size,
|
||||
const char* fmt,
|
||||
ValueType value) {
|
||||
intptr_t written = Utils::SNPrint(*buf, *size, fmt, value);
|
||||
if (written < *size) {
|
||||
*buf += written;
|
||||
*size += written;
|
||||
} else {
|
||||
*size = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void Fmt___(char** buf, intptr_t* size, uword pc, int32_t value) {}
|
||||
|
||||
static void Fmttgt(char** buf, intptr_t* size, uword pc, int32_t value) {
|
||||
FormatOperand(buf, size, "-> %" Px, pc + (value << 2));
|
||||
}
|
||||
|
||||
static void Fmtlit(char** buf, intptr_t* size, uword pc, int32_t value) {
|
||||
FormatOperand(buf, size, "k%d", value);
|
||||
}
|
||||
|
||||
static void Fmtreg(char** buf, intptr_t* size, uword pc, int32_t value) {
|
||||
FormatOperand(buf, size, "r%d", value);
|
||||
}
|
||||
|
||||
static void Fmtxeg(char** buf, intptr_t* size, uword pc, int32_t value) {
|
||||
if (value < 0) {
|
||||
FormatOperand(buf, size, "FP[%d]", value);
|
||||
} else {
|
||||
Fmtreg(buf, size, pc, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void Fmtnum(char** buf, intptr_t* size, uword pc, int32_t value) {
|
||||
FormatOperand(buf, size, "#%d", value);
|
||||
}
|
||||
|
||||
static void Apply(char** buf,
|
||||
intptr_t* size,
|
||||
uword pc,
|
||||
Fmt fmt,
|
||||
int32_t value,
|
||||
const char* suffix) {
|
||||
if (*size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fmt(buf, size, pc, value);
|
||||
if (*size > 0) {
|
||||
FormatOperand(buf, size, "%s", suffix);
|
||||
}
|
||||
}
|
||||
|
||||
static void Format0(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {}
|
||||
|
||||
static void FormatT(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t x = static_cast<int32_t>(op) >> 8;
|
||||
Apply(&buf, &size, pc, op1, x, "");
|
||||
}
|
||||
|
||||
static void FormatA(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = (op & 0xFF00) >> 8;
|
||||
Apply(&buf, &size, pc, op1, a, "");
|
||||
}
|
||||
|
||||
static void FormatA_D(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = (op & 0xFF00) >> 8;
|
||||
const int32_t bc = op >> 16;
|
||||
Apply(&buf, &size, pc, op1, a, ", ");
|
||||
Apply(&buf, &size, pc, op2, bc, "");
|
||||
}
|
||||
|
||||
static void FormatA_X(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = (op & 0xFF00) >> 8;
|
||||
const int32_t bc = static_cast<int32_t>(op) >> 16;
|
||||
Apply(&buf, &size, pc, op1, a, ", ");
|
||||
Apply(&buf, &size, pc, op2, bc, "");
|
||||
}
|
||||
|
||||
static void FormatX(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t bc = static_cast<int32_t>(op) >> 16;
|
||||
Apply(&buf, &size, pc, op1, bc, "");
|
||||
}
|
||||
|
||||
static void FormatD(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t bc = op >> 16;
|
||||
Apply(&buf, &size, pc, op1, bc, "");
|
||||
}
|
||||
|
||||
static void FormatA_B_C(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = (op >> 8) & 0xFF;
|
||||
const int32_t b = (op >> 16) & 0xFF;
|
||||
const int32_t c = (op >> 24) & 0xFF;
|
||||
Apply(&buf, &size, pc, op1, a, ", ");
|
||||
Apply(&buf, &size, pc, op2, b, ", ");
|
||||
Apply(&buf, &size, pc, op3, c, "");
|
||||
}
|
||||
|
||||
static void FormatA_B_Y(char* buf,
|
||||
intptr_t size,
|
||||
uword pc,
|
||||
uint32_t op,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = (op >> 8) & 0xFF;
|
||||
const int32_t b = (op >> 16) & 0xFF;
|
||||
const int32_t y = static_cast<int8_t>((op >> 24) & 0xFF);
|
||||
Apply(&buf, &size, pc, op1, a, ", ");
|
||||
Apply(&buf, &size, pc, op2, b, ", ");
|
||||
Apply(&buf, &size, pc, op3, y, "");
|
||||
}
|
||||
|
||||
#define BYTECODE_FORMATTER(name, encoding, op1, op2, op3) \
|
||||
static void Format##name(char* buf, intptr_t size, uword pc, uint32_t op) { \
|
||||
Format##encoding(buf, size, pc, op, Fmt##op1, Fmt##op2, Fmt##op3); \
|
||||
}
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
|
||||
#undef BYTECODE_FORMATTER
|
||||
|
||||
static const BytecodeFormatter kFormatters[] = {
|
||||
#define BYTECODE_FORMATTER(name, encoding, op1, op2, op3) &Format##name,
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
|
||||
#undef BYTECODE_FORMATTER
|
||||
};
|
||||
|
||||
static bool HasLoadFromPool(KBCInstr instr) {
|
||||
switch (KernelBytecode::DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kLoadConstant:
|
||||
case KernelBytecode::kPushConstant:
|
||||
case KernelBytecode::kStaticCall:
|
||||
case KernelBytecode::kIndirectStaticCall:
|
||||
case KernelBytecode::kInstanceCall1:
|
||||
case KernelBytecode::kInstanceCall2:
|
||||
case KernelBytecode::kInstanceCall1Opt:
|
||||
case KernelBytecode::kInstanceCall2Opt:
|
||||
case KernelBytecode::kStoreStaticTOS:
|
||||
case KernelBytecode::kPushStatic:
|
||||
case KernelBytecode::kAllocate:
|
||||
case KernelBytecode::kInstantiateType:
|
||||
case KernelBytecode::kInstantiateTypeArgumentsTOS:
|
||||
case KernelBytecode::kAssertAssignable:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetLoadedObjectAt(uword pc,
|
||||
const ObjectPool& object_pool,
|
||||
Object* obj) {
|
||||
KBCInstr instr = KernelBytecode::At(pc);
|
||||
if (HasLoadFromPool(instr)) {
|
||||
uint16_t index = KernelBytecode::DecodeD(instr);
|
||||
if (object_pool.TypeAt(index) == ObjectPool::kTaggedObject) {
|
||||
*obj = object_pool.ObjectAt(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KernelBytecodeDisassembler::DecodeInstruction(char* hex_buffer,
|
||||
intptr_t hex_size,
|
||||
char* human_buffer,
|
||||
intptr_t human_size,
|
||||
int* out_instr_size,
|
||||
const Code& bytecode,
|
||||
Object** object,
|
||||
uword pc) {
|
||||
#if !defined(PRODUCT)
|
||||
const uint32_t instr = *reinterpret_cast<uint32_t*>(pc);
|
||||
const uint8_t opcode = instr & 0xFF;
|
||||
ASSERT(opcode < kOpcodeCount);
|
||||
size_t name_size =
|
||||
Utils::SNPrint(human_buffer, human_size, "%-10s\t", kOpcodeNames[opcode]);
|
||||
|
||||
human_buffer += name_size;
|
||||
human_size -= name_size;
|
||||
kFormatters[opcode](human_buffer, human_size, pc, instr);
|
||||
|
||||
Utils::SNPrint(hex_buffer, hex_size, "%08x", instr);
|
||||
if (out_instr_size) {
|
||||
*out_instr_size = sizeof(uint32_t);
|
||||
}
|
||||
|
||||
*object = NULL;
|
||||
if (!bytecode.IsNull()) {
|
||||
*object = &Object::Handle();
|
||||
const ObjectPool& pool = ObjectPool::Handle(bytecode.object_pool());
|
||||
if (!GetLoadedObjectAt(pc, pool, *object)) {
|
||||
*object = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void KernelBytecodeDisassembler::Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Code& bytecode) {
|
||||
#if !defined(PRODUCT)
|
||||
const Code::Comments& comments =
|
||||
bytecode.IsNull() ? Code::Comments::New(0) : bytecode.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) {
|
||||
char str[4000];
|
||||
BufferFormatter f(str, sizeof(str));
|
||||
// Comment emitted, emit inlining information.
|
||||
bytecode.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(str);
|
||||
}
|
||||
}
|
||||
int instruction_length;
|
||||
Object* object;
|
||||
DecodeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
|
||||
sizeof(human_buffer), &instruction_length, bytecode,
|
||||
&object, pc);
|
||||
formatter->ConsumeInstruction(bytecode, hex_buffer, sizeof(hex_buffer),
|
||||
human_buffer, sizeof(human_buffer), object,
|
||||
pc);
|
||||
pc += instruction_length;
|
||||
}
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void KernelBytecodeDisassembler::Disassemble(const Function& function) {
|
||||
#if !defined(PRODUCT)
|
||||
ASSERT(function.HasBytecode());
|
||||
const char* function_fullname = function.ToFullyQualifiedCString();
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Code& bytecode = Code::Handle(zone, function.Bytecode());
|
||||
THR_Print("Bytecode for function '%s' {\n", function_fullname);
|
||||
const Instructions& instr = Instructions::Handle(bytecode.instructions());
|
||||
uword start = instr.PayloadStart();
|
||||
DisassembleToStdout stdout_formatter;
|
||||
LogBlock lb;
|
||||
Disassemble(start, start + instr.Size(), &stdout_formatter, bytecode);
|
||||
THR_Print("}\n");
|
||||
|
||||
const ObjectPool& object_pool =
|
||||
ObjectPool::Handle(zone, bytecode.GetObjectPool());
|
||||
object_pool.DebugPrint();
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#ifndef RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
|
||||
#define RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
|
||||
|
||||
#include "vm/globals.h"
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// Disassemble instructions.
|
||||
class KernelBytecodeDisassembler : public AllStatic {
|
||||
public:
|
||||
// Disassemble instructions between start and end.
|
||||
// (The assumption is that start is at a valid instruction).
|
||||
// Return true if all instructions were successfully decoded, false otherwise.
|
||||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Code& bytecode);
|
||||
|
||||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter) {
|
||||
Disassemble(start, end, formatter, Code::Handle());
|
||||
}
|
||||
|
||||
static void Disassemble(uword start, uword end, const Code& bytecode) {
|
||||
#if !defined(PRODUCT)
|
||||
DisassembleToStdout stdout_formatter;
|
||||
LogBlock lb;
|
||||
Disassemble(start, end, &stdout_formatter, bytecode);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Disassemble(uword start, uword end) {
|
||||
#if !defined(PRODUCT)
|
||||
DisassembleToStdout stdout_formatter;
|
||||
LogBlock lb;
|
||||
Disassemble(start, end, &stdout_formatter);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
char* buffer,
|
||||
uintptr_t buffer_size) {
|
||||
#if !defined(PRODUCT)
|
||||
DisassembleToMemory memory_formatter(buffer, buffer_size);
|
||||
LogBlock lb;
|
||||
Disassemble(start, end, &memory_formatter);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Decodes one instruction.
|
||||
// Writes a hexadecimal representation into the hex_buffer and a
|
||||
// human-readable representation into the human_buffer.
|
||||
// Writes the length of the decoded instruction in bytes in out_instr_len.
|
||||
static void DecodeInstruction(char* hex_buffer,
|
||||
intptr_t hex_size,
|
||||
char* human_buffer,
|
||||
intptr_t human_size,
|
||||
int* out_instr_len,
|
||||
const Code& bytecode,
|
||||
Object** object,
|
||||
uword pc);
|
||||
|
||||
static void Disassemble(const Function& function);
|
||||
|
||||
private:
|
||||
static const int kHexadecimalBufferSize = 32;
|
||||
static const int kUserReadableBufferSize = 256;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
|
||||
#endif // RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
|
||||
@@ -26,6 +26,8 @@ compiler_sources = [
|
||||
"assembler/disassembler_arm.cc",
|
||||
"assembler/disassembler_arm64.cc",
|
||||
"assembler/disassembler_dbc.cc",
|
||||
"assembler/disassembler_kbc.cc",
|
||||
"assembler/disassembler_kbc.h",
|
||||
"assembler/disassembler_x86.cc",
|
||||
"backend/block_scheduler.cc",
|
||||
"backend/block_scheduler.h",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "vm/compiler/frontend/kernel_binary_flowgraph.h"
|
||||
#include "vm/compiler/aot/precompiler.h"
|
||||
#include "vm/compiler/assembler/disassembler_kbc.h"
|
||||
#include "vm/compiler/frontend/prologue_builder.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/longjump.h"
|
||||
@@ -14,6 +15,11 @@
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode");
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
|
||||
namespace kernel {
|
||||
|
||||
#define Z (zone_)
|
||||
@@ -907,6 +913,262 @@ InferredTypeMetadata InferredTypeMetadataHelper::GetInferredType(
|
||||
return InferredTypeMetadata(cid, nullable);
|
||||
}
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
void BytecodeMetadataHelper::CopyBytecode(const Function& function) {
|
||||
// TODO(regis): Avoid copying bytecode from mapped kernel binary.
|
||||
const intptr_t node_offset = function.kernel_offset();
|
||||
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
|
||||
if (md_offset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlternativeReadingScope alt(&builder_->reader_, &H.metadata_payloads(),
|
||||
md_offset - MetadataPayloadOffset);
|
||||
|
||||
// Read bytecode.
|
||||
intptr_t bytecode_size = builder_->reader_.ReadUInt();
|
||||
intptr_t bytecode_offset = builder_->reader_.offset();
|
||||
uint8_t* bytecode_data = builder_->reader_.CopyDataIntoZone(
|
||||
builder_->zone_, bytecode_offset, bytecode_size);
|
||||
|
||||
// This enum and the code below reading the constant pool from kernel must be
|
||||
// kept in sync with pkg/vm/lib/bytecode/constant_pool.dart.
|
||||
enum ConstantPoolTag {
|
||||
kInvalid,
|
||||
kNull,
|
||||
kString,
|
||||
kInt,
|
||||
kDouble,
|
||||
kBool,
|
||||
kArgDesc,
|
||||
kICData,
|
||||
kStaticICData,
|
||||
kField,
|
||||
kFieldOffset,
|
||||
kClass,
|
||||
kTypeArgumentsFieldOffset,
|
||||
kTearOff,
|
||||
kType,
|
||||
kTypeArguments,
|
||||
kList,
|
||||
kInstance,
|
||||
kSymbol,
|
||||
kTypeArgumentsForInstanceAllocation,
|
||||
};
|
||||
|
||||
// Read object pool.
|
||||
builder_->reader_.set_offset(bytecode_offset + bytecode_size);
|
||||
intptr_t obj_count = builder_->reader_.ReadListLength();
|
||||
const ObjectPool& obj_pool =
|
||||
ObjectPool::Handle(builder_->zone_, ObjectPool::New(obj_count));
|
||||
Object& obj = Object::Handle(builder_->zone_);
|
||||
Object& elem = Object::Handle(builder_->zone_);
|
||||
Array& array = Array::Handle(builder_->zone_);
|
||||
Field& field = Field::Handle(builder_->zone_);
|
||||
String& name = String::Handle(builder_->zone_);
|
||||
for (intptr_t i = 0; i < obj_count; ++i) {
|
||||
const intptr_t tag = builder_->ReadTag();
|
||||
switch (tag) {
|
||||
case ConstantPoolTag::kInvalid:
|
||||
UNREACHABLE();
|
||||
case ConstantPoolTag::kNull:
|
||||
obj = Object::null();
|
||||
break;
|
||||
case ConstantPoolTag::kString:
|
||||
obj = H.DartString(builder_->ReadStringReference()).raw();
|
||||
ASSERT(obj.IsString());
|
||||
obj = H.Canonicalize(String::Cast(obj));
|
||||
break;
|
||||
case ConstantPoolTag::kInt: {
|
||||
uint32_t low_bits = builder_->ReadUInt32();
|
||||
int64_t value = builder_->ReadUInt32();
|
||||
value = (value << 32) | low_bits;
|
||||
obj = Integer::New(value);
|
||||
} break;
|
||||
case ConstantPoolTag::kDouble: {
|
||||
uint32_t low_bits = builder_->ReadUInt32();
|
||||
uint64_t bits = builder_->ReadUInt32();
|
||||
bits = (bits << 32) | low_bits;
|
||||
double value = bit_cast<double, uint64_t>(bits);
|
||||
obj = Double::New(value);
|
||||
} break;
|
||||
case ConstantPoolTag::kBool:
|
||||
if (builder_->ReadUInt() == 1) {
|
||||
obj = Bool::True().raw();
|
||||
} else {
|
||||
obj = Bool::False().raw();
|
||||
}
|
||||
break;
|
||||
case ConstantPoolTag::kArgDesc: {
|
||||
intptr_t num_arguments = builder_->ReadUInt();
|
||||
intptr_t num_type_args = builder_->ReadUInt();
|
||||
intptr_t num_arg_names = builder_->ReadListLength();
|
||||
if (num_arg_names == 0) {
|
||||
obj = ArgumentsDescriptor::New(num_type_args, num_arguments);
|
||||
} else {
|
||||
array = Array::New(num_arg_names);
|
||||
for (intptr_t j = 0; j < num_arg_names; j++) {
|
||||
array.SetAt(j, H.DartSymbolPlain(builder_->ReadStringReference()));
|
||||
}
|
||||
obj = ArgumentsDescriptor::New(num_type_args, num_arguments, array);
|
||||
}
|
||||
} break;
|
||||
case ConstantPoolTag::kICData: {
|
||||
NameIndex target = builder_->ReadCanonicalNameReference();
|
||||
name = H.DartProcedureName(target).raw();
|
||||
intptr_t arg_desc_index = builder_->ReadUInt();
|
||||
ASSERT(arg_desc_index < i);
|
||||
array ^= obj_pool.ObjectAt(arg_desc_index);
|
||||
// TODO(regis): Should num_args_tested be explicitly provided?
|
||||
obj = ICData::New(function, name,
|
||||
array, // Arguments descriptor.
|
||||
Thread::kNoDeoptId, 1 /* num_args_tested */,
|
||||
ICData::RebindRule::kInstance);
|
||||
#if defined(TAG_IC_DATA)
|
||||
ICData::Cast(obj).set_tag(Instruction::kInstanceCall);
|
||||
#endif
|
||||
} break;
|
||||
case ConstantPoolTag::kStaticICData: {
|
||||
NameIndex target = builder_->ReadCanonicalNameReference();
|
||||
if (H.IsConstructor(target)) {
|
||||
name = H.DartConstructorName(target).raw();
|
||||
elem = H.LookupConstructorByKernelConstructor(target);
|
||||
} else {
|
||||
name = H.DartProcedureName(target).raw();
|
||||
elem = H.LookupStaticMethodByKernelProcedure(target);
|
||||
}
|
||||
ASSERT(elem.IsFunction());
|
||||
intptr_t arg_desc_index = builder_->ReadUInt();
|
||||
ASSERT(arg_desc_index < i);
|
||||
array ^= obj_pool.ObjectAt(arg_desc_index);
|
||||
obj = ICData::New(function, name,
|
||||
array, // Arguments descriptor.
|
||||
Thread::kNoDeoptId, 0 /* num_args_tested */,
|
||||
ICData::RebindRule::kStatic);
|
||||
ICData::Cast(obj).AddTarget(Function::Cast(elem));
|
||||
#if defined(TAG_IC_DATA)
|
||||
ICData::Cast(obj).set_tag(Instruction::kStaticCall);
|
||||
#endif
|
||||
} break;
|
||||
case ConstantPoolTag::kField:
|
||||
obj =
|
||||
H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsField());
|
||||
break;
|
||||
case ConstantPoolTag::kFieldOffset:
|
||||
obj =
|
||||
H.LookupFieldByKernelField(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsField());
|
||||
obj = Smi::New(Field::Cast(obj).Offset() / kWordSize);
|
||||
break;
|
||||
case ConstantPoolTag::kClass:
|
||||
obj =
|
||||
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsClass());
|
||||
break;
|
||||
case ConstantPoolTag::kTypeArgumentsFieldOffset:
|
||||
obj =
|
||||
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsClass());
|
||||
obj = Smi::New(Class::Cast(obj).type_arguments_field_offset() /
|
||||
kWordSize);
|
||||
break;
|
||||
case ConstantPoolTag::kTearOff:
|
||||
obj = H.LookupStaticMethodByKernelProcedure(
|
||||
builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsFunction());
|
||||
obj = Function::Cast(obj).ImplicitClosureFunction();
|
||||
ASSERT(obj.IsFunction());
|
||||
obj = Function::Cast(obj).ImplicitStaticClosure();
|
||||
ASSERT(obj.IsInstance());
|
||||
obj = H.Canonicalize(Instance::Cast(obj));
|
||||
break;
|
||||
case ConstantPoolTag::kType:
|
||||
UNIMPLEMENTED(); // Encoding is under discussion with CFE team.
|
||||
obj = builder_->type_translator_.BuildType().raw();
|
||||
ASSERT(obj.IsAbstractType());
|
||||
break;
|
||||
case ConstantPoolTag::kTypeArguments:
|
||||
UNIMPLEMENTED(); // Encoding is under discussion with CFE team.
|
||||
obj = builder_->type_translator_
|
||||
.BuildTypeArguments(builder_->ReadListLength())
|
||||
.raw();
|
||||
ASSERT(obj.IsNull() || obj.IsTypeArguments());
|
||||
break;
|
||||
case ConstantPoolTag::kList: {
|
||||
obj = builder_->type_translator_.BuildType().raw();
|
||||
ASSERT(obj.IsAbstractType());
|
||||
const intptr_t length = builder_->ReadListLength();
|
||||
array = Array::New(length, AbstractType::Cast(obj));
|
||||
for (intptr_t j = 0; j < length; j++) {
|
||||
intptr_t elem_index = builder_->ReadUInt();
|
||||
ASSERT(elem_index < i);
|
||||
elem = obj_pool.ObjectAt(elem_index);
|
||||
array.SetAt(j, elem);
|
||||
}
|
||||
obj = H.Canonicalize(Array::Cast(obj));
|
||||
ASSERT(!obj.IsNull());
|
||||
} break;
|
||||
case ConstantPoolTag::kInstance: {
|
||||
obj =
|
||||
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsClass());
|
||||
obj = Instance::New(Class::Cast(obj), Heap::kOld);
|
||||
intptr_t elem_index = builder_->ReadUInt();
|
||||
ASSERT(elem_index < i);
|
||||
elem = obj_pool.ObjectAt(elem_index);
|
||||
if (!elem.IsNull()) {
|
||||
ASSERT(elem.IsTypeArguments());
|
||||
Instance::Cast(obj).SetTypeArguments(TypeArguments::Cast(elem));
|
||||
}
|
||||
intptr_t num_fields = builder_->ReadUInt();
|
||||
for (intptr_t j = 0; j < num_fields; j++) {
|
||||
NameIndex field_name = builder_->ReadCanonicalNameReference();
|
||||
ASSERT(H.IsField(field_name));
|
||||
field = H.LookupFieldByKernelField(field_name);
|
||||
intptr_t elem_index = builder_->ReadUInt();
|
||||
ASSERT(elem_index < i);
|
||||
elem = obj_pool.ObjectAt(elem_index);
|
||||
Instance::Cast(obj).SetField(field, elem);
|
||||
}
|
||||
obj = H.Canonicalize(Instance::Cast(obj));
|
||||
} break;
|
||||
case ConstantPoolTag::kSymbol:
|
||||
obj = H.DartSymbolPlain(builder_->ReadStringReference()).raw();
|
||||
ASSERT(String::Cast(obj).IsSymbol());
|
||||
break;
|
||||
case kTypeArgumentsForInstanceAllocation: {
|
||||
obj =
|
||||
H.LookupClassByKernelClass(builder_->ReadCanonicalNameReference());
|
||||
ASSERT(obj.IsClass());
|
||||
intptr_t elem_index = builder_->ReadUInt();
|
||||
ASSERT(elem_index < i);
|
||||
elem = obj_pool.ObjectAt(elem_index);
|
||||
ASSERT(elem.IsNull() || elem.IsTypeArguments());
|
||||
elem = Type::New(Class::Cast(obj), TypeArguments::Cast(elem),
|
||||
TokenPosition::kNoSource);
|
||||
elem = ClassFinalizer::FinalizeType(Class::Cast(obj), Type::Cast(elem));
|
||||
obj = Type::Cast(elem).arguments();
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
obj_pool.SetTypeAt(i, ObjectPool::kTaggedObject);
|
||||
obj_pool.SetObjectAt(i, obj);
|
||||
}
|
||||
|
||||
const Code& bytecode = Code::Handle(
|
||||
builder_->zone_,
|
||||
Code::FinalizeBytecode(reinterpret_cast<void*>(bytecode_data),
|
||||
bytecode_size, obj_pool));
|
||||
function.AttachBytecode(bytecode);
|
||||
|
||||
if (FLAG_dump_kernel_bytecode) {
|
||||
KernelBytecodeDisassembler::Disassemble(function);
|
||||
}
|
||||
}
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
|
||||
StreamingScopeBuilder::StreamingScopeBuilder(ParsedFunction* parsed_function)
|
||||
: result_(NULL),
|
||||
parsed_function_(parsed_function),
|
||||
@@ -5788,6 +6050,17 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph(intptr_t kernel_offset) {
|
||||
|
||||
SetOffset(kernel_offset);
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// TODO(regis): Clean up this logic of when to compile.
|
||||
// If the bytecode was previously loaded, we really want to compile.
|
||||
if (!function.HasBytecode()) {
|
||||
bytecode_metadata_helper_.CopyBytecode(function);
|
||||
if (function.HasBytecode()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// We need to read out the NSM-forwarder bit before we can build scopes.
|
||||
switch (function.kind()) {
|
||||
case RawFunction::kImplicitClosureFunction:
|
||||
@@ -10684,6 +10957,9 @@ void StreamingFlowGraphBuilder::EnsureMetadataIsScanned() {
|
||||
procedure_attributes_metadata_helper_.SetMetadataMappings(
|
||||
offset + kUInt32Size, mappings_num);
|
||||
}
|
||||
} else if (H.StringEquals(tag, BytecodeMetadataHelper::tag())) {
|
||||
bytecode_metadata_helper_.SetMetadataMappings(offset + kUInt32Size,
|
||||
mappings_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,6 +664,19 @@ class ProcedureAttributesMetadataHelper : public MetadataHelper {
|
||||
ProcedureAttributesMetadata* metadata);
|
||||
};
|
||||
|
||||
// Helper class which provides access to bytecode metadata.
|
||||
class BytecodeMetadataHelper : public MetadataHelper {
|
||||
public:
|
||||
static const char* tag() { return "vm.bytecode"; }
|
||||
|
||||
explicit BytecodeMetadataHelper(StreamingFlowGraphBuilder* builder)
|
||||
: MetadataHelper(builder) {}
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
void CopyBytecode(const Function& function);
|
||||
#endif
|
||||
};
|
||||
|
||||
class StreamingDartTypeTranslator {
|
||||
public:
|
||||
StreamingDartTypeTranslator(StreamingFlowGraphBuilder* builder,
|
||||
@@ -1179,6 +1192,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
direct_call_metadata_helper_(this),
|
||||
inferred_type_metadata_helper_(this),
|
||||
procedure_attributes_metadata_helper_(this),
|
||||
bytecode_metadata_helper_(this),
|
||||
metadata_scanned_(false) {}
|
||||
|
||||
StreamingFlowGraphBuilder(TranslationHelper* translation_helper,
|
||||
@@ -1201,6 +1215,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
direct_call_metadata_helper_(this),
|
||||
inferred_type_metadata_helper_(this),
|
||||
procedure_attributes_metadata_helper_(this),
|
||||
bytecode_metadata_helper_(this),
|
||||
metadata_scanned_(false) {}
|
||||
|
||||
StreamingFlowGraphBuilder(TranslationHelper* translation_helper,
|
||||
@@ -1223,6 +1238,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
direct_call_metadata_helper_(this),
|
||||
inferred_type_metadata_helper_(this),
|
||||
procedure_attributes_metadata_helper_(this),
|
||||
bytecode_metadata_helper_(this),
|
||||
metadata_scanned_(false) {}
|
||||
|
||||
virtual ~StreamingFlowGraphBuilder() {}
|
||||
@@ -1552,6 +1568,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
DirectCallMetadataHelper direct_call_metadata_helper_;
|
||||
InferredTypeMetadataHelper inferred_type_metadata_helper_;
|
||||
ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper_;
|
||||
BytecodeMetadataHelper bytecode_metadata_helper_;
|
||||
bool metadata_scanned_;
|
||||
|
||||
friend class ClassHelper;
|
||||
@@ -1559,6 +1576,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
friend class ConstructorHelper;
|
||||
friend class DirectCallMetadataHelper;
|
||||
friend class ProcedureAttributesMetadataHelper;
|
||||
friend class BytecodeMetadataHelper;
|
||||
friend class FieldHelper;
|
||||
friend class FunctionNodeHelper;
|
||||
friend class InferredTypeMetadataHelper;
|
||||
|
||||
@@ -161,7 +161,11 @@ FlowGraph* DartCompilationPipeline::BuildFlowGraph(
|
||||
/* not building var desc */ NULL,
|
||||
/* not inlining */ NULL, optimized, osr_id);
|
||||
FlowGraph* graph = builder.BuildGraph();
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
ASSERT((graph != NULL) || parsed_function->function().HasBytecode());
|
||||
#else
|
||||
ASSERT(graph != NULL);
|
||||
#endif
|
||||
return graph;
|
||||
}
|
||||
FlowGraphBuilder builder(*parsed_function, ic_data_array,
|
||||
@@ -255,6 +259,14 @@ DEFINE_RUNTIME_ENTRY(CompileFunction, 1) {
|
||||
}
|
||||
Exceptions::PropagateError(Error::Cast(result));
|
||||
}
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// TODO(regis): Revisit.
|
||||
if (!function.HasCode() && function.HasBytecode()) {
|
||||
// Function was not actually compiled, but its bytecode was loaded.
|
||||
// Verify that InterpretCall stub code was installed.
|
||||
ASSERT(function.CurrentCode() == StubCode::InterpretCall_entry()->code());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Compiler::CanOptimizeFunction(Thread* thread, const Function& function) {
|
||||
@@ -816,6 +828,13 @@ RawCode* CompileParsedFunctionHelper::Compile(CompilationPipeline* pipeline) {
|
||||
zone, parsed_function(), *ic_data_array, osr_id(), optimized());
|
||||
}
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// TODO(regis): Revisit.
|
||||
if (flow_graph == NULL && function.HasBytecode()) {
|
||||
return Code::null();
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool print_flow_graph =
|
||||
(FLAG_print_flow_graph ||
|
||||
(optimized() && FLAG_print_flow_graph_optimized)) &&
|
||||
@@ -997,6 +1016,14 @@ static RawObject* CompileFunctionHelper(CompilationPipeline* pipeline,
|
||||
}
|
||||
|
||||
const Code& result = Code::Handle(helper.Compile(pipeline));
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// TODO(regis): Revisit.
|
||||
if (result.IsNull() && function.HasBytecode()) {
|
||||
return Object::null();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!result.IsNull()) {
|
||||
if (!optimized) {
|
||||
function.SetWasCompiled(true);
|
||||
|
||||
@@ -953,7 +953,7 @@ class Bytecode {
|
||||
const char* names[] = {
|
||||
#define NAME(name, encoding, op1, op2, op3) #name,
|
||||
BYTECODES_LIST(NAME)
|
||||
#undef DECLARE_BYTECODE
|
||||
#undef NAME
|
||||
};
|
||||
return names[DecodeOpcode(instr)];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/resolver.h"
|
||||
#include "vm/runtime_entry.h"
|
||||
@@ -113,11 +114,32 @@ RawObject* DartEntry::InvokeFunction(const Function& function,
|
||||
ASSERT(thread->IsMutatorThread());
|
||||
ScopedIsolateStackLimits stack_limit(thread, current_sp);
|
||||
if (!function.HasCode()) {
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// The function is not compiled yet. Interpret it if it has bytecode.
|
||||
// The bytecode is loaded as part as an aborted compilation step.
|
||||
if (!function.HasBytecode()) {
|
||||
const Object& result =
|
||||
Object::Handle(zone, Compiler::CompileFunction(thread, function));
|
||||
if (result.IsError()) {
|
||||
return Error::Cast(result).raw();
|
||||
}
|
||||
}
|
||||
if (!function.HasCode() && function.HasBytecode()) {
|
||||
const Code& bytecode = Code::Handle(zone, function.Bytecode());
|
||||
ASSERT(!bytecode.IsNull());
|
||||
ASSERT(thread->no_callback_scope_depth() == 0);
|
||||
SuspendLongJumpScope suspend_long_jump_scope(thread);
|
||||
TransitionToGenerated transition(thread);
|
||||
return Interpreter::Current()->Call(bytecode, arguments_descriptor,
|
||||
arguments, thread);
|
||||
}
|
||||
#else
|
||||
const Object& result =
|
||||
Object::Handle(zone, Compiler::CompileFunction(thread, function));
|
||||
if (result.IsError()) {
|
||||
return Error::Cast(result).raw();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Now Call the invoke stub which will invoke the dart function.
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
|
||||
@@ -125,6 +125,8 @@ class ArgumentsDescriptor : public ValueObject {
|
||||
friend class SnapshotWriter;
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class Simulator;
|
||||
friend class SimulatorHelpers;
|
||||
DISALLOW_COPY_AND_ASSIGN(ArgumentsDescriptor);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#ifndef RUNTIME_VM_INTERPRETER_H_
|
||||
#define RUNTIME_VM_INTERPRETER_H_
|
||||
|
||||
#include "vm/compiler/method_recognizer.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class Isolate;
|
||||
class RawObject;
|
||||
class InterpreterSetjmpBuffer;
|
||||
class Thread;
|
||||
class Code;
|
||||
class Array;
|
||||
class RawICData;
|
||||
class RawImmutableArray;
|
||||
class RawArray;
|
||||
class RawObjectPool;
|
||||
class RawFunction;
|
||||
class ObjectPointerVisitor;
|
||||
|
||||
// Interpreter intrinsic handler. It is invoked on entry to the intrinsified
|
||||
// function via Intrinsic bytecode before the frame is setup.
|
||||
// If the handler returns true then Intrinsic bytecode works as a return
|
||||
// instruction returning the value in result. Otherwise interpreter proceeds to
|
||||
// execute the body of the function.
|
||||
typedef bool (*IntrinsicHandler)(Thread* thread,
|
||||
RawObject** FP,
|
||||
RawObject** result);
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
static const uword kInterpreterStackUnderflowSize = 0x80;
|
||||
|
||||
Interpreter();
|
||||
~Interpreter();
|
||||
|
||||
// The currently executing Interpreter instance, which is associated to the
|
||||
// current isolate
|
||||
static Interpreter* Current();
|
||||
|
||||
// Low address (KBC stack grows up).
|
||||
uword stack_base() const { return stack_base_; }
|
||||
// High address (KBC stack grows up).
|
||||
uword stack_limit() const { return stack_limit_; }
|
||||
|
||||
// The thread's top_exit_frame_info refers to a Dart frame in the interpreter
|
||||
// stack. The interpreter's top_exit_frame_info refers to a C++ frame in the
|
||||
// native stack.
|
||||
uword top_exit_frame_info() const { return top_exit_frame_info_; }
|
||||
void set_top_exit_frame_info(uword value) { top_exit_frame_info_ = value; }
|
||||
|
||||
// Call on program start.
|
||||
static void InitOnce();
|
||||
|
||||
RawObject* Call(const Code& code,
|
||||
const Array& arguments_descriptor,
|
||||
const Array& arguments,
|
||||
Thread* thread);
|
||||
|
||||
void JumpToFrame(uword pc, uword sp, uword fp, Thread* thread);
|
||||
|
||||
uword get_sp() const { return reinterpret_cast<uword>(fp_); } // Yes, fp_.
|
||||
uword get_fp() const { return reinterpret_cast<uword>(fp_); }
|
||||
uword get_pc() const { return pc_; }
|
||||
|
||||
enum IntrinsicId {
|
||||
#define V(test_class_name, test_function_name, enum_name, type, fp) \
|
||||
k##enum_name##Intrinsic,
|
||||
ALL_INTRINSICS_LIST(V) GRAPH_INTRINSICS_LIST(V)
|
||||
#undef V
|
||||
kIntrinsicCount,
|
||||
};
|
||||
|
||||
static bool IsSupportedIntrinsic(IntrinsicId id) {
|
||||
return intrinsics_[id] != NULL;
|
||||
}
|
||||
|
||||
enum SpecialIndex {
|
||||
kExceptionSpecialIndex,
|
||||
kStackTraceSpecialIndex,
|
||||
kSpecialIndexCount
|
||||
};
|
||||
|
||||
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
||||
|
||||
private:
|
||||
uintptr_t* stack_;
|
||||
uword stack_base_;
|
||||
uword stack_limit_;
|
||||
|
||||
RawObject** fp_;
|
||||
uword pc_;
|
||||
DEBUG_ONLY(uint64_t icount_;)
|
||||
|
||||
InterpreterSetjmpBuffer* last_setjmp_buffer_;
|
||||
uword top_exit_frame_info_;
|
||||
|
||||
RawObjectPool* pp_; // Pool Pointer.
|
||||
RawArray* argdesc_; // Arguments Descriptor: used to pass information between
|
||||
// call instruction and the function entry.
|
||||
RawObject* special_[kSpecialIndexCount];
|
||||
|
||||
static IntrinsicHandler intrinsics_[kIntrinsicCount];
|
||||
|
||||
void Exit(Thread* thread,
|
||||
RawObject** base,
|
||||
RawObject** exit_frame,
|
||||
uint32_t* pc);
|
||||
|
||||
void CallRuntime(Thread* thread,
|
||||
RawObject** base,
|
||||
RawObject** exit_frame,
|
||||
uint32_t* pc,
|
||||
intptr_t argc_tag,
|
||||
RawObject** args,
|
||||
RawObject** result,
|
||||
uword target);
|
||||
|
||||
void Invoke(Thread* thread,
|
||||
RawObject** call_base,
|
||||
RawObject** call_top,
|
||||
uint32_t** pc,
|
||||
RawObject*** FP,
|
||||
RawObject*** SP);
|
||||
|
||||
bool InvokeCompiled(Thread* thread,
|
||||
RawFunction* function,
|
||||
RawArray* argdesc,
|
||||
RawObject** call_base,
|
||||
RawObject** call_top,
|
||||
uint32_t** pc,
|
||||
RawObject*** FP,
|
||||
RawObject*** SP);
|
||||
|
||||
bool Deoptimize(Thread* thread,
|
||||
uint32_t** pc,
|
||||
RawObject*** FP,
|
||||
RawObject*** SP,
|
||||
bool is_lazy);
|
||||
|
||||
void InlineCacheMiss(int checked_args,
|
||||
Thread* thread,
|
||||
RawICData* icdata,
|
||||
RawObject** call_base,
|
||||
RawObject** top,
|
||||
uint32_t* pc,
|
||||
RawObject** FP,
|
||||
RawObject** SP);
|
||||
|
||||
void InstanceCall1(Thread* thread,
|
||||
RawICData* icdata,
|
||||
RawObject** call_base,
|
||||
RawObject** call_top,
|
||||
uint32_t** pc,
|
||||
RawObject*** FP,
|
||||
RawObject*** SP,
|
||||
bool optimized);
|
||||
|
||||
void InstanceCall2(Thread* thread,
|
||||
RawICData* icdata,
|
||||
RawObject** call_base,
|
||||
RawObject** call_top,
|
||||
uint32_t** pc,
|
||||
RawObject*** FP,
|
||||
RawObject*** SP,
|
||||
bool optimized);
|
||||
|
||||
void PrepareForTailCall(RawCode* code,
|
||||
RawImmutableArray* args_desc,
|
||||
RawObject** FP,
|
||||
RawObject*** SP,
|
||||
uint32_t** pc);
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
// Returns true if tracing of executed instructions is enabled.
|
||||
bool IsTracingExecution() const;
|
||||
|
||||
// Prints bytecode instruction at given pc for instruction tracing.
|
||||
void TraceInstruction(uint32_t* pc) const;
|
||||
#endif // !defined(PRODUCT)
|
||||
|
||||
// Longjmp support for exceptions.
|
||||
InterpreterSetjmpBuffer* last_setjmp_buffer() { return last_setjmp_buffer_; }
|
||||
void set_last_setjmp_buffer(InterpreterSetjmpBuffer* buffer) {
|
||||
last_setjmp_buffer_ = buffer;
|
||||
}
|
||||
|
||||
friend class InterpreterSetjmpBuffer;
|
||||
DISALLOW_COPY_AND_ASSIGN(Interpreter);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_INTERPRETER_H_
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "vm/flags.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/image_snapshot.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/isolate_reload.h"
|
||||
#include "vm/kernel_isolate.h"
|
||||
#include "vm/lockers.h"
|
||||
@@ -905,6 +906,7 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags)
|
||||
library_tag_handler_(NULL),
|
||||
api_state_(NULL),
|
||||
random_(),
|
||||
interpreter_(NULL),
|
||||
simulator_(NULL),
|
||||
mutex_(new Mutex(NOT_IN_PRODUCT("Isolate::mutex_"))),
|
||||
symbols_mutex_(new Mutex(NOT_IN_PRODUCT("Isolate::symbols_mutex_"))),
|
||||
@@ -980,6 +982,9 @@ Isolate::~Isolate() {
|
||||
delete heap_;
|
||||
delete object_store_;
|
||||
delete api_state_;
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
delete interpreter_;
|
||||
#endif
|
||||
#if defined(USING_SIMULATOR)
|
||||
delete simulator_;
|
||||
#endif
|
||||
@@ -1938,6 +1943,12 @@ void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
if (interpreter() != NULL) {
|
||||
interpreter()->VisitObjectPointers(visitor);
|
||||
}
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
if (simulator() != NULL) {
|
||||
simulator()->VisitObjectPointers(visitor);
|
||||
|
||||
@@ -38,6 +38,7 @@ class HandleScope;
|
||||
class HandleVisitor;
|
||||
class Heap;
|
||||
class ICData;
|
||||
class Interpreter;
|
||||
class IsolateProfilerData;
|
||||
class IsolateReloadContext;
|
||||
class IsolateSpawnState;
|
||||
@@ -403,6 +404,9 @@ class Isolate : public BaseIsolate {
|
||||
|
||||
Random* random() { return &random_; }
|
||||
|
||||
Interpreter* interpreter() const { return interpreter_; }
|
||||
void set_interpreter(Interpreter* value) { interpreter_ = value; }
|
||||
|
||||
Simulator* simulator() const { return simulator_; }
|
||||
void set_simulator(Simulator* value) { simulator_ = value; }
|
||||
|
||||
@@ -936,6 +940,7 @@ class Isolate : public BaseIsolate {
|
||||
Dart_LibraryTagHandler library_tag_handler_;
|
||||
ApiState* api_state_;
|
||||
Random random_;
|
||||
Interpreter* interpreter_;
|
||||
Simulator* simulator_;
|
||||
Mutex* mutex_; // Protects compiler stats.
|
||||
Mutex* symbols_mutex_; // Protects concurrent access to the symbol table.
|
||||
|
||||
@@ -96,13 +96,8 @@ class NativeArguments {
|
||||
|
||||
RawObject* ArgAt(int index) const {
|
||||
ASSERT((index >= 0) && (index < ArgCount()));
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
// On DBC stack is growing upwards, in reverse direction from all other
|
||||
// architectures.
|
||||
RawObject** arg_ptr = &(argv_[index]);
|
||||
#else
|
||||
RawObject** arg_ptr = &(argv_[-index]);
|
||||
#endif
|
||||
RawObject** arg_ptr =
|
||||
&(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]);
|
||||
// Tell MemorySanitizer the RawObject* was initialized (by generated code).
|
||||
MSAN_UNPOISON(arg_ptr, kWordSize);
|
||||
return *arg_ptr;
|
||||
@@ -205,23 +200,32 @@ class NativeArguments {
|
||||
enum ArgcTagBits {
|
||||
kArgcBit = 0,
|
||||
kArgcSize = 24,
|
||||
kFunctionBit = 24,
|
||||
kFunctionBit = kArgcBit + kArgcSize,
|
||||
kFunctionSize = 3,
|
||||
kReverseArgOrderBit = kFunctionBit + kFunctionSize,
|
||||
kReverseArgOrderSize = 1,
|
||||
};
|
||||
class ArgcBits : public BitField<intptr_t, int32_t, kArgcBit, kArgcSize> {};
|
||||
class FunctionBits
|
||||
: public BitField<intptr_t, int, kFunctionBit, kFunctionSize> {};
|
||||
class ReverseArgOrderBit
|
||||
: public BitField<intptr_t, bool, kReverseArgOrderBit, 1> {};
|
||||
friend class Api;
|
||||
friend class BootstrapNatives;
|
||||
friend class Interpreter;
|
||||
friend class Simulator;
|
||||
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
// Allow simulator to create NativeArguments on the stack.
|
||||
#if defined(TARGET_ARCH_DBC) || defined(DART_USE_INTERPRETER)
|
||||
// Allow simulator and interpreter to create NativeArguments in reverse order
|
||||
// on the stack.
|
||||
NativeArguments(Thread* thread,
|
||||
int argc_tag,
|
||||
RawObject** argv,
|
||||
RawObject** retval)
|
||||
: thread_(thread), argc_tag_(argc_tag), argv_(argv), retval_(retval) {}
|
||||
: thread_(thread),
|
||||
argc_tag_(ReverseArgOrderBit::update(kReverseArgOrderBit, argc_tag)),
|
||||
argv_(argv),
|
||||
retval_(retval) {}
|
||||
#endif
|
||||
|
||||
// Since this function is passed a RawObject directly, we need to be
|
||||
|
||||
@@ -5592,10 +5592,46 @@ void Function::AttachCode(const Code& value) const {
|
||||
}
|
||||
|
||||
bool Function::HasCode() const {
|
||||
NoSafepointScope no_safepoint;
|
||||
ASSERT(raw_ptr()->code_ != Code::null());
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
return raw_ptr()->code_ != StubCode::LazyCompile_entry()->code() &&
|
||||
raw_ptr()->code_ != StubCode::InterpretCall_entry()->code();
|
||||
#else
|
||||
return raw_ptr()->code_ != StubCode::LazyCompile_entry()->code();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
void Function::AttachBytecode(const Code& value) const {
|
||||
DEBUG_ASSERT(IsMutatorOrAtSafepoint());
|
||||
// Finish setting up code before activating it.
|
||||
value.set_owner(*this);
|
||||
StorePointer(&raw_ptr()->bytecode_, value.raw());
|
||||
|
||||
// We should not have loaded the bytecode if the function had code.
|
||||
ASSERT(!HasCode());
|
||||
|
||||
// Set the code entry_point to to InterpretCall stub.
|
||||
SetInstructions(Code::Handle(StubCode::InterpretCall_entry()->code()));
|
||||
}
|
||||
|
||||
bool Function::HasBytecode() const {
|
||||
return raw_ptr()->bytecode_ != Code::null();
|
||||
}
|
||||
|
||||
bool Function::HasCode(RawFunction* function) {
|
||||
NoSafepointScope no_safepoint;
|
||||
ASSERT(function->ptr()->code_ != Code::null());
|
||||
return function->ptr()->code_ != StubCode::LazyCompile_entry()->code() &&
|
||||
function->ptr()->code_ != StubCode::InterpretCall_entry()->code();
|
||||
}
|
||||
|
||||
bool Function::HasBytecode(RawFunction* function) {
|
||||
return function->ptr()->bytecode_ != Code::null();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Function::ClearCode() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
@@ -14565,6 +14601,64 @@ RawCode* Code::FinalizeCode(const Function& function,
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
RawCode* Code::FinalizeBytecode(void* bytecode_data,
|
||||
intptr_t bytecode_size,
|
||||
const ObjectPool& object_pool,
|
||||
CodeStatistics* stats /* = nullptr */) {
|
||||
// Allocate the Code and Instructions objects. Code is allocated first
|
||||
// because a GC during allocation of the code will leave the instruction
|
||||
// pages read-only.
|
||||
const intptr_t pointer_offset_count = 0; // No fixups in bytecode.
|
||||
Code& code = Code::ZoneHandle(Code::New(pointer_offset_count));
|
||||
Instructions& instrs = Instructions::ZoneHandle(
|
||||
Instructions::New(bytecode_size, true /* has_single_entry_point */));
|
||||
INC_STAT(Thread::Current(), total_instr_size, bytecode_size);
|
||||
INC_STAT(Thread::Current(), total_code_size, bytecode_size);
|
||||
|
||||
// Copy the bytecode data into the instruction area. No fixups to apply.
|
||||
MemoryRegion instrs_region(reinterpret_cast<void*>(instrs.PayloadStart()),
|
||||
instrs.Size());
|
||||
MemoryRegion bytecode_region(bytecode_data, bytecode_size);
|
||||
// TODO(regis): Avoid copying bytecode.
|
||||
instrs_region.CopyFrom(0, bytecode_region);
|
||||
|
||||
// TODO(regis): Keep following lines or not?
|
||||
code.set_compile_timestamp(OS::GetCurrentMonotonicMicros());
|
||||
// TODO(regis): Do we need to notify CodeObservers for bytecode too?
|
||||
// If so, provide a better name using ToLibNamePrefixedQualifiedCString().
|
||||
CodeObservers::NotifyAll("bytecode", instrs.PayloadStart(),
|
||||
0 /* prologue_offset */, instrs.Size(),
|
||||
false /* optimized */);
|
||||
{
|
||||
NoSafepointScope no_safepoint;
|
||||
|
||||
// Hook up Code and Instructions objects.
|
||||
code.SetActiveInstructions(instrs);
|
||||
code.set_instructions(instrs);
|
||||
code.set_is_alive(true);
|
||||
|
||||
// Set object pool in Instructions object.
|
||||
INC_STAT(Thread::Current(), total_code_size,
|
||||
object_pool.Length() * sizeof(uintptr_t));
|
||||
code.set_object_pool(object_pool.raw());
|
||||
|
||||
if (FLAG_write_protect_code) {
|
||||
uword address = RawObject::ToAddr(instrs.raw());
|
||||
VirtualMemory::Protect(reinterpret_cast<void*>(address),
|
||||
instrs.raw()->Size(), VirtualMemory::kReadExecute);
|
||||
}
|
||||
}
|
||||
// No Code::Comments to set. Default is 0 length Comments.
|
||||
// No prologue was ever entered, optimistically assume nothing was ever
|
||||
// pushed onto the stack.
|
||||
code.SetPrologueOffset(bytecode_size); // TODO(regis): Correct?
|
||||
INC_STAT(Thread::Current(), total_code_size,
|
||||
code.comments().comments_.Length());
|
||||
return code.raw();
|
||||
}
|
||||
#endif // defined(DART_USE_INTERPRETER)
|
||||
|
||||
bool Code::SlowFindRawCodeVisitor::FindObject(RawObject* raw_obj) const {
|
||||
return RawCode::ContainsPC(raw_obj, pc_);
|
||||
}
|
||||
|
||||
@@ -2229,6 +2229,10 @@ class Function : public Object {
|
||||
}
|
||||
void set_unoptimized_code(const Code& value) const;
|
||||
bool HasCode() const;
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
static bool HasCode(RawFunction* function);
|
||||
static bool HasBytecode(RawFunction* function);
|
||||
#endif
|
||||
|
||||
static intptr_t code_offset() { return OFFSET_OF(RawFunction, code_); }
|
||||
|
||||
@@ -2236,6 +2240,12 @@ class Function : public Object {
|
||||
return OFFSET_OF(RawFunction, entry_point_);
|
||||
}
|
||||
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
void AttachBytecode(const Code& bytecode) const;
|
||||
RawCode* Bytecode() const { return raw_ptr()->bytecode_; }
|
||||
bool HasBytecode() const;
|
||||
#endif
|
||||
|
||||
virtual intptr_t Hash() const;
|
||||
|
||||
// Returns true if there is at least one debugger breakpoint
|
||||
@@ -4915,6 +4925,12 @@ class Code : public Object {
|
||||
Assembler* assembler,
|
||||
bool optimized,
|
||||
CodeStatistics* stats = nullptr);
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
static RawCode* FinalizeBytecode(void* bytecode_data,
|
||||
intptr_t bytecode_size,
|
||||
const ObjectPool& object_pool,
|
||||
CodeStatistics* stats = nullptr);
|
||||
#endif
|
||||
#endif
|
||||
static RawCode* LookupCode(uword pc);
|
||||
static RawCode* LookupCodeInVmIsolate(uword pc);
|
||||
|
||||
@@ -260,6 +260,8 @@ enum TypedDataElementType {
|
||||
friend class object; \
|
||||
friend class RawObject; \
|
||||
friend class Heap; \
|
||||
friend class Interpreter; \
|
||||
friend class InterpreterHelpers; \
|
||||
friend class Simulator; \
|
||||
friend class SimulatorHelpers; \
|
||||
DISALLOW_ALLOCATION(); \
|
||||
@@ -725,6 +727,8 @@ class RawObject {
|
||||
friend class CodeLookupTableBuilder; // profiler
|
||||
friend class NativeEntry; // GetClassId
|
||||
friend class WritePointerVisitor; // GetClassId
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class Simulator;
|
||||
friend class SimulatorHelpers;
|
||||
friend class ObjectLocator;
|
||||
@@ -926,6 +930,9 @@ class RawFunction : public RawObject {
|
||||
RawObject** to_no_code() {
|
||||
return reinterpret_cast<RawObject**>(&ptr()->ic_data_array_);
|
||||
}
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
RawCode* bytecode_;
|
||||
#endif
|
||||
RawCode* code_; // Currently active code. Accessed from generated code.
|
||||
NOT_IN_PRECOMPILED(RawCode* unoptimized_code_); // Unoptimized code, keep it
|
||||
// after optimization.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/instructions.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/kernel_isolate.h"
|
||||
#include "vm/message.h"
|
||||
#include "vm/message_handler.h"
|
||||
@@ -1716,6 +1717,42 @@ DEFINE_RUNTIME_ENTRY(InvokeClosureNoSuchMethod, 3) {
|
||||
arguments.SetReturn(result);
|
||||
}
|
||||
|
||||
// Interpret a function call. Should be called only for uncompiled functions.
|
||||
// Arg0: function object
|
||||
// Arg1: ICData or MegamorphicCache
|
||||
// Arg2: arguments descriptor array
|
||||
// Arg3: arguments array
|
||||
DEFINE_RUNTIME_ENTRY(InterpretCall, 4) {
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
const Function& function = Function::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
// TODO(regis): Use icdata.
|
||||
// const Object& ic_data_or_cache = Object::Handle(zone, arguments.ArgAt(1));
|
||||
const Array& orig_arguments_desc =
|
||||
Array::CheckedHandle(zone, arguments.ArgAt(2));
|
||||
const Array& orig_arguments = Array::CheckedHandle(zone, arguments.ArgAt(3));
|
||||
ASSERT(!function.HasCode());
|
||||
ASSERT(function.HasBytecode());
|
||||
const Code& bytecode = Code::Handle(zone, function.Bytecode());
|
||||
Object& result = Object::Handle(zone);
|
||||
Interpreter* interpreter = Interpreter::Current();
|
||||
ASSERT(interpreter != NULL);
|
||||
{
|
||||
TransitionToGenerated transition(thread);
|
||||
result = interpreter->Call(bytecode, orig_arguments_desc, orig_arguments,
|
||||
thread);
|
||||
}
|
||||
if (result.IsError()) {
|
||||
if (result.IsLanguageError()) {
|
||||
Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
|
||||
UNREACHABLE();
|
||||
}
|
||||
Exceptions::PropagateError(Error::Cast(result));
|
||||
}
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// The following code is used to stress test
|
||||
// - deoptimization
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace dart {
|
||||
V(UpdateFieldCid) \
|
||||
V(InitStaticField) \
|
||||
V(CompileFunction) \
|
||||
V(InterpretCall) \
|
||||
V(MonomorphicMiss) \
|
||||
V(SingleTargetMiss) \
|
||||
V(UnlinkedCall)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#ifndef RUNTIME_VM_STACK_FRAME_KBC_H_
|
||||
#define RUNTIME_VM_STACK_FRAME_KBC_H_
|
||||
|
||||
namespace dart {
|
||||
|
||||
/* Kernel Bytecode Frame Layout
|
||||
|
||||
IMPORTANT: KBC stack is growing upwards which is different from all other
|
||||
architectures. This enables efficient addressing for locals via unsigned index.
|
||||
|
||||
| | <- TOS
|
||||
Callee frame | ... |
|
||||
| saved FP | (FP of current frame)
|
||||
| saved PC | (PC of current frame)
|
||||
| code object |
|
||||
| function object |
|
||||
+--------------------+
|
||||
Current frame | ... T| <- SP of current frame
|
||||
| ... T|
|
||||
| first local T| <- FP of current frame
|
||||
| caller's FP *|
|
||||
| caller's PC *|
|
||||
| code object T| (current frame's code object)
|
||||
| function object T| (current frame's function object)
|
||||
+--------------------+
|
||||
Caller frame | last parameter | <- SP of caller frame
|
||||
| ... |
|
||||
|
||||
T against a slot indicates it needs to be traversed during GC.
|
||||
* against a slot indicates that it can be traversed during GC
|
||||
because it will look like a smi to the visitor.
|
||||
*/
|
||||
|
||||
static const int kKBCDartFrameFixedSize = 4; // Function, Code, PC, FP
|
||||
static const int kKBCSavedPcSlotFromSp = 3;
|
||||
|
||||
static const int kKBCFirstObjectSlotFromFp = -4; // Used by GC.
|
||||
static const int kKBCLastFixedObjectSlotFromFp = -3;
|
||||
|
||||
static const int kKBCSavedCallerFpSlotFromFp = -1;
|
||||
static const int kKBCSavedCallerPpSlotFromFp = kKBCSavedCallerFpSlotFromFp;
|
||||
static const int kKBCSavedCallerPcSlotFromFp = -2;
|
||||
static const int kKBCCallerSpSlotFromFp = -kKBCDartFrameFixedSize - 1;
|
||||
static const int kKBCPcMarkerSlotFromFp = -3;
|
||||
static const int kKBCFunctionSlotFromFp = -4;
|
||||
|
||||
// Note: These constants don't match actual KBC behavior. This is done because
|
||||
// setting kKBCFirstLocalSlotFromFp to 0 breaks assumptions spread across the
|
||||
// code.
|
||||
// Instead for the purposes of local variable allocation we pretend that KBC
|
||||
// behaves as other architectures (stack growing downwards) and later fix
|
||||
// these indices during code generation in the backend.
|
||||
static const int kKBCParamEndSlotFromFp = 4; // One slot past last parameter.
|
||||
static const int kKBCFirstLocalSlotFromFp = -1;
|
||||
static const int kKBCExitLinkSlotFromEntryFp = 0;
|
||||
|
||||
// Value for stack limit that is used to cause an interrupt.
|
||||
// Note that on KBC stack is growing upwards so interrupt limit is 0 unlike
|
||||
// on all other architectures.
|
||||
static const uword kKBCInterruptStackLimit = 0;
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_STACK_FRAME_KBC_H_
|
||||
@@ -28,8 +28,11 @@ class SnapshotWriter;
|
||||
V(DeoptForRewind) \
|
||||
V(UpdateStoreBuffer) \
|
||||
V(PrintStopMessage) \
|
||||
V(AllocateArray) \
|
||||
V(AllocateContext) \
|
||||
V(CallToRuntime) \
|
||||
V(LazyCompile) \
|
||||
V(InterpretCall) \
|
||||
V(CallBootstrapNative) \
|
||||
V(CallNoScopeNative) \
|
||||
V(CallAutoScopeNative) \
|
||||
@@ -37,6 +40,7 @@ class SnapshotWriter;
|
||||
V(CallStaticFunction) \
|
||||
V(OptimizeFunction) \
|
||||
V(InvokeDartCode) \
|
||||
V(InvokeDartCodeFromBytecode) \
|
||||
V(DebugStepCheck) \
|
||||
V(UnlinkedCall) \
|
||||
V(MonomorphicMiss) \
|
||||
@@ -52,8 +56,6 @@ class SnapshotWriter;
|
||||
V(OptimizedIdenticalWithNumberCheck) \
|
||||
V(ICCallBreakpoint) \
|
||||
V(RuntimeCallBreakpoint) \
|
||||
V(AllocateArray) \
|
||||
V(AllocateContext) \
|
||||
V(OneArgCheckInlineCache) \
|
||||
V(TwoArgsCheckInlineCache) \
|
||||
V(SmiAddInlineCache) \
|
||||
|
||||
@@ -886,6 +886,10 @@ void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void StubCode::GenerateInvokeDartCodeFromBytecodeStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// Called for inline allocation of contexts.
|
||||
// Input:
|
||||
// R1: number of context variables.
|
||||
@@ -1678,6 +1682,10 @@ void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
|
||||
__ bx(R2);
|
||||
}
|
||||
|
||||
void StubCode::GenerateInterpretCallStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// R9: Contains an ICData.
|
||||
void StubCode::GenerateICCallBreakpointStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
|
||||
@@ -943,6 +943,10 @@ void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
void StubCode::GenerateInvokeDartCodeFromBytecodeStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// Called for inline allocation of contexts.
|
||||
// Input:
|
||||
// R1: number of context variables.
|
||||
@@ -1725,6 +1729,10 @@ void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
|
||||
__ br(R2);
|
||||
}
|
||||
|
||||
void StubCode::GenerateInterpretCallStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// R5: Contains an ICData.
|
||||
void StubCode::GenerateICCallBreakpointStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
|
||||
@@ -800,6 +800,10 @@ void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
void StubCode::GenerateInvokeDartCodeFromBytecodeStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// Called for inline allocation of contexts.
|
||||
// Input:
|
||||
// EDX: number of context variables.
|
||||
@@ -1606,6 +1610,10 @@ void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
|
||||
__ jmp(EAX);
|
||||
}
|
||||
|
||||
void StubCode::GenerateInterpretCallStub(Assembler* assembler) {
|
||||
__ Unimplemented("Interpreter not yet supported");
|
||||
}
|
||||
|
||||
// ECX: Contains an ICData.
|
||||
void StubCode::GenerateICCallBreakpointStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
|
||||
+182
-4
@@ -789,9 +789,10 @@ void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
|
||||
__ pushq(RAX);
|
||||
__ movq(Address(THR, Thread::top_resource_offset()), Immediate(0));
|
||||
__ movq(RAX, Address(THR, Thread::top_exit_frame_info_offset()));
|
||||
__ pushq(RAX);
|
||||
|
||||
// The constant kExitLinkSlotFromEntryFp must be kept in sync with the
|
||||
// code below.
|
||||
__ pushq(RAX);
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
@@ -871,6 +872,146 @@ void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// RSP : points to return address.
|
||||
// RDI : target raw code
|
||||
// RSI : arguments raw descriptor array.
|
||||
// RDX : address of first argument.
|
||||
// RCX : current thread.
|
||||
void StubCode::GenerateInvokeDartCodeFromBytecodeStub(Assembler* assembler) {
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
// Save frame pointer coming in.
|
||||
__ EnterFrame(0);
|
||||
|
||||
const Register kTargetCodeReg = CallingConventions::kArg1Reg;
|
||||
const Register kArgDescReg = CallingConventions::kArg2Reg;
|
||||
const Register kArg0Reg = CallingConventions::kArg3Reg;
|
||||
const Register kThreadReg = CallingConventions::kArg4Reg;
|
||||
|
||||
// Push code object to PC marker slot.
|
||||
__ pushq(Address(kThreadReg,
|
||||
Thread::invoke_dart_code_from_bytecode_stub_offset()));
|
||||
|
||||
// At this point, the stack looks like:
|
||||
// | stub code object
|
||||
// | saved RBP | <-- RBP
|
||||
// | saved PC (return to interpreter's InvokeCompiled) |
|
||||
|
||||
const intptr_t kInitialOffset = 2;
|
||||
// Save arguments descriptor array, later replaced by Smi argument count.
|
||||
const intptr_t kArgumentsDescOffset = -(kInitialOffset)*kWordSize;
|
||||
__ pushq(kArgDescReg);
|
||||
|
||||
// Save C++ ABI callee-saved registers.
|
||||
__ PushRegisters(CallingConventions::kCalleeSaveCpuRegisters,
|
||||
CallingConventions::kCalleeSaveXmmRegisters);
|
||||
|
||||
// If any additional (or fewer) values are pushed, the offsets in
|
||||
// kExitLinkSlotFromEntryFp will need to be changed.
|
||||
|
||||
// Set up THR, which caches the current thread in Dart code.
|
||||
if (THR != kThreadReg) {
|
||||
__ movq(THR, kThreadReg);
|
||||
}
|
||||
|
||||
// Save the current VMTag on the stack.
|
||||
__ movq(RAX, Assembler::VMTagAddress());
|
||||
__ pushq(RAX);
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
|
||||
// Save top resource and top exit frame info. Use RAX as a temporary register.
|
||||
// StackFrameIterator reads the top exit frame info saved in this frame.
|
||||
__ movq(RAX, Address(THR, Thread::top_resource_offset()));
|
||||
__ pushq(RAX);
|
||||
__ movq(Address(THR, Thread::top_resource_offset()), Immediate(0));
|
||||
__ movq(RAX, Address(THR, Thread::top_exit_frame_info_offset()));
|
||||
__ pushq(RAX);
|
||||
|
||||
// The constant kExitLinkSlotFromEntryFp must be kept in sync with the
|
||||
// code below.
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
__ leaq(RAX, Address(RBP, kExitLinkSlotFromEntryFp * kWordSize));
|
||||
__ cmpq(RAX, RSP);
|
||||
__ j(EQUAL, &ok);
|
||||
__ Stop("kExitLinkSlotFromEntryFp mismatch");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
__ movq(Address(THR, Thread::top_exit_frame_info_offset()), Immediate(0));
|
||||
|
||||
// Load arguments descriptor array into R10, which is passed to Dart code.
|
||||
__ movq(R10, kArgDescReg);
|
||||
|
||||
// Push arguments. At this point we only need to preserve kTargetCodeReg.
|
||||
ASSERT(kTargetCodeReg != RDX);
|
||||
|
||||
// Load number of arguments into RBX and adjust count for type arguments.
|
||||
__ movq(RBX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
|
||||
__ cmpq(FieldAddress(R10, ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ addq(RBX, Immediate(Smi::RawValue(1))); // Include the type arguments.
|
||||
__ Bind(&args_count_ok);
|
||||
// Save number of arguments as Smi on stack, replacing saved ArgumentsDesc.
|
||||
__ movq(Address(RBP, kArgumentsDescOffset), RBX);
|
||||
__ SmiUntag(RBX);
|
||||
|
||||
// Compute address of first argument into RDX.
|
||||
ASSERT(kArg0Reg == RDX);
|
||||
|
||||
// Set up arguments for the Dart call.
|
||||
Label push_arguments;
|
||||
Label done_push_arguments;
|
||||
__ j(ZERO, &done_push_arguments, Assembler::kNearJump);
|
||||
__ LoadImmediate(RAX, Immediate(0));
|
||||
__ Bind(&push_arguments);
|
||||
__ pushq(Address(RDX, RAX, TIMES_8, 0));
|
||||
__ incq(RAX);
|
||||
__ cmpq(RAX, RBX);
|
||||
__ j(LESS, &push_arguments, Assembler::kNearJump);
|
||||
__ Bind(&done_push_arguments);
|
||||
|
||||
// Call the Dart code entrypoint.
|
||||
__ xorq(PP, PP); // GC-safe value into PP.
|
||||
__ movq(CODE_REG, kTargetCodeReg);
|
||||
__ movq(kTargetCodeReg, FieldAddress(CODE_REG, Code::entry_point_offset()));
|
||||
__ call(kTargetCodeReg); // R10 is the arguments descriptor array.
|
||||
|
||||
// Read the saved number of passed arguments as Smi.
|
||||
__ movq(RDX, Address(RBP, kArgumentsDescOffset));
|
||||
|
||||
// Get rid of arguments pushed on the stack.
|
||||
__ leaq(RSP, Address(RSP, RDX, TIMES_4, 0)); // RDX is a Smi.
|
||||
|
||||
// Restore the saved top exit frame info and top resource back into the
|
||||
// Isolate structure.
|
||||
__ popq(Address(THR, Thread::top_exit_frame_info_offset()));
|
||||
__ popq(Address(THR, Thread::top_resource_offset()));
|
||||
|
||||
// Restore the current VMTag from the stack.
|
||||
__ popq(Assembler::VMTagAddress());
|
||||
|
||||
// Restore C++ ABI callee-saved registers.
|
||||
__ PopRegisters(CallingConventions::kCalleeSaveCpuRegisters,
|
||||
CallingConventions::kCalleeSaveXmmRegisters);
|
||||
__ set_constant_pool_allowed(false);
|
||||
|
||||
// Restore the frame pointer.
|
||||
__ LeaveFrame();
|
||||
|
||||
__ ret();
|
||||
#else
|
||||
__ Stop("Not using interpreter");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Called for inline allocation of contexts.
|
||||
// Input:
|
||||
// R10: number of context variables.
|
||||
@@ -1647,7 +1788,7 @@ void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
|
||||
}
|
||||
|
||||
// Stub for compiling a function and jumping to the compiled code.
|
||||
// RCX: IC-Data (for methods).
|
||||
// RBX: IC-Data (for methods).
|
||||
// R10: Arguments descriptor.
|
||||
// RAX: Function.
|
||||
void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
|
||||
@@ -1661,9 +1802,46 @@ void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
|
||||
__ popq(R10); // Restore arguments descriptor array.
|
||||
__ LeaveStubFrame();
|
||||
|
||||
// When using the interpreter, the function's code may now point to the
|
||||
// InterpretCall stub. Make sure RAX, R10, and RBX are preserved.
|
||||
__ movq(CODE_REG, FieldAddress(RAX, Function::code_offset()));
|
||||
__ movq(RAX, FieldAddress(RAX, Function::entry_point_offset()));
|
||||
__ jmp(RAX);
|
||||
__ movq(RCX, FieldAddress(RAX, Function::entry_point_offset()));
|
||||
__ jmp(RCX);
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// RBX: IC-Data (for methods).
|
||||
// R10: Arguments descriptor.
|
||||
// RAX: Function.
|
||||
void StubCode::GenerateInterpretCallStub(Assembler* assembler) {
|
||||
#if defined(DART_USE_INTERPRETER)
|
||||
__ EnterStubFrame();
|
||||
__ movq(RDI, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
|
||||
__ pushq(Immediate(0)); // Setup space on stack for result.
|
||||
__ pushq(RAX); // Function.
|
||||
__ pushq(RBX); // ICData/MegamorphicCache.
|
||||
__ pushq(R10); // Arguments descriptor array.
|
||||
|
||||
// Adjust arguments count.
|
||||
__ cmpq(FieldAddress(R10, ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
__ movq(R10, RDI);
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ addq(R10, Immediate(Smi::RawValue(1))); // Include the type arguments.
|
||||
__ Bind(&args_count_ok);
|
||||
|
||||
// R10: Smi-tagged arguments array length.
|
||||
PushArrayOfArguments(assembler);
|
||||
const intptr_t kNumArgs = 4;
|
||||
__ CallRuntime(kInterpretCallRuntimeEntry, kNumArgs);
|
||||
__ Drop(kNumArgs);
|
||||
__ popq(RAX); // Return value.
|
||||
__ LeaveStubFrame();
|
||||
__ ret();
|
||||
#else
|
||||
__ Stop("Not using interpreter");
|
||||
#endif
|
||||
}
|
||||
|
||||
// RBX: Contains an ICData.
|
||||
|
||||
@@ -89,6 +89,8 @@ class Zone;
|
||||
StubCode::FixAllocationStubTarget_entry()->code(), NULL) \
|
||||
V(RawCode*, invoke_dart_code_stub_, \
|
||||
StubCode::InvokeDartCode_entry()->code(), NULL) \
|
||||
V(RawCode*, invoke_dart_code_from_bytecode_stub_, \
|
||||
StubCode::InvokeDartCodeFromBytecode_entry()->code(), NULL) \
|
||||
V(RawCode*, call_to_runtime_stub_, StubCode::CallToRuntime_entry()->code(), \
|
||||
NULL) \
|
||||
V(RawCode*, monomorphic_miss_stub_, \
|
||||
@@ -871,6 +873,7 @@ class Thread : public BaseThread {
|
||||
#undef REUSABLE_FRIEND_DECLARATION
|
||||
|
||||
friend class ApiZone;
|
||||
friend class Interpreter;
|
||||
friend class InterruptChecker;
|
||||
friend class Isolate;
|
||||
friend class IsolateTestHelper;
|
||||
|
||||
@@ -48,7 +48,9 @@ vm_sources = [
|
||||
"compiler_stats.h",
|
||||
"constants_arm.h",
|
||||
"constants_arm64.h",
|
||||
"constants_dbc.h",
|
||||
"constants_ia32.h",
|
||||
"constants_kbc.h",
|
||||
"constants_x64.h",
|
||||
"cpu.h",
|
||||
"cpu_arm.cc",
|
||||
@@ -129,6 +131,8 @@ vm_sources = [
|
||||
"instructions_ia32.h",
|
||||
"instructions_x64.cc",
|
||||
"instructions_x64.h",
|
||||
"interpreter.cc",
|
||||
"interpreter.h",
|
||||
"isolate.cc",
|
||||
"isolate.h",
|
||||
"isolate_reload.cc",
|
||||
@@ -296,7 +300,9 @@ vm_sources = [
|
||||
"stack_frame.h",
|
||||
"stack_frame_arm.h",
|
||||
"stack_frame_arm64.h",
|
||||
"stack_frame_dbc",
|
||||
"stack_frame_ia32.h",
|
||||
"stack_frame_kbc",
|
||||
"stack_frame_x64.h",
|
||||
"stack_trace.cc",
|
||||
"stack_trace.h",
|
||||
|
||||
Reference in New Issue
Block a user