[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:
Régis Crelier
2018-05-09 20:29:27 +00:00
committed by commit-bot@chromium.org
parent a4772ea629
commit 41fcbd097c
32 changed files with 6763 additions and 18 deletions
+66
View File
@@ -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
+4
View File
@@ -143,6 +143,10 @@ config("dart_config") {
]
}
if (dart_use_interpreter) {
defines += [ "DART_USE_INTERPRETER" ]
}
if (!is_win) {
cflags = [
"-Werror",
+5
View File
@@ -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
+4
View File
@@ -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_
+2
View File
@@ -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;
+27
View File
@@ -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);
+1 -1
View File
@@ -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
+22
View File
@@ -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)
+2
View File
@@ -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
+199
View File
@@ -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_
+11
View File
@@ -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);
+5
View File
@@ -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.
+15 -11
View File
@@ -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
+94
View File
@@ -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_);
}
+16
View File
@@ -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);
+7
View File
@@ -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.
+37
View File
@@ -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
+1
View File
@@ -45,6 +45,7 @@ namespace dart {
V(UpdateFieldCid) \
V(InitStaticField) \
V(CompileFunction) \
V(InterpretCall) \
V(MonomorphicMiss) \
V(SingleTargetMiss) \
V(UnlinkedCall)
+68
View File
@@ -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_
+4 -2
View File
@@ -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) \
+8
View File
@@ -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();
+8
View File
@@ -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();
+8
View File
@@ -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
View File
@@ -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.
+3
View File
@@ -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;
+6
View File
@@ -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",