[vm] Initial implementation of dynamic modules in the VM/AOT
TEST=Manually tested dynamic modules Change-Id: Icb2616e414167bd1fbd10f01dea64c57dbdeeac7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/380281 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
a09fbf8468
commit
8fbca8ba67
@@ -172,6 +172,10 @@ config("dart_config") {
|
||||
defines += [ "SUPPORT_PERFETTO" ]
|
||||
}
|
||||
|
||||
if (dart_dynamic_modules) {
|
||||
defines += [ "DART_DYNAMIC_MODULES" ]
|
||||
}
|
||||
|
||||
if (is_fuchsia) {
|
||||
lib_dirs = [ "${fuchsia_arch_root}/lib" ]
|
||||
|
||||
|
||||
+18
-14
@@ -31,21 +31,25 @@ static ScriptPtr FindScript(DartFrameIterator* iterator) {
|
||||
ASSERT(!assert_error_class.IsNull());
|
||||
bool hit_assertion_error = false;
|
||||
for (; stack_frame != nullptr; stack_frame = iterator->NextFrame()) {
|
||||
code = stack_frame->LookupDartCode();
|
||||
if (code.is_optimized()) {
|
||||
InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc());
|
||||
while (!inlined_iterator.Done()) {
|
||||
func = inlined_iterator.function();
|
||||
if (hit_assertion_error) {
|
||||
return func.script();
|
||||
}
|
||||
ASSERT(!hit_assertion_error);
|
||||
hit_assertion_error = (func.Owner() == assert_error_class.ptr());
|
||||
inlined_iterator.Advance();
|
||||
}
|
||||
continue;
|
||||
if (stack_frame->is_interpreted()) {
|
||||
func = stack_frame->LookupDartFunction();
|
||||
} else {
|
||||
func = code.function();
|
||||
code = stack_frame->LookupDartCode();
|
||||
if (code.is_optimized()) {
|
||||
InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc());
|
||||
while (!inlined_iterator.Done()) {
|
||||
func = inlined_iterator.function();
|
||||
if (hit_assertion_error) {
|
||||
return func.script();
|
||||
}
|
||||
ASSERT(!hit_assertion_error);
|
||||
hit_assertion_error = (func.Owner() == assert_error_class.ptr());
|
||||
inlined_iterator.Advance();
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
func = code.function();
|
||||
}
|
||||
}
|
||||
ASSERT(!func.IsNull());
|
||||
if (hit_assertion_error) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "vm/bootstrap_natives.h"
|
||||
|
||||
#include "lib/invocation_mirror.h"
|
||||
#include "vm/bytecode_reader.h"
|
||||
#include "vm/code_patcher.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/exceptions.h"
|
||||
@@ -560,6 +561,61 @@ DEFINE_NATIVE_ENTRY(Internal_boundsCheckForPartialInstantiation, 0, 2) {
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Internal_loadDynamicModule, 0, 1) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(TypedData, module_bytes,
|
||||
arguments->NativeArgAt(0));
|
||||
|
||||
const intptr_t length = module_bytes.LengthInBytes();
|
||||
uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(length));
|
||||
if (data == nullptr) {
|
||||
const auto& exception = Instance::Handle(
|
||||
zone, thread->isolate_group()->object_store()->out_of_memory());
|
||||
Exceptions::Throw(thread, exception);
|
||||
}
|
||||
{
|
||||
NoSafepointScope no_safepoint;
|
||||
// The memory does not overlap.
|
||||
memcpy(data, module_bytes.DataAddr(0), length); // NOLINT
|
||||
}
|
||||
const ExternalTypedData& typed_data = ExternalTypedData::Handle(
|
||||
zone,
|
||||
ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length));
|
||||
auto& function = Function::Handle();
|
||||
{
|
||||
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
bytecode::BytecodeLoader loader(thread, typed_data);
|
||||
function = loader.LoadBytecode();
|
||||
}
|
||||
|
||||
if (function.IsNull()) {
|
||||
return Object::null();
|
||||
}
|
||||
ASSERT(function.is_static());
|
||||
ASSERT(function.is_declared_in_bytecode());
|
||||
auto& result = Object::Handle(zone);
|
||||
if (function.NumParameters() == 0) {
|
||||
result = DartEntry::InvokeFunction(function, Object::empty_array());
|
||||
} else {
|
||||
ASSERT(function.NumParameters() == 1);
|
||||
// <String>[]
|
||||
const auto& arg0 = Array::Handle(
|
||||
zone, Array::New(0, Type::Handle(zone, Type::StringType())));
|
||||
const auto& args = Array::Handle(zone, Array::New(1));
|
||||
args.SetAt(0, arg0);
|
||||
result = DartEntry::InvokeFunction(function, args);
|
||||
}
|
||||
if (result.IsError()) {
|
||||
Exceptions::PropagateError(Error::Cast(result));
|
||||
}
|
||||
return result.ptr();
|
||||
#else
|
||||
Exceptions::ThrowUnsupportedError(
|
||||
"Loading of dynamic modules is not supported.");
|
||||
return Object::null();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(InvocationMirror_unpackTypeArguments, 0, 2) {
|
||||
const TypeArguments& type_arguments =
|
||||
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
|
||||
@@ -46,7 +46,11 @@ static StackTracePtr CurrentStackTrace(Thread* thread,
|
||||
// Collect the frames.
|
||||
StackTraceUtils::CollectFrames(thread, skip_frames,
|
||||
[&](const StackTraceUtils::Frame& frame) {
|
||||
code_array.Add(frame.code);
|
||||
if (!frame.bytecode.IsNull()) {
|
||||
code_array.Add(frame.bytecode);
|
||||
} else {
|
||||
code_array.Add(frame.code);
|
||||
}
|
||||
pc_offset_array.Add(frame.pc_offset);
|
||||
});
|
||||
|
||||
@@ -72,6 +76,7 @@ static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != nullptr); // We expect to find a dart invocation frame.
|
||||
Code& code = Code::Handle(zone);
|
||||
Bytecode& bytecode = Bytecode::Handle(zone);
|
||||
for (; frame != nullptr; frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
@@ -81,10 +86,20 @@ static void AppendFrames(const GrowableObjectArray& code_list,
|
||||
continue;
|
||||
}
|
||||
|
||||
code = frame->LookupDartCode();
|
||||
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
|
||||
code_list.Add(code);
|
||||
pc_offset_list->Add(pc_offset);
|
||||
if (frame->is_interpreted()) {
|
||||
bytecode = frame->LookupDartBytecode();
|
||||
if (bytecode.function() == Function::null()) {
|
||||
continue;
|
||||
}
|
||||
const intptr_t pc_offset = frame->pc() - bytecode.PayloadStart();
|
||||
code_list.Add(bytecode);
|
||||
pc_offset_list->Add(pc_offset);
|
||||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
|
||||
code_list.Add(code);
|
||||
pc_offset_list->Add(pc_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ declare_args() {
|
||||
# being built on platforms which have a problem linking in the Perfetto
|
||||
# library.
|
||||
dart_support_perfetto = true
|
||||
|
||||
# Whether to support dynamic loading and interpretation of Dart bytecode.
|
||||
dart_dynamic_modules = false
|
||||
}
|
||||
|
||||
declare_args() {
|
||||
|
||||
@@ -1702,7 +1702,7 @@ class FunctionSerializationCluster : public SerializationCluster {
|
||||
} else if (kind == Snapshot::kFullJIT) {
|
||||
NOT_IN_PRECOMPILED(s->Push(func->untag()->unoptimized_code()));
|
||||
s->Push(func->untag()->code());
|
||||
s->Push(func->untag()->ic_data_array());
|
||||
s->Push(func->untag()->ic_data_array_or_bytecode());
|
||||
}
|
||||
if (kind != Snapshot::kFullAOT) {
|
||||
NOT_IN_PRECOMPILED(s->Push(func->untag()->positional_parameter_names()));
|
||||
@@ -1737,7 +1737,7 @@ class FunctionSerializationCluster : public SerializationCluster {
|
||||
} else if (s->kind() == Snapshot::kFullJIT) {
|
||||
NOT_IN_PRECOMPILED(WriteCompressedField(func, unoptimized_code));
|
||||
WriteCompressedField(func, code);
|
||||
WriteCompressedField(func, ic_data_array);
|
||||
WriteCompressedField(func, ic_data_array_or_bytecode);
|
||||
}
|
||||
|
||||
if (kind != Snapshot::kFullAOT) {
|
||||
@@ -1917,7 +1917,7 @@ class FunctionDeserializationCluster : public DeserializationCluster {
|
||||
if (kind == Snapshot::kFullJIT) {
|
||||
func->untag()->unoptimized_code_ = static_cast<CodePtr>(d.ReadRef());
|
||||
func->untag()->code_ = static_cast<CodePtr>(d.ReadRef());
|
||||
func->untag()->ic_data_array_ = static_cast<ArrayPtr>(d.ReadRef());
|
||||
func->untag()->ic_data_array_or_bytecode_ = d.ReadRef();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -268,6 +268,7 @@ namespace dart {
|
||||
V(Internal_extractTypeArguments, 2) \
|
||||
V(Internal_prependTypeArguments, 4) \
|
||||
V(Internal_boundsCheckForPartialInstantiation, 2) \
|
||||
V(Internal_loadDynamicModule, 1) \
|
||||
V(Internal_allocateOneByteString, 1) \
|
||||
V(Internal_allocateTwoByteString, 1) \
|
||||
V(Internal_writeIntoOneByteString, 3) \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,521 @@
|
||||
// Copyright (c) 2024, 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_BYTECODE_READER_H_
|
||||
#define RUNTIME_VM_BYTECODE_READER_H_
|
||||
|
||||
#include "vm/globals.h"
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#include "vm/bit_vector.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
#include "vm/hash_table.h"
|
||||
#include "vm/object.h"
|
||||
|
||||
namespace dart {
|
||||
namespace bytecode {
|
||||
|
||||
class BytecodeComponentData;
|
||||
|
||||
class BytecodeLoader {
|
||||
public:
|
||||
BytecodeLoader(Thread* thread, const TypedDataBase& binary);
|
||||
~BytecodeLoader();
|
||||
|
||||
FunctionPtr LoadBytecode();
|
||||
|
||||
TypedDataBasePtr binary() const { return binary_.ptr(); }
|
||||
ArrayPtr bytecode_component_array() const {
|
||||
return bytecode_component_array_.ptr();
|
||||
}
|
||||
|
||||
void SetOffset(const Object& obj, intptr_t offset);
|
||||
intptr_t GetOffset(const Object& obj);
|
||||
|
||||
private:
|
||||
Thread* thread_;
|
||||
const TypedDataBase& binary_;
|
||||
Array& bytecode_component_array_;
|
||||
Array& bytecode_offsets_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BytecodeLoader);
|
||||
};
|
||||
|
||||
class Reader : public ValueObject {
|
||||
public:
|
||||
explicit Reader(const TypedDataBase& typed_data) : typed_data_(&typed_data) {
|
||||
Init();
|
||||
}
|
||||
|
||||
uint32_t ReadUInt32At(intptr_t offset) const {
|
||||
ASSERT((size_ >= 4) && (offset >= 0) && (offset <= size_ - 4));
|
||||
uint32_t value =
|
||||
LoadUnaligned(reinterpret_cast<const uint32_t*>(raw_buffer_ + offset));
|
||||
return Utils::BigEndianToHost32(value);
|
||||
}
|
||||
|
||||
uint32_t ReadUInt32() {
|
||||
uint32_t value = ReadUInt32At(offset_);
|
||||
offset_ += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t ReadUInt() {
|
||||
ASSERT((size_ >= 1) && (offset_ >= 0) && (offset_ <= size_ - 1));
|
||||
|
||||
const uint8_t* buffer = raw_buffer_;
|
||||
uword byte0 = buffer[offset_];
|
||||
if ((byte0 & 0x80) == 0) {
|
||||
// 0...
|
||||
offset_++;
|
||||
return byte0;
|
||||
} else if ((byte0 & 0xc0) == 0x80) {
|
||||
// 10...
|
||||
ASSERT((size_ >= 2) && (offset_ >= 0) && (offset_ <= size_ - 2));
|
||||
uint32_t value =
|
||||
((byte0 & ~static_cast<uword>(0x80)) << 8) | (buffer[offset_ + 1]);
|
||||
offset_ += 2;
|
||||
return value;
|
||||
} else {
|
||||
// 11...
|
||||
ASSERT((size_ >= 4) && (offset_ >= 0) && (offset_ <= size_ - 4));
|
||||
uint32_t value = ((byte0 & ~static_cast<uword>(0xc0)) << 24) |
|
||||
(buffer[offset_ + 1] << 16) |
|
||||
(buffer[offset_ + 2] << 8) | (buffer[offset_ + 3] << 0);
|
||||
offset_ += 4;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t ReadSLEB128() {
|
||||
ReadStream stream(raw_buffer_, size_, offset_);
|
||||
const intptr_t result = stream.ReadSLEB128();
|
||||
offset_ = stream.Position();
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t ReadSLEB128AsInt64() {
|
||||
ReadStream stream(raw_buffer_, size_, offset_);
|
||||
const int64_t result = stream.ReadSLEB128<int64_t>();
|
||||
offset_ = stream.Position();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a TokenPosition from this reader.
|
||||
*/
|
||||
TokenPosition ReadPosition() {
|
||||
// Position is saved as unsigned,
|
||||
// but actually ranges from -1 and up (thus the -1)
|
||||
intptr_t value = ReadUInt() - 1;
|
||||
TokenPosition result = TokenPosition::Deserialize(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
intptr_t ReadListLength() { return ReadUInt(); }
|
||||
|
||||
uint8_t ReadByte() { return raw_buffer_[offset_++]; }
|
||||
|
||||
uint8_t PeekByte() { return raw_buffer_[offset_]; }
|
||||
|
||||
void ReadBytes(uint8_t* buffer, uint8_t size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
buffer[i] = ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
const TypedDataBase* typed_data() { return typed_data_; }
|
||||
|
||||
intptr_t offset() const { return offset_; }
|
||||
void set_offset(intptr_t offset) {
|
||||
ASSERT(offset <= size_);
|
||||
offset_ = offset;
|
||||
}
|
||||
intptr_t size() const { return size_; }
|
||||
|
||||
TypedDataViewPtr ViewFromTo(intptr_t start, intptr_t end) {
|
||||
return typed_data_->ViewFromTo(start, end, Heap::kOld);
|
||||
}
|
||||
|
||||
const uint8_t* BufferAt(intptr_t offset) {
|
||||
ASSERT((offset >= 0) && (offset < size_));
|
||||
return &raw_buffer_[offset];
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AlternativeReadingScope;
|
||||
|
||||
void Init() {
|
||||
ASSERT(typed_data_->IsExternalOrExternalView());
|
||||
raw_buffer_ = reinterpret_cast<uint8_t*>(typed_data_->DataAddr(0));
|
||||
size_ = typed_data_->LengthInBytes();
|
||||
offset_ = 0;
|
||||
}
|
||||
|
||||
// A external typed data or a view on an external typed data.
|
||||
const TypedDataBase* typed_data_ = nullptr;
|
||||
|
||||
// The raw data size/length of [typed_data_].
|
||||
const uint8_t* raw_buffer_ = nullptr;
|
||||
intptr_t size_ = 0;
|
||||
|
||||
intptr_t offset_ = 0;
|
||||
};
|
||||
|
||||
// A helper class that saves the current reader position, goes to another reader
|
||||
// position, and upon destruction, resets to the original reader position.
|
||||
class AlternativeReadingScope {
|
||||
public:
|
||||
AlternativeReadingScope(Reader* reader, intptr_t new_position)
|
||||
: reader_(reader), saved_offset_(reader_->offset_) {
|
||||
reader_->offset_ = new_position;
|
||||
}
|
||||
|
||||
~AlternativeReadingScope() { reader_->offset_ = saved_offset_; }
|
||||
|
||||
private:
|
||||
Reader* const reader_;
|
||||
const intptr_t saved_offset_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AlternativeReadingScope);
|
||||
};
|
||||
|
||||
// Helper class for reading bytecode.
|
||||
class BytecodeReaderHelper : public ValueObject {
|
||||
public:
|
||||
explicit BytecodeReaderHelper(Thread* thread,
|
||||
const TypedDataBase& typed_data);
|
||||
|
||||
explicit BytecodeReaderHelper(Thread* thread,
|
||||
BytecodeComponentData* bytecode_component);
|
||||
|
||||
Reader& reader() { return reader_; }
|
||||
|
||||
void ReadCode(const Function& function, intptr_t code_offset);
|
||||
|
||||
void ReadMembers(const Class& cls, bool discard_fields);
|
||||
|
||||
void ReadFieldDeclarations(const Class& cls, bool discard_fields);
|
||||
void ReadFunctionDeclarations(const Class& cls);
|
||||
void ReadClassDeclaration(const Class& cls);
|
||||
void ReadLibraryDeclaration(const Library& library,
|
||||
bool lookup_classes,
|
||||
const GrowableObjectArray& pending_classes);
|
||||
void ReadLibraryDeclarations(intptr_t num_libraries);
|
||||
|
||||
LibraryPtr ReadMain();
|
||||
|
||||
ArrayPtr ReadBytecodeComponent();
|
||||
void ResetObjects();
|
||||
|
||||
// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
|
||||
// according to covariance attributes of [function] parameters.
|
||||
//
|
||||
// [function] should be declared in bytecode.
|
||||
// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
|
||||
// of function.NumParameters() length.
|
||||
void ReadParameterCovariance(const Function& function,
|
||||
intptr_t code_offset,
|
||||
BitVector* is_covariant,
|
||||
BitVector* is_generic_covariant_impl);
|
||||
|
||||
// Read bytecode PackedObject.
|
||||
ObjectPtr ReadObject();
|
||||
|
||||
private:
|
||||
// These constants should match corresponding constants in class ObjectHandle
|
||||
// (pkg/dart2bytecode/lib/object_table.dart).
|
||||
static const int kReferenceBit = 1 << 0;
|
||||
static const int kIndexShift = 1;
|
||||
static const int kKindShift = 1;
|
||||
static const int kKindMask = 0x0f;
|
||||
static const int kFlagBit0 = 1 << 5;
|
||||
static const int kFlagBit1 = 1 << 6;
|
||||
static const int kFlagBit2 = 1 << 7;
|
||||
static const int kFlagBit3 = 1 << 8;
|
||||
static const int kFlagBit4 = 1 << 9;
|
||||
static const int kFlagBit5 = 1 << 10;
|
||||
static const int kTagMask = (kFlagBit0 | kFlagBit1 | kFlagBit2 | kFlagBit3);
|
||||
static const int kFlagIsNullable = kFlagBit4;
|
||||
static const int kFlagsMask = (kTagMask | kFlagBit4 | kFlagBit5);
|
||||
|
||||
// Code flags, must be in sync with Code constants in
|
||||
// pkg/dart2bytecode/lib/declarations.dart.
|
||||
struct Code {
|
||||
static const int kHasExceptionsTableFlag = 1 << 0;
|
||||
static const int kHasSourcePositionsFlag = 1 << 1;
|
||||
static const int kHasNullableFieldsFlag = 1 << 2;
|
||||
static const int kHasClosuresFlag = 1 << 3;
|
||||
static const int kHasParameterFlagsFlag = 1 << 4;
|
||||
static const int kHasForwardingStubTargetFlag = 1 << 5;
|
||||
static const int kHasDefaultFunctionTypeArgsFlag = 1 << 6;
|
||||
static const int kHasLocalVariablesFlag = 1 << 7;
|
||||
};
|
||||
|
||||
// Closure code flags, must be in sync with ClosureCode constants in
|
||||
// pkg/dart2bytecode/lib/declarations.dart.
|
||||
struct ClosureCode {
|
||||
static const int kHasExceptionsTableFlag = 1 << 0;
|
||||
static const int kHasSourcePositionsFlag = 1 << 1;
|
||||
static const int kHasLocalVariablesFlag = 1 << 2;
|
||||
};
|
||||
|
||||
// Parameter flags, must be in sync with ParameterDeclaration constants in
|
||||
// pkg/dart2bytecode/lib/declarations.dart.
|
||||
struct Parameter {
|
||||
static const int kIsCovariantFlag = 1 << 0;
|
||||
static const int kIsGenericCovariantImplFlag = 1 << 1;
|
||||
static const int kIsFinalFlag = 1 << 2;
|
||||
static const int kIsRequiredFlag = 1 << 3;
|
||||
};
|
||||
|
||||
class FunctionTypeScope : public ValueObject {
|
||||
public:
|
||||
explicit FunctionTypeScope(BytecodeReaderHelper* bytecode_reader,
|
||||
const FunctionType& type)
|
||||
: bytecode_reader_(bytecode_reader) {
|
||||
bytecode_reader_->enclosing_function_types_.Add(&type);
|
||||
}
|
||||
|
||||
~FunctionTypeScope() {
|
||||
bytecode_reader_->enclosing_function_types_.RemoveLast();
|
||||
}
|
||||
|
||||
private:
|
||||
BytecodeReaderHelper* const bytecode_reader_;
|
||||
};
|
||||
|
||||
class FunctionScope : public ValueObject {
|
||||
public:
|
||||
FunctionScope(BytecodeReaderHelper* bytecode_reader,
|
||||
const Function& function,
|
||||
const String& name,
|
||||
const Class& cls)
|
||||
: bytecode_reader_(bytecode_reader) {
|
||||
ASSERT(bytecode_reader_->scoped_function_.IsNull());
|
||||
ASSERT(bytecode_reader_->scoped_function_name_.IsNull());
|
||||
ASSERT(bytecode_reader_->scoped_function_class_.IsNull());
|
||||
ASSERT(name.IsSymbol());
|
||||
bytecode_reader_->scoped_function_ = function.ptr();
|
||||
bytecode_reader_->scoped_function_name_ = name.ptr();
|
||||
bytecode_reader_->scoped_function_class_ = cls.ptr();
|
||||
}
|
||||
|
||||
~FunctionScope() {
|
||||
bytecode_reader_->scoped_function_ = Function::null();
|
||||
bytecode_reader_->scoped_function_name_ = String::null();
|
||||
bytecode_reader_->scoped_function_class_ = Class::null();
|
||||
}
|
||||
|
||||
private:
|
||||
BytecodeReaderHelper* bytecode_reader_;
|
||||
};
|
||||
|
||||
void ReadClosureDeclaration(const Function& function, intptr_t closureIndex);
|
||||
FunctionTypePtr ReadFunctionSignature(const FunctionType& signature,
|
||||
bool has_optional_positional_params,
|
||||
bool has_optional_named_params,
|
||||
bool has_type_params,
|
||||
bool has_positional_param_names,
|
||||
bool has_parameter_flags);
|
||||
void ReadTypeParametersDeclaration(
|
||||
const Class& parameterized_class,
|
||||
const FunctionType& parameterized_signature);
|
||||
|
||||
// Read portion of constant pool corresponding to one function/closure.
|
||||
// Start with [start_index], and stop when reaching EndClosureFunctionScope.
|
||||
// Return index of the last read constant pool entry.
|
||||
intptr_t ReadConstantPool(const Function& function,
|
||||
const ObjectPool& pool,
|
||||
intptr_t start_index);
|
||||
|
||||
BytecodePtr ReadBytecode(const ObjectPool& pool);
|
||||
void ReadExceptionsTable(const Function& function,
|
||||
const Bytecode& bytecode,
|
||||
bool has_exceptions_table);
|
||||
void ReadSourcePositions(const Bytecode& bytecode, bool has_source_positions);
|
||||
void ReadLocalVariables(const Bytecode& bytecode, bool has_local_variables);
|
||||
StringPtr ConstructorName(const Class& cls, const String& name);
|
||||
|
||||
ObjectPtr ReadObjectContents(uint32_t header);
|
||||
ObjectPtr ReadConstObject(intptr_t tag);
|
||||
ObjectPtr ReadType(intptr_t tag, Nullability nullability);
|
||||
StringPtr ReadString(bool is_canonical = true);
|
||||
TypeArgumentsPtr ReadTypeArguments();
|
||||
void SetupFieldAccessorFunction(const Class& klass,
|
||||
const Function& function,
|
||||
const AbstractType& field_type);
|
||||
PatchClassPtr GetPatchClass(const Class& cls, const Script& script);
|
||||
InstancePtr Canonicalize(const Instance& instance);
|
||||
|
||||
// Similar to cls.EnsureClassDeclaration, but may be more efficient if
|
||||
// class is from the current kernel binary.
|
||||
void LoadReferencedClass(const Class& cls);
|
||||
|
||||
Reader reader_;
|
||||
Thread* const thread_;
|
||||
Zone* const zone_;
|
||||
BytecodeComponentData* bytecode_component_;
|
||||
Array* closures_ = nullptr;
|
||||
PatchClass* patch_class_ = nullptr;
|
||||
Array* functions_ = nullptr;
|
||||
intptr_t function_index_ = 0;
|
||||
GrowableArray<const FunctionType*> enclosing_function_types_;
|
||||
Function& scoped_function_;
|
||||
String& scoped_function_name_;
|
||||
Class& scoped_function_class_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BytecodeReaderHelper);
|
||||
};
|
||||
|
||||
class BytecodeComponentData : ValueObject {
|
||||
public:
|
||||
enum {
|
||||
kTypedData,
|
||||
kVersion,
|
||||
kStringsHeaderOffset,
|
||||
kStringsContentsOffset,
|
||||
kObjectOffsetsOffset,
|
||||
kNumObjects,
|
||||
kObjectsContentsOffset,
|
||||
kMainOffset,
|
||||
kNumLibraries,
|
||||
kLibraryIndexOffset,
|
||||
kLibrariesOffset,
|
||||
kNumClasses,
|
||||
kClassesOffset,
|
||||
kMembersOffset,
|
||||
kNumCodes,
|
||||
kCodesOffset,
|
||||
kSourcePositionsOffset,
|
||||
kSourceFilesOffset,
|
||||
kLineStartsOffset,
|
||||
kLocalVariablesOffset,
|
||||
kAnnotationsOffset,
|
||||
kNumFields
|
||||
};
|
||||
|
||||
explicit BytecodeComponentData(const Array& data) : data_(data) {}
|
||||
|
||||
TypedDataBasePtr GetTypedData() const;
|
||||
intptr_t GetVersion() const;
|
||||
intptr_t GetStringsHeaderOffset() const;
|
||||
intptr_t GetStringsContentsOffset() const;
|
||||
intptr_t GetObjectOffsetsOffset() const;
|
||||
intptr_t GetNumObjects() const;
|
||||
intptr_t GetObjectsContentsOffset() const;
|
||||
intptr_t GetMainOffset() const;
|
||||
intptr_t GetNumLibraries() const;
|
||||
intptr_t GetLibraryIndexOffset() const;
|
||||
intptr_t GetLibrariesOffset() const;
|
||||
intptr_t GetNumClasses() const;
|
||||
intptr_t GetClassesOffset() const;
|
||||
intptr_t GetMembersOffset() const;
|
||||
intptr_t GetNumCodes() const;
|
||||
intptr_t GetCodesOffset() const;
|
||||
intptr_t GetSourcePositionsOffset() const;
|
||||
intptr_t GetSourceFilesOffset() const;
|
||||
intptr_t GetLineStartsOffset() const;
|
||||
intptr_t GetLocalVariablesOffset() const;
|
||||
intptr_t GetAnnotationsOffset() const;
|
||||
void SetObject(intptr_t index, const Object& obj) const;
|
||||
ObjectPtr GetObject(intptr_t index) const;
|
||||
|
||||
bool IsNull() const { return data_.IsNull(); }
|
||||
|
||||
static ArrayPtr New(Zone* zone,
|
||||
const TypedDataBase& typed_data,
|
||||
intptr_t version,
|
||||
intptr_t num_objects,
|
||||
intptr_t strings_header_offset,
|
||||
intptr_t strings_contents_offset,
|
||||
intptr_t object_offsets_offset,
|
||||
intptr_t objects_contents_offset,
|
||||
intptr_t main_offset,
|
||||
intptr_t num_libraries,
|
||||
intptr_t library_index_offset,
|
||||
intptr_t libraries_offset,
|
||||
intptr_t num_classes,
|
||||
intptr_t classes_offset,
|
||||
intptr_t members_offset,
|
||||
intptr_t num_codes,
|
||||
intptr_t codes_offset,
|
||||
intptr_t source_positions_offset,
|
||||
intptr_t source_files_offset,
|
||||
intptr_t line_starts_offset,
|
||||
intptr_t local_variables_offset,
|
||||
intptr_t annotations_offset,
|
||||
Heap::Space space);
|
||||
|
||||
private:
|
||||
const Array& data_;
|
||||
};
|
||||
|
||||
class BytecodeReader : public AllStatic {
|
||||
public:
|
||||
// Read declaration of the given class.
|
||||
static void LoadClassDeclaration(const Class& cls);
|
||||
|
||||
// Read members of the given class.
|
||||
static void FinishClassLoading(const Class& cls);
|
||||
|
||||
static void ReadParameterCovariance(const Function& function,
|
||||
BitVector* is_covariant,
|
||||
BitVector* is_generic_covariant_impl);
|
||||
};
|
||||
|
||||
class BytecodeSourcePositionsIterator : ValueObject {
|
||||
public:
|
||||
// These constants should match corresponding constants in class
|
||||
// SourcePositions (pkg/dart2bytecode/lib/source_positions.dart).
|
||||
static const intptr_t kSyntheticCodeMarker = -1;
|
||||
static const intptr_t kYieldPointMarker = -2;
|
||||
|
||||
BytecodeSourcePositionsIterator(Zone* zone, const Bytecode& bytecode)
|
||||
: reader_(TypedDataBase::Handle(zone, bytecode.binary())) {
|
||||
ASSERT(bytecode.HasSourcePositions());
|
||||
reader_.set_offset(bytecode.source_positions_binary_offset());
|
||||
pairs_remaining_ = reader_.ReadUInt();
|
||||
}
|
||||
|
||||
bool MoveNext() {
|
||||
if (pairs_remaining_ == 0) {
|
||||
return false;
|
||||
}
|
||||
ASSERT(pairs_remaining_ > 0);
|
||||
--pairs_remaining_;
|
||||
cur_bci_ += reader_.ReadUInt();
|
||||
cur_token_pos_ += reader_.ReadSLEB128();
|
||||
is_yield_point_ = false;
|
||||
if (cur_token_pos_ == kYieldPointMarker) {
|
||||
const bool result = MoveNext();
|
||||
is_yield_point_ = true;
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uword PcOffset() const { return cur_bci_; }
|
||||
|
||||
TokenPosition TokenPos() const {
|
||||
return (cur_token_pos_ == kSyntheticCodeMarker)
|
||||
? TokenPosition::kNoSource
|
||||
: TokenPosition::Deserialize(cur_token_pos_);
|
||||
}
|
||||
|
||||
bool IsYieldPoint() const { return is_yield_point_; }
|
||||
|
||||
private:
|
||||
Reader reader_;
|
||||
intptr_t pairs_remaining_ = 0;
|
||||
intptr_t cur_bci_ = 0;
|
||||
intptr_t cur_token_pos_ = 0;
|
||||
bool is_yield_point_ = false;
|
||||
};
|
||||
|
||||
} // namespace bytecode
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
#endif // RUNTIME_VM_BYTECODE_READER_H_
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "vm/class_finalizer.h"
|
||||
|
||||
#include "vm/bytecode_reader.h"
|
||||
#include "vm/canonical_tables.h"
|
||||
#include "vm/closure_functions_cache.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
@@ -436,7 +437,7 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#if defined(TARGET_ARCH_X64)
|
||||
static bool IsPotentialExactGeneric(const AbstractType& type) {
|
||||
@@ -521,7 +522,7 @@ void ClassFinalizer::FinalizeMemberTypes(const Class& cls) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
|
||||
Thread* thread = Thread::Current();
|
||||
@@ -531,9 +532,7 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
Zone* zone = thread->zone();
|
||||
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
if (cls.is_type_finalized()) {
|
||||
@@ -585,6 +584,8 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
|
||||
cls.set_is_type_finalized();
|
||||
cls.set_is_isolate_unsendable_due_to_pragma(has_isolate_unsendable_pragma);
|
||||
cls.set_is_future_subtype(is_future_subtype);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_future_subtype && !cls.is_abstract()) {
|
||||
MarkClassCanBeFuture(zone, cls);
|
||||
}
|
||||
@@ -593,7 +594,11 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
|
||||
}
|
||||
|
||||
ClassHiearchyUpdater(zone).Register(cls);
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
@@ -709,7 +714,7 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
Thread* thread = Thread::Current();
|
||||
@@ -729,9 +734,22 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
|
||||
#endif // defined(SUPPORT_TIMELINE)
|
||||
|
||||
// If loading from a kernel, make sure that the class is fully loaded.
|
||||
ASSERT(cls.IsTopLevel() || (cls.kernel_offset() > 0));
|
||||
ASSERT(cls.IsTopLevel() || cls.is_declared_in_bytecode() ||
|
||||
(cls.kernel_offset() > 0));
|
||||
if (!cls.is_loaded()) {
|
||||
kernel::KernelLoader::FinishLoading(cls);
|
||||
if (cls.is_declared_in_bytecode()) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bytecode::BytecodeReader::FinishClassLoading(cls);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
} else {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
kernel::KernelLoader::FinishLoading(cls);
|
||||
#endif
|
||||
}
|
||||
if (cls.is_finalized()) {
|
||||
return;
|
||||
}
|
||||
@@ -747,9 +765,11 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
|
||||
}
|
||||
// Mark as loaded and finalized.
|
||||
cls.Finalize();
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (FLAG_print_classes) {
|
||||
PrintClassInformation(cls);
|
||||
}
|
||||
#endif
|
||||
FinalizeMemberTypes(cls);
|
||||
|
||||
// The rest of finalization for non-top-level class has to be done with
|
||||
@@ -758,7 +778,15 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
|
||||
if (cls.IsTopLevel()) {
|
||||
cls.set_is_allocate_finalized();
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Allocate-finalization is a no-op in AOT, so
|
||||
// mark finalized classed as allocate-finalized eagerly.
|
||||
if (!cls.is_allocate_finalized()) {
|
||||
cls.set_is_allocate_finalized();
|
||||
}
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
@@ -814,15 +842,16 @@ ErrorPtr ClassFinalizer::AllocateFinalizeClass(const Class& cls) {
|
||||
return Error::null();
|
||||
}
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
ErrorPtr ClassFinalizer::LoadClassMembers(const Class& cls) {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
ASSERT(!cls.is_finalized());
|
||||
|
||||
LongJumpScope jump;
|
||||
if (setjmp(*jump.Set()) == 0) {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
cls.EnsureDeclarationLoaded();
|
||||
#endif
|
||||
ASSERT(cls.is_type_finalized());
|
||||
ClassFinalizer::FinalizeClass(cls);
|
||||
return Error::null();
|
||||
@@ -830,6 +859,9 @@ ErrorPtr ClassFinalizer::LoadClassMembers(const Class& cls) {
|
||||
return Thread::Current()->StealStickyError();
|
||||
}
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
void ClassFinalizer::PrintClassInformation(const Class& cls) {
|
||||
Thread* thread = Thread::Current();
|
||||
|
||||
@@ -68,13 +68,17 @@ class ClassFinalizer : public AllStatic {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Makes class instantiatable and usable by generated code.
|
||||
static ErrorPtr AllocateFinalizeClass(const Class& cls);
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
// Completes loading of the class, this populates the function
|
||||
// and fields of the class.
|
||||
//
|
||||
// Returns Error::null() if there is no loading error.
|
||||
static ErrorPtr LoadClassMembers(const Class& cls);
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Verify that the classes have been properly prefinalized. This is
|
||||
// needed during bootstrapping where the classes have been preloaded.
|
||||
static void VerifyBootstrapClasses();
|
||||
@@ -91,8 +95,10 @@ class ClassFinalizer : public AllStatic {
|
||||
const TypeParameters& type_params,
|
||||
FinalizationKind finalization);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
static void FinalizeMemberTypes(const Class& cls);
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
static void PrintClassInformation(const Class& cls);
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ static constexpr intptr_t kClassIdTagMax = (1 << 20) - 1;
|
||||
V(WeakSerializationReference) \
|
||||
V(WeakArray) \
|
||||
V(Code) \
|
||||
V(Bytecode) \
|
||||
V(Instructions) \
|
||||
V(InstructionsSection) \
|
||||
V(InstructionsTable) \
|
||||
|
||||
@@ -1948,8 +1948,15 @@ void Precompiler::TraceForRetainedFunctions() {
|
||||
function ^= functions.At(j);
|
||||
function.DropUncompiledImplicitClosureFunction();
|
||||
|
||||
const bool retained =
|
||||
possibly_retained_functions_.ContainsKey(function);
|
||||
bool retained = possibly_retained_functions_.ContainsKey(function);
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Retain abstract functions annotated with entry point
|
||||
// pragmas as they can be used as targets of interface calls.
|
||||
if (function.is_abstract() &&
|
||||
functions_with_entry_point_pragmas_.ContainsKey(function)) {
|
||||
retained = true;
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
if (retained) {
|
||||
AddTypesOf(function);
|
||||
}
|
||||
@@ -2575,6 +2582,14 @@ void Precompiler::DropTransitiveUserDefinedConstants() {
|
||||
if (cls.constants() == Array::null()) {
|
||||
continue;
|
||||
}
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Retain constant tables of exported classes to allow constant
|
||||
// canonicalization at runtime.
|
||||
if (HasApiUse(cls)) {
|
||||
continue;
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
typedef UnorderedHashSet<CanonicalInstanceTraits> CanonicalInstancesSet;
|
||||
|
||||
CanonicalInstancesSet constants_set(cls.constants());
|
||||
|
||||
@@ -2017,7 +2017,7 @@ void AsmIntrinsifier::Timeline_isDartStreamEnabled(Assembler* assembler,
|
||||
#else
|
||||
Label true_label;
|
||||
// Load TimelineStream*.
|
||||
__ lx(A0, Address(THR, target::Thread::dart_stream_offset()));
|
||||
__ LoadFromOffset(A0, THR, target::Thread::dart_stream_offset());
|
||||
// Load uintptr_t from TimelineStream*.
|
||||
__ lx(A0, Address(A0, target::TimelineStream::enabled_offset()));
|
||||
__ bnez(A0, &true_label, Assembler::kNearJump);
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2024, 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_DYNAMIC_MODULES)
|
||||
|
||||
#include "vm/compiler/assembler/disassembler_kbc.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/bytecode_reader.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
static const char* kOpcodeNames[] = {
|
||||
#define BYTECODE_NAME(name, encoding, kind, op1, op2, op3) #name,
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_NAME)
|
||||
#undef BYTECODE_NAME
|
||||
};
|
||||
|
||||
static const size_t kOpcodeCount =
|
||||
sizeof(kOpcodeNames) / sizeof(kOpcodeNames[0]);
|
||||
static_assert(kOpcodeCount <= 256, "Opcode should fit into a byte");
|
||||
|
||||
typedef void (*BytecodeFormatter)(char* buffer,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr);
|
||||
typedef void (*Fmt)(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
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,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {}
|
||||
|
||||
static void Fmttgt(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {
|
||||
if (FLAG_disassemble_relative) {
|
||||
FormatOperand(buf, size, "-> %" Pd, value);
|
||||
} else {
|
||||
FormatOperand(buf, size, "-> %" Px, instr + value);
|
||||
}
|
||||
}
|
||||
|
||||
static void Fmtlit(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {
|
||||
FormatOperand(buf, size, "k%d", value);
|
||||
}
|
||||
|
||||
static void Fmtreg(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {
|
||||
FormatOperand(buf, size, "r%d", value);
|
||||
}
|
||||
|
||||
static void Fmtxeg(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {
|
||||
if (value < 0) {
|
||||
FormatOperand(buf, size, "FP[%d]", value);
|
||||
} else {
|
||||
Fmtreg(buf, size, instr, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void Fmtnum(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
int32_t value) {
|
||||
FormatOperand(buf, size, "#%d", value);
|
||||
}
|
||||
|
||||
static void Apply(char** buf,
|
||||
intptr_t* size,
|
||||
const KBCInstr* instr,
|
||||
Fmt fmt,
|
||||
int32_t value,
|
||||
const char* suffix) {
|
||||
if (*size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fmt(buf, size, instr, value);
|
||||
if (*size > 0) {
|
||||
FormatOperand(buf, size, "%s", suffix);
|
||||
}
|
||||
}
|
||||
|
||||
static void Format0(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {}
|
||||
|
||||
static void FormatA(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = KernelBytecode::DecodeA(instr);
|
||||
Apply(&buf, &size, instr, op1, a, "");
|
||||
}
|
||||
|
||||
static void FormatD(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t bc = KernelBytecode::DecodeD(instr);
|
||||
Apply(&buf, &size, instr, op1, bc, "");
|
||||
}
|
||||
|
||||
static void FormatX(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t bc = KernelBytecode::DecodeX(instr);
|
||||
Apply(&buf, &size, instr, op1, bc, "");
|
||||
}
|
||||
|
||||
static void FormatT(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t x = KernelBytecode::DecodeT(instr);
|
||||
Apply(&buf, &size, instr, op1, x, "");
|
||||
}
|
||||
|
||||
static void FormatA_E(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = KernelBytecode::DecodeA(instr);
|
||||
const int32_t e = KernelBytecode::DecodeE(instr);
|
||||
Apply(&buf, &size, instr, op1, a, ", ");
|
||||
Apply(&buf, &size, instr, op2, e, "");
|
||||
}
|
||||
|
||||
static void FormatA_Y(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = KernelBytecode::DecodeA(instr);
|
||||
const int32_t y = KernelBytecode::DecodeY(instr);
|
||||
Apply(&buf, &size, instr, op1, a, ", ");
|
||||
Apply(&buf, &size, instr, op2, y, "");
|
||||
}
|
||||
|
||||
static void FormatD_F(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t d = KernelBytecode::DecodeD(instr);
|
||||
const int32_t f = KernelBytecode::DecodeF(instr);
|
||||
Apply(&buf, &size, instr, op1, d, ", ");
|
||||
Apply(&buf, &size, instr, op2, f, "");
|
||||
}
|
||||
|
||||
static void FormatA_B_C(char* buf,
|
||||
intptr_t size,
|
||||
KernelBytecode::Opcode opcode,
|
||||
const KBCInstr* instr,
|
||||
Fmt op1,
|
||||
Fmt op2,
|
||||
Fmt op3) {
|
||||
const int32_t a = KernelBytecode::DecodeA(instr);
|
||||
const int32_t b = KernelBytecode::DecodeB(instr);
|
||||
const int32_t c = KernelBytecode::DecodeC(instr);
|
||||
Apply(&buf, &size, instr, op1, a, ", ");
|
||||
Apply(&buf, &size, instr, op2, b, ", ");
|
||||
Apply(&buf, &size, instr, op3, c, "");
|
||||
}
|
||||
|
||||
#define BYTECODE_FORMATTER(name, encoding, kind, op1, op2, op3) \
|
||||
static void Format##name(char* buf, intptr_t size, \
|
||||
KernelBytecode::Opcode opcode, \
|
||||
const KBCInstr* instr) { \
|
||||
Format##encoding(buf, size, opcode, instr, Fmt##op1, Fmt##op2, Fmt##op3); \
|
||||
}
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
|
||||
#undef BYTECODE_FORMATTER
|
||||
|
||||
static const BytecodeFormatter kFormatters[] = {
|
||||
#define BYTECODE_FORMATTER(name, encoding, kind, op1, op2, op3) &Format##name,
|
||||
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
|
||||
#undef BYTECODE_FORMATTER
|
||||
};
|
||||
|
||||
static intptr_t GetConstantPoolIndex(const KBCInstr* instr) {
|
||||
switch (KernelBytecode::DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kLoadConstant:
|
||||
case KernelBytecode::kLoadConstant_Wide:
|
||||
case KernelBytecode::kInstantiateTypeArgumentsTOS:
|
||||
case KernelBytecode::kInstantiateTypeArgumentsTOS_Wide:
|
||||
case KernelBytecode::kAssertAssignable:
|
||||
case KernelBytecode::kAssertAssignable_Wide:
|
||||
return KernelBytecode::DecodeE(instr);
|
||||
|
||||
case KernelBytecode::kPushConstant:
|
||||
case KernelBytecode::kPushConstant_Wide:
|
||||
case KernelBytecode::kInitLateField:
|
||||
case KernelBytecode::kInitLateField_Wide:
|
||||
case KernelBytecode::kStoreStaticTOS:
|
||||
case KernelBytecode::kStoreStaticTOS_Wide:
|
||||
case KernelBytecode::kLoadStatic:
|
||||
case KernelBytecode::kLoadStatic_Wide:
|
||||
case KernelBytecode::kAllocate:
|
||||
case KernelBytecode::kAllocate_Wide:
|
||||
case KernelBytecode::kAllocateClosure:
|
||||
case KernelBytecode::kAllocateClosure_Wide:
|
||||
case KernelBytecode::kInstantiateType:
|
||||
case KernelBytecode::kInstantiateType_Wide:
|
||||
case KernelBytecode::kDirectCall:
|
||||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kUncheckedDirectCall:
|
||||
case KernelBytecode::kUncheckedDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
case KernelBytecode::kUncheckedInterfaceCall_Wide:
|
||||
case KernelBytecode::kDynamicCall:
|
||||
case KernelBytecode::kDynamicCall_Wide:
|
||||
return KernelBytecode::DecodeD(instr);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetLoadedObjectAt(uword pc,
|
||||
const ObjectPool& object_pool,
|
||||
Object* obj) {
|
||||
const KBCInstr* instr = reinterpret_cast<const KBCInstr*>(pc);
|
||||
const intptr_t index = GetConstantPoolIndex(instr);
|
||||
if (index >= 0) {
|
||||
if (object_pool.TypeAt(index) == ObjectPool::EntryType::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 Bytecode& bytecode,
|
||||
Object** object,
|
||||
uword pc) {
|
||||
const KBCInstr* instr = reinterpret_cast<const KBCInstr*>(pc);
|
||||
const KernelBytecode::Opcode opcode = KernelBytecode::DecodeOpcode(instr);
|
||||
const intptr_t instr_size = KernelBytecode::kInstructionSize[opcode];
|
||||
|
||||
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, opcode, instr);
|
||||
|
||||
const intptr_t kCharactersPerByte = 3;
|
||||
if (hex_size > instr_size * kCharactersPerByte) {
|
||||
for (intptr_t i = 0; i < instr_size; ++i) {
|
||||
Utils::SNPrint(hex_buffer + (i * kCharactersPerByte),
|
||||
hex_size - (i * kCharactersPerByte), " %02x", instr[i]);
|
||||
}
|
||||
}
|
||||
if (out_instr_size != nullptr) {
|
||||
*out_instr_size = instr_size;
|
||||
}
|
||||
|
||||
*object = NULL;
|
||||
if (!bytecode.IsNull()) {
|
||||
*object = &Object::Handle();
|
||||
const ObjectPool& pool = ObjectPool::Handle(bytecode.object_pool());
|
||||
if (!GetLoadedObjectAt(pc, pool, *object)) {
|
||||
*object = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KernelBytecodeDisassembler::Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter,
|
||||
const Bytecode& bytecode) {
|
||||
#if !defined(PRODUCT)
|
||||
ASSERT(formatter != NULL);
|
||||
char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
|
||||
char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
|
||||
uword pc = start;
|
||||
GrowableArray<const Function*> inlined_functions;
|
||||
GrowableArray<TokenPosition> token_positions;
|
||||
while (pc < end) {
|
||||
int instruction_length;
|
||||
Object* object;
|
||||
DecodeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
|
||||
sizeof(human_buffer), &instruction_length, bytecode,
|
||||
&object, pc);
|
||||
formatter->ConsumeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
|
||||
sizeof(human_buffer), object,
|
||||
FLAG_disassemble_relative ? pc - start : 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 Bytecode& bytecode = Bytecode::Handle(zone, function.GetBytecode());
|
||||
THR_Print("Bytecode for function '%s' {\n", function_fullname);
|
||||
const uword start = bytecode.PayloadStart();
|
||||
const uword base = FLAG_disassemble_relative ? 0 : start;
|
||||
DisassembleToStdout stdout_formatter;
|
||||
LogBlock lb;
|
||||
Disassemble(start, start + bytecode.Size(), &stdout_formatter, bytecode);
|
||||
THR_Print("}\n");
|
||||
|
||||
const ObjectPool& object_pool =
|
||||
ObjectPool::Handle(zone, bytecode.object_pool());
|
||||
object_pool.DebugPrint();
|
||||
|
||||
THR_Print("PC Descriptors for function '%s' {\n", function_fullname);
|
||||
const PcDescriptors& descriptors =
|
||||
PcDescriptors::Handle(zone, bytecode.pc_descriptors());
|
||||
THR_Print("%s}\n", descriptors.ToCString());
|
||||
|
||||
if (bytecode.HasSourcePositions()) {
|
||||
THR_Print("Source positions for function '%s' {\n", function_fullname);
|
||||
// 4 bits per hex digit + 2 for "0x".
|
||||
const int addr_width = (kBitsPerWord / 4) + 2;
|
||||
// "*" in a printf format specifier tells it to read the field width from
|
||||
// the printf argument list.
|
||||
THR_Print("%-*s\tpos\tline\tcolumn\tyield\n", addr_width, "pc");
|
||||
const Script& script = Script::Handle(zone, function.script());
|
||||
bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
|
||||
while (iter.MoveNext()) {
|
||||
TokenPosition pos = iter.TokenPos();
|
||||
intptr_t line = -1, column = -1;
|
||||
script.GetTokenLocation(pos, &line, &column);
|
||||
THR_Print("%#-*" Px "\t%s\t%" Pd "\t%" Pd "\t%s\n", addr_width,
|
||||
base + iter.PcOffset(), pos.ToCString(), line, column,
|
||||
iter.IsYieldPoint() ? "yield" : "");
|
||||
}
|
||||
THR_Print("}\n");
|
||||
}
|
||||
|
||||
THR_Print("Exception Handlers for function '%s' {\n", function_fullname);
|
||||
const ExceptionHandlers& handlers =
|
||||
ExceptionHandlers::Handle(zone, bytecode.exception_handlers());
|
||||
THR_Print("%s}\n", handlers.ToCString());
|
||||
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2024, 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_DYNAMIC_MODULES)
|
||||
|
||||
#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 Bytecode& bytecode);
|
||||
|
||||
static void Disassemble(uword start,
|
||||
uword end,
|
||||
DisassemblyFormatter* formatter) {
|
||||
Disassemble(start, end, formatter, Bytecode::Handle());
|
||||
}
|
||||
|
||||
static void Disassemble(uword start, uword end, const Bytecode& 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 Bytecode& 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_DYNAMIC_MODULES)
|
||||
|
||||
#endif // RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
|
||||
@@ -809,6 +809,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// R0: Closure with a cached entry point.
|
||||
__ ldr(R2, compiler::FieldAddress(
|
||||
R0, compiler::target::Closure::entry_point_offset()));
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
ASSERT(FUNCTION_REG != R2);
|
||||
__ ldr(FUNCTION_REG, compiler::FieldAddress(
|
||||
R0, compiler::target::Closure::function_offset()));
|
||||
#endif
|
||||
} else {
|
||||
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
|
||||
// FUNCTION_REG: Function.
|
||||
|
||||
@@ -651,6 +651,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// R0: Closure with a cached entry point.
|
||||
__ LoadFieldFromOffset(R2, R0,
|
||||
compiler::target::Closure::entry_point_offset());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
ASSERT(FUNCTION_REG != R2);
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
FUNCTION_REG, R0, compiler::target::Closure::function_offset());
|
||||
#endif
|
||||
} else {
|
||||
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
|
||||
// FUNCTION_REG: Function.
|
||||
|
||||
@@ -700,6 +700,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// T0: Closure with a cached entry point.
|
||||
__ LoadFieldFromOffset(A1, T0,
|
||||
compiler::target::Closure::entry_point_offset());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
ASSERT(FUNCTION_REG != A1);
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
FUNCTION_REG, T0, compiler::target::Closure::function_offset());
|
||||
#endif
|
||||
} else {
|
||||
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
|
||||
// FUNCTION_REG: Function.
|
||||
|
||||
@@ -6615,6 +6615,12 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// RAX: Closure with cached entry point.
|
||||
__ movq(RCX, compiler::FieldAddress(
|
||||
RAX, compiler::target::Closure::entry_point_offset()));
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
ASSERT(FUNCTION_REG != RCX);
|
||||
__ LoadCompressed(FUNCTION_REG,
|
||||
compiler::FieldAddress(
|
||||
RAX, compiler::target::Closure::function_offset()));
|
||||
#endif
|
||||
} else {
|
||||
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
|
||||
// FUNCTION_REG: Function.
|
||||
|
||||
@@ -205,6 +205,8 @@ disassembler_sources = [
|
||||
"assembler/disassembler.h",
|
||||
"assembler/disassembler_arm.cc",
|
||||
"assembler/disassembler_arm64.cc",
|
||||
"assembler/disassembler_kbc.cc",
|
||||
"assembler/disassembler_kbc.h",
|
||||
"assembler/disassembler_riscv.cc",
|
||||
"assembler/disassembler_x86.cc",
|
||||
]
|
||||
|
||||
@@ -798,6 +798,7 @@ LocalVariable* FlowGraphBuilder::LookupVariable(intptr_t kernel_offset) {
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraph() {
|
||||
const Function& function = parsed_function_->function();
|
||||
ASSERT(!function.is_declared_in_bytecode());
|
||||
|
||||
#ifdef DEBUG
|
||||
// Check that all functions that are explicitly marked as recognized with the
|
||||
|
||||
@@ -1390,7 +1390,6 @@ class KernelReaderHelper {
|
||||
friend class VariableDeclarationHelper;
|
||||
friend class ObfuscationProhibitionsMetadataHelper;
|
||||
friend class LoadingUnitsMetadataHelper;
|
||||
friend bool NeedsDynamicInvocationForwarder(const Function& function);
|
||||
friend ArrayPtr CollectConstConstructorCoverageFrom(
|
||||
const Script& interesting_script);
|
||||
|
||||
|
||||
@@ -877,6 +877,12 @@ class KernelProgramInfo : public AllStatic {
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class Bytecode : public AllStatic {
|
||||
public:
|
||||
static word InstanceSize();
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
class PcDescriptors : public AllStatic {
|
||||
public:
|
||||
static word HeaderSize();
|
||||
@@ -1215,6 +1221,8 @@ class Thread : public AllStatic {
|
||||
static word slow_type_test_stub_offset();
|
||||
static word call_to_runtime_stub_offset();
|
||||
static word invoke_dart_code_stub_offset();
|
||||
static word interpret_call_entry_point_offset();
|
||||
static word invoke_dart_code_from_bytecode_stub_offset();
|
||||
static word late_initialization_error_shared_without_fpu_regs_stub_offset();
|
||||
static word late_initialization_error_shared_with_fpu_regs_stub_offset();
|
||||
static word null_error_shared_without_fpu_regs_stub_offset();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -281,6 +281,8 @@
|
||||
FIELD(Thread, float_not_address_offset) \
|
||||
FIELD(Thread, float_zerow_address_offset) \
|
||||
FIELD(Thread, global_object_pool_offset) \
|
||||
FIELD(Thread, interpret_call_entry_point_offset) \
|
||||
FIELD(Thread, invoke_dart_code_from_bytecode_stub_offset) \
|
||||
FIELD(Thread, invoke_dart_code_stub_offset) \
|
||||
FIELD(Thread, exit_through_ffi_offset) \
|
||||
FIELD(Thread, isolate_offset) \
|
||||
@@ -410,6 +412,7 @@
|
||||
SIZEOF(ApiError, InstanceSize, UntaggedApiError) \
|
||||
SIZEOF(Array, header_size, UntaggedArray) \
|
||||
SIZEOF(Bool, InstanceSize, UntaggedBool) \
|
||||
SIZEOF(Bytecode, InstanceSize, UntaggedBytecode) \
|
||||
SIZEOF(Capability, InstanceSize, UntaggedCapability) \
|
||||
SIZEOF(Class, InstanceSize, UntaggedClass) \
|
||||
SIZEOF(Closure, InstanceSize, UntaggedClosure) \
|
||||
|
||||
@@ -150,8 +150,14 @@ void StubCodeCompiler::GenerateInitLateInstanceFieldStub(bool is_final) {
|
||||
if (!FLAG_precompiled_mode) {
|
||||
__ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
|
||||
target::Function::code_offset());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// InterpretCall stub needs arguments descriptor for all function calls.
|
||||
__ LoadObject(ARGS_DESC_REG, ArgumentsDescriptorBoxed(/*type_args_len=*/0,
|
||||
/*num_arguments=*/1));
|
||||
#else
|
||||
// Load a GC-safe value for the arguments descriptor (unused but tagged).
|
||||
__ LoadImmediate(ARGS_DESC_REG, 0);
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
__ Drop(1); // Drop argument.
|
||||
|
||||
@@ -1227,7 +1227,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
// Input parameters:
|
||||
// LR : points to return address.
|
||||
// R0 : target code or entry point (in bare instructions mode).
|
||||
// R0 : target code or entry point (in AOT mode).
|
||||
// R1 : arguments descriptor array.
|
||||
// R2 : arguments array.
|
||||
// R3 : current thread.
|
||||
@@ -1356,6 +1356,143 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// LR : points to return address.
|
||||
// R0 : target code or entry point (in AOT mode).
|
||||
// R1 : arguments descriptor array.
|
||||
// R2 : address of first argument.
|
||||
// R3 : current thread.
|
||||
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
SPILLS_LR_TO_FRAME(__ EnterFrame((1 << FP) | (1 << LR), 0));
|
||||
|
||||
// Push code object to PC marker slot.
|
||||
__ ldr(IP,
|
||||
Address(R3,
|
||||
target::Thread::invoke_dart_code_from_bytecode_stub_offset()));
|
||||
__ Push(IP);
|
||||
|
||||
__ PushNativeCalleeSavedRegisters();
|
||||
|
||||
// Set up THR, which caches the current thread in Dart code.
|
||||
if (THR != R3) {
|
||||
__ mov(THR, Operand(R3));
|
||||
}
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
// Save the current VMTag on the stack.
|
||||
__ LoadFromOffset(R9, THR, target::Thread::vm_tag_offset());
|
||||
__ Push(R9);
|
||||
|
||||
// Save top resource and top exit frame info. Use R4-6 as temporary registers.
|
||||
// StackFrameIterator reads the top exit frame info saved in this frame.
|
||||
__ LoadFromOffset(R4, THR, target::Thread::top_resource_offset());
|
||||
__ Push(R4);
|
||||
__ LoadImmediate(R8, 0);
|
||||
__ StoreToOffset(R8, THR, target::Thread::top_resource_offset());
|
||||
|
||||
__ LoadFromOffset(R8, THR, target::Thread::exit_through_ffi_offset());
|
||||
__ Push(R8);
|
||||
__ LoadImmediate(R8, 0);
|
||||
__ StoreToOffset(R8, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
__ LoadFromOffset(R9, THR, target::Thread::top_exit_frame_info_offset());
|
||||
__ StoreToOffset(R8, THR, target::Thread::top_exit_frame_info_offset());
|
||||
|
||||
// target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
|
||||
// with the code below.
|
||||
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
|
||||
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -27);
|
||||
#else
|
||||
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -28);
|
||||
#endif
|
||||
__ Push(R9);
|
||||
|
||||
__ EmitEntryFrameVerification(R9);
|
||||
|
||||
// Mark that the thread is executing Dart code. Do this after initializing the
|
||||
// exit link for the profiler.
|
||||
__ LoadImmediate(R9, VMTag::kDartTagId);
|
||||
__ StoreToOffset(R9, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
// Load arguments descriptor array into R4, which is passed to Dart code.
|
||||
__ mov(R4, Operand(R1));
|
||||
|
||||
// Load number of arguments into R9 and adjust count for type arguments.
|
||||
__ ldr(R3,
|
||||
FieldAddress(R4, target::ArgumentsDescriptor::type_args_len_offset()));
|
||||
__ ldr(R9, FieldAddress(R4, target::ArgumentsDescriptor::count_offset()));
|
||||
__ cmp(R3, Operand(0));
|
||||
__ AddImmediate(R9, R9, target::ToRawSmi(1),
|
||||
NE); // Include the type arguments.
|
||||
__ SmiUntag(R9);
|
||||
|
||||
// R2 points to first argument.
|
||||
// Set up arguments for the Dart call.
|
||||
Label push_arguments;
|
||||
Label done_push_arguments;
|
||||
__ CompareImmediate(R9, 0); // check if there are arguments.
|
||||
__ b(&done_push_arguments, EQ);
|
||||
__ LoadImmediate(R1, 0);
|
||||
__ Bind(&push_arguments);
|
||||
__ ldr(R3, Address(R2));
|
||||
__ Push(R3);
|
||||
__ AddImmediate(R2, target::kWordSize);
|
||||
__ AddImmediate(R1, 1);
|
||||
__ cmp(R1, Operand(R9));
|
||||
__ b(&push_arguments, LT);
|
||||
__ Bind(&done_push_arguments);
|
||||
|
||||
// Call the Dart code entrypoint.
|
||||
if (FLAG_precompiled_mode) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
__ LoadImmediate(CODE_REG, 0); // GC safe value into CODE_REG.
|
||||
} else {
|
||||
__ LoadImmediate(PP, 0); // GC safe value into PP.
|
||||
__ mov(CODE_REG, Operand(R0));
|
||||
__ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
|
||||
}
|
||||
__ blx(R0); // R4 is the arguments descriptor array.
|
||||
|
||||
// Get rid of arguments pushed on the stack.
|
||||
__ AddImmediate(
|
||||
SP, FP,
|
||||
target::frame_layout.exit_link_slot_from_entry_fp * target::kWordSize);
|
||||
|
||||
// Restore the saved top exit frame info and top resource back into the
|
||||
// Isolate structure. Uses R9 as a temporary register for this.
|
||||
__ Pop(R9);
|
||||
__ StoreToOffset(R9, THR, target::Thread::top_exit_frame_info_offset());
|
||||
__ Pop(R9);
|
||||
__ StoreToOffset(R9, THR, target::Thread::exit_through_ffi_offset());
|
||||
__ Pop(R9);
|
||||
__ StoreToOffset(R9, THR, target::Thread::top_resource_offset());
|
||||
|
||||
// Restore the current VMTag from the stack.
|
||||
__ Pop(R4);
|
||||
__ StoreToOffset(R4, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
__ PopNativeCalleeSavedRegisters();
|
||||
|
||||
__ set_constant_pool_allowed(false);
|
||||
|
||||
// Restore the frame pointer and return.
|
||||
RESTORES_LR_FROM_FRAME(__ LeaveFrame((1 << FP) | (1 << LR)));
|
||||
__ Ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Helper to generate space allocation of context stub.
|
||||
// This does not initialise the fields of the context.
|
||||
// Input:
|
||||
@@ -2656,6 +2793,90 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
|
||||
__ Branch(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// R4: Arguments descriptor.
|
||||
// R0: Function.
|
||||
void StubCodeCompiler::GenerateInterpretCallStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
__ EnterStubFrame();
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
// Check that we are always entering from Dart code.
|
||||
__ LoadFromOffset(kWord, R8, THR, target::Thread::vm_tag_offset());
|
||||
__ CompareImmediate(R8, VMTag::kDartTagId);
|
||||
__ b(&ok, EQ);
|
||||
__ Stop("Not coming from Dart code.");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust arguments count for type arguments vector.
|
||||
__ LoadFieldFromOffset(kWord, R2, R4,
|
||||
target::ArgumentsDescriptor::count_offset());
|
||||
__ SmiUntag(R2);
|
||||
__ LoadFieldFromOffset(kWord, R1, R4,
|
||||
target::ArgumentsDescriptor::type_args_len_offset());
|
||||
__ cmp(R1, Operand(0));
|
||||
__ AddImmediate(R2, R2, 1, NE); // Include the type arguments.
|
||||
|
||||
// Compute argv.
|
||||
__ mov(R3, Operand(R2, LSL, 2));
|
||||
__ add(R3, FP, Operand(R3));
|
||||
__ AddImmediate(R3,
|
||||
target::frame_layout.param_end_from_fp * target::kWordSize);
|
||||
|
||||
// Indicate decreasing memory addresses of arguments with negative argc.
|
||||
__ rsb(R2, R2, Operand(0));
|
||||
|
||||
// Align frame before entering C++ world. Fifth argument passed on the stack.
|
||||
__ ReserveAlignedFrameSpace(1 * target::kWordSize);
|
||||
|
||||
// Pass arguments in registers.
|
||||
// R0: Function.
|
||||
__ mov(R1, Operand(R4)); // Arguments descriptor.
|
||||
// R2: Negative argc.
|
||||
// R3: Argv.
|
||||
__ str(THR, Address(SP, 0)); // Fifth argument: Thread.
|
||||
|
||||
// Save exit frame information to enable stack walking as we are about
|
||||
// to transition to Dart VM C++ code.
|
||||
__ StoreToOffset(kWord, FP, THR,
|
||||
target::Thread::top_exit_frame_info_offset());
|
||||
|
||||
// Mark that the thread exited generated code through a runtime call.
|
||||
__ LoadImmediate(R5, target::Thread::exit_through_runtime_call());
|
||||
__ StoreToOffset(kWord, R5, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
// Mark that the thread is executing VM code.
|
||||
__ LoadFromOffset(kWord, R5, THR,
|
||||
target::Thread::interpret_call_entry_point_offset());
|
||||
__ StoreToOffset(kWord, R5, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
__ blx(R5);
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ LoadImmediate(R2, VMTag::kDartTagId);
|
||||
__ StoreToOffset(kWord, R2, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
// Mark that the thread has not exited generated Dart code.
|
||||
__ LoadImmediate(R2, 0);
|
||||
__ StoreToOffset(kWord, R2, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
// Reset exit frame information in Isolate's mutator thread structure.
|
||||
__ StoreToOffset(kWord, R2, THR,
|
||||
target::Thread::top_exit_frame_info_offset());
|
||||
|
||||
__ LeaveStubFrame();
|
||||
__ Ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// R9: Contains an ICData.
|
||||
void StubCodeCompiler::GenerateICCallBreakpointStub() {
|
||||
#if defined(PRODUCT)
|
||||
@@ -3165,8 +3386,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
|
||||
if (FLAG_precompiled_mode) {
|
||||
const intptr_t entry_offset =
|
||||
target::ICData::EntryPointIndexFor(1) * target::kWordSize;
|
||||
__ LoadCompressed(R0, Address(R8, entry_offset));
|
||||
__ Branch(FieldAddress(R0, target::Function::entry_point_offset()));
|
||||
__ LoadCompressed(FUNCTION_REG, Address(R8, entry_offset));
|
||||
__ Branch(
|
||||
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
} else {
|
||||
const intptr_t code_offset =
|
||||
target::ICData::CodeIndexFor(1) * target::kWordSize;
|
||||
|
||||
@@ -1512,7 +1512,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
// Input parameters:
|
||||
// LR : points to return address.
|
||||
// R0 : target code or entry point (in bare instructions mode).
|
||||
// R0 : target code or entry point (in AOT mode).
|
||||
// R1 : arguments descriptor array.
|
||||
// R2 : arguments array.
|
||||
// R3 : current thread.
|
||||
@@ -1653,6 +1653,155 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// LR : points to return address.
|
||||
// R0 : target code or entry point (in AOT mode).
|
||||
// R1 : arguments descriptor array.
|
||||
// R2 : address of first argument.
|
||||
// R3 : current thread.
|
||||
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
__ Comment("InvokeDartCodeFromBytecodeStub");
|
||||
|
||||
// Copy the C stack pointer (CSP/R31) into the stack pointer we'll actually
|
||||
// use to access the stack (SP/R15) and set the C stack pointer to near the
|
||||
// stack limit, loaded from the Thread held in R3, to prevent signal handlers
|
||||
// from over-writing Dart frames.
|
||||
__ mov(SP, CSP);
|
||||
__ SetupCSPFromThread(R3);
|
||||
__ EnterFrame(0);
|
||||
|
||||
// Push code object to PC marker slot.
|
||||
__ ldr(TMP,
|
||||
Address(R3,
|
||||
target::Thread::invoke_dart_code_from_bytecode_stub_offset()));
|
||||
__ Push(TMP);
|
||||
|
||||
#if defined(DART_TARGET_OS_FUCHSIA)
|
||||
__ str(R18, Address(R3, target::Thread::saved_shadow_call_stack_offset()));
|
||||
#elif defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
__ PushNativeCalleeSavedRegisters();
|
||||
|
||||
// Set up THR, which caches the current thread in Dart code.
|
||||
if (THR != R3) {
|
||||
__ mov(THR, R3);
|
||||
}
|
||||
|
||||
// Refresh pinned registers (write barrier mask, null, dispatch table, etc).
|
||||
__ RestorePinnedRegisters();
|
||||
|
||||
// Save the current VMTag on the stack.
|
||||
__ LoadFromOffset(R4, THR, target::Thread::vm_tag_offset());
|
||||
__ Push(R4);
|
||||
|
||||
// Save top resource and top exit frame info. Use R6 as a temporary register.
|
||||
// StackFrameIterator reads the top exit frame info saved in this frame.
|
||||
__ LoadFromOffset(R6, THR, target::Thread::top_resource_offset());
|
||||
__ StoreToOffset(ZR, THR, target::Thread::top_resource_offset());
|
||||
__ Push(R6);
|
||||
|
||||
__ LoadFromOffset(R6, THR, target::Thread::exit_through_ffi_offset());
|
||||
__ Push(R6);
|
||||
__ StoreToOffset(ZR, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
__ LoadFromOffset(R6, THR, target::Thread::top_exit_frame_info_offset());
|
||||
__ StoreToOffset(ZR, THR, target::Thread::top_exit_frame_info_offset());
|
||||
// target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
|
||||
// with the code below.
|
||||
#if defined(DART_TARGET_OS_FUCHSIA)
|
||||
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -24);
|
||||
#else
|
||||
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -23);
|
||||
#endif
|
||||
__ Push(R6);
|
||||
// In debug mode, verify that we've pushed the top exit frame info at the
|
||||
// correct offset from FP.
|
||||
__ EmitEntryFrameVerification();
|
||||
|
||||
// Mark that the thread is executing Dart code. Do this after initializing the
|
||||
// exit link for the profiler.
|
||||
__ LoadImmediate(R6, VMTag::kDartTagId);
|
||||
__ StoreToOffset(R6, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
// Load arguments descriptor array into R4, which is passed to Dart code.
|
||||
__ mov(R4, R1);
|
||||
|
||||
// Load number of arguments into R5 and adjust count for type arguments.
|
||||
__ LoadCompressedSmiFieldFromOffset(
|
||||
R5, R4, target::ArgumentsDescriptor::count_offset());
|
||||
__ LoadCompressedSmiFieldFromOffset(
|
||||
R3, R4, target::ArgumentsDescriptor::type_args_len_offset());
|
||||
__ SmiUntag(R5);
|
||||
// Include the type arguments.
|
||||
__ cmp(R3, Operand(0), kObjectBytes);
|
||||
__ csinc(R5, R5, R5, EQ); // R5 <- (R3 == 0) ? R5 : R5 + 1
|
||||
|
||||
// R2 points to first argument.
|
||||
// Set up arguments for the Dart call.
|
||||
Label push_arguments;
|
||||
Label done_push_arguments;
|
||||
__ cmp(R5, Operand(0));
|
||||
__ b(&done_push_arguments, EQ); // check if there are arguments.
|
||||
__ LoadImmediate(R1, 0);
|
||||
__ Bind(&push_arguments);
|
||||
__ ldr(R3, Address(R2));
|
||||
__ Push(R3);
|
||||
__ add(R1, R1, Operand(1));
|
||||
__ add(R2, R2, Operand(target::kWordSize));
|
||||
__ cmp(R1, Operand(R5));
|
||||
__ b(&push_arguments, LT);
|
||||
__ Bind(&done_push_arguments);
|
||||
|
||||
if (FLAG_precompiled_mode) {
|
||||
__ SetupGlobalPoolAndDispatchTable();
|
||||
__ mov(CODE_REG, ZR); // GC-safe value into CODE_REG.
|
||||
} else {
|
||||
// We now load the pool pointer(PP) with a GC safe value as we are about to
|
||||
// invoke dart code. We don't need a real object pool here.
|
||||
// Smi zero does not work because ARM64 assumes PP to be untagged.
|
||||
__ LoadObject(PP, NullObject());
|
||||
__ mov(CODE_REG, R0);
|
||||
__ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
|
||||
}
|
||||
|
||||
// Call the Dart code entrypoint.
|
||||
__ blr(R0); // R4 is the arguments descriptor array.
|
||||
__ Comment("InvokeDartCodeFromBytecodeStub return");
|
||||
|
||||
// Get rid of arguments pushed on the stack.
|
||||
__ AddImmediate(
|
||||
SP, FP,
|
||||
target::frame_layout.exit_link_slot_from_entry_fp * target::kWordSize);
|
||||
|
||||
// Restore the saved top exit frame info and top resource back into the
|
||||
// Isolate structure. Uses R6 as a temporary register for this.
|
||||
__ Pop(R6);
|
||||
__ StoreToOffset(R6, THR, target::Thread::top_exit_frame_info_offset());
|
||||
__ Pop(R6);
|
||||
__ StoreToOffset(R6, THR, target::Thread::exit_through_ffi_offset());
|
||||
__ Pop(R6);
|
||||
__ StoreToOffset(R6, THR, target::Thread::top_resource_offset());
|
||||
|
||||
// Restore the current VMTag from the stack.
|
||||
__ Pop(R4);
|
||||
__ StoreToOffset(R4, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
__ PopNativeCalleeSavedRegisters();
|
||||
|
||||
// Restore the frame pointer and C stack pointer and return.
|
||||
__ LeaveFrame();
|
||||
__ RestoreCSP();
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Helper to generate space allocation of context stub.
|
||||
// This does not initialise the fields of the context.
|
||||
// Input:
|
||||
@@ -3015,6 +3164,101 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
|
||||
__ br(R2);
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// R4: Arguments descriptor.
|
||||
// R0: Function.
|
||||
void StubCodeCompiler::GenerateInterpretCallStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
__ SetPrologueOffset();
|
||||
__ EnterStubFrame();
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
// Check that we are always entering from Dart code.
|
||||
__ LoadFromOffset(R8, THR, target::Thread::vm_tag_offset());
|
||||
__ CompareImmediate(R8, VMTag::kDartTagId);
|
||||
__ b(&ok, EQ);
|
||||
__ Stop("Not coming from Dart code.");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust arguments count for type arguments vector.
|
||||
__ LoadCompressedSmiFieldFromOffset(
|
||||
R2, R4, target::ArgumentsDescriptor::count_offset());
|
||||
__ SmiUntag(R2);
|
||||
__ LoadCompressedSmiFieldFromOffset(
|
||||
R1, R4, target::ArgumentsDescriptor::type_args_len_offset());
|
||||
__ cmp(R1, Operand(0));
|
||||
__ csinc(R2, R2, R2, EQ); // R2 <- (R1 == 0) ? R2 : R2 + 1.
|
||||
|
||||
// Compute argv.
|
||||
__ add(R3, ZR, Operand(R2, LSL, 3));
|
||||
__ add(R3, FP, Operand(R3));
|
||||
__ AddImmediate(R3,
|
||||
target::frame_layout.param_end_from_fp * target::kWordSize);
|
||||
|
||||
// Indicate decreasing memory addresses of arguments with negative argc.
|
||||
__ neg(R2, R2);
|
||||
|
||||
// Align frame before entering C++ world. No shadow stack space required.
|
||||
__ ReserveAlignedFrameSpace(0 * target::kWordSize);
|
||||
|
||||
// Pass arguments in registers.
|
||||
// R0: Function.
|
||||
__ mov(R1, R4); // Arguments descriptor.
|
||||
// R2: Negative argc.
|
||||
// R3: Argv.
|
||||
__ mov(R4, THR); // Thread.
|
||||
|
||||
// Save exit frame information to enable stack walking as we are about
|
||||
// to transition to Dart VM C++ code.
|
||||
__ StoreToOffset(FP, THR, target::Thread::top_exit_frame_info_offset());
|
||||
|
||||
// Mark that the thread exited generated code through a runtime call.
|
||||
__ LoadImmediate(R5, target::Thread::exit_through_runtime_call());
|
||||
__ StoreToOffset(R5, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
// Mark that the thread is executing VM code.
|
||||
__ LoadFromOffset(R5, THR,
|
||||
target::Thread::interpret_call_entry_point_offset());
|
||||
__ StoreToOffset(R5, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
// We are entering runtime code, so the C stack pointer must be restored from
|
||||
// the stack limit to the top of the stack. We cache the stack limit address
|
||||
// in a callee-saved register.
|
||||
__ mov(R25, CSP);
|
||||
__ mov(CSP, SP);
|
||||
|
||||
__ blr(R5);
|
||||
|
||||
// Restore SP and CSP.
|
||||
__ mov(SP, CSP);
|
||||
__ mov(CSP, R25);
|
||||
|
||||
// Refresh pinned registers values (inc. write barrier mask and null object).
|
||||
__ RestorePinnedRegisters();
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ LoadImmediate(R2, VMTag::kDartTagId);
|
||||
__ StoreToOffset(R2, THR, target::Thread::vm_tag_offset());
|
||||
|
||||
// Mark that the thread has not exited generated Dart code.
|
||||
__ StoreToOffset(ZR, THR, target::Thread::exit_through_ffi_offset());
|
||||
|
||||
// Reset exit frame information in Isolate's mutator thread structure.
|
||||
__ StoreToOffset(ZR, THR, target::Thread::top_exit_frame_info_offset());
|
||||
|
||||
__ LeaveStubFrame();
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// R5: Contains an ICData.
|
||||
void StubCodeCompiler::GenerateICCallBreakpointStub() {
|
||||
#if defined(PRODUCT)
|
||||
@@ -3482,8 +3726,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
|
||||
if (FLAG_precompiled_mode) {
|
||||
const intptr_t entry_offset =
|
||||
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
|
||||
__ LoadCompressed(R1, Address(R8, entry_offset));
|
||||
__ ldr(R1, FieldAddress(R1, target::Function::entry_point_offset()));
|
||||
__ LoadCompressed(FUNCTION_REG, Address(R8, entry_offset));
|
||||
__ ldr(R1,
|
||||
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
} else {
|
||||
const intptr_t code_offset =
|
||||
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
|
||||
|
||||
@@ -1161,6 +1161,137 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// ESP : points to return address.
|
||||
// ESP + 4 : code object of the dart function to call.
|
||||
// ESP + 8 : arguments descriptor array.
|
||||
// ESP + 12: address of first argument.
|
||||
// ESP + 16 : current thread.
|
||||
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const intptr_t kTargetCodeOffset = 2 * target::kWordSize;
|
||||
const intptr_t kArgumentsDescOffset = 3 * target::kWordSize;
|
||||
const intptr_t kArgumentsOffset = 4 * target::kWordSize;
|
||||
const intptr_t kThreadOffset = 5 * target::kWordSize;
|
||||
__ EnterFrame(0);
|
||||
|
||||
// Push code object to PC marker slot.
|
||||
__ movl(EAX, Address(EBP, kThreadOffset));
|
||||
__ pushl(Address(EAX, target::Thread::invoke_dart_code_stub_offset()));
|
||||
|
||||
// Save C++ ABI callee-saved registers.
|
||||
__ pushl(EBX);
|
||||
__ pushl(ESI);
|
||||
__ pushl(EDI);
|
||||
|
||||
// Set up THR, which caches the current thread in Dart code.
|
||||
__ movl(THR, EAX);
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
// Save the current VMTag on the stack.
|
||||
__ movl(ECX, Assembler::VMTagAddress());
|
||||
__ pushl(ECX);
|
||||
|
||||
// Save top resource and top exit frame info. Use EDX as a temporary register.
|
||||
// StackFrameIterator reads the top exit frame info saved in this frame.
|
||||
__ movl(EDX, Address(THR, target::Thread::top_resource_offset()));
|
||||
__ pushl(EDX);
|
||||
__ movl(Address(THR, target::Thread::top_resource_offset()), Immediate(0));
|
||||
__ movl(EAX, Address(THR, target::Thread::exit_through_ffi_offset()));
|
||||
__ pushl(EAX);
|
||||
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(0));
|
||||
// The constant target::frame_layout.exit_link_slot_from_entry_fp must be
|
||||
// kept in sync with the code below.
|
||||
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -8);
|
||||
__ movl(EDX, Address(THR, target::Thread::top_exit_frame_info_offset()));
|
||||
__ pushl(EDX);
|
||||
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()),
|
||||
Immediate(0));
|
||||
|
||||
// In debug mode, verify that we've pushed the top exit frame info at the
|
||||
// correct offset from FP.
|
||||
__ EmitEntryFrameVerification();
|
||||
|
||||
// Mark that the thread is executing Dart code. Do this after initializing the
|
||||
// exit link for the profiler.
|
||||
__ movl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
|
||||
// Load arguments descriptor array into EDX.
|
||||
__ movl(EDX, Address(EBP, kArgumentsDescOffset));
|
||||
|
||||
// Load number of arguments into EBX and adjust count for type arguments.
|
||||
__ movl(EBX, FieldAddress(EDX, target::ArgumentsDescriptor::count_offset()));
|
||||
__ cmpl(
|
||||
FieldAddress(EDX, target::ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ addl(EBX, Immediate(target::ToRawSmi(1))); // Include the type arguments.
|
||||
__ Bind(&args_count_ok);
|
||||
// Save number of arguments as Smi on stack, replacing ArgumentsDesc.
|
||||
__ movl(Address(EBP, kArgumentsDescOffset), EBX);
|
||||
__ SmiUntag(EBX);
|
||||
|
||||
// Set up arguments for the dart call.
|
||||
Label push_arguments;
|
||||
Label done_push_arguments;
|
||||
__ testl(EBX, EBX); // check if there are arguments.
|
||||
__ j(ZERO, &done_push_arguments, Assembler::kNearJump);
|
||||
__ movl(EAX, Immediate(0));
|
||||
|
||||
// Compute address of 'arguments array' data area into EDI.
|
||||
__ movl(EDI, Address(EBP, kArgumentsOffset));
|
||||
|
||||
__ Bind(&push_arguments);
|
||||
__ movl(ECX, Address(EDI, EAX, TIMES_4, 0));
|
||||
__ pushl(ECX);
|
||||
__ incl(EAX);
|
||||
__ cmpl(EAX, EBX);
|
||||
__ j(LESS, &push_arguments, Assembler::kNearJump);
|
||||
__ Bind(&done_push_arguments);
|
||||
|
||||
// Call the dart code entrypoint.
|
||||
__ movl(EAX, Address(EBP, kTargetCodeOffset));
|
||||
__ call(FieldAddress(EAX, target::Code::entry_point_offset()));
|
||||
|
||||
// Read the saved number of passed arguments as Smi.
|
||||
__ movl(EDX, Address(EBP, kArgumentsDescOffset));
|
||||
// Get rid of arguments pushed on the stack.
|
||||
__ leal(ESP, Address(ESP, EDX, TIMES_2, 0)); // EDX is a Smi.
|
||||
|
||||
// Restore the saved top exit frame info and top resource back into the
|
||||
// Isolate structure.
|
||||
__ popl(Address(THR, target::Thread::top_exit_frame_info_offset()));
|
||||
__ popl(Address(THR, target::Thread::exit_through_ffi_offset()));
|
||||
__ popl(Address(THR, target::Thread::top_resource_offset()));
|
||||
|
||||
// Restore the current VMTag from the stack.
|
||||
__ popl(Assembler::VMTagAddress());
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
// Restore C++ ABI callee-saved registers.
|
||||
__ popl(EDI);
|
||||
__ popl(ESI);
|
||||
__ popl(EBX);
|
||||
|
||||
// Restore the frame pointer.
|
||||
__ LeaveFrame();
|
||||
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Helper to generate space allocation of context stub.
|
||||
// This does not initialise the fields of the context.
|
||||
// Input:
|
||||
@@ -2365,6 +2496,86 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
|
||||
__ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// EDX: Arguments descriptor.
|
||||
// EAX: Function.
|
||||
void StubCodeCompiler::GenerateInterpretCallStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
__ EnterStubFrame();
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
// Check that we are always entering from Dart code.
|
||||
__ cmpl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
__ j(EQUAL, &ok, Assembler::kNearJump);
|
||||
__ Stop("Not coming from Dart code.");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust arguments count for type arguments vector.
|
||||
__ movl(ECX, FieldAddress(EDX, target::ArgumentsDescriptor::count_offset()));
|
||||
__ SmiUntag(ECX);
|
||||
__ cmpl(
|
||||
FieldAddress(EDX, target::ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ incl(ECX);
|
||||
__ Bind(&args_count_ok);
|
||||
|
||||
// Compute argv.
|
||||
__ leal(EBX,
|
||||
Address(EBP, ECX, TIMES_4,
|
||||
target::frame_layout.param_end_from_fp * target::kWordSize));
|
||||
|
||||
// Indicate decreasing memory addresses of arguments with negative argc.
|
||||
__ negl(ECX);
|
||||
|
||||
__ pushl(THR); // Arg 4: Thread.
|
||||
__ pushl(EBX); // Arg 3: Argv.
|
||||
__ pushl(ECX); // Arg 2: Negative argc.
|
||||
__ pushl(EDX); // Arg 1: Arguments descriptor
|
||||
__ pushl(EAX); // Arg 0: Function
|
||||
|
||||
// Save exit frame information to enable stack walking as we are about
|
||||
// to transition to Dart VM C++ code.
|
||||
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()), EBP);
|
||||
|
||||
// Mark that the thread exited generated code through a runtime call.
|
||||
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(target::Thread::exit_through_runtime_call()));
|
||||
|
||||
// Mark that the thread is executing VM code.
|
||||
__ movl(EAX,
|
||||
Address(THR, target::Thread::interpret_call_entry_point_offset()));
|
||||
__ movl(Assembler::VMTagAddress(), EAX);
|
||||
|
||||
__ call(EAX);
|
||||
|
||||
__ Drop(5);
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ movl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
|
||||
// Mark that the thread has not exited generated Dart code.
|
||||
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(0));
|
||||
|
||||
// Reset exit frame information in Isolate's mutator thread structure.
|
||||
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()),
|
||||
Immediate(0));
|
||||
|
||||
__ LeaveFrame();
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// ECX: Contains an ICData.
|
||||
void StubCodeCompiler::GenerateICCallBreakpointStub() {
|
||||
#if defined(PRODUCT)
|
||||
|
||||
@@ -1341,7 +1341,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
// Input parameters:
|
||||
// RA : points to return address.
|
||||
// A0 : target code or entry point (in bare instructions mode).
|
||||
// A0 : target code or entry point (in AOT mode).
|
||||
// A1 : arguments descriptor array.
|
||||
// A2 : arguments array.
|
||||
// A3 : current thread.
|
||||
@@ -1471,6 +1471,17 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// RSP : points to return address.
|
||||
// RDI : target code or entry point (in AOT mode).
|
||||
// RSI : arguments descriptor array.
|
||||
// RDX : address of first argument.
|
||||
// RCX : current thread.
|
||||
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
|
||||
__ Stop("Not implemented on RISC-V.");
|
||||
}
|
||||
|
||||
// Helper to generate space allocation of context stub.
|
||||
// This does not initialise the fields of the context.
|
||||
// Input:
|
||||
@@ -2765,6 +2776,13 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
|
||||
__ jr(TMP);
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// ARGS_DESC_REG: Arguments descriptor.
|
||||
// FUNCTION_REG: Function.
|
||||
void StubCodeCompiler::GenerateInterpretCallStub() {
|
||||
__ Stop("Not implemented on RISC-V.");
|
||||
}
|
||||
|
||||
// A0: Receiver
|
||||
// S5: ICData
|
||||
void StubCodeCompiler::GenerateICCallBreakpointStub() {
|
||||
@@ -3243,8 +3261,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
|
||||
if (FLAG_precompiled_mode) {
|
||||
const intptr_t entry_offset =
|
||||
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
|
||||
__ LoadCompressed(A1, Address(T1, entry_offset));
|
||||
__ lx(A1, FieldAddress(A1, target::Function::entry_point_offset()));
|
||||
__ LoadCompressed(FUNCTION_REG, Address(T1, entry_offset));
|
||||
__ lx(A1,
|
||||
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
} else {
|
||||
const intptr_t code_offset =
|
||||
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
|
||||
|
||||
@@ -1431,7 +1431,7 @@ static const RegisterSet kCalleeSavedRegisterSet(
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
// Input parameters:
|
||||
// RSP : points to return address.
|
||||
// RDI : target code or entry point (in bare instructions mode).
|
||||
// RDI : target code or entry point (in AOT mode).
|
||||
// RSI : arguments descriptor array.
|
||||
// RDX : arguments array.
|
||||
// RCX : current thread.
|
||||
@@ -1581,6 +1581,161 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Called when invoking compiled Dart code from interpreted Dart code.
|
||||
// Input parameters:
|
||||
// RSP : points to return address.
|
||||
// RDI : target code or entry point (in AOT mode).
|
||||
// RSI : arguments descriptor array.
|
||||
// RDX : address of first argument.
|
||||
// RCX : current thread.
|
||||
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
__ EnterFrame(0);
|
||||
|
||||
const Register kTargetReg = 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,
|
||||
target::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)*target::kWordSize;
|
||||
__ pushq(kArgDescReg);
|
||||
|
||||
// Save C++ ABI callee-saved registers.
|
||||
__ PushRegisters(kCalleeSavedRegisterSet);
|
||||
|
||||
// If any additional (or fewer) values are pushed, the offsets in
|
||||
// target::frame_layout.exit_link_slot_from_entry_fp will need to be changed.
|
||||
|
||||
// Set up THR, which caches the current thread in Dart code.
|
||||
if (THR != kThreadReg) {
|
||||
__ movq(THR, kThreadReg);
|
||||
}
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
// Save the current VMTag on the stack.
|
||||
__ movq(RAX, Assembler::VMTagAddress());
|
||||
__ pushq(RAX);
|
||||
|
||||
// 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, target::Thread::top_resource_offset()));
|
||||
__ pushq(RAX);
|
||||
__ movq(Address(THR, target::Thread::top_resource_offset()), Immediate(0));
|
||||
|
||||
__ movq(RAX, Address(THR, target::Thread::exit_through_ffi_offset()));
|
||||
__ pushq(RAX);
|
||||
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(0));
|
||||
|
||||
__ movq(RAX, Address(THR, target::Thread::top_exit_frame_info_offset()));
|
||||
__ pushq(RAX);
|
||||
|
||||
// The constant target::frame_layout.exit_link_slot_from_entry_fp must be kept
|
||||
// in sync with the code above.
|
||||
__ EmitEntryFrameVerification();
|
||||
|
||||
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
|
||||
Immediate(0));
|
||||
|
||||
// Mark that the thread is executing Dart code. Do this after initializing the
|
||||
// exit link for the profiler.
|
||||
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
|
||||
// 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 kTargetReg.
|
||||
ASSERT(kTargetReg != RDX);
|
||||
|
||||
// Load number of arguments into RBX and adjust count for type arguments.
|
||||
__ OBJ(mov)(RBX,
|
||||
FieldAddress(R10, target::ArgumentsDescriptor::count_offset()));
|
||||
__ OBJ(cmp)(
|
||||
FieldAddress(R10, target::ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ addq(RBX, Immediate(target::ToRawSmi(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.
|
||||
__ MoveRegister(RDX, kArg0Reg);
|
||||
|
||||
// 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.
|
||||
if (FLAG_precompiled_mode) {
|
||||
__ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
|
||||
__ xorq(CODE_REG, CODE_REG); // GC-safe value into CODE_REG.
|
||||
} else {
|
||||
__ xorq(PP, PP); // GC-safe value into PP.
|
||||
__ movq(CODE_REG, kTargetReg);
|
||||
__ movq(kTargetReg,
|
||||
FieldAddress(CODE_REG, target::Code::entry_point_offset()));
|
||||
}
|
||||
__ call(kTargetReg); // 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, target::Thread::top_exit_frame_info_offset()));
|
||||
__ popq(Address(THR, target::Thread::exit_through_ffi_offset()));
|
||||
__ popq(Address(THR, target::Thread::top_resource_offset()));
|
||||
|
||||
// Restore the current VMTag from the stack.
|
||||
__ popq(Assembler::VMTagAddress());
|
||||
|
||||
#if defined(USING_SHADOW_CALL_STACK)
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
// Restore C++ ABI callee-saved registers.
|
||||
__ PopRegisters(kCalleeSavedRegisterSet);
|
||||
__ set_constant_pool_allowed(false);
|
||||
|
||||
// Restore the frame pointer.
|
||||
__ LeaveFrame();
|
||||
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Helper to generate space allocation of context stub.
|
||||
// This does not initialize the fields of the context.
|
||||
// Input:
|
||||
@@ -2898,6 +3053,97 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
|
||||
__ jmp(RCX);
|
||||
}
|
||||
|
||||
// Stub for interpreting a function call.
|
||||
// ARGS_DESC_REG: Arguments descriptor.
|
||||
// FUNCTION_REG: Function.
|
||||
void StubCodeCompiler::GenerateInterpretCallStub() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
__ EnterStubFrame();
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
// Check that we are always entering from Dart code.
|
||||
__ movq(R8, Immediate(VMTag::kDartTagId));
|
||||
__ cmpq(R8, Assembler::VMTagAddress());
|
||||
__ j(EQUAL, &ok, Assembler::kNearJump);
|
||||
__ Stop("Not coming from Dart code.");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust arguments count for type arguments vector.
|
||||
__ OBJ(mov)(R11, FieldAddress(ARGS_DESC_REG,
|
||||
target::ArgumentsDescriptor::count_offset()));
|
||||
__ SmiUntag(R11);
|
||||
__ OBJ(cmp)(FieldAddress(ARGS_DESC_REG,
|
||||
target::ArgumentsDescriptor::type_args_len_offset()),
|
||||
Immediate(0));
|
||||
Label args_count_ok;
|
||||
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
|
||||
__ incq(R11);
|
||||
__ Bind(&args_count_ok);
|
||||
|
||||
// Compute argv.
|
||||
__ leaq(R12,
|
||||
Address(RBP, R11, TIMES_8,
|
||||
target::frame_layout.param_end_from_fp * target::kWordSize));
|
||||
|
||||
// Indicate decreasing memory addresses of arguments with negative argc.
|
||||
__ negq(R11);
|
||||
|
||||
// Reserve shadow space for args and align frame before entering C++ world.
|
||||
__ subq(RSP, Immediate(5 * target::kWordSize));
|
||||
if (OS::ActivationFrameAlignment() > 1) {
|
||||
__ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
|
||||
}
|
||||
|
||||
__ movq(CallingConventions::kArg1Reg, FUNCTION_REG); // Function.
|
||||
__ movq(CallingConventions::kArg2Reg,
|
||||
ARGS_DESC_REG); // Arguments descriptor.
|
||||
__ movq(CallingConventions::kArg3Reg, R11); // Negative argc.
|
||||
__ movq(CallingConventions::kArg4Reg, R12); // Argv.
|
||||
|
||||
#if defined(TARGET_OS_WINDOWS)
|
||||
__ movq(Address(RSP, 0 * target::kWordSize), THR); // Thread.
|
||||
#else
|
||||
__ movq(CallingConventions::kArg5Reg, THR); // Thread.
|
||||
#endif
|
||||
// Save exit frame information to enable stack walking as we are about
|
||||
// to transition to Dart VM C++ code.
|
||||
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()), RBP);
|
||||
|
||||
// Mark that the thread exited generated code through a runtime call.
|
||||
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(target::Thread::exit_through_runtime_call()));
|
||||
|
||||
// Mark that the thread is executing VM code.
|
||||
__ movq(RAX,
|
||||
Address(THR, target::Thread::interpret_call_entry_point_offset()));
|
||||
__ movq(Assembler::VMTagAddress(), RAX);
|
||||
|
||||
__ call(RAX);
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
|
||||
|
||||
// Mark that the thread has not exited generated Dart code.
|
||||
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
|
||||
Immediate(0));
|
||||
|
||||
// Reset exit frame information in Isolate's mutator thread structure.
|
||||
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
|
||||
Immediate(0));
|
||||
|
||||
__ LeaveStubFrame();
|
||||
__ ret();
|
||||
|
||||
#else
|
||||
__ Stop("Not using Dart dynamic modules");
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// RBX: Contains an ICData.
|
||||
// TOS(0): return address (Dart code).
|
||||
void StubCodeCompiler::GenerateICCallBreakpointStub() {
|
||||
@@ -3399,8 +3645,8 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
|
||||
if (FLAG_precompiled_mode) {
|
||||
const intptr_t entry_offset =
|
||||
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
|
||||
__ LoadCompressed(RCX, Address(R13, entry_offset));
|
||||
__ jmp(FieldAddress(RCX, target::Function::entry_point_offset()));
|
||||
__ LoadCompressed(FUNCTION_REG, Address(R13, entry_offset));
|
||||
__ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
} else {
|
||||
const intptr_t code_offset =
|
||||
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2024, 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/constants_kbc.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
static const intptr_t kInstructionSize0 = 1;
|
||||
static const intptr_t kInstructionSizeA = 2;
|
||||
static const intptr_t kInstructionSizeD = 2;
|
||||
static const intptr_t kInstructionSizeWideD = 5;
|
||||
static const intptr_t kInstructionSizeX = 2;
|
||||
static const intptr_t kInstructionSizeWideX = 5;
|
||||
static const intptr_t kInstructionSizeT = 2;
|
||||
static const intptr_t kInstructionSizeWideT = 4;
|
||||
static const intptr_t kInstructionSizeA_E = 3;
|
||||
static const intptr_t kInstructionSizeWideA_E = 6;
|
||||
static const intptr_t kInstructionSizeA_Y = 3;
|
||||
static const intptr_t kInstructionSizeWideA_Y = 6;
|
||||
static const intptr_t kInstructionSizeD_F = 3;
|
||||
static const intptr_t kInstructionSizeWideD_F = 6;
|
||||
static const intptr_t kInstructionSizeA_B_C = 4;
|
||||
|
||||
const intptr_t KernelBytecode::kInstructionSize[] = {
|
||||
#define SIZE_ORDN(encoding) kInstructionSize##encoding
|
||||
#define SIZE_WIDE(encoding) kInstructionSizeWide##encoding
|
||||
#define SIZE_RESV(encoding) SIZE_ORDN(encoding)
|
||||
#define SIZE(name, encoding, kind, op1, op2, op3) SIZE_##kind(encoding),
|
||||
KERNEL_BYTECODES_LIST(SIZE)
|
||||
#undef SIZE_ORDN
|
||||
#undef SIZE_WIDE
|
||||
#undef SIZE_RESV
|
||||
#undef SIZE
|
||||
};
|
||||
|
||||
#define DECLARE_INSTRUCTIONS(name, fmt, kind, fmta, fmtb, fmtc) \
|
||||
static const KBCInstr k##name##Instructions[] = { \
|
||||
KernelBytecode::k##name, \
|
||||
KernelBytecode::kReturnTOS, \
|
||||
};
|
||||
INTERNAL_KERNEL_BYTECODES_LIST(DECLARE_INSTRUCTIONS)
|
||||
#undef DECLARE_INSTRUCTIONS
|
||||
|
||||
void KernelBytecode::GetVMInternalBytecodeInstructions(
|
||||
Opcode opcode,
|
||||
const KBCInstr** instructions,
|
||||
intptr_t* instructions_size) {
|
||||
switch (opcode) {
|
||||
#define CASE(name, fmt, kind, fmta, fmtb, fmtc) \
|
||||
case k##name: \
|
||||
*instructions = k##name##Instructions; \
|
||||
*instructions_size = sizeof(k##name##Instructions); \
|
||||
return;
|
||||
|
||||
INTERNAL_KERNEL_BYTECODES_LIST(CASE)
|
||||
#undef CASE
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
@@ -0,0 +1,977 @@
|
||||
// Copyright (c) 2024, 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_CONSTANTS_KBC_H_
|
||||
#define RUNTIME_VM_CONSTANTS_KBC_H_
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
#include "platform/utils.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// clang-format off
|
||||
// List of KernelBytecode instructions.
|
||||
//
|
||||
// INTERPRETER STATE
|
||||
//
|
||||
// current frame info (see stack_frame_kbc.h for layout)
|
||||
// v-----^-----v
|
||||
// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~
|
||||
// ~ | ~ ~ | FP[0] | FP[1] | ~ ~ | SP[-1]| SP[0] |
|
||||
// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~
|
||||
// ^ ^
|
||||
// FP SP
|
||||
//
|
||||
//
|
||||
// The state of execution is captured in few interpreter registers:
|
||||
//
|
||||
// FP - base of the current frame
|
||||
// SP - top of the stack (TOS) for the current frame
|
||||
// PP - object pool for the currently execution function
|
||||
//
|
||||
// Frame info stored below FP additionally contains pointers to the currently
|
||||
// executing function and code.
|
||||
//
|
||||
// In the unoptimized code most of bytecodes take operands implicitly from
|
||||
// stack and store results again on the stack. Constant operands are usually
|
||||
// taken from the object pool by index.
|
||||
//
|
||||
// ENCODING
|
||||
//
|
||||
// Each instruction starts with opcode byte. Certain instructions have
|
||||
// wide encoding variant. In such case, the least significant bit of opcode is
|
||||
// not set for compact variant and set for wide variant.
|
||||
//
|
||||
// The following operand encodings are used:
|
||||
//
|
||||
// 0........8.......16.......24.......32.......40.......48
|
||||
// +--------+
|
||||
// | opcode | 0: no operands
|
||||
// +--------+
|
||||
//
|
||||
// +--------+--------+
|
||||
// | opcode | A | A: unsigned 8-bit operand
|
||||
// +--------+--------+
|
||||
//
|
||||
// +--------+--------+
|
||||
// | opcode | D | D: unsigned 8/32-bit operand
|
||||
// +--------+--------+
|
||||
//
|
||||
// +--------+----------------------------------+
|
||||
// | opcode | D | D (wide)
|
||||
// +--------+----------------------------------+
|
||||
//
|
||||
// +--------+--------+
|
||||
// | opcode | X | X: signed 8/32-bit operand
|
||||
// +--------+--------+
|
||||
//
|
||||
// +--------+----------------------------------+
|
||||
// | opcode | X | X (wide)
|
||||
// +--------+----------------------------------+
|
||||
//
|
||||
// +--------+--------+
|
||||
// | opcode | T | T: signed 8/24-bit operand
|
||||
// +--------+--------+
|
||||
//
|
||||
// +--------+--------------------------+
|
||||
// | opcode | T | T (wide)
|
||||
// +--------+--------------------------+
|
||||
//
|
||||
// +--------+--------+--------+
|
||||
// | opcode | A | E | A_E: unsigned 8-bit operand and
|
||||
// +--------+--------+--------+ unsigned 8/32-bit operand
|
||||
//
|
||||
// +--------+--------+----------------------------------+
|
||||
// | opcode | A | E | A_E (wide)
|
||||
// +--------+--------+----------------------------------+
|
||||
//
|
||||
// +--------+--------+--------+
|
||||
// | opcode | A | Y | A_Y: unsigned 8-bit operand and
|
||||
// +--------+--------+--------+ signed 8/32-bit operand
|
||||
//
|
||||
// +--------+--------+----------------------------------+
|
||||
// | opcode | A | Y | A_Y (wide)
|
||||
// +--------+--------+----------------------------------+
|
||||
//
|
||||
// +--------+--------+--------+
|
||||
// | opcode | D | F | D_F: unsigned 8/32-bit operand and
|
||||
// +--------+--------+--------+ unsigned 8-bit operand
|
||||
//
|
||||
// +--------+----------------------------------+--------+
|
||||
// | opcode | D | F | D_F (wide)
|
||||
// +--------+----------------------------------+--------+
|
||||
//
|
||||
// +--------+--------+--------+--------+
|
||||
// | opcode | A | B | C | A_B_C: 3 unsigned 8-bit operands
|
||||
// +--------+--------+--------+--------+
|
||||
//
|
||||
//
|
||||
// INSTRUCTIONS
|
||||
//
|
||||
// - Trap
|
||||
//
|
||||
// Unreachable instruction.
|
||||
//
|
||||
// - Entry rD
|
||||
//
|
||||
// Function prologue for the function
|
||||
// rD - number of local slots to reserve;
|
||||
//
|
||||
// - EntryOptional A, B, C
|
||||
//
|
||||
// Function prologue for the function with optional or named arguments:
|
||||
// A - expected number of positional arguments;
|
||||
// B - number of optional arguments;
|
||||
// C - number of named arguments;
|
||||
//
|
||||
// Only one of B and C can be not 0.
|
||||
//
|
||||
// If B is not 0 then EntryOptional bytecode is followed by B LoadConstant
|
||||
// bytecodes specifying default values for optional arguments.
|
||||
//
|
||||
// If C is not 0 then EntryOptional is followed by 2 * C LoadConstant
|
||||
// bytecodes.
|
||||
// Bytecode at 2 * i specifies name of the i-th named argument and at
|
||||
// 2 * i + 1 default value. rA part of the LoadConstant bytecode specifies
|
||||
// the location of the parameter on the stack. Here named arguments are
|
||||
// sorted alphabetically to enable linear matching similar to how function
|
||||
// prologues are implemented on other architectures.
|
||||
//
|
||||
// Note: Unlike Entry bytecode EntryOptional does not setup the frame for
|
||||
// local variables this is done by a separate bytecode Frame, which should
|
||||
// follow EntryOptional and its LoadConstant instructions.
|
||||
//
|
||||
// - EntrySuspendable A, B, C
|
||||
//
|
||||
// Similar to EntryOptional, but also reserves a local variable slot
|
||||
// for suspend state variable.
|
||||
//
|
||||
// - LoadConstant rA, D
|
||||
//
|
||||
// Used in conjunction with EntryOptional instruction to describe names and
|
||||
// default values of optional parameters.
|
||||
//
|
||||
// - Frame D
|
||||
//
|
||||
// Reserve and initialize with null space for D local variables.
|
||||
//
|
||||
// - CheckFunctionTypeArgs A, D
|
||||
//
|
||||
// Check for a passed-in type argument vector of length A and
|
||||
// store it at FP[D].
|
||||
//
|
||||
// - CheckStack A
|
||||
//
|
||||
// Compare SP against isolate stack limit and call StackOverflow handler if
|
||||
// necessary. Should be used in prologue (A = 0), or at the beginning of
|
||||
// a loop with depth A.
|
||||
//
|
||||
// - Allocate D
|
||||
//
|
||||
// Allocate object of class PP[D] with no type arguments.
|
||||
//
|
||||
// - AllocateT
|
||||
//
|
||||
// Allocate object of class SP[0] with type arguments SP[-1].
|
||||
//
|
||||
// - CreateArrayTOS
|
||||
//
|
||||
// Allocate array of length SP[0] with type arguments SP[-1].
|
||||
//
|
||||
// - AllocateContext A, D
|
||||
//
|
||||
// Allocate Context object holding D context variables.
|
||||
// A is a static ID of the context. Static ID of a context may be used to
|
||||
// disambiguate accesses to different context objects.
|
||||
// Context objects with the same ID should have the same number of
|
||||
// context variables.
|
||||
//
|
||||
// - CloneContext A, D
|
||||
//
|
||||
// Clone Context object SP[0] holding D context variables.
|
||||
// A is a static ID of the context. Cloned context has the same ID.
|
||||
//
|
||||
// - LoadContextParent
|
||||
//
|
||||
// Load parent from context SP[0].
|
||||
//
|
||||
// - StoreContextParent
|
||||
//
|
||||
// Store context SP[0] into `parent` field of context SP[-1].
|
||||
//
|
||||
// - LoadContextVar A, D
|
||||
//
|
||||
// Load value from context SP[0] at index D.
|
||||
// A is a static ID of the context.
|
||||
//
|
||||
// - StoreContextVar A, D
|
||||
//
|
||||
// Store value SP[0] into context SP[-1] at index D.
|
||||
// A is a static ID of the context.
|
||||
//
|
||||
// - PushConstant D
|
||||
//
|
||||
// Push value at index D from constant pool onto the stack.
|
||||
//
|
||||
// - PushNull
|
||||
//
|
||||
// Push `null` onto the stack.
|
||||
//
|
||||
// - PushTrue
|
||||
//
|
||||
// Push `true` onto the stack.
|
||||
//
|
||||
// - PushFalse
|
||||
//
|
||||
// Push `false` onto the stack.
|
||||
//
|
||||
// - PushInt rX
|
||||
//
|
||||
// Push int rX onto the stack.
|
||||
//
|
||||
// - Drop1
|
||||
//
|
||||
// Drop 1 value from the stack
|
||||
//
|
||||
// - Push rX
|
||||
//
|
||||
// Push FP[rX] to the stack.
|
||||
//
|
||||
// - StoreLocal rX; PopLocal rX
|
||||
//
|
||||
// Store top of the stack into FP[rX] and pop it if needed.
|
||||
//
|
||||
// - LoadFieldTOS D
|
||||
//
|
||||
// Push value at offset (in words) PP[D] from object SP[0].
|
||||
//
|
||||
// - StoreFieldTOS D
|
||||
//
|
||||
// Store value SP[0] into object SP[-1] at offset (in words) PP[D].
|
||||
//
|
||||
// - StoreIndexedTOS
|
||||
//
|
||||
// Store SP[0] into array SP[-2] at index SP[-1]. No typechecking is done.
|
||||
// SP[-2] is assumed to be a RawArray, SP[-1] to be a smi.
|
||||
//
|
||||
// - PushStatic D
|
||||
//
|
||||
// Pushes value of the static field PP[D] on to the stack.
|
||||
//
|
||||
// - StoreStaticTOS D
|
||||
//
|
||||
// Stores TOS into the static field PP[D].
|
||||
//
|
||||
// - Jump target
|
||||
//
|
||||
// Jump to the given target. Target is specified as offset from the PC of the
|
||||
// jump instruction.
|
||||
//
|
||||
// - JumpIfNoAsserts target
|
||||
//
|
||||
// Jump to the given target if assertions are not enabled.
|
||||
// Target is specified as offset from the PC of the jump instruction.
|
||||
//
|
||||
// - JumpIfNotZeroTypeArgs target
|
||||
//
|
||||
// Jump to the given target if number of passed function type
|
||||
// arguments is not zero.
|
||||
// Target is specified as offset from the PC of the jump instruction.
|
||||
//
|
||||
// - JumpIfEqStrict target; JumpIfNeStrict target
|
||||
//
|
||||
// Jump to the given target if SP[-1] is the same (JumpIfEqStrict) /
|
||||
// not the same (JumpIfNeStrict) object as SP[0].
|
||||
//
|
||||
// - JumpIfTrue target; JumpIfFalse target
|
||||
// - JumpIfNull target; JumpIfNotNull target
|
||||
//
|
||||
// Jump to the given target if SP[0] is true/false/null/not null.
|
||||
//
|
||||
// - IndirectStaticCall ArgC, D
|
||||
//
|
||||
// Invoke the function given by the ICData in SP[0] with arguments
|
||||
// SP[-(1+ArgC)], ..., SP[-1] and argument descriptor PP[D], which
|
||||
// indicates whether the first argument is a type argument vector.
|
||||
//
|
||||
// - DirectCall ArgC, D
|
||||
//
|
||||
// Invoke the function PP[D] with arguments
|
||||
// SP[-(ArgC-1)], ..., SP[0] and argument descriptor PP[D+1].
|
||||
//
|
||||
// - InterfaceCall ArgC, D
|
||||
//
|
||||
// Lookup and invoke method using ICData in PP[D]
|
||||
// with arguments SP[-(1+ArgC)], ..., SP[-1].
|
||||
// Method has to be declared (explicitly or implicitly) in an interface
|
||||
// implemented by a receiver, and passed arguments are valid for the
|
||||
// interface method declaration.
|
||||
// The ICData indicates whether the first argument is a type argument vector.
|
||||
//
|
||||
// - UncheckedInterfaceCall ArgC, D
|
||||
//
|
||||
// Same as InterfaceCall, but can omit type checks of generic-covariant
|
||||
// parameters.
|
||||
//
|
||||
// - DynamicCall ArgC, D
|
||||
//
|
||||
// Lookup and invoke method using ICData in PP[D]
|
||||
// with arguments SP[-(1+ArgC)], ..., SP[-1].
|
||||
// The ICData indicates whether the first argument is a type argument vector.
|
||||
//
|
||||
// - ReturnTOS
|
||||
//
|
||||
// Return to the caller using a value from the top-of-stack as a result.
|
||||
//
|
||||
// Note: return instruction knows how many arguments to remove from the
|
||||
// stack because it can look at the call instruction at caller's PC and
|
||||
// take argument count from it.
|
||||
//
|
||||
// - ReturnAsync
|
||||
//
|
||||
// Return to the caller from async function using a value from
|
||||
// the top-of-stack as a result.
|
||||
//
|
||||
// - ReturnAsyncStar
|
||||
//
|
||||
// Return to the caller from async* function using a value from
|
||||
// the top-of-stack as a result.
|
||||
//
|
||||
// - ReturnSyncStar
|
||||
//
|
||||
// Return to the caller from sync* function using a value from
|
||||
// the top-of-stack as a result.
|
||||
//
|
||||
// - AssertAssignable A, D
|
||||
//
|
||||
// Assert that instance SP[-4] is assignable to variable named SP[0] of
|
||||
// type SP[-1] with instantiator type arguments SP[-3] and function type
|
||||
// arguments SP[-2] using SubtypeTestCache PP[D].
|
||||
// If A is 1, then the instance may be a Smi.
|
||||
//
|
||||
// Instance remains on stack. Other arguments are consumed.
|
||||
//
|
||||
// - AssertBoolean A
|
||||
//
|
||||
// Assert that TOS is a boolean (A = 1) or that TOS is not null (A = 0).
|
||||
//
|
||||
// - AssertSubtype
|
||||
//
|
||||
// Assert that one type is a subtype of another. Throws a TypeError
|
||||
// otherwise. The stack has the following arguments on it:
|
||||
//
|
||||
// SP[-4] instantiator type args
|
||||
// SP[-3] function type args
|
||||
// SP[-2] sub_type
|
||||
// SP[-1] super_type
|
||||
// SP[-0] dst_name
|
||||
//
|
||||
// All 5 arguments are consumed from the stack and no results is pushed.
|
||||
//
|
||||
// - LoadTypeArgumentsField D
|
||||
//
|
||||
// Load instantiator type arguments from an instance SP[0].
|
||||
// PP[D] = offset (in words) of type arguments field corresponding
|
||||
// to an instance's class.
|
||||
//
|
||||
// - InstantiateType D
|
||||
//
|
||||
// Instantiate type PP[D] with instantiator type arguments SP[-1] and
|
||||
// function type arguments SP[0].
|
||||
//
|
||||
// - InstantiateTypeArgumentsTOS A, D
|
||||
//
|
||||
// Instantiate type arguments PP[D] with instantiator type arguments SP[-1]
|
||||
// and function type arguments SP[0]. A != 0 indicates that resulting type
|
||||
// arguments are all dynamic if both instantiator and function type
|
||||
// arguments are all dynamic.
|
||||
//
|
||||
// - Throw A
|
||||
//
|
||||
// Throw (Rethrow if A != 0) exception. Exception object and stack object
|
||||
// are taken from TOS.
|
||||
//
|
||||
// - MoveSpecial A, rX
|
||||
//
|
||||
// Copy value from special variable to FP[rX]. Currently only
|
||||
// used to pass exception object (A = 0) and stack trace object (A = 1) to
|
||||
// catch handler.
|
||||
//
|
||||
// - SetFrame A
|
||||
//
|
||||
// Reinitialize SP assuming that current frame has size A.
|
||||
// Used to drop temporaries from the stack in the exception handler.
|
||||
//
|
||||
// - BooleanNegateTOS
|
||||
//
|
||||
// SP[0] = !SP[0]
|
||||
//
|
||||
// - EqualsNull
|
||||
//
|
||||
// SP[0] = (SP[0] == null) ? true : false
|
||||
//
|
||||
// - NegateInt
|
||||
//
|
||||
// Equivalent to invocation of unary int operator-.
|
||||
// Receiver should have static type int.
|
||||
// Check SP[0] for null; SP[0] = -SP[0].
|
||||
//
|
||||
// - AddInt; SubInt; MulInt; TruncDivInt; ModInt; BitAndInt; BitOrInt;
|
||||
// BitXorInt; ShlInt; ShrInt
|
||||
//
|
||||
// Equivalent to invocation of binary int operator +, -, *, ~/, %, &, |,
|
||||
// ^, << or >>. Receiver and argument should have static type int.
|
||||
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0].
|
||||
//
|
||||
// - CompareIntEq; CompareIntGt; CompareIntLt; CompareIntGe; CompareIntLe
|
||||
//
|
||||
// Equivalent to invocation of binary int operator ==, >, <, >= or <=.
|
||||
// Receiver and argument should have static type int.
|
||||
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0] ? true : false.
|
||||
//
|
||||
// - NegateDouble
|
||||
//
|
||||
// Equivalent to invocation of unary double operator-.
|
||||
// Receiver should have static type double.
|
||||
// Check SP[0] for null; SP[0] = -SP[0].
|
||||
//
|
||||
// - AddDouble; SubDouble; MulDouble; DivDouble
|
||||
//
|
||||
// Equivalent to invocation of binary int operator +, -, *, /.
|
||||
// Receiver and argument should have static type double.
|
||||
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0].
|
||||
//
|
||||
// - CompareDoubleEq; CompareDoubleGt; CompareDoubleLt; CompareDoubleGe;
|
||||
// CompareDoubleLe
|
||||
//
|
||||
// Equivalent to invocation of binary double operator ==, >, <, >= or <=.
|
||||
// Receiver and argument should have static type double.
|
||||
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0] ? true : false.
|
||||
//
|
||||
// - AllocateClosure D
|
||||
//
|
||||
// Allocate closure object for closure function ConstantPool[D].
|
||||
//
|
||||
// BYTECODE LIST FORMAT
|
||||
//
|
||||
// KernelBytecode list below is specified using the following format:
|
||||
//
|
||||
// V(BytecodeName, OperandForm, BytecodeKind, Op1, Op2, Op3)
|
||||
//
|
||||
// - OperandForm specifies operand encoding and should be one of 0, A, D, X, T,
|
||||
// A_E, A_Y, D_F or A_B_C (see ENCODING section above).
|
||||
//
|
||||
// - BytecodeKind is one of WIDE, RESV (reserved), ORDN (ordinary)
|
||||
//
|
||||
// - Op1, Op2, Op3 specify operand meaning. Possible values:
|
||||
//
|
||||
// ___ ignored / non-existent operand
|
||||
// num immediate operand
|
||||
// lit constant literal from object pool
|
||||
// reg register (unsigned FP relative local)
|
||||
// xeg x-register (signed FP relative local)
|
||||
// tgt jump target relative to the PC of the current instruction
|
||||
//
|
||||
// TODO(vegorov) jump targets should be encoded relative to PC of the next
|
||||
// instruction because PC is incremented immediately after fetch
|
||||
// and before decoding.
|
||||
//
|
||||
#define PUBLIC_KERNEL_BYTECODES_LIST(V) \
|
||||
V(Trap, 0, ORDN, ___, ___, ___) \
|
||||
V(Unused00, 0, RESV, ___, ___, ___) \
|
||||
V(Entry, D, ORDN, num, ___, ___) \
|
||||
V(Entry_Wide, D, WIDE, num, ___, ___) \
|
||||
V(EntryOptional, A_B_C, ORDN, num, num, num) \
|
||||
V(EntrySuspendable, A_B_C, ORDN, num, num, num) \
|
||||
V(LoadConstant, A_E, ORDN, reg, lit, ___) \
|
||||
V(LoadConstant_Wide, A_E, WIDE, reg, lit, ___) \
|
||||
V(Frame, D, ORDN, num, ___, ___) \
|
||||
V(Frame_Wide, D, WIDE, num, ___, ___) \
|
||||
V(CheckFunctionTypeArgs, A_E, ORDN, num, reg, ___) \
|
||||
V(CheckFunctionTypeArgs_Wide, A_E, WIDE, num, reg, ___) \
|
||||
V(CheckStack, A, ORDN, num, ___, ___) \
|
||||
V(DebugCheck, 0, ORDN, ___, ___, ___) \
|
||||
V(JumpIfUnchecked, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfUnchecked_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(Allocate, D, ORDN, lit, ___, ___) \
|
||||
V(Allocate_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(AllocateT, 0, ORDN, ___, ___, ___) \
|
||||
V(CreateArrayTOS, 0, ORDN, ___, ___, ___) \
|
||||
V(AllocateClosure, D, ORDN, lit, ___, ___) \
|
||||
V(AllocateClosure_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(AllocateContext, A_E, ORDN, num, num, ___) \
|
||||
V(AllocateContext_Wide, A_E, WIDE, num, num, ___) \
|
||||
V(CloneContext, A_E, ORDN, num, num, ___) \
|
||||
V(CloneContext_Wide, A_E, WIDE, num, num, ___) \
|
||||
V(LoadContextParent, 0, ORDN, ___, ___, ___) \
|
||||
V(StoreContextParent, 0, ORDN, ___, ___, ___) \
|
||||
V(LoadContextVar, A_E, ORDN, num, num, ___) \
|
||||
V(LoadContextVar_Wide, A_E, WIDE, num, num, ___) \
|
||||
V(Unused04, 0, RESV, ___, ___, ___) \
|
||||
V(Unused05, 0, RESV, ___, ___, ___) \
|
||||
V(StoreContextVar, A_E, ORDN, num, num, ___) \
|
||||
V(StoreContextVar_Wide, A_E, WIDE, num, num, ___) \
|
||||
V(PushConstant, D, ORDN, lit, ___, ___) \
|
||||
V(PushConstant_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(Unused06, 0, RESV, ___, ___, ___) \
|
||||
V(Unused07, 0, RESV, ___, ___, ___) \
|
||||
V(PushTrue, 0, ORDN, ___, ___, ___) \
|
||||
V(PushFalse, 0, ORDN, ___, ___, ___) \
|
||||
V(PushInt, X, ORDN, num, ___, ___) \
|
||||
V(PushInt_Wide, X, WIDE, num, ___, ___) \
|
||||
V(Unused08, 0, RESV, ___, ___, ___) \
|
||||
V(Unused09, 0, RESV, ___, ___, ___) \
|
||||
V(Unused10, 0, RESV, ___, ___, ___) \
|
||||
V(Unused11, 0, RESV, ___, ___, ___) \
|
||||
V(PushNull, 0, ORDN, ___, ___, ___) \
|
||||
V(Drop1, 0, ORDN, ___, ___, ___) \
|
||||
V(Push, X, ORDN, xeg, ___, ___) \
|
||||
V(Push_Wide, X, WIDE, xeg, ___, ___) \
|
||||
V(Unused12, 0, RESV, ___, ___, ___) \
|
||||
V(Unused13, 0, RESV, ___, ___, ___) \
|
||||
V(Unused14, 0, RESV, ___, ___, ___) \
|
||||
V(Unused15, 0, RESV, ___, ___, ___) \
|
||||
V(Unused16, 0, RESV, ___, ___, ___) \
|
||||
V(Unused17, 0, RESV, ___, ___, ___) \
|
||||
V(PopLocal, X, ORDN, xeg, ___, ___) \
|
||||
V(PopLocal_Wide, X, WIDE, xeg, ___, ___) \
|
||||
V(LoadStatic, D, ORDN, lit, ___, ___) \
|
||||
V(LoadStatic_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(StoreLocal, X, ORDN, xeg, ___, ___) \
|
||||
V(StoreLocal_Wide, X, WIDE, xeg, ___, ___) \
|
||||
V(LoadFieldTOS, D, ORDN, lit, ___, ___) \
|
||||
V(LoadFieldTOS_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(StoreFieldTOS, D, ORDN, lit, ___, ___) \
|
||||
V(StoreFieldTOS_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(StoreIndexedTOS, 0, ORDN, ___, ___, ___) \
|
||||
V(Unused20, 0, RESV, ___, ___, ___) \
|
||||
V(JumpIfInitialized, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfInitialized_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(PushUninitializedSentinel, 0, ORDN, ___, ___, ___) \
|
||||
V(Unused21, 0, RESV, ___, ___, ___) \
|
||||
V(InitLateField, D, ORDN, lit, ___, ___) \
|
||||
V(InitLateField_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(StoreStaticTOS, D, ORDN, lit, ___, ___) \
|
||||
V(StoreStaticTOS_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(Jump, T, ORDN, tgt, ___, ___) \
|
||||
V(Jump_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfNoAsserts, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfNoAsserts_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfNotZeroTypeArgs, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfNotZeroTypeArgs_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfEqStrict, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfEqStrict_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfNeStrict, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfNeStrict_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfTrue, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfTrue_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfFalse, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfFalse_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfNull, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfNull_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(JumpIfNotNull, T, ORDN, tgt, ___, ___) \
|
||||
V(JumpIfNotNull_Wide, T, WIDE, tgt, ___, ___) \
|
||||
V(DirectCall, D_F, ORDN, num, num, ___) \
|
||||
V(DirectCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedDirectCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedDirectCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(InterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(Unused23, 0, RESV, ___, ___, ___) \
|
||||
V(Unused24, 0, RESV, ___, ___, ___) \
|
||||
V(InstantiatedInterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
V(InstantiatedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedClosureCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedClosureCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedInterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(DynamicCall, D_F, ORDN, num, num, ___) \
|
||||
V(DynamicCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(ReturnTOS, 0, ORDN, ___, ___, ___) \
|
||||
V(ReturnAsync, 0, ORDN, ___, ___, ___) \
|
||||
V(ReturnAsyncStar, 0, ORDN, ___, ___, ___) \
|
||||
V(ReturnSyncStar, 0, ORDN, ___, ___, ___) \
|
||||
V(AssertAssignable, A_E, ORDN, num, lit, ___) \
|
||||
V(AssertAssignable_Wide, A_E, WIDE, num, lit, ___) \
|
||||
V(Unused30, 0, RESV, ___, ___, ___) \
|
||||
V(Unused31, 0, RESV, ___, ___, ___) \
|
||||
V(AssertBoolean, A, ORDN, num, ___, ___) \
|
||||
V(AssertSubtype, 0, ORDN, ___, ___, ___) \
|
||||
V(LoadTypeArgumentsField, D, ORDN, lit, ___, ___) \
|
||||
V(LoadTypeArgumentsField_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(InstantiateType, D, ORDN, lit, ___, ___) \
|
||||
V(InstantiateType_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(InstantiateTypeArgumentsTOS, A_E, ORDN, num, lit, ___) \
|
||||
V(InstantiateTypeArgumentsTOS_Wide, A_E, WIDE, num, lit, ___) \
|
||||
V(Unused32, 0, RESV, ___, ___, ___) \
|
||||
V(Unused33, 0, RESV, ___, ___, ___) \
|
||||
V(Unused34, 0, RESV, ___, ___, ___) \
|
||||
V(Unused35, 0, RESV, ___, ___, ___) \
|
||||
V(Throw, A, ORDN, num, ___, ___) \
|
||||
V(SetFrame, A, ORDN, num, ___, num) \
|
||||
V(MoveSpecial, A_Y, ORDN, num, xeg, ___) \
|
||||
V(MoveSpecial_Wide, A_Y, WIDE, num, xeg, ___) \
|
||||
V(BooleanNegateTOS, 0, ORDN, ___, ___, ___) \
|
||||
V(EqualsNull, 0, ORDN, ___, ___, ___) \
|
||||
V(NullCheck, D, ORDN, lit, ___, ___) \
|
||||
V(NullCheck_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(NegateInt, 0, ORDN, ___, ___, ___) \
|
||||
V(AddInt, 0, ORDN, ___, ___, ___) \
|
||||
V(SubInt, 0, ORDN, ___, ___, ___) \
|
||||
V(MulInt, 0, ORDN, ___, ___, ___) \
|
||||
V(TruncDivInt, 0, ORDN, ___, ___, ___) \
|
||||
V(ModInt, 0, ORDN, ___, ___, ___) \
|
||||
V(BitAndInt, 0, ORDN, ___, ___, ___) \
|
||||
V(BitOrInt, 0, ORDN, ___, ___, ___) \
|
||||
V(BitXorInt, 0, ORDN, ___, ___, ___) \
|
||||
V(ShlInt, 0, ORDN, ___, ___, ___) \
|
||||
V(ShrInt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareIntEq, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareIntGt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareIntLt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareIntGe, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareIntLe, 0, ORDN, ___, ___, ___) \
|
||||
V(NegateDouble, 0, ORDN, ___, ___, ___) \
|
||||
V(AddDouble, 0, ORDN, ___, ___, ___) \
|
||||
V(SubDouble, 0, ORDN, ___, ___, ___) \
|
||||
V(MulDouble, 0, ORDN, ___, ___, ___) \
|
||||
V(DivDouble, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleEq, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleGt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleLt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleGe, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleLe, 0, ORDN, ___, ___, ___) \
|
||||
|
||||
// These bytecodes are only generated within the VM. Reassigning their
|
||||
// opcodes is not a breaking change.
|
||||
#define INTERNAL_KERNEL_BYTECODES_LIST(V) \
|
||||
V(VMInternal_ImplicitGetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitSetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitStaticGetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_MethodExtractor, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_InvokeClosure, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_InvokeField, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_NoSuchMethodDispatcher, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitStaticClosure, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitInstanceClosure, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitConstructorClosure, 0, ORDN, ___, ___, ___) \
|
||||
|
||||
#define KERNEL_BYTECODES_LIST(V) \
|
||||
PUBLIC_KERNEL_BYTECODES_LIST(V) \
|
||||
INTERNAL_KERNEL_BYTECODES_LIST(V)
|
||||
|
||||
// clang-format on
|
||||
|
||||
typedef uint8_t KBCInstr;
|
||||
|
||||
class KernelBytecode {
|
||||
public:
|
||||
// Magic value of bytecode files.
|
||||
static const intptr_t kMagicValue = 0x44424333; // 'DBC3'
|
||||
// Bytecode format version supported by the VM
|
||||
// (should match pkg/dart2bytecode/lib/dbc.dart).
|
||||
static const intptr_t kBytecodeFormatVersion = 1;
|
||||
|
||||
enum Opcode {
|
||||
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
|
||||
KERNEL_BYTECODES_LIST(DECLARE_BYTECODE)
|
||||
#undef DECLARE_BYTECODE
|
||||
};
|
||||
|
||||
static const char* NameOf(Opcode op) {
|
||||
const char* names[] = {
|
||||
#define NAME(name, encoding, kind, op1, op2, op3) #name,
|
||||
KERNEL_BYTECODES_LIST(NAME)
|
||||
#undef NAME
|
||||
};
|
||||
return names[op];
|
||||
}
|
||||
|
||||
static const intptr_t kInstructionSize[];
|
||||
|
||||
enum SpecialIndex {
|
||||
kExceptionSpecialIndex,
|
||||
kStackTraceSpecialIndex,
|
||||
kSpecialIndexCount
|
||||
};
|
||||
|
||||
private:
|
||||
static const intptr_t kWideModifier = 1;
|
||||
|
||||
// Should be used only on instructions with wide variants.
|
||||
DART_FORCE_INLINE static bool IsWide(const KBCInstr* instr) {
|
||||
return ((DecodeOpcode(instr) & kWideModifier) != 0);
|
||||
}
|
||||
|
||||
public:
|
||||
DART_FORCE_INLINE static uint8_t DecodeA(const KBCInstr* bc) { return bc[1]; }
|
||||
|
||||
DART_FORCE_INLINE static uint8_t DecodeB(const KBCInstr* bc) { return bc[2]; }
|
||||
|
||||
DART_FORCE_INLINE static uint8_t DecodeC(const KBCInstr* bc) { return bc[3]; }
|
||||
|
||||
DART_FORCE_INLINE static uint32_t DecodeD(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return static_cast<uint32_t>(bc[1]) |
|
||||
(static_cast<uint32_t>(bc[2]) << 8) |
|
||||
(static_cast<uint32_t>(bc[3]) << 16) |
|
||||
(static_cast<uint32_t>(bc[4]) << 24);
|
||||
} else {
|
||||
return bc[1];
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static int32_t DecodeX(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return static_cast<int32_t>(static_cast<uint32_t>(bc[1]) |
|
||||
(static_cast<uint32_t>(bc[2]) << 8) |
|
||||
(static_cast<uint32_t>(bc[3]) << 16) |
|
||||
(static_cast<uint32_t>(bc[4]) << 24));
|
||||
} else {
|
||||
return static_cast<int8_t>(bc[1]);
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static int32_t DecodeT(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return static_cast<int32_t>((static_cast<uint32_t>(bc[1]) << 8) |
|
||||
(static_cast<uint32_t>(bc[2]) << 16) |
|
||||
(static_cast<uint32_t>(bc[3]) << 24)) >>
|
||||
8;
|
||||
} else {
|
||||
return static_cast<int8_t>(bc[1]);
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static uint32_t DecodeE(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return static_cast<uint32_t>(bc[2]) |
|
||||
(static_cast<uint32_t>(bc[3]) << 8) |
|
||||
(static_cast<uint32_t>(bc[4]) << 16) |
|
||||
(static_cast<uint32_t>(bc[5]) << 24);
|
||||
} else {
|
||||
return bc[2];
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static int32_t DecodeY(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return static_cast<int32_t>(static_cast<uint32_t>(bc[2]) |
|
||||
(static_cast<uint32_t>(bc[3]) << 8) |
|
||||
(static_cast<uint32_t>(bc[4]) << 16) |
|
||||
(static_cast<uint32_t>(bc[5]) << 24));
|
||||
} else {
|
||||
return static_cast<int8_t>(bc[2]);
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static uint8_t DecodeF(const KBCInstr* bc) {
|
||||
if (IsWide(bc)) {
|
||||
return bc[5];
|
||||
} else {
|
||||
return bc[2];
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static Opcode DecodeOpcode(const KBCInstr* bc) {
|
||||
return static_cast<Opcode>(bc[0]);
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static const KBCInstr* Next(const KBCInstr* bc) {
|
||||
return bc + kInstructionSize[DecodeOpcode(bc)];
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static uword Next(uword pc) {
|
||||
return pc + kInstructionSize[DecodeOpcode(
|
||||
reinterpret_cast<const KBCInstr*>(pc))];
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsJumpOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kJump:
|
||||
case KernelBytecode::kJump_Wide:
|
||||
case KernelBytecode::kJumpIfNoAsserts:
|
||||
case KernelBytecode::kJumpIfNoAsserts_Wide:
|
||||
case KernelBytecode::kJumpIfNotZeroTypeArgs:
|
||||
case KernelBytecode::kJumpIfNotZeroTypeArgs_Wide:
|
||||
case KernelBytecode::kJumpIfEqStrict:
|
||||
case KernelBytecode::kJumpIfEqStrict_Wide:
|
||||
case KernelBytecode::kJumpIfNeStrict:
|
||||
case KernelBytecode::kJumpIfNeStrict_Wide:
|
||||
case KernelBytecode::kJumpIfTrue:
|
||||
case KernelBytecode::kJumpIfTrue_Wide:
|
||||
case KernelBytecode::kJumpIfFalse:
|
||||
case KernelBytecode::kJumpIfFalse_Wide:
|
||||
case KernelBytecode::kJumpIfNull:
|
||||
case KernelBytecode::kJumpIfNull_Wide:
|
||||
case KernelBytecode::kJumpIfNotNull:
|
||||
case KernelBytecode::kJumpIfNotNull_Wide:
|
||||
case KernelBytecode::kJumpIfUnchecked:
|
||||
case KernelBytecode::kJumpIfUnchecked_Wide:
|
||||
case KernelBytecode::kJumpIfInitialized:
|
||||
case KernelBytecode::kJumpIfInitialized_Wide:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsJumpIfUncheckedOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kJumpIfUnchecked:
|
||||
case KernelBytecode::kJumpIfUnchecked_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsLoadConstantOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kLoadConstant:
|
||||
case KernelBytecode::kLoadConstant_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsCheckStackOpcode(const KBCInstr* instr) {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kCheckStack;
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsCheckFunctionTypeArgs(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kCheckFunctionTypeArgs:
|
||||
case KernelBytecode::kCheckFunctionTypeArgs_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsEntryOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kEntry:
|
||||
case KernelBytecode::kEntry_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsEntryOptionalOpcode(const KBCInstr* instr) {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kEntryOptional;
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsFrameOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kFrame:
|
||||
case KernelBytecode::kFrame_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsSetFrameOpcode(const KBCInstr* instr) {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kSetFrame;
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsDebugCheckOpcode(const KBCInstr* instr) {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kDebugCheck;
|
||||
}
|
||||
|
||||
// The interpreter, the bytecode generator, the bytecode compiler, and this
|
||||
// function must agree on this list of opcodes.
|
||||
// For each instruction with listed opcode:
|
||||
// - The interpreter checks for a debug break.
|
||||
// - The bytecode generator emits a source position.
|
||||
// - The bytecode compiler may emit a DebugStepCheck call.
|
||||
DART_FORCE_INLINE static bool IsDebugCheckedOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kDebugCheck:
|
||||
case KernelBytecode::kDirectCall:
|
||||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kUncheckedDirectCall:
|
||||
case KernelBytecode::kUncheckedDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
case KernelBytecode::kUncheckedInterfaceCall_Wide:
|
||||
case KernelBytecode::kDynamicCall:
|
||||
case KernelBytecode::kDynamicCall_Wide:
|
||||
case KernelBytecode::kReturnTOS:
|
||||
case KernelBytecode::kReturnAsync:
|
||||
case KernelBytecode::kReturnAsyncStar:
|
||||
case KernelBytecode::kReturnSyncStar:
|
||||
case KernelBytecode::kEqualsNull:
|
||||
case KernelBytecode::kNegateInt:
|
||||
case KernelBytecode::kNegateDouble:
|
||||
case KernelBytecode::kAddInt:
|
||||
case KernelBytecode::kSubInt:
|
||||
case KernelBytecode::kMulInt:
|
||||
case KernelBytecode::kTruncDivInt:
|
||||
case KernelBytecode::kModInt:
|
||||
case KernelBytecode::kBitAndInt:
|
||||
case KernelBytecode::kBitOrInt:
|
||||
case KernelBytecode::kBitXorInt:
|
||||
case KernelBytecode::kShlInt:
|
||||
case KernelBytecode::kShrInt:
|
||||
case KernelBytecode::kCompareIntEq:
|
||||
case KernelBytecode::kCompareIntGt:
|
||||
case KernelBytecode::kCompareIntLt:
|
||||
case KernelBytecode::kCompareIntGe:
|
||||
case KernelBytecode::kCompareIntLe:
|
||||
case KernelBytecode::kAddDouble:
|
||||
case KernelBytecode::kSubDouble:
|
||||
case KernelBytecode::kMulDouble:
|
||||
case KernelBytecode::kDivDouble:
|
||||
case KernelBytecode::kCompareDoubleEq:
|
||||
case KernelBytecode::kCompareDoubleGt:
|
||||
case KernelBytecode::kCompareDoubleLt:
|
||||
case KernelBytecode::kCompareDoubleGe:
|
||||
case KernelBytecode::kCompareDoubleLe:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static uint8_t DecodeArgc(const KBCInstr* ret_addr) {
|
||||
// All call instructions have DF encoding, with argc being the last byte
|
||||
// regardless of whether the wide variant is used or not.
|
||||
return ret_addr[-1];
|
||||
}
|
||||
|
||||
// Converts bytecode PC into an offset.
|
||||
// For return addresses used in PcDescriptors, PC is also augmented by 1.
|
||||
// TODO(regis): Eliminate this correction.
|
||||
static intptr_t BytecodePcToOffset(uint32_t pc, bool is_return_address) {
|
||||
return pc + (is_return_address ? 1 : 0);
|
||||
}
|
||||
|
||||
static uint32_t OffsetToBytecodePc(intptr_t offset, bool is_return_address) {
|
||||
return offset - (is_return_address ? 1 : 0);
|
||||
}
|
||||
|
||||
static void GetVMInternalBytecodeInstructions(Opcode opcode,
|
||||
const KBCInstr** instructions,
|
||||
intptr_t* instructions_size);
|
||||
|
||||
private:
|
||||
DISALLOW_ALLOCATION();
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(KernelBytecode);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_CONSTANTS_KBC_H_
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/dispatch_table.h"
|
||||
#include "vm/heap/safepoint.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/resolver.h"
|
||||
#include "vm/runtime_entry.h"
|
||||
@@ -120,6 +121,17 @@ ObjectPtr DartEntry::InvokeFunction(const Function& function,
|
||||
ASSERT(thread->IsDartMutatorThread());
|
||||
ASSERT(!function.IsNull());
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (function.HasBytecode()) {
|
||||
// SuspendLongJumpScope suspend_long_jump_scope(thread);
|
||||
TransitionToGenerated transition(thread);
|
||||
return Interpreter::Current()->Call(function, arguments_descriptor,
|
||||
arguments, thread);
|
||||
} else {
|
||||
ASSERT(!function.is_declared_in_bytecode());
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (!function.HasCode()) {
|
||||
const Object& result = Object::Handle(
|
||||
|
||||
@@ -179,6 +179,8 @@ class ArgumentsDescriptor : public ValueObject {
|
||||
// A cache of VM heap allocated arguments descriptors.
|
||||
static ArrayPtr cached_args_descriptors_[kCachedDescriptorCount];
|
||||
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class VMSerializationRoots;
|
||||
friend class VMDeserializationRoots;
|
||||
DISALLOW_COPY_AND_ASSIGN(ArgumentsDescriptor);
|
||||
|
||||
@@ -1684,7 +1684,7 @@ DebuggerStackTrace* DebuggerStackTrace::Collect() {
|
||||
OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
|
||||
frame->ToCString());
|
||||
}
|
||||
if (frame->IsDartFrame()) {
|
||||
if (frame->IsDartFrame() && !frame->is_interpreted()) {
|
||||
code = frame->LookupDartCode();
|
||||
stack_trace->AppendCodeFrames(frame, code);
|
||||
}
|
||||
@@ -1743,6 +1743,9 @@ DebuggerStackTrace* DebuggerStackTrace::CollectAsyncAwaiters() {
|
||||
StackTraceUtils::CollectFrames(
|
||||
thread, /*skip_frames=*/0,
|
||||
[&](const StackTraceUtils::Frame& frame) {
|
||||
if (frame.code.IsNull()) {
|
||||
return;
|
||||
}
|
||||
if (frame.frame != nullptr) { // Synchronous portion of the stack.
|
||||
stack_trace->AppendCodeFrames(frame.frame, frame.code);
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/longjump.h"
|
||||
#include "vm/object.h"
|
||||
@@ -105,14 +106,25 @@ static void BuildStackTrace(StackTraceBuilder* builder) {
|
||||
StackFrame* frame = frames.NextFrame();
|
||||
ASSERT(frame != nullptr); // We expect to find a dart invocation frame.
|
||||
Code& code = Code::Handle();
|
||||
Bytecode& bytecode = Bytecode::Handle();
|
||||
for (; frame != nullptr; frame = frames.NextFrame()) {
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
code = frame->LookupDartCode();
|
||||
ASSERT(code.ContainsInstructionAt(frame->pc()));
|
||||
const uword pc_offset = frame->pc() - code.PayloadStart();
|
||||
builder->AddFrame(code, pc_offset);
|
||||
if (frame->is_interpreted()) {
|
||||
bytecode = frame->LookupDartBytecode();
|
||||
ASSERT(bytecode.ContainsInstructionAt(frame->pc()));
|
||||
if (bytecode.function() == Function::null()) {
|
||||
continue;
|
||||
}
|
||||
const uword pc_offset = frame->pc() - bytecode.PayloadStart();
|
||||
builder->AddFrame(bytecode, pc_offset);
|
||||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
ASSERT(code.ContainsInstructionAt(frame->pc()));
|
||||
const uword pc_offset = frame->pc() - code.PayloadStart();
|
||||
builder->AddFrame(code, pc_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +599,9 @@ static void ClearLazyDeopts(Thread* thread, uword frame_pointer) {
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
for (StackFrame* frame = frames.NextFrame(); frame != nullptr;
|
||||
frame = frames.NextFrame()) {
|
||||
if (frame->fp() >= frame_pointer) {
|
||||
if (frame->is_interpreted()) {
|
||||
continue;
|
||||
} else if (frame->fp() >= frame_pointer) {
|
||||
break;
|
||||
}
|
||||
if (frame->IsMarkedForLazyDeopt()) {
|
||||
@@ -634,6 +648,15 @@ NO_SANITIZE_SAFE_STACK // This function manipulates the safestack pointer.
|
||||
uword frame_pointer,
|
||||
bool clear_deopt_at_target) {
|
||||
ASSERT(thread->execution_state() == Thread::kThreadInVM);
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Interpreter* interpreter = thread->interpreter();
|
||||
if ((interpreter != nullptr) && interpreter->HasFrame(frame_pointer)) {
|
||||
interpreter->JumpToFrame(program_counter, stack_pointer, frame_pointer,
|
||||
thread);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
const uword fp_for_clearing =
|
||||
(clear_deopt_at_target ? frame_pointer + 1 : frame_pointer);
|
||||
ClearLazyDeopts(thread, fp_for_clearing);
|
||||
|
||||
@@ -87,6 +87,28 @@ void _printGeneratedStackTrace(uword fp, uword sp, uword pc) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Like _printDartStackTrace, but works in the interpreter loop.
|
||||
// Must be called with the current interpreter fp, sp, and pc.
|
||||
// Note that sp[0] is not modified, but sp[1] will be trashed.
|
||||
DART_EXPORT
|
||||
void _printInterpreterStackTrace(ObjectPtr* fp,
|
||||
ObjectPtr* sp,
|
||||
const KBCInstr* pc) {
|
||||
Thread* thread = Thread::Current();
|
||||
sp[1] = Function::null();
|
||||
sp[2] = Bytecode::null();
|
||||
sp[3] = static_cast<ObjectPtr>(reinterpret_cast<uword>(pc));
|
||||
sp[4] = static_cast<ObjectPtr>(reinterpret_cast<uword>(fp));
|
||||
ObjectPtr* exit_fp = sp + 1 + kKBCDartFrameFixedSize;
|
||||
thread->set_top_exit_frame_info(reinterpret_cast<uword>(exit_fp));
|
||||
thread->set_execution_state(Thread::kThreadInVM);
|
||||
_printDartStackTrace();
|
||||
thread->set_execution_state(Thread::kThreadInGenerated);
|
||||
thread->set_top_exit_frame_info(0);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
class PrintObjectPointersVisitor : public ObjectPointerVisitor {
|
||||
public:
|
||||
PrintObjectPointersVisitor()
|
||||
|
||||
@@ -726,6 +726,20 @@ class MarkingWeakVisitor : public HandleVisitor {
|
||||
void GCMarker::Prologue() {
|
||||
isolate_group_->ReleaseStoreBuffers();
|
||||
new_marking_stack_.PushAll(tlab_deferred_marking_stack_.PopAll());
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
isolate_group_->ForEachIsolate(
|
||||
[&](Isolate* isolate) {
|
||||
Thread* mutator_thread = isolate->mutator_thread();
|
||||
if (mutator_thread != nullptr) {
|
||||
Interpreter* interpreter = mutator_thread->interpreter();
|
||||
if (interpreter != nullptr) {
|
||||
interpreter->ClearLookupCache();
|
||||
}
|
||||
}
|
||||
},
|
||||
/*at_safepoint=*/true);
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
void GCMarker::Epilogue() {}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) 2024, 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/globals.h"
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#include "vm/compiler/method_recognizer.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
#include "vm/tagged_pointer.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class Array;
|
||||
class Code;
|
||||
class InterpreterSetjmpBuffer;
|
||||
class Isolate;
|
||||
class ObjectPointerVisitor;
|
||||
class Thread;
|
||||
|
||||
class LookupCache : public ValueObject {
|
||||
public:
|
||||
LookupCache() {
|
||||
ASSERT(Utils::IsPowerOfTwo(sizeof(Entry)));
|
||||
ASSERT(Utils::IsPowerOfTwo(sizeof(kNumEntries)));
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear();
|
||||
bool Lookup(intptr_t receiver_cid,
|
||||
StringPtr function_name,
|
||||
ArrayPtr arguments_descriptor,
|
||||
FunctionPtr* target) const;
|
||||
void Insert(intptr_t receiver_cid,
|
||||
StringPtr function_name,
|
||||
ArrayPtr arguments_descriptor,
|
||||
FunctionPtr target);
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
intptr_t receiver_cid;
|
||||
StringPtr function_name;
|
||||
ArrayPtr arguments_descriptor;
|
||||
FunctionPtr target;
|
||||
};
|
||||
|
||||
static const intptr_t kNumEntries = 1024;
|
||||
static const intptr_t kTableMask = kNumEntries - 1;
|
||||
|
||||
Entry entries_[kNumEntries];
|
||||
};
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
static const uword kInterpreterStackUnderflowSize = 0x80;
|
||||
// The entry frame pc marker must be non-zero (a valid exception handler pc).
|
||||
static const word kEntryFramePcMarker = -1;
|
||||
|
||||
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_; }
|
||||
// Limit for StackOverflowError.
|
||||
uword overflow_stack_limit() const { return overflow_stack_limit_; }
|
||||
// High address (KBC stack grows up).
|
||||
uword stack_limit() const { return stack_limit_; }
|
||||
|
||||
// Returns true if the interpreter's stack contains the given frame.
|
||||
// TODO(regis): We should rely on a new thread vm_tag to identify an
|
||||
// interpreter frame and not need this HasFrame() method.
|
||||
bool HasFrame(uword frame) const {
|
||||
return frame >= stack_base() && frame < stack_limit();
|
||||
}
|
||||
|
||||
// Identify an entry frame by looking at its pc marker value.
|
||||
static bool IsEntryFrameMarker(const KBCInstr* pc) {
|
||||
return reinterpret_cast<word>(pc) == kEntryFramePcMarker;
|
||||
}
|
||||
|
||||
ObjectPtr Call(const Function& function,
|
||||
const Array& arguments_descriptor,
|
||||
const Array& arguments,
|
||||
Thread* thread);
|
||||
|
||||
ObjectPtr Call(FunctionPtr function,
|
||||
ArrayPtr argdesc,
|
||||
intptr_t argc,
|
||||
ObjectPtr const* argv,
|
||||
ArrayPtr args_array,
|
||||
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 reinterpret_cast<uword>(pc_); }
|
||||
|
||||
void Unexit(Thread* thread);
|
||||
|
||||
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
||||
void ClearLookupCache() { lookup_cache_.Clear(); }
|
||||
|
||||
#ifndef PRODUCT
|
||||
void set_is_debugging(bool value) { is_debugging_ = value; }
|
||||
bool is_debugging() const { return is_debugging_; }
|
||||
#endif // !PRODUCT
|
||||
|
||||
private:
|
||||
uintptr_t* stack_;
|
||||
uword stack_base_;
|
||||
uword overflow_stack_limit_;
|
||||
uword stack_limit_;
|
||||
|
||||
ObjectPtr* volatile fp_;
|
||||
const KBCInstr* volatile pc_;
|
||||
DEBUG_ONLY(uint64_t icount_;)
|
||||
|
||||
InterpreterSetjmpBuffer* last_setjmp_buffer_;
|
||||
|
||||
ObjectPoolPtr pp_; // Pool Pointer.
|
||||
ArrayPtr argdesc_; // Arguments Descriptor: used to pass information between
|
||||
// call instruction and the function entry.
|
||||
ObjectPtr special_[KernelBytecode::kSpecialIndexCount];
|
||||
|
||||
LookupCache lookup_cache_;
|
||||
|
||||
void Exit(Thread* thread,
|
||||
ObjectPtr* base,
|
||||
ObjectPtr* exit_frame,
|
||||
const KBCInstr* pc);
|
||||
|
||||
bool Invoke(Thread* thread,
|
||||
ObjectPtr* call_base,
|
||||
ObjectPtr* call_top,
|
||||
const KBCInstr** pc,
|
||||
ObjectPtr** FP,
|
||||
ObjectPtr** SP);
|
||||
|
||||
bool InvokeCompiled(Thread* thread,
|
||||
FunctionPtr function,
|
||||
ObjectPtr* call_base,
|
||||
ObjectPtr* call_top,
|
||||
const KBCInstr** pc,
|
||||
ObjectPtr** FP,
|
||||
ObjectPtr** SP);
|
||||
|
||||
bool InvokeBytecode(Thread* thread,
|
||||
FunctionPtr function,
|
||||
ObjectPtr* call_base,
|
||||
ObjectPtr* call_top,
|
||||
const KBCInstr** pc,
|
||||
ObjectPtr** FP,
|
||||
ObjectPtr** SP);
|
||||
|
||||
bool InstanceCall(Thread* thread,
|
||||
StringPtr target_name,
|
||||
ObjectPtr* call_base,
|
||||
ObjectPtr* call_top,
|
||||
const KBCInstr** pc,
|
||||
ObjectPtr** FP,
|
||||
ObjectPtr** SP);
|
||||
|
||||
bool CopyParameters(Thread* thread,
|
||||
const KBCInstr** pc,
|
||||
ObjectPtr** FP,
|
||||
ObjectPtr** SP,
|
||||
const intptr_t num_fixed_params,
|
||||
const intptr_t num_opt_pos_params,
|
||||
const intptr_t num_opt_named_params,
|
||||
const intptr_t num_reserved_locals);
|
||||
|
||||
bool AssertAssignable(Thread* thread,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* call_top,
|
||||
ObjectPtr* args,
|
||||
SubtypeTestCachePtr cache);
|
||||
template <bool is_getter>
|
||||
bool AssertAssignableField(Thread* thread,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP,
|
||||
InstancePtr instance,
|
||||
FieldPtr field,
|
||||
InstancePtr value);
|
||||
|
||||
bool AllocateMint(Thread* thread,
|
||||
int64_t value,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateDouble(Thread* thread,
|
||||
double value,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateFloat32x4(Thread* thread,
|
||||
simd128_value_t value,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateFloat64x2(Thread* thread,
|
||||
simd128_value_t value,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateArray(Thread* thread,
|
||||
TypeArgumentsPtr type_args,
|
||||
ObjectPtr length,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateContext(Thread* thread,
|
||||
intptr_t num_variables,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateClosure(Thread* thread,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
|
||||
#if defined(DEBUG)
|
||||
// Returns true if tracing of executed instructions is enabled.
|
||||
bool IsTracingExecution() const;
|
||||
|
||||
// Prints bytecode instruction at given pc for instruction tracing.
|
||||
void TraceInstruction(const KBCInstr* pc) const;
|
||||
|
||||
bool IsWritingTraceFile() const;
|
||||
void FlushTraceBuffer();
|
||||
void WriteInstructionToTrace(const KBCInstr* pc);
|
||||
|
||||
void* trace_file_;
|
||||
uint64_t trace_file_bytes_written_;
|
||||
|
||||
static const intptr_t kTraceBufferSizeInBytes = 10 * KB;
|
||||
static const intptr_t kTraceBufferInstrs =
|
||||
kTraceBufferSizeInBytes / sizeof(KBCInstr);
|
||||
KBCInstr* trace_buffer_;
|
||||
intptr_t trace_buffer_idx_;
|
||||
#endif // defined(DEBUG)
|
||||
|
||||
// Longjmp support for exceptions.
|
||||
InterpreterSetjmpBuffer* last_setjmp_buffer() { return last_setjmp_buffer_; }
|
||||
void set_last_setjmp_buffer(InterpreterSetjmpBuffer* buffer) {
|
||||
last_setjmp_buffer_ = buffer;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
bool is_debugging_ = false;
|
||||
#endif // !PRODUCT
|
||||
|
||||
friend class InterpreterSetjmpBuffer;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Interpreter);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
#endif // RUNTIME_VM_INTERPRETER_H_
|
||||
@@ -530,9 +530,9 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
|
||||
Mutex* unlinked_call_map_mutex() { return &unlinked_call_map_mutex_; }
|
||||
#endif
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
Mutex* initializer_functions_mutex() { return &initializer_functions_mutex_; }
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
SafepointRwLock* program_lock() { return program_lock_.get(); }
|
||||
|
||||
@@ -907,9 +907,9 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
|
||||
Mutex unlinked_call_map_mutex_;
|
||||
#endif
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
Mutex initializer_functions_mutex_;
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// Protect access to boxed_field_list_.
|
||||
Mutex field_list_mutex_;
|
||||
|
||||
@@ -2387,67 +2387,6 @@ FunctionPtr KernelLoader::GetClosureFunction(Thread* thread,
|
||||
return function.ptr();
|
||||
}
|
||||
|
||||
FunctionPtr CreateFieldInitializerFunction(Thread* thread,
|
||||
Zone* zone,
|
||||
const Field& field) {
|
||||
ASSERT(field.InitializerFunction() == Function::null());
|
||||
|
||||
String& init_name = String::Handle(zone, field.name());
|
||||
init_name = Symbols::FromConcat(thread, Symbols::InitPrefix(), init_name);
|
||||
|
||||
// Static field initializers are not added as members of their owning class,
|
||||
// so they must be preemptively given a patch class to avoid the meaning of
|
||||
// their kernel/token position changing during a reload. Compare
|
||||
// Class::PatchFieldsAndFunctions().
|
||||
// This might also be necessary for lazy computation of local var descriptors.
|
||||
// Compare https://codereview.chromium.org//1317753004
|
||||
const Script& script = Script::Handle(zone, field.Script());
|
||||
const Class& field_owner = Class::Handle(zone, field.Owner());
|
||||
const auto& kernel_program_info =
|
||||
KernelProgramInfo::Handle(zone, field.KernelProgramInfo());
|
||||
const PatchClass& initializer_owner = PatchClass::Handle(
|
||||
zone, PatchClass::New(field_owner, kernel_program_info, script));
|
||||
const Library& lib = Library::Handle(zone, field_owner.library());
|
||||
initializer_owner.set_kernel_library_index(lib.kernel_library_index());
|
||||
|
||||
// Create a static initializer.
|
||||
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
|
||||
const Function& initializer_fun = Function::Handle(
|
||||
zone,
|
||||
Function::New(signature, init_name, UntaggedFunction::kFieldInitializer,
|
||||
field.is_static(), // is_static
|
||||
false, // is_const
|
||||
false, // is_abstract
|
||||
false, // is_external
|
||||
false, // is_native
|
||||
initializer_owner, TokenPosition::kNoSource));
|
||||
if (!field.is_static()) {
|
||||
signature.set_num_fixed_parameters(1);
|
||||
signature.set_parameter_types(
|
||||
Array::Handle(zone, Array::New(1, Heap::kOld)));
|
||||
signature.SetParameterTypeAt(
|
||||
0, AbstractType::Handle(zone, field_owner.DeclarationType()));
|
||||
initializer_fun.CreateNameArray();
|
||||
initializer_fun.SetParameterNameAt(0, Symbols::This());
|
||||
}
|
||||
signature.set_result_type(AbstractType::Handle(zone, field.type()));
|
||||
initializer_fun.set_is_reflectable(false);
|
||||
initializer_fun.set_is_inlinable(false);
|
||||
initializer_fun.set_token_pos(field.token_pos());
|
||||
initializer_fun.set_end_token_pos(field.end_token_pos());
|
||||
initializer_fun.set_accessor_field(field);
|
||||
initializer_fun.InheritKernelOffsetFrom(field);
|
||||
initializer_fun.set_is_extension_member(field.is_extension_member());
|
||||
initializer_fun.set_is_extension_type_member(
|
||||
field.is_extension_type_member());
|
||||
|
||||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
initializer_fun.SetSignature(signature);
|
||||
|
||||
field.SetInitializerFunction(initializer_fun);
|
||||
return initializer_fun.ptr();
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace dart
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
@@ -420,10 +420,6 @@ class KernelLoader : public ValueObject {
|
||||
DISALLOW_COPY_AND_ASSIGN(KernelLoader);
|
||||
};
|
||||
|
||||
FunctionPtr CreateFieldInitializerFunction(Thread* thread,
|
||||
Zone* zone,
|
||||
const Field& field);
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace dart
|
||||
|
||||
|
||||
@@ -85,7 +85,8 @@ class NativeArguments {
|
||||
|
||||
ObjectPtr ArgAt(int index) const {
|
||||
ASSERT((index >= 0) && (index < ArgCount()));
|
||||
ObjectPtr* arg_ptr = &(argv_[-index]);
|
||||
ObjectPtr* arg_ptr =
|
||||
&(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]);
|
||||
// Tell MemorySanitizer the ObjectPtr was initialized (by generated code).
|
||||
MSAN_UNPOISON(arg_ptr, kWordSize);
|
||||
return *arg_ptr;
|
||||
@@ -94,7 +95,7 @@ class NativeArguments {
|
||||
void SetArgAt(int index, const Object& value) const {
|
||||
ASSERT(thread_->execution_state() == Thread::kThreadInVM);
|
||||
ASSERT((index >= 0) && (index < ArgCount()));
|
||||
argv_[-index] = value.ptr();
|
||||
argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index] = value.ptr();
|
||||
}
|
||||
|
||||
// Does not include hidden type arguments vector.
|
||||
@@ -198,14 +199,30 @@ class NativeArguments {
|
||||
kArgcSize = 24,
|
||||
kFunctionBit = kArgcBit + kArgcSize,
|
||||
kFunctionSize = 1,
|
||||
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 Interpreter;
|
||||
friend class NativeEntry;
|
||||
friend class Simulator;
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
NativeArguments(Thread* thread,
|
||||
int argc_tag,
|
||||
ObjectPtr* argv,
|
||||
ObjectPtr* retval)
|
||||
: thread_(thread),
|
||||
argc_tag_(ReverseArgOrderBit::update(true, argc_tag)),
|
||||
argv_(argv),
|
||||
retval_(retval) {}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// Since this function is passed an ObjectPtr directly, we need to be
|
||||
// exceedingly careful when we use it. If there are any other side
|
||||
// effects in the statement that may cause GC, it could lead to
|
||||
|
||||
+553
-30
@@ -16,6 +16,7 @@
|
||||
#include "platform/unicode.h"
|
||||
#include "vm/bit_vector.h"
|
||||
#include "vm/bootstrap.h"
|
||||
#include "vm/bytecode_reader.h"
|
||||
#include "vm/canonical_tables.h"
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/class_id.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "vm/code_descriptors.h"
|
||||
#include "vm/code_observers.h"
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/compiler/assembler/disassembler_kbc.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/cpu.h"
|
||||
@@ -173,6 +175,7 @@ ClassPtr Object::var_descriptors_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::exception_handlers_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::context_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::context_scope_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::bytecode_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::sentinel_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::singletargetcache_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
ClassPtr Object::unlinkedcall_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
@@ -551,6 +554,23 @@ static type SpecialCharacter(type value) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
static BytecodePtr CreateVMInternalBytecode(KernelBytecode::Opcode opcode) {
|
||||
const KBCInstr* instructions = nullptr;
|
||||
intptr_t instructions_size = 0;
|
||||
|
||||
KernelBytecode::GetVMInternalBytecodeInstructions(opcode, &instructions,
|
||||
&instructions_size);
|
||||
|
||||
const auto& bytecode = Bytecode::Handle(
|
||||
Bytecode::New(reinterpret_cast<uword>(instructions), instructions_size,
|
||||
-1, TypedDataBase::Handle(), Object::empty_object_pool()));
|
||||
bytecode.set_pc_descriptors(Object::empty_descriptors());
|
||||
bytecode.set_exception_handlers(Object::empty_exception_handlers());
|
||||
return bytecode.ptr();
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void Object::InitNullAndBool(IsolateGroup* isolate_group) {
|
||||
// Should only be run by the vm isolate.
|
||||
ASSERT(isolate_group == Dart::vm_isolate_group());
|
||||
@@ -912,6 +932,9 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
cls = Class::New<ContextScope, RTN::ContextScope>(isolate_group);
|
||||
context_scope_class_ = cls.ptr();
|
||||
|
||||
cls = Class::New<Bytecode, RTN::Bytecode>(isolate_group);
|
||||
bytecode_class_ = cls.ptr();
|
||||
|
||||
cls = Class::New<SingleTargetCache, RTN::SingleTargetCache>(isolate_group);
|
||||
singletargetcache_class_ = cls.ptr();
|
||||
|
||||
@@ -1270,6 +1293,41 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
// synthetic_getter_parameter_names_ object needs to be created earlier as
|
||||
// VM isolate snapshot reader references it before Object::FinalizeVMIsolate.
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
*implicit_getter_bytecode_ =
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_ImplicitGetter);
|
||||
|
||||
*implicit_setter_bytecode_ =
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_ImplicitSetter);
|
||||
|
||||
*implicit_static_getter_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ImplicitStaticGetter);
|
||||
|
||||
*method_extractor_bytecode_ =
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_MethodExtractor);
|
||||
|
||||
*invoke_closure_bytecode_ =
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_InvokeClosure);
|
||||
|
||||
*invoke_field_bytecode_ =
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_InvokeField);
|
||||
|
||||
*nsm_dispatcher_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_NoSuchMethodDispatcher);
|
||||
|
||||
*dynamic_invocation_forwarder_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ForwardDynamicInvocation);
|
||||
|
||||
*implicit_static_closure_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ImplicitStaticClosure);
|
||||
|
||||
*implicit_instance_closure_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ImplicitInstanceClosure);
|
||||
|
||||
*implicit_constructor_closure_bytecode_ = CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ImplicitConstructorClosure);
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// Some thread fields need to be reinitialized as null constants have not been
|
||||
// initialized until now.
|
||||
thread->ClearStickyError();
|
||||
@@ -1347,6 +1405,28 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
ASSERT(synthetic_getter_parameter_types_->IsArray());
|
||||
ASSERT(!synthetic_getter_parameter_names_->IsSmi());
|
||||
ASSERT(synthetic_getter_parameter_names_->IsArray());
|
||||
ASSERT(!implicit_getter_bytecode_->IsSmi());
|
||||
ASSERT(implicit_getter_bytecode_->IsBytecode());
|
||||
ASSERT(!implicit_setter_bytecode_->IsSmi());
|
||||
ASSERT(implicit_setter_bytecode_->IsBytecode());
|
||||
ASSERT(!implicit_static_getter_bytecode_->IsSmi());
|
||||
ASSERT(implicit_static_getter_bytecode_->IsBytecode());
|
||||
ASSERT(!method_extractor_bytecode_->IsSmi());
|
||||
ASSERT(method_extractor_bytecode_->IsBytecode());
|
||||
ASSERT(!invoke_closure_bytecode_->IsSmi());
|
||||
ASSERT(invoke_closure_bytecode_->IsBytecode());
|
||||
ASSERT(!invoke_field_bytecode_->IsSmi());
|
||||
ASSERT(invoke_field_bytecode_->IsBytecode());
|
||||
ASSERT(!nsm_dispatcher_bytecode_->IsSmi());
|
||||
ASSERT(nsm_dispatcher_bytecode_->IsBytecode());
|
||||
ASSERT(!dynamic_invocation_forwarder_bytecode_->IsSmi());
|
||||
ASSERT(dynamic_invocation_forwarder_bytecode_->IsBytecode());
|
||||
ASSERT(!implicit_static_closure_bytecode_->IsSmi());
|
||||
ASSERT(implicit_static_closure_bytecode_->IsBytecode());
|
||||
ASSERT(!implicit_instance_closure_bytecode_->IsSmi());
|
||||
ASSERT(implicit_instance_closure_bytecode_->IsBytecode());
|
||||
ASSERT(!implicit_constructor_closure_bytecode_->IsSmi());
|
||||
ASSERT(implicit_constructor_closure_bytecode_->IsBytecode());
|
||||
}
|
||||
|
||||
void Object::FinishInit(IsolateGroup* isolate_group) {
|
||||
@@ -1392,6 +1472,7 @@ void Object::Cleanup() {
|
||||
exception_handlers_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
context_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
context_scope_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
bytecode_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
singletargetcache_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
unlinkedcall_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
monomorphicsmiablecall_class_ = static_cast<ClassPtr>(RAW_NULL);
|
||||
@@ -1509,6 +1590,7 @@ void Object::FinalizeVMIsolate(IsolateGroup* isolate_group) {
|
||||
SET_CLASS_NAME(exception_handlers, ExceptionHandlers);
|
||||
SET_CLASS_NAME(context, Context);
|
||||
SET_CLASS_NAME(context_scope, ContextScope);
|
||||
SET_CLASS_NAME(bytecode, Bytecode);
|
||||
SET_CLASS_NAME(sentinel, Sentinel);
|
||||
SET_CLASS_NAME(singletargetcache, SingleTargetCache);
|
||||
SET_CLASS_NAME(unlinkedcall, UnlinkedCall);
|
||||
@@ -3105,7 +3187,7 @@ ClassPtr Class::New(IsolateGroup* isolate_group, bool register_class) {
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
static void ReportTooManyTypeArguments(const Class& cls) {
|
||||
Report::MessageF(Report::kError, Script::Handle(cls.script()),
|
||||
cls.token_pos(), Report::AtLocation,
|
||||
@@ -3114,10 +3196,10 @@ static void ReportTooManyTypeArguments(const Class& cls) {
|
||||
String::Handle(cls.Name()).ToCString());
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void Class::set_num_type_arguments(intptr_t value) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
if (!Utils::IsInt(16, value)) {
|
||||
@@ -3129,7 +3211,7 @@ void Class::set_num_type_arguments(intptr_t value) const {
|
||||
DEBUG_ASSERT(old_value == kUnknownNumTypeArguments || old_value == value);
|
||||
StoreNonPointer<int16_t, int16_t, std::memory_order_relaxed>(
|
||||
&untag()->num_type_arguments_, value);
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
void Class::set_num_type_arguments_unsafe(intptr_t value) const {
|
||||
@@ -3451,7 +3533,7 @@ void Class::set_library(const Library& value) const {
|
||||
|
||||
void Class::set_type_parameters(const TypeParameters& value) const {
|
||||
ASSERT((num_type_arguments() == kUnknownNumTypeArguments) ||
|
||||
is_prefinalized());
|
||||
is_declared_in_bytecode() || is_prefinalized());
|
||||
untag()->set_type_parameters(value.ptr());
|
||||
}
|
||||
|
||||
@@ -3966,6 +4048,26 @@ FunctionPtr Class::CreateInvocationDispatcher(
|
||||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
invocation.SetSignature(signature);
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
const bool attach_bytecode = true;
|
||||
#else
|
||||
const bool attach_bytecode = is_declared_in_bytecode();
|
||||
#endif
|
||||
if (attach_bytecode) {
|
||||
switch (kind) {
|
||||
case UntaggedFunction::kNoSuchMethodDispatcher:
|
||||
invocation.AttachBytecode(Object::nsm_dispatcher_bytecode());
|
||||
break;
|
||||
case UntaggedFunction::kInvokeFieldDispatcher:
|
||||
invocation.AttachBytecode(Object::invoke_field_bytecode());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
return invocation.ptr();
|
||||
}
|
||||
|
||||
@@ -4015,6 +4117,17 @@ FunctionPtr Function::CreateMethodExtractor(const String& getter_name) const {
|
||||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
extractor.SetSignature(signature);
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
const bool attach_bytecode = true;
|
||||
#else
|
||||
const bool attach_bytecode = is_declared_in_bytecode();
|
||||
#endif
|
||||
if (attach_bytecode) {
|
||||
extractor.AttachBytecode(Object::method_extractor_bytecode());
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
owner.AddFunction(extractor);
|
||||
|
||||
return extractor.ptr();
|
||||
@@ -4216,7 +4329,7 @@ StringPtr Function::CreateDynamicInvocationForwarderName(const String& name) {
|
||||
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
FunctionPtr Function::CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const {
|
||||
Thread* thread = Thread::Current();
|
||||
@@ -4252,6 +4365,17 @@ FunctionPtr Function::CreateDynamicInvocationForwarder(
|
||||
forwarder.InheritKernelOffsetFrom(*this);
|
||||
forwarder.SetForwardingTarget(*this);
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
const bool attach_bytecode = true;
|
||||
#else
|
||||
const bool attach_bytecode = is_declared_in_bytecode();
|
||||
#endif
|
||||
if (attach_bytecode) {
|
||||
forwarder.AttachBytecode(Object::dynamic_invocation_forwarder_bytecode());
|
||||
}
|
||||
#endif
|
||||
|
||||
return forwarder.ptr();
|
||||
}
|
||||
|
||||
@@ -4271,7 +4395,12 @@ FunctionPtr Function::GetDynamicInvocationForwarder(
|
||||
if (!result.IsNull()) return result.ptr();
|
||||
|
||||
const bool needs_dyn_forwarder =
|
||||
#if defined(DART_DYNAMIC_MODULES) && defined(DART_PRECOMPILED_RUNTIME)
|
||||
// TODO(alexmarkov)
|
||||
false;
|
||||
#else
|
||||
kernel::NeedsDynamicInvocationForwarder(*this);
|
||||
#endif
|
||||
if (!needs_dyn_forwarder) {
|
||||
return ptr();
|
||||
}
|
||||
@@ -4292,6 +4421,22 @@ FunctionPtr Function::GetDynamicInvocationForwarder(
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
void Function::ReadParameterCovariance(
|
||||
BitVector* is_covariant,
|
||||
BitVector* is_generic_covariant_impl) const {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (is_declared_in_bytecode()) {
|
||||
bytecode::BytecodeReader::ReadParameterCovariance(
|
||||
*this, is_covariant, is_generic_covariant_impl);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
kernel::ReadParameterCovariance(*this, is_covariant,
|
||||
is_generic_covariant_impl);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool AbstractType::InstantiateAndTestSubtype(
|
||||
@@ -4922,11 +5067,21 @@ ObjectPtr Instance::EvaluateCompiledExpression(
|
||||
|
||||
void Class::EnsureDeclarationLoaded() const {
|
||||
if (!is_declaration_loaded()) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Loading of class declaration can be postponed until needed
|
||||
// if class comes from bytecode.
|
||||
if (is_declared_in_bytecode()) {
|
||||
bytecode::BytecodeReader::LoadClassDeclaration(*this);
|
||||
ASSERT(is_declaration_loaded());
|
||||
ASSERT(is_type_finalized());
|
||||
return;
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
FATAL("Unable to use class %s which is not loaded yet.", ToCString());
|
||||
#endif
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4936,7 +5091,7 @@ ErrorPtr Class::EnsureIsFinalized(Thread* thread) const {
|
||||
if (is_finalized()) {
|
||||
return Error::null();
|
||||
}
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
return Error::null();
|
||||
#else
|
||||
@@ -4956,7 +5111,7 @@ ErrorPtr Class::EnsureIsFinalized(Thread* thread) const {
|
||||
}
|
||||
}
|
||||
return error.ptr();
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Ensure that code outdated by finalized class is cleaned up, new instance of
|
||||
@@ -5497,6 +5652,8 @@ const char* Class::GenerateUserVisibleName() const {
|
||||
return Symbols::WeakArray().ToCString();
|
||||
case kCodeCid:
|
||||
return Symbols::Code().ToCString();
|
||||
case kBytecodeCid:
|
||||
return Symbols::Bytecode().ToCString();
|
||||
case kInstructionsCid:
|
||||
return Symbols::Instructions().ToCString();
|
||||
case kInstructionsSectionCid:
|
||||
@@ -5621,6 +5778,9 @@ uint32_t Class::Hash(ClassPtr obj) {
|
||||
|
||||
int32_t Class::SourceFingerprint() const {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_declared_in_bytecode()) {
|
||||
return 0;
|
||||
}
|
||||
return kernel::KernelSourceFingerprintHelper::CalculateClassFingerprint(
|
||||
*this);
|
||||
#else
|
||||
@@ -5729,6 +5889,13 @@ void Class::set_is_loaded(bool value) const {
|
||||
set_state_bits(IsLoadedBit::update(value, state_bits()));
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
void Class::set_is_declared_in_bytecode(bool value) const {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
set_state_bits(IsDeclaredInBytecodeBit::update(value, state_bits()));
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void Class::set_is_finalized() const {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
ASSERT(!is_finalized());
|
||||
@@ -7957,6 +8124,24 @@ bool Function::HasCode() const {
|
||||
return untag()->code() != StubCode::LazyCompile().ptr();
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void Function::AttachBytecode(const Bytecode& value) const {
|
||||
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
|
||||
ASSERT(!value.IsNull());
|
||||
// Finish setting up code before activating it.
|
||||
if (!value.InVMIsolateHeap()) {
|
||||
value.set_function(*this);
|
||||
}
|
||||
ASSERT(untag()->ic_data_array_or_bytecode() == Object::null());
|
||||
untag()->set_ic_data_array_or_bytecode(value.ptr());
|
||||
|
||||
// Set the code entry_point to InterpretCall stub.
|
||||
SetInstructions(StubCode::InterpretCall());
|
||||
}
|
||||
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
bool Function::HasCode(FunctionPtr function) {
|
||||
NoSafepointScope no_safepoint;
|
||||
ASSERT(function->untag()->code() != Code::null());
|
||||
@@ -8681,7 +8866,7 @@ StringPtr FunctionType::ParameterNameAt(intptr_t index) const {
|
||||
|
||||
void FunctionType::SetParameterNameAt(intptr_t index,
|
||||
const String& value) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
ASSERT(!value.IsNull() && value.IsSymbol());
|
||||
@@ -8716,7 +8901,7 @@ void Function::CreateNameArray(Heap::Space space) const {
|
||||
}
|
||||
|
||||
void FunctionType::CreateNameArrayIncludingFlags(Heap::Space space) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
const intptr_t num_named_parameters = NumOptionalNamedParameters();
|
||||
@@ -10281,7 +10466,7 @@ FunctionPtr Function::New(const FunctionType& signature,
|
||||
ASSERT(!signature.IsNull());
|
||||
const Function& result = Function::Handle(Function::New(space));
|
||||
result.set_kind_tag(0);
|
||||
result.set_packed_fields(0);
|
||||
NOT_IN_PRECOMPILED(result.set_packed_fields(0));
|
||||
result.set_name(name);
|
||||
result.set_kind_tag(0); // Ensure determinism of uninitialized bits.
|
||||
result.set_kind(kind);
|
||||
@@ -10309,7 +10494,7 @@ FunctionPtr Function::New(const FunctionType& signature,
|
||||
NOT_IN_PRECOMPILED(result.set_optimized_call_site_count(0));
|
||||
NOT_IN_PRECOMPILED(result.set_inlining_depth(0));
|
||||
NOT_IN_PRECOMPILED(result.set_kernel_offset(0));
|
||||
result.set_is_optimizable(is_native ? false : true);
|
||||
NOT_IN_PRECOMPILED(result.set_is_optimizable(is_native ? false : true));
|
||||
result.set_is_inlinable(true);
|
||||
result.reset_unboxed_parameters_and_return();
|
||||
result.SetInstructionsSafe(StubCode::LazyCompile());
|
||||
@@ -10414,7 +10599,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
return implicit_closure_function();
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
// In AOT mode all implicit closures are pre-created.
|
||||
FATAL("Cannot create implicit closure in AOT!");
|
||||
return Function::null();
|
||||
@@ -10434,6 +10619,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
zone, NewImplicitClosureFunction(closure_name, *this, token_pos()));
|
||||
|
||||
// Set closure function's context scope.
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_static() || IsConstructor()) {
|
||||
closure_function.set_context_scope(Object::empty_context_scope());
|
||||
} else {
|
||||
@@ -10441,6 +10627,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
zone, LocalScope::CreateImplicitClosureScope(*this));
|
||||
closure_function.set_context_scope(context_scope);
|
||||
}
|
||||
#endif
|
||||
|
||||
FunctionType& closure_signature =
|
||||
FunctionType::Handle(zone, closure_function.signature());
|
||||
@@ -10548,7 +10735,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
closure_signature.set_result_type(result_type);
|
||||
|
||||
// Set closure function's end token to this end token.
|
||||
closure_function.set_end_token_pos(end_token_pos());
|
||||
NOT_IN_PRECOMPILED(closure_function.set_end_token_pos(end_token_pos()));
|
||||
|
||||
// The closurized method stub just calls into the original method and should
|
||||
// therefore be skipped by the debugger and in stack traces.
|
||||
@@ -10571,21 +10758,22 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
has_opt_pos_params);
|
||||
closure_signature.set_parameter_types(
|
||||
Array::Handle(zone, Array::New(num_params, Heap::kOld)));
|
||||
closure_function.CreateNameArray();
|
||||
NOT_IN_PRECOMPILED(closure_function.CreateNameArray());
|
||||
closure_signature.CreateNameArrayIncludingFlags();
|
||||
AbstractType& param_type = AbstractType::Handle(zone);
|
||||
String& param_name = String::Handle(zone);
|
||||
// Add implicit closure object parameter.
|
||||
param_type = Type::DynamicType();
|
||||
closure_signature.SetParameterTypeAt(0, param_type);
|
||||
closure_function.SetParameterNameAt(0, Symbols::ClosureParameter());
|
||||
NOT_IN_PRECOMPILED(
|
||||
closure_function.SetParameterNameAt(0, Symbols::ClosureParameter()));
|
||||
for (int i = kClosure; i < num_pos_params; i++) {
|
||||
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
|
||||
transform_type(param_type);
|
||||
closure_signature.SetParameterTypeAt(i, param_type);
|
||||
param_name = ParameterNameAt(num_implicit_params - kClosure + i);
|
||||
// Set the name in the function for positional parameters.
|
||||
closure_function.SetParameterNameAt(i, param_name);
|
||||
NOT_IN_PRECOMPILED(closure_function.SetParameterNameAt(i, param_name));
|
||||
}
|
||||
for (int i = num_pos_params; i < num_params; i++) {
|
||||
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
|
||||
@@ -10605,8 +10793,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
// Change covariant parameter types to Object?.
|
||||
BitVector is_covariant(zone, NumParameters());
|
||||
BitVector is_generic_covariant_impl(zone, NumParameters());
|
||||
kernel::ReadParameterCovariance(*this, &is_covariant,
|
||||
&is_generic_covariant_impl);
|
||||
ReadParameterCovariance(&is_covariant, &is_generic_covariant_impl);
|
||||
|
||||
ObjectStore* object_store = IsolateGroup::Current()->object_store();
|
||||
const auto& object_type =
|
||||
@@ -10620,6 +10807,27 @@ FunctionPtr Function::ImplicitClosureFunction() const {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
const bool attach_bytecode = true;
|
||||
#else
|
||||
const bool attach_bytecode = is_declared_in_bytecode();
|
||||
#endif
|
||||
if (attach_bytecode) {
|
||||
if (is_static()) {
|
||||
closure_function.AttachBytecode(
|
||||
Object::implicit_static_closure_bytecode());
|
||||
} else if (IsConstructor()) {
|
||||
closure_function.AttachBytecode(
|
||||
Object::implicit_constructor_closure_bytecode());
|
||||
} else {
|
||||
closure_function.AttachBytecode(
|
||||
Object::implicit_instance_closure_bytecode());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ASSERT(!closure_signature.IsFinalized());
|
||||
closure_signature ^= ClassFinalizer::FinalizeType(closure_signature);
|
||||
closure_function.SetSignature(closure_signature);
|
||||
@@ -10874,9 +11082,17 @@ ClassPtr Function::Owner() const {
|
||||
return PatchClass::Cast(obj).wrapped_class();
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool Function::is_declared_in_bytecode() const {
|
||||
return Class::Handle(Owner()).is_declared_in_bytecode();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Function::InheritKernelOffsetFrom(const Function& src) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
#else
|
||||
StoreNonPointer(&untag()->kernel_offset_, src.untag()->kernel_offset_);
|
||||
#endif
|
||||
@@ -10884,7 +11100,9 @@ void Function::InheritKernelOffsetFrom(const Function& src) const {
|
||||
|
||||
void Function::InheritKernelOffsetFrom(const Field& src) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
#else
|
||||
set_kernel_offset(src.kernel_offset());
|
||||
#endif
|
||||
@@ -10972,6 +11190,7 @@ intptr_t Function::KernelLibraryOffset() const {
|
||||
}
|
||||
|
||||
intptr_t Function::KernelLibraryIndex() const {
|
||||
ASSERT(!is_declared_in_bytecode());
|
||||
if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher() ||
|
||||
IsFfiCallbackTrampoline()) {
|
||||
return -1;
|
||||
@@ -11194,6 +11413,9 @@ StringPtr Function::GetSource() const {
|
||||
// arguments.
|
||||
int32_t Function::SourceFingerprint() const {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_declared_in_bytecode()) {
|
||||
return 0;
|
||||
}
|
||||
return kernel::KernelSourceFingerprintHelper::CalculateFunctionFingerprint(
|
||||
*this);
|
||||
#else
|
||||
@@ -11292,11 +11514,22 @@ ArrayPtr Function::GetCoverageArray() const {
|
||||
}
|
||||
|
||||
void Function::set_ic_data_array(const Array& value) const {
|
||||
untag()->set_ic_data_array<std::memory_order_release>(value.ptr());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
ASSERT(!HasBytecode());
|
||||
#endif
|
||||
untag()->set_ic_data_array_or_bytecode<std::memory_order_release>(
|
||||
value.ptr());
|
||||
}
|
||||
|
||||
ArrayPtr Function::ic_data_array() const {
|
||||
return untag()->ic_data_array<std::memory_order_acquire>();
|
||||
ObjectPtr value =
|
||||
untag()->ic_data_array_or_bytecode<std::memory_order_acquire>();
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (value->IsBytecode()) {
|
||||
return Array::null();
|
||||
}
|
||||
#endif
|
||||
return Array::RawCast(value);
|
||||
}
|
||||
|
||||
void Function::ClearICDataArray() const {
|
||||
@@ -11350,6 +11583,8 @@ bool Function::CheckSourceFingerprint(int32_t fp, const char* kind) const {
|
||||
return true; // The kernel structure has been altered, skip checking.
|
||||
}
|
||||
|
||||
ASSERT(!is_declared_in_bytecode());
|
||||
|
||||
if (SourceFingerprint() != fp) {
|
||||
// This output can be copied into a file, then used with sed
|
||||
// to replace the old values.
|
||||
@@ -11483,6 +11718,12 @@ bool Function::PrologueNeedsArgumentsDescriptor() const {
|
||||
if (HasSavedArgumentsDescriptor()) {
|
||||
return false;
|
||||
}
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Entering interpreter needs arguments descriptor.
|
||||
if (is_declared_in_bytecode()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
// The prologue of those functions need to examine the arg descriptor for
|
||||
// various purposes.
|
||||
return IsGeneric() || HasOptionalParameters();
|
||||
@@ -11938,6 +12179,12 @@ uint32_t Field::Hash() const {
|
||||
return String::HashRawSymbol(name());
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool Field::is_declared_in_bytecode() const {
|
||||
return Class::Handle(Owner()).is_declared_in_bytecode();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Field::InheritKernelOffsetFrom(const Field& src) const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
@@ -12110,6 +12357,9 @@ FieldPtr Field::Clone(const Field& original) const {
|
||||
|
||||
int32_t Field::SourceFingerprint() const {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (is_declared_in_bytecode()) {
|
||||
return 0;
|
||||
}
|
||||
return kernel::KernelSourceFingerprintHelper::CalculateFieldFingerprint(
|
||||
*this);
|
||||
#else
|
||||
@@ -12343,7 +12593,7 @@ FunctionPtr Field::EnsureInitializerFunction() const {
|
||||
Zone* zone = thread->zone();
|
||||
Function& initializer = Function::Handle(zone, InitializerFunction());
|
||||
if (initializer.IsNull()) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
SafepointMutexLocker ml(
|
||||
@@ -12351,17 +12601,82 @@ FunctionPtr Field::EnsureInitializerFunction() const {
|
||||
// Double check after grabbing the lock.
|
||||
initializer = InitializerFunction();
|
||||
if (initializer.IsNull()) {
|
||||
initializer = kernel::CreateFieldInitializerFunction(thread, zone, *this);
|
||||
initializer = CreateFieldInitializerFunction(thread);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return initializer.ptr();
|
||||
}
|
||||
|
||||
void Field::SetInitializerFunction(const Function& initializer) const {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
FunctionPtr Field::CreateFieldInitializerFunction(Thread* thread) const {
|
||||
Zone* zone = thread->zone();
|
||||
ASSERT(InitializerFunction() == Function::null());
|
||||
|
||||
String& init_name = String::Handle(zone, name());
|
||||
init_name = Symbols::FromConcat(thread, Symbols::InitPrefix(), init_name);
|
||||
|
||||
const auto& field_owner = Class::Handle(zone, Owner());
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
UNREACHABLE();
|
||||
const auto& initializer_owner = Class::Handle(zone, field_owner.ptr());
|
||||
#else
|
||||
// Static field initializers are not added as members of their owning class,
|
||||
// so they must be preemptively given a patch class to avoid the meaning of
|
||||
// their kernel/token position changing during a reload. Compare
|
||||
// Class::PatchFieldsAndFunctions().
|
||||
// This might also be necessary for lazy computation of local var descriptors.
|
||||
// Compare https://codereview.chromium.org//1317753004
|
||||
const auto& script = Script::Handle(zone, Script());
|
||||
const auto& kernel_program_info =
|
||||
KernelProgramInfo::Handle(zone, KernelProgramInfo());
|
||||
const auto& initializer_owner = PatchClass::Handle(
|
||||
zone, PatchClass::New(field_owner, kernel_program_info, script));
|
||||
if (!is_declared_in_bytecode()) {
|
||||
const Library& lib = Library::Handle(zone, field_owner.library());
|
||||
initializer_owner.set_kernel_library_index(lib.kernel_library_index());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create a static initializer.
|
||||
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
|
||||
const Function& initializer_fun = Function::Handle(
|
||||
zone,
|
||||
Function::New(signature, init_name, UntaggedFunction::kFieldInitializer,
|
||||
is_static(), // is_static
|
||||
false, // is_const
|
||||
false, // is_abstract
|
||||
false, // is_external
|
||||
false, // is_native
|
||||
initializer_owner, TokenPosition::kNoSource));
|
||||
if (!is_static()) {
|
||||
signature.set_num_fixed_parameters(1);
|
||||
signature.set_parameter_types(
|
||||
Array::Handle(zone, Array::New(1, Heap::kOld)));
|
||||
signature.SetParameterTypeAt(
|
||||
0, AbstractType::Handle(zone, field_owner.DeclarationType()));
|
||||
NOT_IN_PRECOMPILED(initializer_fun.CreateNameArray());
|
||||
NOT_IN_PRECOMPILED(initializer_fun.SetParameterNameAt(0, Symbols::This()));
|
||||
}
|
||||
signature.set_result_type(AbstractType::Handle(zone, type()));
|
||||
initializer_fun.set_is_reflectable(false);
|
||||
initializer_fun.set_is_inlinable(false);
|
||||
NOT_IN_PRECOMPILED(initializer_fun.set_token_pos(token_pos()));
|
||||
NOT_IN_PRECOMPILED(initializer_fun.set_end_token_pos(end_token_pos()));
|
||||
initializer_fun.set_accessor_field(*this);
|
||||
initializer_fun.InheritKernelOffsetFrom(*this);
|
||||
initializer_fun.set_is_extension_member(is_extension_member());
|
||||
initializer_fun.set_is_extension_type_member(is_extension_type_member());
|
||||
|
||||
signature ^= ClassFinalizer::FinalizeType(signature);
|
||||
initializer_fun.SetSignature(signature);
|
||||
|
||||
SetInitializerFunction(initializer_fun);
|
||||
return initializer_fun.ptr();
|
||||
}
|
||||
|
||||
void Field::SetInitializerFunction(const Function& initializer) const {
|
||||
ASSERT(IsOriginal());
|
||||
ASSERT(IsolateGroup::Current()
|
||||
->initializer_functions_mutex()
|
||||
@@ -12371,9 +12686,10 @@ void Field::SetInitializerFunction(const Function& initializer) const {
|
||||
// accessed without grabbing the lock.
|
||||
untag()->set_initializer_function<std::memory_order_release>(
|
||||
initializer.ptr());
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
bool Field::HasInitializerFunction() const {
|
||||
return untag()->initializer_function() != Function::null();
|
||||
}
|
||||
@@ -18513,6 +18829,196 @@ void Code::DumpSourcePositions(bool relative_addresses) const {
|
||||
reader.DumpSourcePositions(relative_addresses ? 0 : PayloadStart());
|
||||
}
|
||||
|
||||
void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (!FLAG_support_disassembler) {
|
||||
return;
|
||||
}
|
||||
uword start = PayloadStart();
|
||||
intptr_t size = Size();
|
||||
if (formatter == NULL) {
|
||||
KernelBytecodeDisassembler::Disassemble(start, start + size, *this);
|
||||
} else {
|
||||
KernelBytecodeDisassembler::Disassemble(start, start + size, formatter,
|
||||
*this);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
}
|
||||
|
||||
BytecodePtr Bytecode::New(uword instructions,
|
||||
intptr_t instructions_size,
|
||||
intptr_t instructions_offset,
|
||||
const TypedDataBase& binary,
|
||||
const ObjectPool& object_pool) {
|
||||
ASSERT(Object::bytecode_class() != Class::null());
|
||||
Bytecode& result = Bytecode::Handle();
|
||||
{
|
||||
auto raw = Object::Allocate<Bytecode>(Heap::kOld);
|
||||
NoSafepointScope no_safepoint;
|
||||
result = raw;
|
||||
result.set_instructions(instructions);
|
||||
result.set_instructions_size(instructions_size);
|
||||
result.set_object_pool(object_pool);
|
||||
result.set_pc_descriptors(Object::empty_descriptors());
|
||||
result.set_binary(binary);
|
||||
result.set_instructions_binary_offset(instructions_offset);
|
||||
result.set_code_offset(0);
|
||||
result.set_source_positions_binary_offset(0);
|
||||
}
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
TokenPosition Bytecode::GetTokenIndexOfPC(uword return_address) const {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (!HasSourcePositions()) {
|
||||
return TokenPosition::kNoSource;
|
||||
}
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
uword pc_offset = return_address - PayloadStart();
|
||||
// pc_offset could equal to bytecode size if the last instruction is Throw.
|
||||
ASSERT(pc_offset <= static_cast<uword>(Size()));
|
||||
bytecode::BytecodeSourcePositionsIterator iter(zone, *this);
|
||||
TokenPosition token_pos = TokenPosition::kNoSource;
|
||||
while (iter.MoveNext()) {
|
||||
if (pc_offset <= iter.PcOffset()) {
|
||||
break;
|
||||
}
|
||||
token_pos = iter.TokenPos();
|
||||
}
|
||||
return token_pos;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
intptr_t Bytecode::GetTryIndexAtPc(uword return_address) const {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
intptr_t try_index = -1;
|
||||
const uword pc_offset = return_address - PayloadStart();
|
||||
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
|
||||
PcDescriptors::Iterator iter(descriptors, UntaggedPcDescriptors::kAnyKind);
|
||||
while (iter.MoveNext()) {
|
||||
// PC descriptors for try blocks in bytecode are generated in pairs,
|
||||
// marking start and end of a try block.
|
||||
// See BytecodeReaderHelper::ReadExceptionsTable for details.
|
||||
const intptr_t current_try_index = iter.TryIndex();
|
||||
const uword start_pc = iter.PcOffset();
|
||||
if (pc_offset < start_pc) {
|
||||
break;
|
||||
}
|
||||
const bool has_next = iter.MoveNext();
|
||||
ASSERT(has_next);
|
||||
const uword end_pc = iter.PcOffset();
|
||||
if (start_pc <= pc_offset && pc_offset < end_pc) {
|
||||
ASSERT(try_index < current_try_index);
|
||||
try_index = current_try_index;
|
||||
}
|
||||
}
|
||||
return try_index;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
uword Bytecode::GetFirstDebugCheckOpcodePc() const {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
uword pc = PayloadStart();
|
||||
const uword end_pc = pc + Size();
|
||||
while (pc < end_pc) {
|
||||
if (KernelBytecode::IsDebugCheckOpcode(
|
||||
reinterpret_cast<const KBCInstr*>(pc))) {
|
||||
return pc;
|
||||
}
|
||||
pc = KernelBytecode::Next(pc);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
uword Bytecode::GetDebugCheckedOpcodeReturnAddress(uword from_offset,
|
||||
uword to_offset) const {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
uword pc = PayloadStart() + from_offset;
|
||||
const uword end_pc = pc + (to_offset - from_offset);
|
||||
while (pc < end_pc) {
|
||||
uword next_pc = KernelBytecode::Next(pc);
|
||||
if (KernelBytecode::IsDebugCheckedOpcode(
|
||||
reinterpret_cast<const KBCInstr*>(pc))) {
|
||||
// Return the pc after the opcode, i.e. its 'return address'.
|
||||
return next_pc;
|
||||
}
|
||||
pc = next_pc;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* Bytecode::ToCString() const {
|
||||
return Thread::Current()->zone()->PrintToString("Bytecode(%s)",
|
||||
QualifiedName());
|
||||
}
|
||||
|
||||
static const char* BytecodeStubName(const Bytecode& bytecode) {
|
||||
if (bytecode.ptr() == Object::implicit_getter_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_ImplicitGetter";
|
||||
} else if (bytecode.ptr() == Object::implicit_setter_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_ImplicitSetter";
|
||||
} else if (bytecode.ptr() ==
|
||||
Object::implicit_static_getter_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_ImplicitStaticGetter";
|
||||
} else if (bytecode.ptr() == Object::method_extractor_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_MethodExtractor";
|
||||
} else if (bytecode.ptr() == Object::invoke_closure_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_InvokeClosure";
|
||||
} else if (bytecode.ptr() == Object::invoke_field_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_InvokeField";
|
||||
}
|
||||
return "[unknown stub]";
|
||||
}
|
||||
|
||||
const char* Bytecode::Name() const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Function& fun = Function::Handle(zone, function());
|
||||
if (fun.IsNull()) {
|
||||
return BytecodeStubName(*this);
|
||||
}
|
||||
const char* function_name =
|
||||
String::Handle(zone, fun.UserVisibleName()).ToCString();
|
||||
return zone->PrintToString("[Bytecode] %s", function_name);
|
||||
}
|
||||
|
||||
const char* Bytecode::QualifiedName() const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Function& fun = Function::Handle(zone, function());
|
||||
if (fun.IsNull()) {
|
||||
return BytecodeStubName(*this);
|
||||
}
|
||||
const char* function_name =
|
||||
String::Handle(zone, fun.QualifiedScrubbedName()).ToCString();
|
||||
return zone->PrintToString("[Bytecode] %s", function_name);
|
||||
}
|
||||
|
||||
const char* Bytecode::FullyQualifiedName() const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const Function& fun = Function::Handle(zone, function());
|
||||
if (fun.IsNull()) {
|
||||
return BytecodeStubName(*this);
|
||||
}
|
||||
const char* function_name = fun.ToFullyQualifiedCString();
|
||||
return zone->PrintToString("[Bytecode] %s", function_name);
|
||||
}
|
||||
|
||||
void Bytecode::set_binary(const TypedDataBase& binary) const {
|
||||
ASSERT(binary.IsNull() || binary.IsExternalOrExternalView());
|
||||
untag()->set_binary(binary.ptr());
|
||||
}
|
||||
|
||||
intptr_t Context::GetLevel() const {
|
||||
intptr_t level = 0;
|
||||
Context& parent_ctx = Context::Handle(parent());
|
||||
@@ -26389,6 +26895,26 @@ const char* StackTrace::ToCString() const {
|
||||
}
|
||||
|
||||
const uword pc_offset = stack_trace.PcOffsetAtFrame(i);
|
||||
|
||||
// A visible frame ends any gap we might be in.
|
||||
in_gap = false;
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (code_object.IsBytecode()) {
|
||||
const auto& bytecode = Bytecode::Cast(code_object);
|
||||
function = bytecode.function();
|
||||
|
||||
if (!function.IsNull() &&
|
||||
(FLAG_show_invisible_frames || function.is_visible())) {
|
||||
auto const pos =
|
||||
bytecode.GetTokenIndexOfPC(bytecode.PayloadStart() + pc_offset);
|
||||
PrintSymbolicStackFrame(zone, &buffer, function, pos, frame_index);
|
||||
frame_index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
ASSERT(code_object.IsCode());
|
||||
code ^= code_object.ptr();
|
||||
ASSERT(code.IsFunctionCode());
|
||||
@@ -26403,9 +26929,6 @@ const char* StackTrace::ToCString() const {
|
||||
const bool is_future_listener =
|
||||
pc_offset == StackTraceUtils::kFutureListenerPcOffset;
|
||||
|
||||
// A visible frame ends any gap we might be in.
|
||||
in_gap = false;
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// When printing non-symbolic frames, we normally print call
|
||||
// addresses, not return addresses, by subtracting one from the PC to
|
||||
|
||||
+190
-2
@@ -56,6 +56,7 @@ CLASS_LIST(DEFINE_FORWARD_DECLARATION)
|
||||
#undef DEFINE_FORWARD_DECLARATION
|
||||
class Api;
|
||||
class ArgumentsDescriptor;
|
||||
class BitVector;
|
||||
class Closure;
|
||||
class Code;
|
||||
class DeoptInstr;
|
||||
@@ -473,6 +474,17 @@ class Object {
|
||||
V(ExceptionHandlers, empty_async_exception_handlers) \
|
||||
V(Array, synthetic_getter_parameter_types) \
|
||||
V(Array, synthetic_getter_parameter_names) \
|
||||
V(Bytecode, implicit_getter_bytecode) \
|
||||
V(Bytecode, implicit_setter_bytecode) \
|
||||
V(Bytecode, implicit_static_getter_bytecode) \
|
||||
V(Bytecode, method_extractor_bytecode) \
|
||||
V(Bytecode, invoke_closure_bytecode) \
|
||||
V(Bytecode, invoke_field_bytecode) \
|
||||
V(Bytecode, nsm_dispatcher_bytecode) \
|
||||
V(Bytecode, dynamic_invocation_forwarder_bytecode) \
|
||||
V(Bytecode, implicit_static_closure_bytecode) \
|
||||
V(Bytecode, implicit_instance_closure_bytecode) \
|
||||
V(Bytecode, implicit_constructor_closure_bytecode) \
|
||||
V(Sentinel, sentinel) \
|
||||
V(Sentinel, unknown_constant) \
|
||||
V(Sentinel, non_constant) \
|
||||
@@ -542,6 +554,7 @@ class Object {
|
||||
}
|
||||
static ClassPtr context_class() { return context_class_; }
|
||||
static ClassPtr context_scope_class() { return context_scope_class_; }
|
||||
static ClassPtr bytecode_class() { return bytecode_class_; }
|
||||
static ClassPtr sentinel_class() { return sentinel_class_; }
|
||||
static ClassPtr api_error_class() { return api_error_class_; }
|
||||
static ClassPtr language_error_class() { return language_error_class_; }
|
||||
@@ -989,6 +1002,7 @@ class Object {
|
||||
static ClassPtr exception_handlers_class_;
|
||||
static ClassPtr context_class_;
|
||||
static ClassPtr context_scope_class_;
|
||||
static ClassPtr bytecode_class_;
|
||||
static ClassPtr sentinel_class_;
|
||||
static ClassPtr singletargetcache_class_;
|
||||
static ClassPtr unlinkedcall_class_;
|
||||
@@ -1012,6 +1026,7 @@ class Object {
|
||||
friend void UntaggedObject::Validate(IsolateGroup* isolate_group) const;
|
||||
friend class Closure;
|
||||
friend class InstanceDeserializationCluster;
|
||||
friend class Interpreter;
|
||||
friend class ObjectGraphCopier; // For Object::InitializeObject
|
||||
friend class Simd128MessageDeserializationCluster;
|
||||
friend class OneByteString;
|
||||
@@ -1787,6 +1802,15 @@ class Class : public Object {
|
||||
bool is_loaded() const { return IsLoadedBit::decode(state_bits()); }
|
||||
void set_is_loaded(bool value) const;
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_declared_in_bytecode() const {
|
||||
return IsDeclaredInBytecodeBit::decode(state_bits());
|
||||
}
|
||||
void set_is_declared_in_bytecode(bool value) const;
|
||||
#else
|
||||
bool is_declared_in_bytecode() const { return false; }
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
uint16_t num_native_fields() const { return untag()->num_native_fields_; }
|
||||
void set_num_native_fields(uint16_t value) const {
|
||||
StoreNonPointer(&untag()->num_native_fields_, value);
|
||||
@@ -2075,6 +2099,8 @@ class Class : public Object {
|
||||
kIsDynamicallyExtendableBit,
|
||||
// This class has a dynamically extendable subtype.
|
||||
kHasDynamicallyExtendableSubtypesBit,
|
||||
// This class was loaded from bytecode at runtime.
|
||||
kIsDeclaredInBytecodeBit,
|
||||
};
|
||||
class ConstBit : public BitField<uint32_t, bool, kConstBit, 1> {};
|
||||
class ImplementedBit : public BitField<uint32_t, bool, kImplementedBit, 1> {};
|
||||
@@ -2120,6 +2146,8 @@ class Class : public Object {
|
||||
bool,
|
||||
kHasDynamicallyExtendableSubtypesBit,
|
||||
1> {};
|
||||
class IsDeclaredInBytecodeBit
|
||||
: public BitField<uint32_t, bool, kIsDeclaredInBytecodeBit, 1> {};
|
||||
|
||||
void set_name(const String& value) const;
|
||||
void set_user_name(const String& value) const;
|
||||
@@ -2240,6 +2268,7 @@ class Class : public Object {
|
||||
friend class Instance;
|
||||
friend class Object;
|
||||
friend class Type;
|
||||
friend class InterpreterHelpers;
|
||||
friend class Intrinsifier;
|
||||
friend class ProgramWalker;
|
||||
friend class Precompiler;
|
||||
@@ -2920,6 +2949,7 @@ class ICData : public CallSiteData {
|
||||
friend class Class;
|
||||
friend class VMDeserializationRoots;
|
||||
friend class ICDataTestTask;
|
||||
friend class Interpreter;
|
||||
friend class VMSerializationRoots;
|
||||
};
|
||||
|
||||
@@ -3231,6 +3261,16 @@ class Function : public Object {
|
||||
return OFFSET_OF(UntaggedFunction, unchecked_entry_point_);
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
void AttachBytecode(const Bytecode& bytecode) const;
|
||||
inline BytecodePtr GetBytecode() const;
|
||||
static inline BytecodePtr GetBytecode(FunctionPtr function);
|
||||
inline bool HasBytecode() const;
|
||||
static inline bool HasBytecode(FunctionPtr function);
|
||||
#else
|
||||
inline bool HasBytecode() const { return false; }
|
||||
#endif
|
||||
|
||||
virtual uword Hash() const;
|
||||
|
||||
// Returns true if there is at least one debugger breakpoint
|
||||
@@ -3562,6 +3602,12 @@ class Function : public Object {
|
||||
|
||||
#undef DEFINE_GETTERS_AND_SETTERS
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_declared_in_bytecode() const;
|
||||
#else
|
||||
bool is_declared_in_bytecode() const { return false; }
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
intptr_t kernel_offset() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
return 0;
|
||||
@@ -4042,11 +4088,19 @@ class Function : public Object {
|
||||
|
||||
static StringPtr CreateDynamicInvocationForwarderName(const String& name);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
FunctionPtr CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const;
|
||||
|
||||
FunctionPtr GetDynamicInvocationForwarder(const String& mangled_name) const;
|
||||
|
||||
// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
|
||||
// according to covariance attributes of function parameters.
|
||||
//
|
||||
// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
|
||||
// of function.NumParameters() length.
|
||||
void ReadParameterCovariance(BitVector* is_covariant,
|
||||
BitVector* is_generic_covariant_impl) const;
|
||||
#endif
|
||||
|
||||
// Slow function, use in asserts to track changes in important library
|
||||
@@ -4507,6 +4561,12 @@ class Field : public Object {
|
||||
}
|
||||
bool is_shared() const { return SharedBit::decode(kind_bits()); }
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_declared_in_bytecode() const;
|
||||
#else
|
||||
bool is_declared_in_bytecode() const { return false; }
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
intptr_t kernel_offset() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
return 0;
|
||||
@@ -4819,12 +4879,16 @@ class Field : public Object {
|
||||
FunctionPtr InitializerFunction() const {
|
||||
return untag()->initializer_function<std::memory_order_acquire>();
|
||||
}
|
||||
void SetInitializerFunction(const Function& initializer) const;
|
||||
bool HasInitializerFunction() const;
|
||||
static intptr_t initializer_function_offset() {
|
||||
return OFFSET_OF(UntaggedField, initializer_function_);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
FunctionPtr CreateFieldInitializerFunction(Thread* thread) const;
|
||||
void SetInitializerFunction(const Function& initializer) const;
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// For static fields only. Constructs a closure that gets/sets the
|
||||
// field value.
|
||||
InstancePtr GetterClosure() const;
|
||||
@@ -4858,6 +4922,7 @@ class Field : public Object {
|
||||
const Object& owner,
|
||||
TokenPosition token_pos,
|
||||
TokenPosition end_token_pos);
|
||||
friend class Interpreter; // Access to bit field.
|
||||
friend class StoreFieldInstr; // Generated code access to bit field.
|
||||
|
||||
enum {
|
||||
@@ -7418,6 +7483,109 @@ class Code : public Object {
|
||||
friend void DumpStackFrame(intptr_t frame_index, uword pc, uword fp);
|
||||
};
|
||||
|
||||
class Bytecode : public Object {
|
||||
public:
|
||||
uword instructions() const { return untag()->instructions_; }
|
||||
|
||||
uword PayloadStart() const { return instructions(); }
|
||||
intptr_t Size() const { return untag()->instructions_size_; }
|
||||
|
||||
ObjectPoolPtr object_pool() const { return untag()->object_pool(); }
|
||||
|
||||
bool ContainsInstructionAt(uword addr) const {
|
||||
return UntaggedBytecode::ContainsPC(ptr(), addr);
|
||||
}
|
||||
|
||||
PcDescriptorsPtr pc_descriptors() const { return untag()->pc_descriptors(); }
|
||||
void set_pc_descriptors(const PcDescriptors& descriptors) const {
|
||||
ASSERT(descriptors.IsOld());
|
||||
untag()->set_pc_descriptors(descriptors.ptr());
|
||||
}
|
||||
|
||||
void Disassemble(DisassemblyFormatter* formatter = NULL) const;
|
||||
|
||||
ExceptionHandlersPtr exception_handlers() const {
|
||||
return untag()->exception_handlers();
|
||||
}
|
||||
void set_exception_handlers(const ExceptionHandlers& handlers) const {
|
||||
ASSERT(handlers.IsOld());
|
||||
untag()->set_exception_handlers(handlers.ptr());
|
||||
}
|
||||
|
||||
FunctionPtr function() const { return untag()->function(); }
|
||||
|
||||
void set_function(const Function& function) const {
|
||||
ASSERT(function.IsOld());
|
||||
untag()->set_function(function.ptr());
|
||||
}
|
||||
|
||||
TypedDataBasePtr binary() const { return untag()->binary(); }
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(UntaggedBytecode));
|
||||
}
|
||||
static BytecodePtr New(uword instructions,
|
||||
intptr_t instructions_size,
|
||||
intptr_t instructions_offset,
|
||||
const TypedDataBase& binary,
|
||||
const ObjectPool& object_pool);
|
||||
|
||||
TokenPosition GetTokenIndexOfPC(uword return_address) const;
|
||||
intptr_t GetTryIndexAtPc(uword return_address) const;
|
||||
|
||||
// Return the pc of the first 'DebugCheck' opcode of the bytecode.
|
||||
// Return 0 if none is found.
|
||||
uword GetFirstDebugCheckOpcodePc() const;
|
||||
|
||||
// Return the pc after the first 'debug checked' opcode in the range.
|
||||
// Return 0 if none is found.
|
||||
uword GetDebugCheckedOpcodeReturnAddress(uword from_offset,
|
||||
uword to_offset) const;
|
||||
|
||||
intptr_t instructions_binary_offset() const {
|
||||
return untag()->instructions_binary_offset_;
|
||||
}
|
||||
void set_instructions_binary_offset(intptr_t value) const {
|
||||
StoreNonPointer(&untag()->instructions_binary_offset_, value);
|
||||
}
|
||||
|
||||
intptr_t code_offset() const { return untag()->code_offset_; }
|
||||
void set_code_offset(intptr_t value) const {
|
||||
StoreNonPointer(&untag()->code_offset_, value);
|
||||
}
|
||||
|
||||
intptr_t source_positions_binary_offset() const {
|
||||
return untag()->source_positions_binary_offset_;
|
||||
}
|
||||
void set_source_positions_binary_offset(intptr_t value) const {
|
||||
StoreNonPointer(&untag()->source_positions_binary_offset_, value);
|
||||
}
|
||||
bool HasSourcePositions() const {
|
||||
return (source_positions_binary_offset() != 0);
|
||||
}
|
||||
|
||||
const char* Name() const;
|
||||
const char* QualifiedName() const;
|
||||
const char* FullyQualifiedName() const;
|
||||
|
||||
private:
|
||||
void set_instructions(uword instructions) const {
|
||||
StoreNonPointer(&untag()->instructions_, instructions);
|
||||
}
|
||||
void set_instructions_size(intptr_t size) const {
|
||||
StoreNonPointer(&untag()->instructions_size_, size);
|
||||
}
|
||||
void set_object_pool(const ObjectPool& object_pool) const {
|
||||
untag()->set_object_pool(object_pool.ptr());
|
||||
}
|
||||
void set_binary(const TypedDataBase& binary) const;
|
||||
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(Bytecode, Object);
|
||||
friend class BytecodeDeserializationCluster;
|
||||
friend class Class;
|
||||
friend class SnapshotWriter;
|
||||
};
|
||||
|
||||
class Context : public Object {
|
||||
public:
|
||||
ContextPtr parent() const { return untag()->parent(); }
|
||||
@@ -8455,6 +8623,7 @@ class Instance : public Object {
|
||||
friend class TypedDataView;
|
||||
friend class InstanceSerializationCluster;
|
||||
friend class InstanceDeserializationCluster;
|
||||
friend class Interpreter;
|
||||
friend class ClassDeserializationCluster; // vtable
|
||||
friend class InstanceMorpher;
|
||||
friend class Obfuscator; // RawGetFieldAtOffset, RawSetFieldAtOffset
|
||||
@@ -11044,6 +11213,7 @@ class Array : public Instance {
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
|
||||
friend class Class;
|
||||
friend class ImmutableArray;
|
||||
friend class Interpreter;
|
||||
friend class Object;
|
||||
friend class String;
|
||||
friend class MessageDeserializer;
|
||||
@@ -13255,6 +13425,24 @@ void Object::setPtr(ObjectPtr value, intptr_t default_cid) {
|
||||
set_vtable(builtin_vtables_[cid]);
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
BytecodePtr Function::GetBytecode() const {
|
||||
return GetBytecode(ptr());
|
||||
}
|
||||
|
||||
BytecodePtr Function::GetBytecode(FunctionPtr function) {
|
||||
return Bytecode::RawCast(function.untag()->ic_data_array_or_bytecode());
|
||||
}
|
||||
|
||||
bool Function::HasBytecode() const {
|
||||
return HasBytecode(ptr());
|
||||
}
|
||||
|
||||
bool Function::HasBytecode(FunctionPtr function) {
|
||||
return function.untag()->ic_data_array_or_bytecode()->IsBytecode();
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
intptr_t Field::HostOffset() const {
|
||||
ASSERT(is_instance()); // Valid only for dart instance fields.
|
||||
return (Smi::Value(untag()->host_offset_or_field_id()) * kCompressedWordSize);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
V(CodeSourceMap) \
|
||||
V(CompressedStackMaps) \
|
||||
V(ContextScope) \
|
||||
V(Bytecode) \
|
||||
V(DynamicLibrary) \
|
||||
V(Error) \
|
||||
V(ExceptionHandlers) \
|
||||
|
||||
@@ -1084,6 +1084,54 @@ void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
|
||||
void Code::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
|
||||
|
||||
void Bytecode::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
// N.B. This is polymorphic with Code.
|
||||
|
||||
JSONObject jsobj(stream);
|
||||
AddCommonObjectProperties(&jsobj, "Code", ref);
|
||||
int64_t compile_timestamp = 0;
|
||||
jsobj.AddFixedServiceId("code/%" Px64 "-%" Px "", compile_timestamp,
|
||||
PayloadStart());
|
||||
const char* qualified_name = QualifiedName();
|
||||
const char* vm_name = Name();
|
||||
AddNameProperties(&jsobj, qualified_name, vm_name);
|
||||
|
||||
jsobj.AddProperty("kind", "Dart");
|
||||
jsobj.AddProperty("_optimized", false);
|
||||
jsobj.AddProperty("_intrinsic", false);
|
||||
jsobj.AddProperty("_native", false);
|
||||
if (ref) {
|
||||
return;
|
||||
}
|
||||
const Function& fun = Function::Handle(function());
|
||||
jsobj.AddProperty("function", fun);
|
||||
jsobj.AddPropertyF("_startAddress", "%" Px "", PayloadStart());
|
||||
jsobj.AddPropertyF("_endAddress", "%" Px "", PayloadStart() + Size());
|
||||
jsobj.AddProperty("_alive", true);
|
||||
const ObjectPool& obj_pool = ObjectPool::Handle(object_pool());
|
||||
jsobj.AddProperty("_objectPool", obj_pool);
|
||||
{
|
||||
JSONArray jsarr(&jsobj, "_disassembly");
|
||||
DisassembleToJSONStream formatter(jsarr);
|
||||
Disassemble(&formatter);
|
||||
}
|
||||
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
|
||||
if (!descriptors.IsNull()) {
|
||||
JSONObject desc(&jsobj, "_descriptors");
|
||||
descriptors.PrintToJSONObject(&desc, false);
|
||||
}
|
||||
|
||||
{
|
||||
JSONArray inlined_functions(&jsobj, "_inlinedFunctions");
|
||||
}
|
||||
{
|
||||
JSONArray inline_intervals(&jsobj, "_inlinedIntervals");
|
||||
}
|
||||
}
|
||||
|
||||
void Bytecode::PrintImplementationFieldsImpl(
|
||||
const JSONArray& jsarr_fields) const {}
|
||||
|
||||
void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
JSONObject jsobj(stream);
|
||||
// TODO(turnidge): Should the user level type for Context be Context
|
||||
|
||||
@@ -525,6 +525,7 @@ COMPRESSED_VISITOR(TypeParameter)
|
||||
COMPRESSED_VISITOR(Function)
|
||||
COMPRESSED_VISITOR(Closure)
|
||||
COMPRESSED_VISITOR(LibraryPrefix)
|
||||
COMPRESSED_VISITOR(Bytecode)
|
||||
REGULAR_VISITOR(SingleTargetCache)
|
||||
REGULAR_VISITOR(UnlinkedCall)
|
||||
NULL_VISITOR(MonomorphicSmiableCall)
|
||||
@@ -681,6 +682,16 @@ intptr_t UntaggedCode::VisitCodePointers(CodePtr raw_obj,
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UntaggedBytecode::ContainsPC(ObjectPtr raw_obj, uword pc) {
|
||||
if (raw_obj->IsBytecode()) {
|
||||
BytecodePtr raw_bytecode = static_cast<BytecodePtr>(raw_obj);
|
||||
uword start = raw_bytecode->untag()->instructions_;
|
||||
uword size = raw_bytecode->untag()->instructions_size_;
|
||||
return (pc - start) <= size; // pc may point past last instruction.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
intptr_t UntaggedObjectPool::VisitObjectPoolPointers(
|
||||
ObjectPoolPtr raw_obj,
|
||||
ObjectPointerVisitor* visitor) {
|
||||
|
||||
+53
-2
@@ -836,6 +836,8 @@ class UntaggedObject {
|
||||
friend class Instance; // StorePointer
|
||||
friend class StackFrame; // GetCodeObject assertion.
|
||||
friend class CodeLookupTableBuilder; // profiler
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class ObjectLocator;
|
||||
friend class WriteBarrierUpdateVisitor; // CheckHeapPointerStore
|
||||
friend class OffsetsTable;
|
||||
@@ -1179,6 +1181,8 @@ class UntaggedClass : public UntaggedObject {
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
friend class Instance;
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class IsolateGroup;
|
||||
friend class Object;
|
||||
friend class UntaggedInstance;
|
||||
@@ -1387,6 +1391,8 @@ class UntaggedFunction : public UntaggedObject {
|
||||
|
||||
private:
|
||||
friend class Class;
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class UnitDeserializationRoots;
|
||||
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Function);
|
||||
@@ -1416,8 +1422,8 @@ class UntaggedFunction : public UntaggedObject {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
// ICData of unoptimized code.
|
||||
COMPRESSED_POINTER_FIELD(ArrayPtr, ic_data_array);
|
||||
// ICData of unoptimized code or Bytecode.
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, ic_data_array_or_bytecode);
|
||||
// Currently active code. Accessed from generated code.
|
||||
COMPRESSED_POINTER_FIELD(CodePtr, code);
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
@@ -1607,6 +1613,8 @@ class UntaggedField : public UntaggedObject {
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
friend class CidRewriteVisitor;
|
||||
friend class Interpreter;
|
||||
friend class InterpreterHelpers;
|
||||
friend class GuardFieldClassInstr; // For sizeof(guarded_cid_/...)
|
||||
friend class LoadFieldInstr; // For sizeof(guarded_cid_/...)
|
||||
friend class StoreFieldInstr; // For sizeof(guarded_cid_/...)
|
||||
@@ -1964,6 +1972,36 @@ class UntaggedCode : public UntaggedObject {
|
||||
friend class CallSiteResetter;
|
||||
};
|
||||
|
||||
class UntaggedBytecode : public UntaggedObject {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Bytecode);
|
||||
|
||||
uword instructions_;
|
||||
intptr_t instructions_size_;
|
||||
|
||||
COMPRESSED_POINTER_FIELD(ObjectPoolPtr, object_pool);
|
||||
VISIT_FROM(object_pool);
|
||||
COMPRESSED_POINTER_FIELD(FunctionPtr, function);
|
||||
COMPRESSED_POINTER_FIELD(ArrayPtr, closures);
|
||||
COMPRESSED_POINTER_FIELD(TypedDataBasePtr, binary);
|
||||
COMPRESSED_POINTER_FIELD(ExceptionHandlersPtr, exception_handlers);
|
||||
COMPRESSED_POINTER_FIELD(PcDescriptorsPtr, pc_descriptors);
|
||||
VISIT_TO(pc_descriptors);
|
||||
|
||||
ObjectPtr* to_snapshot(Snapshot::Kind kind) {
|
||||
return reinterpret_cast<ObjectPtr*>(&pc_descriptors_);
|
||||
}
|
||||
|
||||
int32_t instructions_binary_offset_;
|
||||
int32_t code_offset_;
|
||||
int32_t source_positions_binary_offset_;
|
||||
|
||||
static bool ContainsPC(ObjectPtr raw_obj, uword pc);
|
||||
|
||||
friend class Function;
|
||||
friend class Interpreter;
|
||||
friend class StackFrame;
|
||||
};
|
||||
|
||||
class UntaggedObjectPool : public UntaggedObject {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(ObjectPool);
|
||||
|
||||
@@ -1988,6 +2026,7 @@ class UntaggedObjectPool : public UntaggedObject {
|
||||
|
||||
friend class Object;
|
||||
friend class CodeSerializationCluster;
|
||||
friend class Interpreter;
|
||||
friend class UnitSerializationRoots;
|
||||
friend class UnitDeserializationRoots;
|
||||
};
|
||||
@@ -2016,6 +2055,7 @@ class UntaggedInstructions : public UntaggedObject {
|
||||
friend class Function;
|
||||
friend class ImageReader;
|
||||
friend class ImageWriter;
|
||||
friend class Interpreter;
|
||||
friend class AssemblyImageWriter;
|
||||
friend class BlobImageWriter;
|
||||
};
|
||||
@@ -2434,6 +2474,7 @@ class UntaggedContext : public UntaggedObject {
|
||||
COMPRESSED_VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
|
||||
|
||||
friend class Object;
|
||||
friend class Interpreter;
|
||||
friend void UpdateLengthField(intptr_t,
|
||||
ObjectPtr,
|
||||
ObjectPtr); // num_variables_
|
||||
@@ -2613,6 +2654,8 @@ class UntaggedSubtypeTestCache : public UntaggedObject {
|
||||
VISIT_TO(cache)
|
||||
uint32_t num_inputs_;
|
||||
uint32_t num_occupied_;
|
||||
|
||||
friend class Interpreter;
|
||||
};
|
||||
|
||||
class UntaggedLoadingUnit : public UntaggedObject {
|
||||
@@ -2741,6 +2784,7 @@ class UntaggedTypeArguments : public UntaggedInstance {
|
||||
COMPRESSED_VARIABLE_POINTER_FIELDS(AbstractTypePtr, element, types)
|
||||
|
||||
friend class Object;
|
||||
friend class Interpreter;
|
||||
};
|
||||
|
||||
class UntaggedTypeParameters : public UntaggedObject {
|
||||
@@ -2797,6 +2841,7 @@ class UntaggedAbstractType : public UntaggedInstance {
|
||||
private:
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(AbstractType);
|
||||
|
||||
friend class Interpreter;
|
||||
friend class ObjectStore;
|
||||
friend class StubCode;
|
||||
};
|
||||
@@ -2961,6 +3006,7 @@ class UntaggedClosure : public UntaggedInstance {
|
||||
|
||||
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
|
||||
|
||||
friend class Interpreter;
|
||||
friend class UnitDeserializationRoots;
|
||||
};
|
||||
|
||||
@@ -2985,6 +3031,7 @@ class UntaggedMint : public UntaggedInteger {
|
||||
friend class Api;
|
||||
friend class Class;
|
||||
friend class Integer;
|
||||
friend class Interpreter;
|
||||
};
|
||||
COMPILE_ASSERT(sizeof(UntaggedMint) == 16);
|
||||
|
||||
@@ -2996,6 +3043,7 @@ class UntaggedDouble : public UntaggedNumber {
|
||||
|
||||
friend class Api;
|
||||
friend class Class;
|
||||
friend class Interpreter;
|
||||
};
|
||||
COMPILE_ASSERT(sizeof(UntaggedDouble) == 16);
|
||||
|
||||
@@ -3234,6 +3282,7 @@ class UntaggedArray : public UntaggedInstance {
|
||||
friend class CodeSerializationCluster;
|
||||
friend class CodeDeserializationCluster;
|
||||
friend class Deserializer;
|
||||
friend class Interpreter;
|
||||
friend class UntaggedCode;
|
||||
friend class UntaggedImmutableArray;
|
||||
friend class GrowableObjectArray;
|
||||
@@ -3315,6 +3364,7 @@ class UntaggedFloat32x4 : public UntaggedInstance {
|
||||
ALIGN8 float value_[4];
|
||||
|
||||
friend class Class;
|
||||
friend class Interpreter;
|
||||
|
||||
public:
|
||||
float x() const { return value_[0]; }
|
||||
@@ -3350,6 +3400,7 @@ class UntaggedFloat64x2 : public UntaggedInstance {
|
||||
ALIGN8 double value_[2];
|
||||
|
||||
friend class Class;
|
||||
friend class Interpreter;
|
||||
|
||||
public:
|
||||
double x() const { return value_[0]; }
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace dart {
|
||||
F(Function, owner_) \
|
||||
F(Function, signature_) \
|
||||
F(Function, data_) \
|
||||
F(Function, ic_data_array_) \
|
||||
F(Function, ic_data_array_or_bytecode_) \
|
||||
F(Function, code_) \
|
||||
F(ClosureData, context_scope_) \
|
||||
F(ClosureData, parent_function_) \
|
||||
@@ -91,6 +91,12 @@ namespace dart {
|
||||
F(Code, compressed_stackmaps_) \
|
||||
F(Code, inlined_id_to_function_) \
|
||||
F(Code, code_source_map_) \
|
||||
F(Bytecode, object_pool_) \
|
||||
F(Bytecode, instructions_) \
|
||||
F(Bytecode, function_) \
|
||||
F(Bytecode, exception_handlers_) \
|
||||
F(Bytecode, pc_descriptors_) \
|
||||
F(Bytecode, closures_) \
|
||||
F(ExceptionHandlers, handled_types_data_) \
|
||||
F(Context, parent_) \
|
||||
F(SingleTargetCache, target_) \
|
||||
|
||||
@@ -83,6 +83,8 @@ REUSABLE_HANDLE_LIST(REUSABLE_SCOPE)
|
||||
ReusableClassHandleScope reused_class_handle(thread);
|
||||
#define REUSABLE_CODE_HANDLESCOPE(thread) \
|
||||
ReusableCodeHandleScope reused_code_handle(thread);
|
||||
#define REUSABLE_BYTECODE_HANDLESCOPE(thread) \
|
||||
ReusableBytecodeHandleScope reused_bytecode_handle(thread);
|
||||
#define REUSABLE_ERROR_HANDLESCOPE(thread) \
|
||||
ReusableErrorHandleScope reused_error_handle(thread);
|
||||
#define REUSABLE_EXCEPTION_HANDLERS_HANDLESCOPE(thread) \
|
||||
|
||||
+298
-11
@@ -23,6 +23,7 @@
|
||||
#include "vm/flags.h"
|
||||
#include "vm/heap/verifier.h"
|
||||
#include "vm/instructions.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/kernel_isolate.h"
|
||||
#include "vm/message.h"
|
||||
#include "vm/message_handler.h"
|
||||
@@ -237,6 +238,7 @@ static void DoThrowNullError(Isolate* isolate,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
const StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(caller_frame->IsDartFrame());
|
||||
ASSERT(!caller_frame->is_interpreted());
|
||||
const Code& code = Code::Handle(zone, caller_frame->LookupDartCode());
|
||||
const uword pc_offset = caller_frame->pc() - code.PayloadStart();
|
||||
|
||||
@@ -857,6 +859,129 @@ DEFINE_RUNTIME_ENTRY(CloneSuspendState, 1) {
|
||||
RuntimeAllocationEpilogue(thread);
|
||||
}
|
||||
|
||||
// Invoke field getter before dispatch.
|
||||
// Arg0: instance.
|
||||
// Arg1: field name (may be demangled during call).
|
||||
// Return value: field value.
|
||||
DEFINE_RUNTIME_ENTRY(GetFieldForDispatch, 2) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
String& name = String::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
const Class& receiver_class = Class::Handle(zone, receiver.clazz());
|
||||
if (Function::IsDynamicInvocationForwarderName(name)) {
|
||||
name = Function::DemangleDynamicInvocationForwarderName(name);
|
||||
arguments.SetArgAt(1, name); // Reflect change in arguments.
|
||||
}
|
||||
const String& getter_name = String::Handle(zone, Field::GetterName(name));
|
||||
const int kTypeArgsLen = 0;
|
||||
const int kNumArguments = 1;
|
||||
ArgumentsDescriptor args_desc(Array::Handle(
|
||||
zone, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments)));
|
||||
const Function& getter = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(
|
||||
receiver_class, getter_name, args_desc, /*allow_add=*/true));
|
||||
ASSERT(!getter.IsNull()); // An InvokeFieldDispatcher function was created.
|
||||
const Array& args = Array::Handle(zone, Array::New(kNumArguments));
|
||||
args.SetAt(0, receiver);
|
||||
const Object& result =
|
||||
Object::Handle(zone, DartEntry::InvokeFunction(getter, args));
|
||||
ThrowIfError(result);
|
||||
arguments.SetReturn(result);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Converts arguments descriptor passed to an implicit closure
|
||||
// into an arguments descriptor for the target function.
|
||||
// Arg0: implicit closure arguments descriptor
|
||||
// Arg1: target function
|
||||
// Return value: target arguments descriptor
|
||||
DEFINE_RUNTIME_ENTRY(AdjustArgumentsDesciptorForImplicitClosure, 2) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const auto& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const auto& target = Function::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
|
||||
const ArgumentsDescriptor args_desc(descriptor);
|
||||
intptr_t type_args_len = args_desc.TypeArgsLen();
|
||||
intptr_t num_arguments = args_desc.Count();
|
||||
|
||||
if (target.is_static()) {
|
||||
if (target.IsFactory()) {
|
||||
// Factory always takes type arguments via a positional parameter.
|
||||
type_args_len = 0;
|
||||
} else {
|
||||
// Drop closure receiver.
|
||||
--num_arguments;
|
||||
}
|
||||
} else {
|
||||
if (target.IsGenerativeConstructor()) {
|
||||
// Type arguments are not passed to a generative constructor.
|
||||
type_args_len = 0;
|
||||
} else {
|
||||
// No need to adjust arguments descriptor.
|
||||
arguments.SetReturn(descriptor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& optional_arguments_names =
|
||||
Array::Handle(zone, args_desc.GetArgumentNames());
|
||||
const auto& result = Array::Handle(
|
||||
zone, ArgumentsDescriptor::NewBoxed(type_args_len, num_arguments,
|
||||
optional_arguments_names));
|
||||
arguments.SetReturn(result);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Check that arguments are valid for the given closure.
|
||||
// Arg0: closure
|
||||
// Arg1: arguments descriptor
|
||||
// Return value: whether the arguments are valid
|
||||
DEFINE_RUNTIME_ENTRY(ClosureArgumentsValid, 2) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const auto& closure = Closure::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const auto& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
|
||||
const auto& function = Function::Handle(zone, closure.function());
|
||||
const ArgumentsDescriptor args_desc(descriptor);
|
||||
if (!function.AreValidArguments(args_desc, nullptr)) {
|
||||
arguments.SetReturn(Bool::False());
|
||||
} else if (!closure.IsGeneric() && args_desc.TypeArgsLen() > 0) {
|
||||
// The arguments may be valid for the closure function itself, but if the
|
||||
// closure has delayed type arguments, no type arguments should be provided.
|
||||
arguments.SetReturn(Bool::False());
|
||||
} else {
|
||||
arguments.SetReturn(Bool::True());
|
||||
}
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Resolve 'call' function of receiver.
|
||||
// Arg0: receiver (not a closure).
|
||||
// Arg1: arguments descriptor
|
||||
// Return value: 'call' function'.
|
||||
DEFINE_RUNTIME_ENTRY(ResolveCallFunction, 2) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const Array& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
ArgumentsDescriptor args_desc(descriptor);
|
||||
ASSERT(!receiver.IsClosure()); // Interpreter tests for closure.
|
||||
Class& cls = Class::Handle(zone, receiver.clazz());
|
||||
Function& call_function = Function::Handle(
|
||||
zone,
|
||||
Resolver::ResolveDynamicForReceiverClass(cls, Symbols::call(), args_desc,
|
||||
/*allow_add=*/false));
|
||||
arguments.SetReturn(call_function);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
// Helper routine for tracing a type check.
|
||||
static void PrintTypeCheck(const char* message,
|
||||
const Instance& instance,
|
||||
@@ -1257,6 +1382,7 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 7) {
|
||||
DartFrameIterator iterator(thread,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(!caller_frame->is_interpreted());
|
||||
const auto& dispatcher =
|
||||
Function::Handle(zone, caller_frame->LookupDartFunction());
|
||||
ASSERT(dispatcher.IsInvokeFieldDispatcher());
|
||||
@@ -1388,6 +1514,7 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 7) {
|
||||
DartFrameIterator iterator(thread,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(!caller_frame->is_interpreted());
|
||||
const Code& caller_code =
|
||||
Code::Handle(zone, caller_frame->LookupDartCode());
|
||||
const ObjectPool& pool =
|
||||
@@ -1490,6 +1617,7 @@ DEFINE_RUNTIME_ENTRY(PatchStaticCall, 0) {
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(caller_frame != nullptr);
|
||||
ASSERT(!caller_frame->is_interpreted());
|
||||
const Code& caller_code = Code::Handle(zone, caller_frame->LookupDartCode());
|
||||
ASSERT(!caller_code.IsNull());
|
||||
ASSERT(caller_code.is_optimized());
|
||||
@@ -1536,8 +1664,10 @@ DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) {
|
||||
StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(caller_frame != nullptr);
|
||||
Code& orig_stub = Code::Handle(zone);
|
||||
orig_stub =
|
||||
isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
|
||||
if (!caller_frame->is_interpreted()) {
|
||||
orig_stub =
|
||||
isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
|
||||
}
|
||||
const Error& error =
|
||||
Error::Handle(zone, isolate->debugger()->PauseBreakpoint());
|
||||
ThrowIfError(error);
|
||||
@@ -1659,6 +1789,7 @@ static void TrySwitchInstanceCall(Thread* thread,
|
||||
#endif
|
||||
|
||||
// Monomorphic/megamorphic calls are only for unoptimized code.
|
||||
if (caller_frame->is_interpreted()) return;
|
||||
ASSERT(!caller_code.is_optimized());
|
||||
|
||||
// Code is detached from its function. This will prevent us from resetting
|
||||
@@ -2742,6 +2873,44 @@ DEFINE_RUNTIME_ENTRY(SwitchableCallMiss, 2) {
|
||||
handler.ResolveSwitchAndReturn(old_data);
|
||||
}
|
||||
|
||||
// Handles interpreted interface call cache miss.
|
||||
// Arg0: receiver
|
||||
// Arg1: target name
|
||||
// Arg2: arguments descriptor
|
||||
// Returns: target function (can only be null in AOT runtime)
|
||||
// Modifies the instance call table in current interpreter.
|
||||
DEFINE_RUNTIME_ENTRY(InterpretedInstanceCallMissHandler, 3) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const String& target_name = String::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
const Array& arg_desc = Array::CheckedHandle(zone, arguments.ArgAt(2));
|
||||
|
||||
ArgumentsDescriptor arguments_descriptor(arg_desc);
|
||||
const Class& receiver_class = Class::Handle(zone, receiver.clazz());
|
||||
Function& target_function = Function::Handle(zone);
|
||||
if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
|
||||
const Class& cls = Class::Handle(zone, receiver.clazz());
|
||||
const bool allow_add = !FLAG_precompiled_mode;
|
||||
target_function = Resolver::ResolveDynamicForReceiverClass(
|
||||
cls, target_name, arguments_descriptor, allow_add);
|
||||
}
|
||||
|
||||
// TODO(regis): In order to substitute 'simple_instance_of_function', the 2nd
|
||||
// arg to the call, the type, is needed.
|
||||
|
||||
if (target_function.IsNull()) {
|
||||
target_function =
|
||||
InlineCacheMissHelper(receiver_class, arg_desc, target_name);
|
||||
}
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
ASSERT(!target_function.IsNull());
|
||||
#endif
|
||||
arguments.SetReturn(target_function);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Used to find the correct receiver and function to invoke or to fall back to
|
||||
// invoking noSuchMethod when lazy dispatchers are disabled. Returns the
|
||||
@@ -2951,6 +3120,40 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromPrologue, 4) {
|
||||
arguments.SetReturn(result);
|
||||
}
|
||||
|
||||
// Invoke appropriate noSuchMethod function (or in the case of no lazy
|
||||
// dispatchers, walk the receiver to find the correct method to call).
|
||||
// Arg0: receiver
|
||||
// Arg1: function name.
|
||||
// Arg2: arguments descriptor array.
|
||||
// Arg3: arguments array.
|
||||
DEFINE_RUNTIME_ENTRY(InvokeNoSuchMethod, 4) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const String& original_function_name =
|
||||
String::CheckedHandle(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));
|
||||
|
||||
auto& result = Object::Handle(zone);
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Failing to find the method could be due to the lack of lazy invoke field
|
||||
// dispatchers, so attempt a deeper search before calling noSuchMethod.
|
||||
result = InvokeCallThroughGetterOrNoSuchMethod(
|
||||
thread, zone, receiver, original_function_name, orig_arguments,
|
||||
orig_arguments_desc);
|
||||
#else
|
||||
result =
|
||||
DartEntry::InvokeNoSuchMethod(thread, receiver, original_function_name,
|
||||
orig_arguments, orig_arguments_desc);
|
||||
#endif
|
||||
ThrowIfError(result);
|
||||
arguments.SetReturn(result);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// The following code is used to stress test
|
||||
// - deoptimization
|
||||
@@ -3000,9 +3203,13 @@ static void HandleStackOverflowTestCases(Thread* thread) {
|
||||
ASSERT(frame != nullptr);
|
||||
Code& code = Code::Handle();
|
||||
Function& function = Function::Handle();
|
||||
code = frame->LookupDartCode();
|
||||
ASSERT(!code.IsNull());
|
||||
function = code.function();
|
||||
if (frame->is_interpreted()) {
|
||||
function = frame->LookupDartFunction();
|
||||
} else {
|
||||
code = frame->LookupDartCode();
|
||||
ASSERT(!code.IsNull());
|
||||
function = code.function();
|
||||
}
|
||||
ASSERT(!function.IsNull());
|
||||
const char* function_name = nullptr;
|
||||
if ((FLAG_deoptimize_filter != nullptr) ||
|
||||
@@ -3152,15 +3359,32 @@ DEFINE_RUNTIME_ENTRY(InterruptOrStackOverflow, 0) {
|
||||
// persist.
|
||||
uword stack_overflow_flags = thread->GetAndClearStackOverflowFlags();
|
||||
|
||||
bool interpreter_stack_overflow = false;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Interpreter* interpreter = thread->interpreter();
|
||||
if (interpreter != nullptr) {
|
||||
interpreter_stack_overflow =
|
||||
interpreter->get_sp() >= interpreter->overflow_stack_limit();
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// If an interrupt happens at the same time as a stack overflow, we
|
||||
// process the stack overflow now and leave the interrupt for next
|
||||
// time.
|
||||
if (!thread->os_thread()->HasStackHeadroom() ||
|
||||
if (interpreter_stack_overflow || !thread->os_thread()->HasStackHeadroom() ||
|
||||
IsCalleeFrameOf(thread->saved_stack_limit(), stack_pos)) {
|
||||
if (FLAG_verbose_stack_overflow) {
|
||||
OS::PrintErr("Stack overflow\n");
|
||||
OS::PrintErr(" Native SP = %" Px ", stack limit = %" Px "\n", stack_pos,
|
||||
thread->saved_stack_limit());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (thread->interpreter() != nullptr) {
|
||||
OS::PrintErr(" Interpreter SP = %" Px ", stack limit = %" Px "\n",
|
||||
thread->interpreter()->get_sp(),
|
||||
thread->interpreter()->overflow_stack_limit());
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
OS::PrintErr("Call stack:\n");
|
||||
OS::PrintErr("size | frame\n");
|
||||
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
|
||||
@@ -3441,6 +3665,7 @@ void DeoptimizeAt(Thread* mutator_thread,
|
||||
|
||||
// N.B.: Update the pending deopt table before updating the frame. The
|
||||
// profiler may attempt a stack walk in between.
|
||||
ASSERT(!frame->is_interpreted());
|
||||
mutator_thread->pending_deopts().AddPendingDeopt(frame->fp(), deopt_pc);
|
||||
frame->MarkForLazyDeopt();
|
||||
|
||||
@@ -3474,10 +3699,12 @@ void DeoptimizeFunctionsOnStack() {
|
||||
mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
|
||||
StackFrame* frame = iterator.NextFrame();
|
||||
while (frame != nullptr) {
|
||||
optimized_code = frame->LookupDartCode();
|
||||
if (optimized_code.is_optimized() &&
|
||||
!optimized_code.is_force_optimized()) {
|
||||
DeoptimizeAt(mutator_thread, optimized_code, frame);
|
||||
if (!frame->is_interpreted()) {
|
||||
optimized_code = frame->LookupDartCode();
|
||||
if (optimized_code.is_optimized() &&
|
||||
!optimized_code.is_force_optimized()) {
|
||||
DeoptimizeAt(mutator_thread, optimized_code, frame);
|
||||
}
|
||||
}
|
||||
frame = iterator.NextFrame();
|
||||
}
|
||||
@@ -3501,7 +3728,7 @@ static void DeoptimizeLastDartFrameIfOptimized() {
|
||||
DartFrameIterator iterator(mutator_thread,
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* frame = iterator.NextFrame();
|
||||
if (frame != nullptr) {
|
||||
if (frame != nullptr && !frame->is_interpreted()) {
|
||||
const auto& optimized_code = Code::Handle(frame->LookupDartCode());
|
||||
if (optimized_code.is_optimized() &&
|
||||
!optimized_code.is_force_optimized()) {
|
||||
@@ -3962,6 +4189,66 @@ DEFINE_RAW_LEAF_RUNTIME_ENTRY(MemoryMove,
|
||||
/*is_float=*/false,
|
||||
static_cast<MemMoveCFunction>(memmove));
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// Interpret a function call. Should be called only for non-jitted functions.
|
||||
// argc indicates the number of arguments, including the type arguments.
|
||||
// argv points to the first argument.
|
||||
// If argc < 0, arguments are passed at decreasing memory addresses from argv.
|
||||
extern "C" uword /*ObjectPtr*/ InterpretCall(uword /*FunctionPtr*/ function_in,
|
||||
uword /*ArrayPtr*/ argdesc_in,
|
||||
intptr_t argc,
|
||||
ObjectPtr* argv,
|
||||
Thread* thread) {
|
||||
FunctionPtr function = static_cast<FunctionPtr>(function_in);
|
||||
ArrayPtr argdesc = static_cast<ArrayPtr>(argdesc_in);
|
||||
Interpreter* interpreter = Interpreter::Current();
|
||||
#if defined(DEBUG)
|
||||
uword exit_fp = thread->top_exit_frame_info();
|
||||
ASSERT(exit_fp != 0);
|
||||
ASSERT(thread == Thread::Current());
|
||||
// Caller is InterpretCall stub called from generated code.
|
||||
// We stay in "in generated code" execution state when interpreting code.
|
||||
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
||||
ASSERT(Function::HasBytecode(function));
|
||||
ASSERT(interpreter != nullptr);
|
||||
#endif
|
||||
// Tell MemorySanitizer 'argv' is initialized by generated code.
|
||||
if (argc < 0) {
|
||||
MSAN_UNPOISON(argv - argc, -argc * sizeof(ObjectPtr));
|
||||
} else {
|
||||
MSAN_UNPOISON(argv, argc * sizeof(ObjectPtr));
|
||||
}
|
||||
ObjectPtr result =
|
||||
interpreter->Call(function, argdesc, argc, argv, Array::null(), thread);
|
||||
DEBUG_ASSERT(thread->top_exit_frame_info() == exit_fp);
|
||||
if (IsErrorClassId(result->GetClassIdMayBeSmi())) {
|
||||
// Must not leak handles in the caller's zone.
|
||||
HANDLESCOPE(thread);
|
||||
// Protect the result in a handle before transitioning, which may trigger
|
||||
// GC.
|
||||
const Error& error = Error::Handle(Error::RawCast(result));
|
||||
// Propagating an error may cause allocation. Check if we need to block for
|
||||
// a safepoint by switching to "in VM" execution state.
|
||||
TransitionGeneratedToVM transition(thread);
|
||||
Exceptions::PropagateError(error);
|
||||
}
|
||||
return static_cast<uword>(result);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
uword RuntimeEntry::InterpretCallEntry() {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
uword entry = reinterpret_cast<uword>(InterpretCall);
|
||||
#if defined(USING_SIMULATOR)
|
||||
entry = Simulator::RedirectExternalReference(entry,
|
||||
Simulator::kLeafRuntimeCall, 5);
|
||||
#endif
|
||||
return entry;
|
||||
#else
|
||||
return 0;
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
|
||||
extern "C" void DFLRT_EnterSafepoint(NativeArguments __unusable_) {
|
||||
CHECK_STACK_ALIGNMENT;
|
||||
TRACE_RUNTIME_CALL("%s", "EnterSafepoint");
|
||||
|
||||
@@ -56,6 +56,8 @@ class RuntimeEntry : public BaseRuntimeEntry {
|
||||
bool can_lazy_deopt() const { return can_lazy_deopt_; }
|
||||
uword GetEntryPoint() const;
|
||||
|
||||
static uword InterpretCallEntry();
|
||||
|
||||
private:
|
||||
const char* const name_;
|
||||
const void* const function_;
|
||||
|
||||
@@ -72,7 +72,13 @@ namespace dart {
|
||||
V(ResumeFrame) \
|
||||
V(SwitchableCallMiss) \
|
||||
V(NotLoaded) \
|
||||
V(FfiAsyncCallbackSend)
|
||||
V(FfiAsyncCallbackSend) \
|
||||
V(GetFieldForDispatch) \
|
||||
V(AdjustArgumentsDesciptorForImplicitClosure) \
|
||||
V(ClosureArgumentsValid) \
|
||||
V(ResolveCallFunction) \
|
||||
V(InterpretedInstanceCallMissHandler) \
|
||||
V(InvokeNoSuchMethod)
|
||||
|
||||
// Note: Leaf runtime function have C linkage, so they cannot pass C++ struct
|
||||
// values like ObjectPtr.
|
||||
|
||||
+161
-30
@@ -148,6 +148,10 @@ bool StackFrame::IsBareInstructionsStubFrame() const {
|
||||
}
|
||||
|
||||
bool StackFrame::IsStubFrame() const {
|
||||
if (is_interpreted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FLAG_precompiled_mode) {
|
||||
return IsBareInstructionsStubFrame();
|
||||
}
|
||||
@@ -169,6 +173,15 @@ bool StackFrame::IsStubFrame() const {
|
||||
const char* StackFrame::ToCString() const {
|
||||
ASSERT(thread_ == Thread::Current());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (is_interpreted()) {
|
||||
const Bytecode& bytecode = Bytecode::Handle(zone, LookupDartBytecode());
|
||||
const char* name = bytecode.IsNull() ? "Cannot find bytecode object"
|
||||
: bytecode.FullyQualifiedName();
|
||||
return zone->PrintToString(" pc 0x%" Pp " fp 0x%" Pp " sp 0x%" Pp " %s",
|
||||
pc(), fp(), sp(), name);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
const Code& code = Code::Handle(zone, GetCodeObject());
|
||||
const char* name =
|
||||
code.IsNull()
|
||||
@@ -180,11 +193,16 @@ const char* StackFrame::ToCString() const {
|
||||
|
||||
void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
ASSERT(visitor != nullptr);
|
||||
// Visit pc marker and saved pool pointer.
|
||||
ObjectPtr* last_fixed = reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
runtime_frame_layout.first_object_from_fp;
|
||||
ObjectPtr* first_fixed = reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
runtime_frame_layout.last_fixed_object_from_fp;
|
||||
// Visit pc marker and saved pool pointer, or, for interpreted frame, code
|
||||
// object and function object.
|
||||
ObjectPtr* last_fixed =
|
||||
reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
(is_interpreted() ? kKBCLastFixedObjectSlotFromFp
|
||||
: runtime_frame_layout.first_object_from_fp);
|
||||
ObjectPtr* first_fixed =
|
||||
reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
(is_interpreted() ? kKBCFirstObjectSlotFromFp
|
||||
: runtime_frame_layout.last_fixed_object_from_fp);
|
||||
if (first_fixed <= last_fixed) {
|
||||
visitor->VisitPointers(first_fixed, last_fixed);
|
||||
} else {
|
||||
@@ -196,9 +214,12 @@ void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
ASSERT(visitor != nullptr);
|
||||
// Visit objects between SP and (FP - callee_save_area).
|
||||
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
|
||||
ObjectPtr* last =
|
||||
reinterpret_cast<ObjectPtr*>(fp()) + kExitLinkSlotFromEntryFp - 1;
|
||||
ObjectPtr* first = is_interpreted() ? reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
kKBCSavedArgDescSlotFromEntryFp
|
||||
: reinterpret_cast<ObjectPtr*>(sp());
|
||||
ObjectPtr* last = is_interpreted() ? reinterpret_cast<ObjectPtr*>(sp())
|
||||
: reinterpret_cast<ObjectPtr*>(fp()) +
|
||||
kExitLinkSlotFromEntryFp - 1;
|
||||
// There may not be any pointer to visit; in this case, first > last.
|
||||
visitor->VisitPointers(first, last);
|
||||
}
|
||||
@@ -227,7 +248,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
global_table = global_table_payload;
|
||||
} else {
|
||||
ObjectPtr pc_marker = *(reinterpret_cast<ObjectPtr*>(
|
||||
fp() + (runtime_frame_layout.code_from_fp * kWordSize)));
|
||||
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
|
||||
: runtime_frame_layout.code_from_fp) *
|
||||
kWordSize)));
|
||||
// May forward raw code. Note we don't just visit the pc marker slot first
|
||||
// because the visitor's forwarding might not be idempotent.
|
||||
visitor->VisitPointer(&pc_marker);
|
||||
@@ -241,7 +264,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
isolate_group()->object_store()->canonicalized_stack_map_entries();
|
||||
}
|
||||
} else {
|
||||
ASSERT(pc_marker == Object::null());
|
||||
ASSERT(pc_marker == Object::null() ||
|
||||
(is_interpreted() && (!pc_marker->IsHeapObject() ||
|
||||
(pc_marker->GetClassId() == kBytecodeCid))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +277,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
maps, global_table);
|
||||
const uint32_t pc_offset = pc() - code_start;
|
||||
if (it.Find(pc_offset)) {
|
||||
if (is_interpreted()) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
|
||||
ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
|
||||
fp() + (runtime_frame_layout.first_local_from_fp * kWordSize));
|
||||
@@ -315,14 +343,33 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
|
||||
// For normal unoptimized Dart frames and Stub frames each slot
|
||||
// between the first and last included are tagged objects.
|
||||
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
|
||||
if (is_interpreted()) {
|
||||
// Do not visit caller's pc or caller's fp.
|
||||
ObjectPtr* first =
|
||||
reinterpret_cast<ObjectPtr*>(fp()) + kKBCFirstObjectSlotFromFp;
|
||||
ObjectPtr* last =
|
||||
reinterpret_cast<ObjectPtr*>(fp()) + kKBCLastFixedObjectSlotFromFp;
|
||||
|
||||
visitor->VisitPointers(first, last);
|
||||
}
|
||||
ObjectPtr* first =
|
||||
reinterpret_cast<ObjectPtr*>(is_interpreted() ? fp() : sp());
|
||||
ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
|
||||
fp() + (runtime_frame_layout.first_object_from_fp * kWordSize));
|
||||
is_interpreted()
|
||||
? sp()
|
||||
: fp() + (runtime_frame_layout.first_object_from_fp * kWordSize));
|
||||
|
||||
visitor->VisitPointers(first, last);
|
||||
}
|
||||
|
||||
FunctionPtr StackFrame::LookupDartFunction() const {
|
||||
if (is_interpreted()) {
|
||||
ObjectPtr result = *(reinterpret_cast<FunctionPtr*>(
|
||||
fp() + kKBCFunctionSlotFromFp * kWordSize));
|
||||
ASSERT((result == Object::null()) ||
|
||||
(result->GetClassId() == kFunctionCid));
|
||||
return static_cast<FunctionPtr>(result);
|
||||
}
|
||||
const Code& code = Code::Handle(LookupDartCode());
|
||||
if (!code.IsNull()) {
|
||||
const Object& owner = Object::Handle(code.owner());
|
||||
@@ -350,6 +397,8 @@ CodePtr StackFrame::LookupDartCode() const {
|
||||
}
|
||||
|
||||
CodePtr StackFrame::GetCodeObject() const {
|
||||
ASSERT(!is_interpreted());
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (FLAG_precompiled_mode) {
|
||||
if (pc() == 0) {
|
||||
@@ -370,6 +419,27 @@ CodePtr StackFrame::GetCodeObject() const {
|
||||
return static_cast<CodePtr>(pc_marker);
|
||||
}
|
||||
|
||||
BytecodePtr StackFrame::LookupDartBytecode() const {
|
||||
// We add a no gc scope to ensure that the code below does not trigger
|
||||
// a GC as we are handling raw object references here. It is possible
|
||||
// that the code is called while a GC is in progress, that is ok.
|
||||
#if !defined(HOST_OS_WINDOWS) && !defined(HOST_OS_FUCHSIA)
|
||||
// On Windows and Fuchsia, the profiler calls this from a separate thread
|
||||
// where Thread::Current() is NULL, so we cannot create a NoSafepointScope.
|
||||
NoSafepointScope no_safepoint;
|
||||
#endif
|
||||
return GetBytecodeObject();
|
||||
}
|
||||
|
||||
BytecodePtr StackFrame::GetBytecodeObject() const {
|
||||
ASSERT(is_interpreted());
|
||||
ObjectPtr pc_marker = *(
|
||||
reinterpret_cast<ObjectPtr*>(fp() + kKBCPcMarkerSlotFromFp * kWordSize));
|
||||
ASSERT((pc_marker == Object::null()) ||
|
||||
(pc_marker->GetClassId() == kBytecodeCid));
|
||||
return static_cast<BytecodePtr>(pc_marker);
|
||||
}
|
||||
|
||||
bool StackFrame::FindExceptionHandler(Thread* thread,
|
||||
uword* handler_pc,
|
||||
bool* needs_stacktrace,
|
||||
@@ -377,19 +447,28 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
|
||||
bool* is_optimized) const {
|
||||
REUSABLE_CODE_HANDLESCOPE(thread);
|
||||
Code& code = reused_code_handle.Handle();
|
||||
REUSABLE_BYTECODE_HANDLESCOPE(thread);
|
||||
Bytecode& bytecode = reused_bytecode_handle.Handle();
|
||||
REUSABLE_EXCEPTION_HANDLERS_HANDLESCOPE(thread);
|
||||
ExceptionHandlers& handlers = reused_exception_handlers_handle.Handle();
|
||||
REUSABLE_PC_DESCRIPTORS_HANDLESCOPE(thread);
|
||||
PcDescriptors& descriptors = reused_pc_descriptors_handle.Handle();
|
||||
uword start;
|
||||
code = LookupDartCode();
|
||||
if (code.IsNull()) {
|
||||
return false; // Stub frames do not have exception handlers.
|
||||
if (is_interpreted()) {
|
||||
bytecode = LookupDartBytecode();
|
||||
ASSERT(!bytecode.IsNull());
|
||||
start = bytecode.PayloadStart();
|
||||
handlers = bytecode.exception_handlers();
|
||||
} else {
|
||||
code = LookupDartCode();
|
||||
if (code.IsNull()) {
|
||||
return false; // Stub frames do not have exception handlers.
|
||||
}
|
||||
start = code.PayloadStart();
|
||||
handlers = code.exception_handlers();
|
||||
descriptors = code.pc_descriptors();
|
||||
*is_optimized = code.is_optimized();
|
||||
}
|
||||
start = code.PayloadStart();
|
||||
handlers = code.exception_handlers();
|
||||
descriptors = code.pc_descriptors();
|
||||
*is_optimized = code.is_optimized();
|
||||
HandlerInfoCache* cache = thread->isolate()->handler_info_cache();
|
||||
ExceptionHandlerInfo* info = cache->Lookup(pc());
|
||||
if (info != nullptr) {
|
||||
@@ -401,13 +480,18 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
|
||||
|
||||
intptr_t try_index = -1;
|
||||
if (handlers.num_entries() != 0) {
|
||||
uword pc_offset = pc() - code.PayloadStart();
|
||||
PcDescriptors::Iterator iter(descriptors, UntaggedPcDescriptors::kAnyKind);
|
||||
while (iter.MoveNext()) {
|
||||
const intptr_t current_try_index = iter.TryIndex();
|
||||
if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
|
||||
try_index = current_try_index;
|
||||
break;
|
||||
if (is_interpreted()) {
|
||||
try_index = bytecode.GetTryIndexAtPc(pc());
|
||||
} else {
|
||||
uword pc_offset = pc() - code.PayloadStart();
|
||||
PcDescriptors::Iterator iter(descriptors,
|
||||
UntaggedPcDescriptors::kAnyKind);
|
||||
while (iter.MoveNext()) {
|
||||
const intptr_t current_try_index = iter.TryIndex();
|
||||
if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
|
||||
try_index = current_try_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -430,6 +514,13 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
|
||||
}
|
||||
|
||||
TokenPosition StackFrame::GetTokenPos() const {
|
||||
if (is_interpreted()) {
|
||||
const Bytecode& bytecode = Bytecode::Handle(LookupDartBytecode());
|
||||
if (bytecode.IsNull()) {
|
||||
return TokenPosition::kNoSource; // Stub frames do not have token_pos.
|
||||
}
|
||||
return bytecode.GetTokenIndexOfPC(pc());
|
||||
}
|
||||
const Code& code = Code::Handle(LookupDartCode());
|
||||
if (code.IsNull()) {
|
||||
return TokenPosition::kNoSource; // Stub frames do not have token_pos.
|
||||
@@ -451,6 +542,9 @@ bool StackFrame::IsValid() const {
|
||||
if (IsEntryFrame() || IsExitFrame() || IsStubFrame()) {
|
||||
return true;
|
||||
}
|
||||
if (is_interpreted()) {
|
||||
return (LookupDartBytecode() != Bytecode::null());
|
||||
}
|
||||
return (LookupDartCode() != Code::null());
|
||||
}
|
||||
|
||||
@@ -471,16 +565,25 @@ void StackFrameIterator::SetupLastExitFrameData() {
|
||||
frames_.fp_ = exit_marker;
|
||||
frames_.sp_ = 0;
|
||||
frames_.pc_ = 0;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
frames_.CheckIfInterpreted(exit_marker);
|
||||
#endif
|
||||
frames_.Unpoison();
|
||||
}
|
||||
|
||||
void StackFrameIterator::SetupNextExitFrameData() {
|
||||
ASSERT(entry_.fp() != 0);
|
||||
uword exit_address = entry_.fp() + (kExitLinkSlotFromEntryFp * kWordSize);
|
||||
uword exit_address =
|
||||
entry_.fp() + ((entry_.is_interpreted() ? kKBCExitLinkSlotFromEntryFp
|
||||
: kExitLinkSlotFromEntryFp) *
|
||||
kWordSize);
|
||||
uword exit_marker = *reinterpret_cast<uword*>(exit_address);
|
||||
frames_.fp_ = exit_marker;
|
||||
frames_.sp_ = 0;
|
||||
frames_.pc_ = 0;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
frames_.CheckIfInterpreted(exit_marker);
|
||||
#endif
|
||||
frames_.Unpoison();
|
||||
}
|
||||
|
||||
@@ -513,6 +616,9 @@ StackFrameIterator::StackFrameIterator(uword last_fp,
|
||||
frames_.fp_ = last_fp;
|
||||
frames_.sp_ = 0;
|
||||
frames_.pc_ = 0;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
frames_.CheckIfInterpreted(last_fp);
|
||||
#endif
|
||||
frames_.Unpoison();
|
||||
}
|
||||
|
||||
@@ -533,6 +639,9 @@ StackFrameIterator::StackFrameIterator(uword fp,
|
||||
frames_.fp_ = fp;
|
||||
frames_.sp_ = sp;
|
||||
frames_.pc_ = pc;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
frames_.CheckIfInterpreted(fp);
|
||||
#endif
|
||||
frames_.Unpoison();
|
||||
}
|
||||
|
||||
@@ -570,8 +679,10 @@ StackFrame* StackFrameIterator::NextFrame() {
|
||||
// Iteration starts from an exit frame given by its fp.
|
||||
current_frame_ = NextExitFrame();
|
||||
} else if (*(reinterpret_cast<uword*>(
|
||||
frames_.fp_ + (kSavedCallerFpSlotFromFp * kWordSize))) ==
|
||||
0) {
|
||||
frames_.fp_ +
|
||||
((frames_.is_interpreted() ? kKBCSavedCallerFpSlotFromFp
|
||||
: kSavedCallerFpSlotFromFp) *
|
||||
kWordSize))) == 0) {
|
||||
// Iteration starts from an entry frame given by its fp, sp, and pc.
|
||||
current_frame_ = NextEntryFrame();
|
||||
} else {
|
||||
@@ -601,6 +712,15 @@ StackFrame* StackFrameIterator::NextFrame() {
|
||||
return current_frame_;
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
void StackFrameIterator::FrameSetIterator::CheckIfInterpreted(
|
||||
uword exit_marker) {
|
||||
Interpreter* interpreter = thread_->interpreter();
|
||||
is_interpreted_ =
|
||||
(interpreter != nullptr) && interpreter->HasFrame(exit_marker);
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
// Tell MemorySanitizer that generated code initializes part of the stack.
|
||||
void StackFrameIterator::FrameSetIterator::Unpoison() {
|
||||
// When using a simulator, all writes to the stack happened from MSAN
|
||||
@@ -610,7 +730,7 @@ void StackFrameIterator::FrameSetIterator::Unpoison() {
|
||||
#if !defined(USING_SIMULATOR)
|
||||
if (fp_ == 0) return;
|
||||
// Note that Thread::os_thread_ is cleared when the thread is descheduled.
|
||||
ASSERT((thread_->os_thread() == nullptr) ||
|
||||
ASSERT(is_interpreted() || (thread_->os_thread() == nullptr) ||
|
||||
((thread_->os_thread()->stack_limit() < fp_) &&
|
||||
(thread_->os_thread()->stack_base() > fp_)));
|
||||
uword lower;
|
||||
@@ -633,10 +753,14 @@ StackFrame* StackFrameIterator::FrameSetIterator::NextFrame(bool validate) {
|
||||
frame->sp_ = sp_;
|
||||
frame->fp_ = fp_;
|
||||
frame->pc_ = pc_;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
frame->is_interpreted_ = is_interpreted();
|
||||
#endif
|
||||
sp_ = frame->GetCallerSp();
|
||||
fp_ = frame->GetCallerFp();
|
||||
pc_ = frame->GetCallerPc();
|
||||
Unpoison();
|
||||
ASSERT(is_interpreted() == frame->is_interpreted());
|
||||
ASSERT(!validate || frame->IsValid());
|
||||
return frame;
|
||||
}
|
||||
@@ -645,10 +769,14 @@ ExitFrame* StackFrameIterator::NextExitFrame() {
|
||||
exit_.sp_ = frames_.sp_;
|
||||
exit_.fp_ = frames_.fp_;
|
||||
exit_.pc_ = frames_.pc_;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
exit_.is_interpreted_ = frames_.is_interpreted();
|
||||
#endif
|
||||
frames_.sp_ = exit_.GetCallerSp();
|
||||
frames_.fp_ = exit_.GetCallerFp();
|
||||
frames_.pc_ = exit_.GetCallerPc();
|
||||
frames_.Unpoison();
|
||||
ASSERT(frames_.is_interpreted() == exit_.is_interpreted());
|
||||
ASSERT(!validate_ || exit_.IsValid());
|
||||
return &exit_;
|
||||
}
|
||||
@@ -658,6 +786,9 @@ EntryFrame* StackFrameIterator::NextEntryFrame() {
|
||||
entry_.sp_ = frames_.sp_;
|
||||
entry_.fp_ = frames_.fp_;
|
||||
entry_.pc_ = frames_.pc_;
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
entry_.is_interpreted_ = frames_.is_interpreted();
|
||||
#endif
|
||||
SetupNextExitFrameData(); // Setup data for next exit frame in chain.
|
||||
ASSERT(!validate_ || entry_.IsValid());
|
||||
return &entry_;
|
||||
|
||||
+58
-10
@@ -7,7 +7,9 @@
|
||||
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/frame_layout.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/stack_frame_kbc.h"
|
||||
#include "vm/stub_code.h"
|
||||
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
@@ -44,6 +46,7 @@ class StackFrame : public ValueObject {
|
||||
|
||||
// The pool pointer is not implemented on all architectures.
|
||||
static int SavedCallerPpSlotFromFp() {
|
||||
// Never called on an interpreter frame.
|
||||
if (runtime_frame_layout.saved_caller_pp_from_fp !=
|
||||
kSavedCallerFpSlotFromFp) {
|
||||
return runtime_frame_layout.saved_caller_pp_from_fp;
|
||||
@@ -53,30 +56,37 @@ class StackFrame : public ValueObject {
|
||||
}
|
||||
|
||||
bool IsMarkedForLazyDeopt() const {
|
||||
ASSERT(!is_interpreted());
|
||||
uword raw_pc =
|
||||
*reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize));
|
||||
return raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint();
|
||||
}
|
||||
void MarkForLazyDeopt() {
|
||||
ASSERT(!is_interpreted());
|
||||
set_pc(StubCode::DeoptimizeLazyFromReturn().EntryPoint());
|
||||
}
|
||||
void UnmarkForLazyDeopt() {
|
||||
// If this frame was marked for lazy deopt, pc_ was computed to be the
|
||||
// original return address using the pending deopts table in GetCallerPc.
|
||||
// Write this value back into the frame.
|
||||
ASSERT(!is_interpreted());
|
||||
uword original_pc = pc();
|
||||
ASSERT(original_pc != StubCode::DeoptimizeLazyFromReturn().EntryPoint());
|
||||
set_pc(original_pc);
|
||||
}
|
||||
|
||||
void set_pc(uword value) {
|
||||
*reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize)) = value;
|
||||
*reinterpret_cast<uword*>(sp() + ((is_interpreted() ? kKBCSavedPcSlotFromSp
|
||||
: kSavedPcSlotFromSp) *
|
||||
kWordSize)) = value;
|
||||
pc_ = value;
|
||||
}
|
||||
|
||||
void set_pc_marker(CodePtr code) {
|
||||
*reinterpret_cast<CodePtr*>(
|
||||
fp() + (runtime_frame_layout.code_from_fp * kWordSize)) = code;
|
||||
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
|
||||
: runtime_frame_layout.code_from_fp) *
|
||||
kWordSize)) = code;
|
||||
}
|
||||
|
||||
// Visit objects in the frame.
|
||||
@@ -102,8 +112,15 @@ class StackFrame : public ValueObject {
|
||||
virtual bool IsEntryFrame() const { return false; }
|
||||
virtual bool IsExitFrame() const { return false; }
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_interpreted() const { return is_interpreted_; }
|
||||
#else
|
||||
bool is_interpreted() const { return false; }
|
||||
#endif
|
||||
|
||||
FunctionPtr LookupDartFunction() const;
|
||||
CodePtr LookupDartCode() const;
|
||||
BytecodePtr LookupDartBytecode() const;
|
||||
bool FindExceptionHandler(Thread* thread,
|
||||
uword* handler_pc,
|
||||
bool* needs_stacktrace,
|
||||
@@ -114,7 +131,11 @@ class StackFrame : public ValueObject {
|
||||
|
||||
static void DumpCurrentTrace();
|
||||
|
||||
uword GetCallerSp() const { return fp() + (kCallerSpSlotFromFp * kWordSize); }
|
||||
uword GetCallerSp() const {
|
||||
return fp() +
|
||||
((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) *
|
||||
kWordSize);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit StackFrame(Thread* thread)
|
||||
@@ -134,15 +155,20 @@ class StackFrame : public ValueObject {
|
||||
|
||||
private:
|
||||
CodePtr GetCodeObject() const;
|
||||
BytecodePtr GetBytecodeObject() const;
|
||||
|
||||
uword GetCallerFp() const {
|
||||
return *(reinterpret_cast<uword*>(fp() +
|
||||
(kSavedCallerFpSlotFromFp * kWordSize)));
|
||||
return *(reinterpret_cast<uword*>(
|
||||
fp() + ((is_interpreted() ? kKBCSavedCallerFpSlotFromFp
|
||||
: kSavedCallerFpSlotFromFp) *
|
||||
kWordSize)));
|
||||
}
|
||||
|
||||
uword GetCallerPc() const {
|
||||
uword raw_pc = *(reinterpret_cast<uword*>(
|
||||
fp() + (kSavedCallerPcSlotFromFp * kWordSize)));
|
||||
fp() + ((is_interpreted() ? kKBCSavedCallerPcSlotFromFp
|
||||
: kSavedCallerPcSlotFromFp) *
|
||||
kWordSize)));
|
||||
ASSERT(raw_pc != StubCode::DeoptimizeLazyFromThrow().EntryPoint());
|
||||
if (raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint()) {
|
||||
return thread_->pending_deopts().FindPendingDeopt(GetCallerFp());
|
||||
@@ -155,6 +181,10 @@ class StackFrame : public ValueObject {
|
||||
uword pc_;
|
||||
Thread* thread_;
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_interpreted_ = false;
|
||||
#endif
|
||||
|
||||
// The iterators FrameSetIterator and StackFrameIterator set the private
|
||||
// fields fp_ and sp_ when they return the respective frame objects.
|
||||
friend class FrameSetIterator;
|
||||
@@ -192,7 +222,9 @@ class ExitFrame : public StackFrame {
|
||||
// dart code.
|
||||
class EntryFrame : public StackFrame {
|
||||
public:
|
||||
bool IsValid() const { return StubCode::InInvocationStub(pc()); }
|
||||
bool IsValid() const {
|
||||
return StubCode::InInvocationStub(pc(), is_interpreted());
|
||||
}
|
||||
bool IsDartFrame(bool validate = true) const { return false; }
|
||||
bool IsStubFrame() const { return false; }
|
||||
bool IsEntryFrame() const { return true; }
|
||||
@@ -261,9 +293,11 @@ class StackFrameIterator {
|
||||
if (fp_ == 0) {
|
||||
return false;
|
||||
}
|
||||
const uword pc =
|
||||
*(reinterpret_cast<uword*>(sp_ + (kSavedPcSlotFromSp * kWordSize)));
|
||||
return !StubCode::InInvocationStub(pc);
|
||||
const uword pc = *(reinterpret_cast<uword*>(
|
||||
sp_ +
|
||||
((is_interpreted() ? kKBCSavedPcSlotFromSp : kSavedPcSlotFromSp) *
|
||||
kWordSize)));
|
||||
return !StubCode::InInvocationStub(pc, is_interpreted());
|
||||
}
|
||||
|
||||
// Get next non entry/exit frame in the set (assumes a next frame exists).
|
||||
@@ -272,6 +306,14 @@ class StackFrameIterator {
|
||||
private:
|
||||
explicit FrameSetIterator(Thread* thread)
|
||||
: fp_(0), sp_(0), pc_(0), stack_frame_(thread), thread_(thread) {}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_interpreted() const { return is_interpreted_; }
|
||||
void CheckIfInterpreted(uword exit_marker);
|
||||
#else
|
||||
bool is_interpreted() const { return false; }
|
||||
#endif
|
||||
|
||||
void Unpoison();
|
||||
|
||||
uword fp_;
|
||||
@@ -280,6 +322,10 @@ class StackFrameIterator {
|
||||
StackFrame stack_frame_; // Singleton frame returned by NextFrame().
|
||||
Thread* thread_;
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
bool is_interpreted_ = false;
|
||||
#endif
|
||||
|
||||
friend class StackFrameIterator;
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameSetIterator);
|
||||
};
|
||||
@@ -299,6 +345,8 @@ class StackFrameIterator {
|
||||
void SetupLastExitFrameData();
|
||||
void SetupNextExitFrameData();
|
||||
|
||||
void CheckInterpreterExitFrame(uword exit_marker);
|
||||
|
||||
bool validate_; // Validate each frame as we traverse the frames.
|
||||
EntryFrame entry_; // Singleton entry frame returned by NextEntryFrame().
|
||||
ExitFrame exit_; // Singleton exit frame returned by NextExitFrame().
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2024, 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_
|
||||
|
||||
#include "platform/globals.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.
|
||||
*/
|
||||
|
||||
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 kKBCSavedCallerPcSlotFromFp = -2;
|
||||
static const int kKBCCallerSpSlotFromFp = -kKBCDartFrameFixedSize - 1;
|
||||
static const int kKBCPcMarkerSlotFromFp = -3;
|
||||
static const int kKBCFunctionSlotFromFp = -4;
|
||||
static const int kKBCParamEndSlotFromFp = 4;
|
||||
static const int kKBCSuspendStateSlotFromFp = 0;
|
||||
|
||||
// Entry and exit frame layout.
|
||||
static const int kKBCEntrySavedSlots = 3;
|
||||
static const int kKBCExitLinkSlotFromEntryFp = 0;
|
||||
static const int kKBCSavedArgDescSlotFromEntryFp = 1;
|
||||
static const int kKBCSavedPpSlotFromEntryFp = 2;
|
||||
|
||||
// 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_
|
||||
@@ -90,6 +90,7 @@ class AsyncAwareStackUnwinder : public ValueObject {
|
||||
encountered_async_catch_error_(encountered_async_catch_error),
|
||||
closure_(Closure::Handle(zone_)),
|
||||
code_(Code::Handle(zone_)),
|
||||
bytecode_(Bytecode::Handle(zone_)),
|
||||
context_(Context::Handle(zone_)),
|
||||
function_(Function::Handle(zone_)),
|
||||
parent_function_(Function::Handle(zone_)),
|
||||
@@ -100,7 +101,9 @@ class AsyncAwareStackUnwinder : public ValueObject {
|
||||
subscription_(Object::Handle(zone_)),
|
||||
stream_iterator_(Object::Handle(zone_)),
|
||||
async_lib_(Library::Handle(zone_, Library::AsyncLibrary())),
|
||||
null_closure_(Closure::Handle(zone_)) {
|
||||
null_closure_(Closure::Handle(zone_)),
|
||||
null_code_(Code::Handle(zone_)),
|
||||
null_bytecode_(Bytecode::Handle(zone_)) {
|
||||
if (encountered_async_catch_error_ != nullptr) {
|
||||
*encountered_async_catch_error_ = false;
|
||||
}
|
||||
@@ -259,6 +262,7 @@ class AsyncAwareStackUnwinder : public ValueObject {
|
||||
|
||||
Closure& closure_;
|
||||
Code& code_;
|
||||
Bytecode& bytecode_;
|
||||
Context& context_;
|
||||
Function& function_;
|
||||
Function& parent_function_;
|
||||
@@ -275,6 +279,8 @@ class AsyncAwareStackUnwinder : public ValueObject {
|
||||
Field* fields_[kUsedFieldCount] = {};
|
||||
|
||||
const Closure& null_closure_;
|
||||
const Code& null_code_;
|
||||
const Bytecode& null_bytecode_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsyncAwareStackUnwinder);
|
||||
};
|
||||
@@ -294,16 +300,27 @@ void AsyncAwareStackUnwinder::Unwind(
|
||||
while (sync_frame_ != nullptr && awaiter_frame_.closure.IsNull()) {
|
||||
const bool was_handled = HandleSynchronousFrame();
|
||||
if (!was_handled) {
|
||||
code_ = sync_frame_->LookupDartCode();
|
||||
const uword pc_offset = sync_frame_->pc() - code_.PayloadStart();
|
||||
handle_frame({sync_frame_, code_, pc_offset, null_closure_});
|
||||
if (sync_frame_->is_interpreted()) {
|
||||
bytecode_ = sync_frame_->LookupDartBytecode();
|
||||
if (bytecode_.function() == Function::null()) {
|
||||
continue;
|
||||
}
|
||||
const uword pc_offset = sync_frame_->pc() - bytecode_.PayloadStart();
|
||||
handle_frame(
|
||||
{sync_frame_, null_code_, bytecode_, pc_offset, null_closure_});
|
||||
} else {
|
||||
code_ = sync_frame_->LookupDartCode();
|
||||
const uword pc_offset = sync_frame_->pc() - code_.PayloadStart();
|
||||
handle_frame(
|
||||
{sync_frame_, code_, null_bytecode_, pc_offset, null_closure_});
|
||||
}
|
||||
}
|
||||
sync_frame_ = sync_frames_.NextFrame();
|
||||
}
|
||||
|
||||
const StackTraceUtils::Frame gap_frame = {nullptr,
|
||||
StubCode::AsynchronousGapMarker(),
|
||||
/*pc_offset=*/0, null_closure_};
|
||||
const StackTraceUtils::Frame gap_frame = {
|
||||
nullptr, StubCode::AsynchronousGapMarker(), null_bytecode_,
|
||||
/*pc_offset=*/0, null_closure_};
|
||||
|
||||
// Traverse awaiter frames.
|
||||
bool any_async = false;
|
||||
@@ -336,7 +353,8 @@ void AsyncAwareStackUnwinder::Unwind(
|
||||
}
|
||||
|
||||
handle_frame(gap_frame);
|
||||
handle_frame({nullptr, code_, pc_offset, awaiter_frame_.closure});
|
||||
handle_frame(
|
||||
{nullptr, code_, null_bytecode_, pc_offset, awaiter_frame_.closure});
|
||||
}
|
||||
|
||||
if (any_async) {
|
||||
|
||||
@@ -26,6 +26,9 @@ class StackTraceUtils : public AllStatic {
|
||||
// Code object corresponding to this frame.
|
||||
const Code& code;
|
||||
|
||||
// Bytecode object corresponding to this frame.
|
||||
const Bytecode& bytecode;
|
||||
|
||||
// Offset into the code object corresponding to this frame.
|
||||
//
|
||||
// Will be set to |kFutureListenerPcOffset| if this frame corresponds to
|
||||
|
||||
+16
-1
@@ -9,6 +9,7 @@
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/heap/safepoint.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/snapshot.h"
|
||||
#include "vm/virtual_memory.h"
|
||||
@@ -129,8 +130,22 @@ void StubCode::Cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
bool StubCode::InInvocationStub(uword pc) {
|
||||
bool StubCode::InInvocationStub(uword pc, bool is_interpreted_frame) {
|
||||
ASSERT(HasBeenInitialized());
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (is_interpreted_frame) {
|
||||
// Recognize special marker set up by interpreter in entry frame.
|
||||
return Interpreter::IsEntryFrameMarker(
|
||||
reinterpret_cast<const KBCInstr*>(pc));
|
||||
}
|
||||
{
|
||||
uword entry = StubCode::InvokeDartCodeFromBytecode().EntryPoint();
|
||||
uword size = StubCode::InvokeDartCodeFromBytecodeSize();
|
||||
if ((pc >= entry) && (pc < (entry + size))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
uword entry = StubCode::InvokeDartCode().EntryPoint();
|
||||
uword size = StubCode::InvokeDartCodeSize();
|
||||
return (pc >= entry) && (pc < (entry + size));
|
||||
|
||||
@@ -47,7 +47,7 @@ class StubCode : public AllStatic {
|
||||
|
||||
// Check if specified pc is in the dart invocation stub used for
|
||||
// transitioning into dart code.
|
||||
static bool InInvocationStub(uword pc);
|
||||
static bool InInvocationStub(uword pc, bool is_interpreted_frame = false);
|
||||
|
||||
// Check if the specified pc is in the jump to frame stub.
|
||||
static bool InJumpToFrameStub(uword pc);
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace dart {
|
||||
V(CloneContext) \
|
||||
V(CallToRuntime) \
|
||||
V(LazyCompile) \
|
||||
V(InterpretCall) \
|
||||
V(CallBootstrapNative) \
|
||||
V(CallNoScopeNative) \
|
||||
V(CallAutoScopeNative) \
|
||||
@@ -78,6 +79,7 @@ namespace dart {
|
||||
V(CallStaticFunction) \
|
||||
V(OptimizeFunction) \
|
||||
V(InvokeDartCode) \
|
||||
V(InvokeDartCodeFromBytecode) \
|
||||
V(DebugStepCheck) \
|
||||
V(SwitchableCallMiss) \
|
||||
V(MonomorphicSmiableCheck) \
|
||||
|
||||
@@ -33,6 +33,7 @@ class ObjectPointerVisitor;
|
||||
V(BooleanExpression, "boolean expression") \
|
||||
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation") \
|
||||
V(ByteData, "ByteData") \
|
||||
V(Bytecode, "Bytecode") \
|
||||
V(Capability, "Capability") \
|
||||
V(CheckLoaded, "_checkLoaded") \
|
||||
V(Class, "Class") \
|
||||
|
||||
@@ -377,6 +377,7 @@ DEFINE_TAGGED_POINTER(KernelProgramInfo, Object)
|
||||
DEFINE_TAGGED_POINTER(WeakSerializationReference, Object)
|
||||
DEFINE_TAGGED_POINTER(WeakArray, Object)
|
||||
DEFINE_TAGGED_POINTER(Code, Object)
|
||||
DEFINE_TAGGED_POINTER(Bytecode, Object)
|
||||
DEFINE_TAGGED_POINTER(ObjectPool, Object)
|
||||
DEFINE_TAGGED_POINTER(Instructions, Object)
|
||||
DEFINE_TAGGED_POINTER(InstructionsSection, Object)
|
||||
|
||||
@@ -25,6 +25,7 @@ class RuntimeEntry;
|
||||
V(ClassLoading) \
|
||||
V(CompileParseRegExp) \
|
||||
V(Dart) \
|
||||
V(DartInterpreted) \
|
||||
V(GCNewSpace) \
|
||||
V(GCOldSpace) \
|
||||
V(GCIdle) \
|
||||
|
||||
@@ -41,6 +41,10 @@ Thread::~Thread() {
|
||||
ASSERT(old_marking_stack_block_ == nullptr);
|
||||
ASSERT(new_marking_stack_block_ == nullptr);
|
||||
ASSERT(deferred_marking_stack_block_ == nullptr);
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
delete interpreter_;
|
||||
interpreter_ = nullptr;
|
||||
#endif
|
||||
// There should be no top api scopes at this point.
|
||||
ASSERT(api_top_scope() == nullptr);
|
||||
// Delete the reusable api scope if there is one.
|
||||
@@ -981,6 +985,12 @@ void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor,
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&active_stacktrace_));
|
||||
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&sticky_error_));
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
if (interpreter() != nullptr) {
|
||||
interpreter()->VisitObjectPointers(visitor);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Visit the api local scope as it has all the api local handles.
|
||||
ApiLocalScope* scope = api_top_scope_;
|
||||
while (scope != nullptr) {
|
||||
@@ -1246,6 +1256,11 @@ bool Thread::TopErrorHandlerIsSetJump() const {
|
||||
// False positives: simulator stack and native stack are unordered.
|
||||
return true;
|
||||
#else
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// False positives: interpreter stack and native stack are unordered.
|
||||
if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_))
|
||||
return true;
|
||||
#endif
|
||||
return reinterpret_cast<uword>(long_jump_base()) < top_exit_frame_info_;
|
||||
#endif
|
||||
}
|
||||
@@ -1257,6 +1272,11 @@ bool Thread::TopErrorHandlerIsExitFrame() const {
|
||||
// False positives: simulator stack and native stack are unordered.
|
||||
return true;
|
||||
#else
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
// False positives: interpreter stack and native stack are unordered.
|
||||
if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_))
|
||||
return true;
|
||||
#endif
|
||||
return top_exit_frame_info_ < reinterpret_cast<uword>(long_jump_base());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class CompilerState;
|
||||
class CompilerTimings;
|
||||
class Class;
|
||||
class Code;
|
||||
class Bytecode;
|
||||
class Error;
|
||||
class ExceptionHandlers;
|
||||
class Field;
|
||||
@@ -49,6 +50,7 @@ class HandleScope;
|
||||
class Heap;
|
||||
class HierarchyInfo;
|
||||
class Instance;
|
||||
class Interpreter;
|
||||
class Isolate;
|
||||
class IsolateGroup;
|
||||
class Library;
|
||||
@@ -69,6 +71,10 @@ class TypeParameter;
|
||||
class TypeUsageInfo;
|
||||
class Zone;
|
||||
|
||||
namespace bytecode {
|
||||
class BytecodeLoader;
|
||||
}
|
||||
|
||||
namespace compiler {
|
||||
namespace target {
|
||||
class Thread;
|
||||
@@ -80,6 +86,7 @@ class Thread;
|
||||
V(Array) \
|
||||
V(Class) \
|
||||
V(Code) \
|
||||
V(Bytecode) \
|
||||
V(Error) \
|
||||
V(ExceptionHandlers) \
|
||||
V(Field) \
|
||||
@@ -104,6 +111,8 @@ class Thread;
|
||||
StubCode::FixAllocationStubTarget().ptr(), nullptr) \
|
||||
V(CodePtr, invoke_dart_code_stub_, StubCode::InvokeDartCode().ptr(), \
|
||||
nullptr) \
|
||||
V(CodePtr, invoke_dart_code_from_bytecode_stub_, \
|
||||
StubCode::InvokeDartCodeFromBytecode().ptr(), nullptr) \
|
||||
V(CodePtr, call_to_runtime_stub_, StubCode::CallToRuntime().ptr(), nullptr) \
|
||||
V(CodePtr, late_initialization_error_shared_without_fpu_regs_stub_, \
|
||||
StubCode::LateInitializationErrorSharedWithoutFPURegs().ptr(), nullptr) \
|
||||
@@ -247,6 +256,7 @@ class Thread;
|
||||
NativeEntry::NoScopeNativeCallWrapperEntry(), 0) \
|
||||
V(uword, auto_scope_native_wrapper_entry_point_, \
|
||||
NativeEntry::AutoScopeNativeCallWrapperEntry(), 0) \
|
||||
V(uword, interpret_call_entry_point_, RuntimeEntry::InterpretCallEntry(), 0) \
|
||||
V(StringPtr*, predefined_symbols_address_, Symbols::PredefinedAddress(), \
|
||||
nullptr) \
|
||||
V(uword, double_nan_address_, reinterpret_cast<uword>(&double_nan_constant), \
|
||||
@@ -1154,6 +1164,16 @@ class Thread : public ThreadState {
|
||||
return SafepointLevel::kGCAndDeoptAndReload;
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Interpreter* interpreter() const { return interpreter_; }
|
||||
void set_interpreter(Interpreter* value) { interpreter_ = value; }
|
||||
|
||||
bytecode::BytecodeLoader* bytecode_loader() const { return bytecode_loader_; }
|
||||
void set_bytecode_loader(bytecode::BytecodeLoader* value) {
|
||||
bytecode_loader_ = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
T* AllocateReusableHandle();
|
||||
@@ -1407,6 +1427,11 @@ class Thread : public ThreadState {
|
||||
HeapProfileSampler heap_sampler_;
|
||||
#endif
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
Interpreter* interpreter_ = nullptr;
|
||||
bytecode::BytecodeLoader* bytecode_loader_ = nullptr;
|
||||
#endif
|
||||
|
||||
explicit Thread(bool is_vm_isolate);
|
||||
|
||||
void StoreBufferRelease(
|
||||
@@ -1472,6 +1497,7 @@ class Thread : public ThreadState {
|
||||
|
||||
friend class ApiZone;
|
||||
friend class ActiveIsolateScope;
|
||||
friend class Interpreter;
|
||||
friend class InterruptChecker;
|
||||
friend class Isolate;
|
||||
friend class IsolateGroup;
|
||||
|
||||
@@ -23,6 +23,8 @@ vm_sources = [
|
||||
"bootstrap_natives.h",
|
||||
"bss_relocs.cc",
|
||||
"bss_relocs.h",
|
||||
"bytecode_reader.cc",
|
||||
"bytecode_reader.h",
|
||||
"canonical_tables.cc",
|
||||
"canonical_tables.h",
|
||||
"class_finalizer.cc",
|
||||
@@ -53,6 +55,8 @@ vm_sources = [
|
||||
"constants_base.h",
|
||||
"constants_ia32.cc",
|
||||
"constants_ia32.h",
|
||||
"constants_kbc.cc",
|
||||
"constants_kbc.h",
|
||||
"constants_riscv.cc",
|
||||
"constants_riscv.h",
|
||||
"constants_x64.cc",
|
||||
@@ -138,6 +142,8 @@ vm_sources = [
|
||||
"instructions_riscv.h",
|
||||
"instructions_x64.cc",
|
||||
"instructions_x64.h",
|
||||
"interpreter.cc",
|
||||
"interpreter.h",
|
||||
"intrusive_dlist.h",
|
||||
"isolate.cc",
|
||||
"isolate.h",
|
||||
@@ -311,6 +317,7 @@ vm_sources = [
|
||||
"stack_frame_arm.h",
|
||||
"stack_frame_arm64.h",
|
||||
"stack_frame_ia32.h",
|
||||
"stack_frame_kbc.h",
|
||||
"stack_frame_x64.h",
|
||||
"stack_trace.cc",
|
||||
"stack_trace.h",
|
||||
|
||||
@@ -445,4 +445,7 @@ external String intern(String str);
|
||||
|
||||
@patch
|
||||
Future<Object?> loadDynamicModule({Uri? uri, Uint8List? bytes}) =>
|
||||
throw 'Unsupported operation';
|
||||
Future.value(_loadDynamicModule(bytes!));
|
||||
|
||||
@pragma("vm:external-name", "Internal_loadDynamicModule")
|
||||
external Object? _loadDynamicModule(Uint8List bytes);
|
||||
|
||||
@@ -257,6 +257,8 @@ def ToGnArgs(args, mode, arch, target_os, sanitizer, verify_sdk_hash,
|
||||
enable_code_coverage = args.code_coverage and gn_args['is_clang']
|
||||
gn_args['dart_vm_code_coverage'] = enable_code_coverage
|
||||
|
||||
gn_args['dart_dynamic_modules'] = args.dart_dynamic_modules
|
||||
|
||||
gn_args['is_asan'] = sanitizer == 'asan'
|
||||
gn_args['is_lsan'] = sanitizer == 'lsan'
|
||||
gn_args['is_msan'] = sanitizer == 'msan'
|
||||
@@ -496,6 +498,11 @@ def AddCommonGnOptionArgs(parser):
|
||||
default=False,
|
||||
dest="code_coverage",
|
||||
action='store_true')
|
||||
parser.add_argument('--dart-dynamic-modules',
|
||||
help='Enable Dart dynamic modules.',
|
||||
default=False,
|
||||
dest='dart_dynamic_modules',
|
||||
action='store_true')
|
||||
parser.add_argument('--debug-opt-level',
|
||||
'-d',
|
||||
help='The optimization level to use for debug builds',
|
||||
|
||||
Reference in New Issue
Block a user