d36adbacaf
The former contents of the VM isolate are now included into each isolate group. This makes each isolate group's heap independent, and in particular allows each heap to be allocated to a separate pointer cage (not done in this CL). The duplicated stubs that allowed PC relative calls are removed, since the originals can now be the target of PC relative calls. The bootstrapping needing to load an AppJIT or AppAOT snapshot is reduced to allocating the oddballs. The code is entirely dropped in the AOT runtime, but the JIT runtime still has it to allow for flags to affect the compilation of the stub code. Further refactoring might be able to remove this for the JIT runtime too, with only gen_snapshot knowing how to bootstrap. Class serialization no longer distinguishes predefined classes. The page containing null is marked as never-evacuate. null, false and true must not move because the compiler relies on their low bits having certain patterns for some optimizations. (Previously, the entire VM isolate heap never moved.) Compaction is disabled for IA32. Due to register pressure, some stub calls must not use a scratch register and embed the address of Code. The page containing the call-through-safepoint stub is frozen when running with --write-protect-code and the stub is created at runtime (instead of loaded from an AppJIT or AppAOT snapshot). This stub must remain executable even during a safepoint, as a foreign call might during return during a safepoint and only block after the stub directs it to the runtime. The snapshot symbols are renamed to kDartSnapshotData and kDartSnapshotText. There is no need to distinguish the VM isolate's snapshot, and snaphots are per isolate group not per isolate. Aliases with the old names are added to ease migration. Some global flags that were automatically set based on the VM isolate's snapshot are now isolate group flags and automatically set by the isolate group's snapshot. TEST=ci Change-Id: Iee82016057d609112e9b021d178fc3d4d18b5044 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500621 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
1112 lines
36 KiB
C++
1112 lines
36 KiB
C++
// Copyright (c) 2019, 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 "platform/thread_sanitizer.h"
|
|
|
|
#include "vm/compiler/runtime_api.h"
|
|
|
|
#include "vm/object.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/compiler/runtime_offsets_list.h"
|
|
#include "vm/dart_api_state.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/native_arguments.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/runtime_entry.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/timeline.h"
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
namespace compiler {
|
|
namespace target {
|
|
|
|
#include "vm/compiler/runtime_offsets_extracted.h"
|
|
|
|
bool IsSmi(int64_t v) {
|
|
return Utils::IsInt(kSmiBits + 1, v);
|
|
}
|
|
|
|
bool WillAllocateNewOrRememberedObject(intptr_t instance_size) {
|
|
ASSERT(Utils::IsAligned(instance_size, ObjectAlignment::kObjectAlignment));
|
|
return dart::Heap::IsAllocatableInNewSpace(instance_size);
|
|
}
|
|
|
|
bool WillAllocateNewOrRememberedContext(intptr_t num_context_variables) {
|
|
if (!dart::Context::IsValidLength(num_context_variables)) return false;
|
|
return dart::Heap::IsAllocatableInNewSpace(
|
|
dart::Context::InstanceSize(num_context_variables));
|
|
}
|
|
|
|
bool WillAllocateNewOrRememberedArray(intptr_t length) {
|
|
if (!dart::Array::IsValidLength(length)) return false;
|
|
return !dart::Array::UseCardMarkingForAllocation(length);
|
|
}
|
|
|
|
} // namespace target
|
|
} // namespace compiler
|
|
} // namespace dart
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
namespace compiler {
|
|
|
|
bool IsSameObject(const Object& a, const Object& b) {
|
|
if (a.IsInstance() && b.IsInstance()) {
|
|
return Instance::Cast(a).IsIdenticalTo(Instance::Cast(b));
|
|
}
|
|
return a.ptr() == b.ptr();
|
|
}
|
|
|
|
bool IsEqualType(const AbstractType& a, const AbstractType& b) {
|
|
return a.Equals(b);
|
|
}
|
|
|
|
bool IsDoubleType(const AbstractType& type) {
|
|
return type.IsDoubleType();
|
|
}
|
|
|
|
bool IsBoolType(const AbstractType& type) {
|
|
return type.IsBoolType();
|
|
}
|
|
|
|
bool IsSubtypeOfInt(const AbstractType& type) {
|
|
return type.IsIntType() || type.IsIntegerImplementationType() ||
|
|
type.IsSmiType() || type.IsMintType();
|
|
}
|
|
|
|
bool IsSmiType(const AbstractType& type) {
|
|
return type.IsSmiType();
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
bool IsNotTemporaryScopedHandle(const Object& obj) {
|
|
return obj.IsNotTemporaryScopedHandle();
|
|
}
|
|
#endif
|
|
|
|
#define DO(clazz) \
|
|
bool Is##clazz##Handle(const Object& obj) { \
|
|
return obj.Is##clazz(); \
|
|
}
|
|
CLASS_LIST_FOR_HANDLES(DO)
|
|
#undef DO
|
|
|
|
bool IsInOldSpace(const Object& obj) {
|
|
return obj.IsSmi() || obj.IsOld();
|
|
}
|
|
|
|
intptr_t ObjectHash(const Object& obj) {
|
|
if (obj.IsNull()) {
|
|
return kNullIdentityHash;
|
|
}
|
|
// TypeArguments should be handled before Instance as TypeArguments extends
|
|
// Instance and TypeArguments::CanonicalizeHash just returns 0.
|
|
if (obj.IsTypeArguments()) {
|
|
return TypeArguments::Cast(obj).Hash();
|
|
}
|
|
if (obj.IsInstance()) {
|
|
return Instance::Cast(obj).CanonicalizeHash();
|
|
}
|
|
if (obj.IsCode()) {
|
|
return Code::Cast(obj).Hash();
|
|
}
|
|
if (obj.IsFunction()) {
|
|
return Function::Cast(obj).Hash();
|
|
}
|
|
if (obj.IsField()) {
|
|
return Field::Cast(obj).Hash();
|
|
}
|
|
if (obj.IsICData()) {
|
|
return ICData::Cast(obj).Hash();
|
|
}
|
|
// Unlikely.
|
|
return obj.GetClassId();
|
|
}
|
|
|
|
const char* ObjectToCString(const Object& obj) {
|
|
return obj.ToCString();
|
|
}
|
|
|
|
void SetToNull(Object* obj) {
|
|
*obj = Object::null();
|
|
}
|
|
|
|
Object& NewZoneHandle(Zone* zone) {
|
|
return Object::ZoneHandle(zone, Object::null());
|
|
}
|
|
|
|
Object& NewZoneHandle(Zone* zone, const Object& obj) {
|
|
return Object::ZoneHandle(zone, obj.ptr());
|
|
}
|
|
|
|
const Object& NullObject() {
|
|
return Object::null_object();
|
|
}
|
|
|
|
const Object& SentinelObject() {
|
|
return Object::sentinel();
|
|
}
|
|
|
|
const Bool& TrueObject() {
|
|
return dart::Bool::True();
|
|
}
|
|
|
|
const Bool& FalseObject() {
|
|
return dart::Bool::False();
|
|
}
|
|
|
|
const Object& EmptyTypeArguments() {
|
|
return Object::empty_type_arguments();
|
|
}
|
|
|
|
const Type& DynamicType() {
|
|
return dart::Type::dynamic_type();
|
|
}
|
|
|
|
const Type& ObjectType() {
|
|
return Type::Handle(dart::Type::ObjectType());
|
|
}
|
|
|
|
const Type& VoidType() {
|
|
return dart::Type::void_type();
|
|
}
|
|
|
|
const Type& IntType() {
|
|
return Type::Handle(dart::Type::IntType());
|
|
}
|
|
|
|
const Class& GrowableObjectArrayClass() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->growable_object_array_class());
|
|
}
|
|
|
|
const Class& MintClass() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->mint_class());
|
|
}
|
|
|
|
const Class& DoubleClass() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->double_class());
|
|
}
|
|
|
|
const Class& Float32x4Class() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->float32x4_class());
|
|
}
|
|
|
|
const Class& Float64x2Class() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->float64x2_class());
|
|
}
|
|
|
|
const Class& Int32x4Class() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->int32x4_class());
|
|
}
|
|
|
|
const Class& ClosureClass() {
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
return Class::Handle(object_store->closure_class());
|
|
}
|
|
|
|
const Array& ArgumentsDescriptorBoxed(intptr_t type_args_len,
|
|
intptr_t num_arguments) {
|
|
return Array::ZoneHandle(
|
|
ArgumentsDescriptor::NewBoxed(type_args_len, num_arguments));
|
|
}
|
|
|
|
bool IsOriginalObject(const Object& object) {
|
|
if (object.IsICData()) {
|
|
return ICData::Cast(object).IsOriginal();
|
|
} else if (object.IsField()) {
|
|
return Field::Cast(object).IsOriginal();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const String& AllocateString(const char* buffer) {
|
|
return String::ZoneHandle(String::New(buffer, dart::Heap::kOld));
|
|
}
|
|
|
|
bool HasIntegerValue(const dart::Object& object, int64_t* value) {
|
|
if (object.IsInteger()) {
|
|
*value = Integer::Cast(object).Value();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int32_t CreateJitCookie() {
|
|
return static_cast<int32_t>(Thread::Current()->random()->NextUInt32());
|
|
}
|
|
|
|
word TypedDataElementSizeInBytes(classid_t cid) {
|
|
return dart::TypedData::ElementSizeInBytes(cid);
|
|
}
|
|
|
|
word TypedDataMaxNewSpaceElements(classid_t cid) {
|
|
return (target::Heap::kNewAllocatableSize - target::TypedData::HeaderSize()) /
|
|
TypedDataElementSizeInBytes(cid);
|
|
}
|
|
|
|
const Field& LookupMathRandomStateFieldOffset() {
|
|
const auto& math_lib = dart::Library::Handle(dart::Library::MathLibrary());
|
|
ASSERT(!math_lib.IsNull());
|
|
const auto& random_class = dart::Class::Handle(
|
|
math_lib.LookupClassAllowPrivate(dart::Symbols::_Random()));
|
|
ASSERT(!random_class.IsNull());
|
|
const auto& state_field = dart::Field::ZoneHandle(
|
|
random_class.LookupInstanceFieldAllowPrivate(dart::Symbols::_state()));
|
|
return state_field;
|
|
}
|
|
|
|
const Field& LookupConvertUtf8DecoderScanFlagsField() {
|
|
const auto& convert_lib =
|
|
dart::Library::Handle(dart::Library::ConvertLibrary());
|
|
ASSERT(!convert_lib.IsNull());
|
|
const auto& _utf8decoder_class = dart::Class::Handle(
|
|
convert_lib.LookupClassAllowPrivate(dart::Symbols::_Utf8Decoder()));
|
|
ASSERT(!_utf8decoder_class.IsNull());
|
|
const auto& scan_flags_field = dart::Field::ZoneHandle(
|
|
_utf8decoder_class.LookupInstanceFieldAllowPrivate(
|
|
dart::Symbols::_scanFlags()));
|
|
return scan_flags_field;
|
|
}
|
|
|
|
word LookupFieldOffsetInBytes(const Field& field) {
|
|
return field.TargetOffset();
|
|
}
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
uword SymbolsPredefinedAddress() {
|
|
return reinterpret_cast<uword>(dart::Symbols::PredefinedAddress());
|
|
}
|
|
#endif
|
|
|
|
const Code& StubCodeAllocateArray() {
|
|
return dart::StubCode::AllocateArray();
|
|
}
|
|
|
|
const Code& StubCodeSubtype2TestCache() {
|
|
return dart::StubCode::Subtype2TestCache();
|
|
}
|
|
|
|
const Code& StubCodeSubtype3TestCache() {
|
|
return dart::StubCode::Subtype3TestCache();
|
|
}
|
|
|
|
const Code& StubCodeSubtype4TestCache() {
|
|
return dart::StubCode::Subtype4TestCache();
|
|
}
|
|
|
|
const Code& StubCodeSubtype6TestCache() {
|
|
return dart::StubCode::Subtype6TestCache();
|
|
}
|
|
|
|
const Code& StubCodeSubtype7TestCache() {
|
|
return dart::StubCode::Subtype7TestCache();
|
|
}
|
|
|
|
#define DEFINE_ALIAS(name) \
|
|
const RuntimeEntry& k##name##RuntimeEntry(dart::k##name##RuntimeEntry);
|
|
RUNTIME_ENTRY_LIST(DEFINE_ALIAS)
|
|
#undef DEFINE_ALIAS
|
|
|
|
#define DEFINE_ALIAS(type, name, ...) \
|
|
const RuntimeEntry& k##name##RuntimeEntry(dart::k##name##RuntimeEntry);
|
|
LEAF_RUNTIME_ENTRY_LIST(DEFINE_ALIAS)
|
|
#undef DEFINE_ALIAS
|
|
|
|
void BailoutWithBranchOffsetError() {
|
|
Thread::Current()->long_jump_base()->Jump(1, Object::branch_offset_error());
|
|
}
|
|
|
|
word RuntimeEntry::OffsetFromThread() const {
|
|
return target::Thread::OffsetFromThread(runtime_entry_);
|
|
}
|
|
|
|
const char* RuntimeEntry::name() const {
|
|
return runtime_entry_->name();
|
|
}
|
|
|
|
bool RuntimeEntry::is_leaf() const {
|
|
return runtime_entry_->is_leaf();
|
|
}
|
|
|
|
intptr_t RuntimeEntry::argument_count() const {
|
|
return runtime_entry_->argument_count();
|
|
}
|
|
|
|
namespace target {
|
|
|
|
static word TranslateOffsetInWordsToHost(word offset) {
|
|
RELEASE_ASSERT((offset % kCompressedWordSize) == 0);
|
|
return (offset / kCompressedWordSize) * dart::kCompressedWordSize;
|
|
}
|
|
|
|
bool SizeFitsInSizeTag(uword instance_size) {
|
|
return dart::UntaggedObject::SizeTag::SizeFits(
|
|
TranslateOffsetInWordsToHost(instance_size));
|
|
}
|
|
|
|
uword MakeTagWordForNewSpaceObject(classid_t cid, uword instance_size) {
|
|
return dart::UntaggedObject::SizeTag::encode(
|
|
TranslateOffsetInWordsToHost(instance_size)) |
|
|
dart::UntaggedObject::ClassIdTag::encode(cid) |
|
|
dart::UntaggedObject::NewOrEvacuationCandidateBit::encode(true) |
|
|
dart::UntaggedObject::AlwaysSetBit::encode(true) |
|
|
dart::UntaggedObject::NotMarkedBit::encode(true) |
|
|
dart::UntaggedObject::ShallowImmutableBit::encode(
|
|
dart::Object::ShouldHaveShallowImmutabilityBitSet(cid)) |
|
|
dart::UntaggedObject::DeeplyImmutableBit::encode(
|
|
dart::Object::ShouldHaveDeeplyImmutabilityBitSet(cid));
|
|
}
|
|
|
|
const word UntaggedObject::kSizeTagMaxSizeTag =
|
|
dart::UntaggedObject::SizeTag::kMaxSizeTagInUnitsOfAlignment *
|
|
ObjectAlignment::kObjectAlignment;
|
|
|
|
const word UntaggedAbstractType::kTypeStateFinalizedInstantiated =
|
|
dart::UntaggedAbstractType::kFinalizedInstantiated;
|
|
const word UntaggedAbstractType::kTypeStateShift =
|
|
dart::UntaggedAbstractType::TypeStateBits::shift();
|
|
const word UntaggedAbstractType::kTypeStateBits =
|
|
dart::UntaggedAbstractType::TypeStateBits::bitsize();
|
|
const word UntaggedAbstractType::kNullabilityMask =
|
|
dart::UntaggedAbstractType::NullabilityBit::mask();
|
|
|
|
const word UntaggedType::kTypeClassIdShift =
|
|
dart::UntaggedType::TypeClassIdBits::shift();
|
|
|
|
const word UntaggedTypeParameter::kIsFunctionTypeParameterBit =
|
|
dart::UntaggedTypeParameter::IsFunctionTypeParameter::shift();
|
|
|
|
bool IsTypedDataClassId(intptr_t cid) {
|
|
return dart::IsTypedDataClassId(cid);
|
|
}
|
|
|
|
const word Class::kNoTypeArguments = dart::Class::kNoTypeArguments;
|
|
|
|
classid_t Class::GetId(const dart::Class& handle) {
|
|
return handle.id();
|
|
}
|
|
|
|
static word TranslateOffsetInWords(word offset) {
|
|
RELEASE_ASSERT((offset % dart::kWordSize) == 0);
|
|
return (offset / dart::kWordSize) * kWordSize;
|
|
}
|
|
|
|
static uword GetInstanceSizeImpl(const dart::Class& handle) {
|
|
switch (handle.id()) {
|
|
case kMintCid:
|
|
return Mint::InstanceSize();
|
|
case kDoubleCid:
|
|
return Double::InstanceSize();
|
|
case kInt32x4Cid:
|
|
return Int32x4::InstanceSize();
|
|
case kFloat32x4Cid:
|
|
return Float32x4::InstanceSize();
|
|
case kFloat64x2Cid:
|
|
return Float64x2::InstanceSize();
|
|
case kObjectCid:
|
|
return Object::InstanceSize();
|
|
case kInstanceCid:
|
|
return Instance::InstanceSize();
|
|
case kGrowableObjectArrayCid:
|
|
return GrowableObjectArray::InstanceSize();
|
|
case kTypedDataBaseCid:
|
|
return TypedDataBase::InstanceSize();
|
|
case kMapCid:
|
|
return Map::InstanceSize();
|
|
case kSetCid:
|
|
return Set::InstanceSize();
|
|
case kUnhandledExceptionCid:
|
|
return UnhandledException::InstanceSize();
|
|
case kWeakPropertyCid:
|
|
return WeakProperty::InstanceSize();
|
|
case kWeakReferenceCid:
|
|
return WeakReference::InstanceSize();
|
|
case kFinalizerCid:
|
|
return Finalizer::InstanceSize();
|
|
case kFinalizerEntryCid:
|
|
return FinalizerEntry::InstanceSize();
|
|
case kNativeFinalizerCid:
|
|
return NativeFinalizer::InstanceSize();
|
|
case kByteBufferCid:
|
|
case kByteDataViewCid:
|
|
case kUnmodifiableByteDataViewCid:
|
|
case kPointerCid:
|
|
case kDynamicLibraryCid:
|
|
#define HANDLE_CASE(clazz) case kFfi##clazz##Cid:
|
|
CLASS_LIST_FFI_TYPE_MARKER(HANDLE_CASE)
|
|
#undef HANDLE_CASE
|
|
#define HANDLE_CASE(clazz) \
|
|
case kTypedData##clazz##Cid: \
|
|
case kTypedData##clazz##ViewCid: \
|
|
case kExternalTypedData##clazz##Cid: \
|
|
case kUnmodifiableTypedData##clazz##ViewCid:
|
|
CLASS_LIST_TYPED_DATA(HANDLE_CASE)
|
|
#undef HANDLE_CASE
|
|
return handle.target_instance_size();
|
|
default:
|
|
if (handle.id() >= kNumPredefinedCids) {
|
|
return handle.target_instance_size();
|
|
}
|
|
}
|
|
FATAL("Unsupported class for size translation: %s (id=%" Pd
|
|
", kNumPredefinedCids=%" Pd ")\n",
|
|
handle.ToCString(), handle.id(), kNumPredefinedCids);
|
|
return -1;
|
|
}
|
|
|
|
uword Class::GetInstanceSize(const dart::Class& handle) {
|
|
return Utils::RoundUp(GetInstanceSizeImpl(handle),
|
|
ObjectAlignment::kObjectAlignment);
|
|
}
|
|
|
|
// Currently, we only have compressed pointers on the target if we also have
|
|
// compressed pointers on the host, since only 64-bit architectures can have
|
|
// compressed pointers and there is no 32-bit host/64-bit target combination.
|
|
// Thus, we cheat a little here and use the host information about compressed
|
|
// pointers for the target, instead of storing this information in the extracted
|
|
// offsets information.
|
|
bool Class::HasCompressedPointers(const dart::Class& handle) {
|
|
return handle.HasCompressedPointers();
|
|
}
|
|
|
|
intptr_t Class::NumTypeArguments(const dart::Class& klass) {
|
|
return klass.NumTypeArguments();
|
|
}
|
|
|
|
bool Class::HasTypeArgumentsField(const dart::Class& klass) {
|
|
return klass.host_type_arguments_field_offset() !=
|
|
dart::Class::kNoTypeArguments;
|
|
}
|
|
|
|
intptr_t Class::TypeArgumentsFieldOffset(const dart::Class& klass) {
|
|
return klass.target_type_arguments_field_offset();
|
|
}
|
|
|
|
bool Class::TraceAllocation(const dart::Class& klass) {
|
|
return klass.TraceAllocation(dart::IsolateGroup::Current());
|
|
}
|
|
|
|
word Instance::native_fields_array_offset() {
|
|
return TranslateOffsetInWords(dart::Instance::NativeFieldsOffset());
|
|
}
|
|
|
|
word Instance::DataOffsetFor(intptr_t cid) {
|
|
if (dart::IsExternalTypedDataClassId(cid)) {
|
|
// Elements start at offset 0 of the external data.
|
|
return 0;
|
|
}
|
|
if (dart::IsTypedDataClassId(cid)) {
|
|
return TypedData::payload_offset();
|
|
}
|
|
switch (cid) {
|
|
case kArrayCid:
|
|
case kImmutableArrayCid:
|
|
return Array::data_offset();
|
|
case kClosureCid:
|
|
return Closure::element_offset(0);
|
|
case kTypeArgumentsCid:
|
|
return TypeArguments::types_offset();
|
|
case kOneByteStringCid:
|
|
return OneByteString::data_offset();
|
|
case kTwoByteStringCid:
|
|
return TwoByteString::data_offset();
|
|
case kRecordCid:
|
|
return Record::field_offset(0);
|
|
default:
|
|
UNIMPLEMENTED();
|
|
return Array::data_offset();
|
|
}
|
|
}
|
|
|
|
word Instance::ElementSizeFor(intptr_t cid) {
|
|
if (dart::IsExternalTypedDataClassId(cid) || dart::IsTypedDataClassId(cid) ||
|
|
dart::IsTypedDataViewClassId(cid) ||
|
|
dart::IsUnmodifiableTypedDataViewClassId(cid)) {
|
|
return dart::TypedDataBase::ElementSizeInBytes(cid);
|
|
}
|
|
switch (cid) {
|
|
case kArrayCid:
|
|
case kImmutableArrayCid:
|
|
return kCompressedWordSize;
|
|
case kTypeArgumentsCid:
|
|
return kCompressedWordSize;
|
|
case kOneByteStringCid:
|
|
return dart::OneByteString::kBytesPerElement;
|
|
case kTwoByteStringCid:
|
|
return dart::TwoByteString::kBytesPerElement;
|
|
default:
|
|
UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
word ICData::CodeIndexFor(word num_args) {
|
|
return dart::ICData::CodeIndexFor(num_args);
|
|
}
|
|
|
|
word ICData::CountIndexFor(word num_args) {
|
|
return dart::ICData::CountIndexFor(num_args);
|
|
}
|
|
|
|
word ICData::TargetIndexFor(word num_args) {
|
|
return dart::ICData::TargetIndexFor(num_args);
|
|
}
|
|
|
|
word ICData::ExactnessIndexFor(word num_args) {
|
|
return dart::ICData::ExactnessIndexFor(num_args);
|
|
}
|
|
|
|
word ICData::TestEntryLengthFor(word num_args, bool exactness_check) {
|
|
return dart::ICData::TestEntryLengthFor(num_args, exactness_check);
|
|
}
|
|
|
|
word ICData::EntryPointIndexFor(word num_args) {
|
|
return dart::ICData::EntryPointIndexFor(num_args);
|
|
}
|
|
|
|
const word MegamorphicCache::kSpreadFactor =
|
|
dart::MegamorphicCache::kSpreadFactor;
|
|
|
|
// Currently we have two different axes for offset generation:
|
|
//
|
|
// * Target architecture
|
|
// * DART_PRECOMPILED_RUNTIME (i.e, AOT vs. JIT)
|
|
//
|
|
// TODO(dartbug.com/43646): Add DART_PRECOMPILER as another axis.
|
|
|
|
#define DEFINE_CONSTANT(Class, Name) const word Class::Name = Class##_##Name;
|
|
|
|
#define DEFINE_ARRAY_SIZEOF(clazz, name, ElementOffset) \
|
|
word clazz::name() { \
|
|
return 0; \
|
|
} \
|
|
word clazz::name(intptr_t length) { \
|
|
return RoundedAllocationSize(clazz::ElementOffset(length)); \
|
|
}
|
|
|
|
#define DEFINE_PAYLOAD_SIZEOF(clazz, name, header) \
|
|
word clazz::name() { \
|
|
return 0; \
|
|
} \
|
|
word clazz::name(word payload_size) { \
|
|
return RoundedAllocationSize(clazz::header() + payload_size); \
|
|
}
|
|
|
|
#define DEFINE_ENUM(Name, Elements)
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
|
|
#define DEFINE_FIELD(clazz, name) \
|
|
word clazz::name() { \
|
|
return clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_ARRAY(clazz, name) \
|
|
word clazz::name(intptr_t index) { \
|
|
return clazz##_elements_start_offset + index * clazz##_element_size; \
|
|
}
|
|
|
|
#define DEFINE_SIZEOF(clazz, name, what) \
|
|
word clazz::name() { \
|
|
return clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_RANGE(Class, Getter, Type, First, Last, Filter) \
|
|
word Class::Getter(Type index) { \
|
|
return Class##_##Getter[static_cast<intptr_t>(index) - \
|
|
static_cast<intptr_t>(First)]; \
|
|
}
|
|
|
|
JIT_OFFSETS_LIST(DEFINE_FIELD,
|
|
DEFINE_ARRAY,
|
|
DEFINE_SIZEOF,
|
|
DEFINE_ARRAY_SIZEOF,
|
|
DEFINE_PAYLOAD_SIZEOF,
|
|
DEFINE_RANGE,
|
|
DEFINE_CONSTANT,
|
|
DEFINE_ENUM)
|
|
|
|
COMMON_OFFSETS_LIST(DEFINE_FIELD,
|
|
DEFINE_ARRAY,
|
|
DEFINE_SIZEOF,
|
|
DEFINE_ARRAY_SIZEOF,
|
|
DEFINE_PAYLOAD_SIZEOF,
|
|
DEFINE_RANGE,
|
|
DEFINE_CONSTANT,
|
|
DEFINE_ENUM)
|
|
|
|
#else
|
|
|
|
#define DEFINE_JIT_FIELD(clazz, name) \
|
|
word clazz::name() { \
|
|
if (FLAG_precompiled_mode) { \
|
|
FATAL("Use of JIT-only field %s in precompiled mode", \
|
|
#clazz "::" #name); \
|
|
} \
|
|
return clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_JIT_ARRAY(clazz, name) \
|
|
word clazz::name(intptr_t index) { \
|
|
if (FLAG_precompiled_mode) { \
|
|
FATAL("Use of JIT-only array %s in precompiled mode", \
|
|
#clazz "::" #name); \
|
|
} \
|
|
return clazz##_elements_start_offset + index * clazz##_element_size; \
|
|
}
|
|
|
|
#define DEFINE_JIT_SIZEOF(clazz, name, what) \
|
|
word clazz::name() { \
|
|
if (FLAG_precompiled_mode) { \
|
|
FATAL("Use of JIT-only sizeof %s in precompiled mode", \
|
|
#clazz "::" #name); \
|
|
} \
|
|
return clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_JIT_RANGE(Class, Getter, Type, First, Last, Filter) \
|
|
word Class::Getter(Type index) { \
|
|
if (FLAG_precompiled_mode) { \
|
|
FATAL("Use of JIT-only range %s in precompiled mode", \
|
|
#Class "::" #Getter); \
|
|
} \
|
|
return Class##_##Getter[static_cast<intptr_t>(index) - \
|
|
static_cast<intptr_t>(First)]; \
|
|
}
|
|
|
|
JIT_OFFSETS_LIST(DEFINE_JIT_FIELD,
|
|
DEFINE_JIT_ARRAY,
|
|
DEFINE_JIT_SIZEOF,
|
|
DEFINE_ARRAY_SIZEOF,
|
|
DEFINE_PAYLOAD_SIZEOF,
|
|
DEFINE_JIT_RANGE,
|
|
DEFINE_CONSTANT,
|
|
DEFINE_ENUM)
|
|
|
|
#undef DEFINE_JIT_FIELD
|
|
#undef DEFINE_JIT_ARRAY
|
|
#undef DEFINE_JIT_SIZEOF
|
|
#undef DEFINE_JIT_RANGE
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
// The following could check FLAG_precompiled_mode for more safety, but that
|
|
// causes problems for defining things like native Slots, where the definition
|
|
// cannot be based on a runtime flag. Instead, we limit the visibility of these
|
|
// definitions using DART_PRECOMPILER.
|
|
|
|
#define DEFINE_AOT_FIELD(clazz, name) \
|
|
word clazz::name() { \
|
|
return AOT_##clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_AOT_ARRAY(clazz, name) \
|
|
word clazz::name(intptr_t index) { \
|
|
return AOT_##clazz##_elements_start_offset + \
|
|
index * AOT_##clazz##_element_size; \
|
|
}
|
|
|
|
#define DEFINE_AOT_SIZEOF(clazz, name, what) \
|
|
word clazz::name() { \
|
|
return AOT_##clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_AOT_RANGE(Class, Getter, Type, First, Last, Filter) \
|
|
word Class::Getter(Type index) { \
|
|
return AOT_##Class##_##Getter[static_cast<intptr_t>(index) - \
|
|
static_cast<intptr_t>(First)]; \
|
|
}
|
|
#else
|
|
#define DEFINE_AOT_FIELD(clazz, name) \
|
|
word clazz::name() { \
|
|
FATAL("Use of AOT-only field %s outside of the precompiler", \
|
|
#clazz "::" #name); \
|
|
}
|
|
|
|
#define DEFINE_AOT_ARRAY(clazz, name) \
|
|
word clazz::name(intptr_t index) { \
|
|
FATAL("Use of AOT-only array %s outside of the precompiler", \
|
|
#clazz "::" #name); \
|
|
}
|
|
|
|
#define DEFINE_AOT_SIZEOF(clazz, name, what) \
|
|
word clazz::name() { \
|
|
FATAL("Use of AOT-only sizeof %s outside of the precompiler", \
|
|
#clazz "::" #name); \
|
|
}
|
|
|
|
#define DEFINE_AOT_RANGE(Class, Getter, Type, First, Last, Filter) \
|
|
word Class::Getter(Type index) { \
|
|
FATAL("Use of AOT-only range %s outside of the precompiler", \
|
|
#Class "::" #Getter); \
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
AOT_OFFSETS_LIST(DEFINE_AOT_FIELD,
|
|
DEFINE_AOT_ARRAY,
|
|
DEFINE_AOT_SIZEOF,
|
|
DEFINE_ARRAY_SIZEOF,
|
|
DEFINE_PAYLOAD_SIZEOF,
|
|
DEFINE_AOT_RANGE,
|
|
DEFINE_CONSTANT,
|
|
DEFINE_ENUM)
|
|
|
|
#undef DEFINE_AOT_FIELD
|
|
#undef DEFINE_AOT_ARRAY
|
|
#undef DEFINE_AOT_SIZEOF
|
|
#undef DEFINE_AOT_RANGE
|
|
|
|
#define DEFINE_FIELD(clazz, name) \
|
|
word clazz::name() { \
|
|
return FLAG_precompiled_mode ? AOT_##clazz##_##name : clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_ARRAY(clazz, name) \
|
|
word clazz::name(intptr_t index) { \
|
|
if (FLAG_precompiled_mode) { \
|
|
return AOT_##clazz##_elements_start_offset + \
|
|
index * AOT_##clazz##_element_size; \
|
|
} else { \
|
|
return clazz##_elements_start_offset + index * clazz##_element_size; \
|
|
} \
|
|
}
|
|
|
|
#define DEFINE_SIZEOF(clazz, name, what) \
|
|
word clazz::name() { \
|
|
return FLAG_precompiled_mode ? AOT_##clazz##_##name : clazz##_##name; \
|
|
}
|
|
|
|
#define DEFINE_RANGE(Class, Getter, Type, First, Last, Filter) \
|
|
word Class::Getter(Type index) { \
|
|
if (FLAG_precompiled_mode) { \
|
|
return AOT_##Class##_##Getter[static_cast<intptr_t>(index) - \
|
|
static_cast<intptr_t>(First)]; \
|
|
} else { \
|
|
return Class##_##Getter[static_cast<intptr_t>(index) - \
|
|
static_cast<intptr_t>(First)]; \
|
|
} \
|
|
}
|
|
|
|
COMMON_OFFSETS_LIST(DEFINE_FIELD,
|
|
DEFINE_ARRAY,
|
|
DEFINE_SIZEOF,
|
|
DEFINE_ARRAY_SIZEOF,
|
|
DEFINE_PAYLOAD_SIZEOF,
|
|
DEFINE_RANGE,
|
|
DEFINE_CONSTANT,
|
|
DEFINE_ENUM)
|
|
|
|
#endif
|
|
|
|
#undef DEFINE_FIELD
|
|
#undef DEFINE_ARRAY
|
|
#undef DEFINE_SIZEOF
|
|
#undef DEFINE_RANGE
|
|
#undef DEFINE_PAYLOAD_SIZEOF
|
|
#undef DEFINE_CONSTANT
|
|
#undef DEFINE_ENUM
|
|
|
|
const word StoreBufferBlock::kSize = dart::StoreBufferBlock::kSize;
|
|
|
|
const word MarkingStackBlock::kSize = dart::MarkingStackBlock::kSize;
|
|
|
|
// For InstructionsSections and Instructions, we define these by hand, because
|
|
// they depend on flags or #defines.
|
|
|
|
// Used for InstructionsSection and Instructions methods, since we don't
|
|
// serialize Instructions objects in bare instructions mode, just payloads.
|
|
DART_FORCE_INLINE static bool BareInstructionsPayloads() {
|
|
return FLAG_precompiled_mode;
|
|
}
|
|
|
|
word Instructions::HeaderSize() {
|
|
return BareInstructionsPayloads()
|
|
? 0
|
|
: Utils::RoundUp(UnalignedHeaderSize(), kNonBarePayloadAlignment);
|
|
}
|
|
|
|
word Instructions::InstanceSize() {
|
|
return 0;
|
|
}
|
|
|
|
word Instructions::InstanceSize(word payload_size) {
|
|
const intptr_t alignment = BareInstructionsPayloads()
|
|
? kBarePayloadAlignment
|
|
: ObjectAlignment::kObjectAlignment;
|
|
return Utils::RoundUp(Instructions::HeaderSize() + payload_size, alignment);
|
|
}
|
|
|
|
word Thread::stack_overflow_shared_stub_entry_point_offset(bool fpu_regs) {
|
|
return fpu_regs ? stack_overflow_shared_with_fpu_regs_entry_point_offset()
|
|
: stack_overflow_shared_without_fpu_regs_entry_point_offset();
|
|
}
|
|
|
|
uword Thread::native_safepoint_state_unacquired() {
|
|
return dart::Thread::native_safepoint_state_unacquired();
|
|
}
|
|
|
|
uword Thread::native_safepoint_state_acquired() {
|
|
return dart::Thread::native_safepoint_state_acquired();
|
|
}
|
|
|
|
uword Thread::generated_execution_state() {
|
|
return dart::Thread::ExecutionState::kThreadInGenerated;
|
|
}
|
|
|
|
uword Thread::native_execution_state() {
|
|
return dart::Thread::ExecutionState::kThreadInNative;
|
|
}
|
|
|
|
uword Thread::vm_execution_state() {
|
|
return dart::Thread::ExecutionState::kThreadInVM;
|
|
}
|
|
|
|
uword Thread::vm_tag_dart_id() {
|
|
return dart::VMTag::kDartTagId;
|
|
}
|
|
|
|
uword Thread::exit_through_runtime_call() {
|
|
return dart::Thread::kExitThroughRuntimeCall;
|
|
}
|
|
|
|
uword Thread::exit_through_ffi() {
|
|
return dart::Thread::kExitThroughFfi;
|
|
}
|
|
|
|
word Thread::OffsetFromThread(const dart::Object& object) {
|
|
auto host_offset = dart::Thread::OffsetFromThread(object);
|
|
return object_null_offset() +
|
|
TranslateOffsetInWords(host_offset -
|
|
dart::Thread::object_null_offset());
|
|
}
|
|
|
|
intptr_t Thread::OffsetFromThread(const dart::RuntimeEntry* runtime_entry) {
|
|
auto host_offset = dart::Thread::OffsetFromThread(runtime_entry);
|
|
if (runtime_entry->is_leaf()) {
|
|
return DeoptimizeCopyFrame_entry_point_offset() +
|
|
TranslateOffsetInWords(
|
|
host_offset -
|
|
dart::Thread::DeoptimizeCopyFrame_entry_point_offset());
|
|
} else {
|
|
return AllocateArray_entry_point_offset() +
|
|
TranslateOffsetInWords(
|
|
host_offset - dart::Thread::AllocateArray_entry_point_offset());
|
|
}
|
|
}
|
|
|
|
bool CanLoadFromThread(const dart::Object& object,
|
|
intptr_t* offset /* = nullptr */) {
|
|
if (dart::Thread::CanLoadFromThread(object)) {
|
|
if (offset != nullptr) {
|
|
*offset = Thread::OffsetFromThread(object);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static_assert(
|
|
kSmiBits <= dart::kSmiBits,
|
|
"Expected that size of Smi on HOST is at least as large as on target.");
|
|
|
|
bool IsSmi(const dart::Object& a) {
|
|
return a.IsSmi() && IsSmi(dart::Smi::Cast(a).Value());
|
|
}
|
|
|
|
word ToRawSmi(const dart::Object& a) {
|
|
RELEASE_ASSERT(IsSmi(a));
|
|
return static_cast<compressed_word>(static_cast<intptr_t>(a.ptr()));
|
|
}
|
|
|
|
word ToRawSmi(intptr_t value) {
|
|
return dart::Smi::RawValue(value);
|
|
}
|
|
|
|
word SmiValue(const dart::Object& a) {
|
|
RELEASE_ASSERT(IsSmi(a));
|
|
return static_cast<word>(dart::Smi::Cast(a).Value());
|
|
}
|
|
|
|
bool IsDouble(const dart::Object& a) {
|
|
return a.IsDouble();
|
|
}
|
|
|
|
double DoubleValue(const dart::Object& a) {
|
|
RELEASE_ASSERT(IsDouble(a));
|
|
return dart::Double::Cast(a).value();
|
|
}
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
uword Code::EntryPointOf(const dart::Code& code) {
|
|
static_assert(kHostWordSize == kWordSize,
|
|
"Can't embed raw pointers to runtime objects when host and "
|
|
"target word sizes are different");
|
|
return code.EntryPoint();
|
|
}
|
|
|
|
bool CanEmbedAsRawPointerInGeneratedCode(const dart::Object& obj) {
|
|
return obj.IsSmi() || obj.IsNull() || obj.IsCode();
|
|
}
|
|
|
|
word ToRawPointer(const dart::Object& a) {
|
|
static_assert(kHostWordSize == kWordSize,
|
|
"Can't embed raw pointers to runtime objects when host and "
|
|
"target word sizes are different");
|
|
return static_cast<word>(a.ptr());
|
|
}
|
|
#endif // defined(TARGET_ARCH_IA32)
|
|
|
|
word RegExp::function_offset(classid_t cid, bool sticky) {
|
|
#if !defined(DART_COMPRESSED_POINTERS)
|
|
return TranslateOffsetInWords(dart::RegExp::function_offset(cid, sticky));
|
|
#else
|
|
// TODO(rmacnak): TranslateOffsetInWords doesn't account for, say, header
|
|
// being 1 word and slots being half words.
|
|
return dart::RegExp::function_offset(cid, sticky);
|
|
#endif
|
|
}
|
|
|
|
const word Symbols::kNumberOfOneCharCodeSymbols =
|
|
dart::Symbols::kNumberOfOneCharCodeSymbols;
|
|
const word Symbols::kNullCharCodeSymbolOffset =
|
|
dart::Symbols::kNullCharCodeSymbolOffset;
|
|
|
|
const word String::kHashBits = dart::String::kHashBits;
|
|
|
|
const uint8_t Nullability::kNullable =
|
|
static_cast<uint8_t>(dart::Nullability::kNullable);
|
|
const uint8_t Nullability::kNonNullable =
|
|
static_cast<uint8_t>(dart::Nullability::kNonNullable);
|
|
|
|
bool Heap::IsAllocatableInNewSpace(intptr_t instance_size) {
|
|
return dart::Heap::IsAllocatableInNewSpace(instance_size);
|
|
}
|
|
|
|
word Field::OffsetOf(const dart::Field& field) {
|
|
return field.TargetOffset();
|
|
}
|
|
|
|
word FieldTable::OffsetOf(const dart::Field& field) {
|
|
return TranslateOffsetInWords(
|
|
dart::FieldTable::FieldOffsetFor(field.field_id()));
|
|
}
|
|
|
|
word FreeListElement::FakeInstance::InstanceSize() {
|
|
return 0;
|
|
}
|
|
|
|
word ForwardingCorpse::FakeInstance::InstanceSize() {
|
|
return 0;
|
|
}
|
|
|
|
word Instance::NextFieldOffset() {
|
|
return TranslateOffsetInWords(dart::Instance::NextFieldOffset());
|
|
}
|
|
|
|
intptr_t Array::index_at_offset(intptr_t offset_in_bytes) {
|
|
return dart::Array::index_at_offset(
|
|
TranslateOffsetInWordsToHost(offset_in_bytes));
|
|
}
|
|
|
|
intptr_t Closure::element_index_at_offset(intptr_t offset_in_bytes) {
|
|
// Note: cannot delegate to dart::Closure::element_index_at_offset as
|
|
// Closure layout is different between AOT and precompiler.
|
|
const intptr_t index =
|
|
(offset_in_bytes - Closure::element_offset(0)) / kCompressedWordSize;
|
|
ASSERT(index >= 0);
|
|
return index;
|
|
}
|
|
|
|
intptr_t Record::field_index_at_offset(intptr_t offset_in_bytes) {
|
|
return dart::Record::field_index_at_offset(
|
|
TranslateOffsetInWordsToHost(offset_in_bytes));
|
|
}
|
|
|
|
word String::InstanceSize(word payload_size) {
|
|
return RoundedAllocationSize(String::InstanceSize() + payload_size);
|
|
}
|
|
|
|
word Integer::NextFieldOffset() {
|
|
return TranslateOffsetInWords(dart::Integer::NextFieldOffset());
|
|
}
|
|
|
|
word Smi::InstanceSize() {
|
|
return 0;
|
|
}
|
|
|
|
word Number::NextFieldOffset() {
|
|
return TranslateOffsetInWords(dart::Number::NextFieldOffset());
|
|
}
|
|
|
|
void UnboxFieldIfSupported(const dart::Field& field,
|
|
const dart::AbstractType& type) {
|
|
if (field.is_static() || field.is_late()) {
|
|
return;
|
|
}
|
|
|
|
if (type.IsNullable()) {
|
|
return;
|
|
}
|
|
|
|
classid_t cid = kIllegalCid;
|
|
if (type.IsDoubleType()) {
|
|
cid = kDoubleCid;
|
|
} else if (type.IsFloat32x4Type()) {
|
|
if (FlowGraphCompiler::SupportsUnboxedSimd128()) {
|
|
cid = kFloat32x4Cid;
|
|
}
|
|
} else if (type.IsFloat64x2Type()) {
|
|
if (FlowGraphCompiler::SupportsUnboxedSimd128()) {
|
|
cid = kFloat64x2Cid;
|
|
}
|
|
}
|
|
|
|
if (cid != kIllegalCid) {
|
|
field.set_guarded_cid(cid);
|
|
field.set_is_nullable(false);
|
|
field.set_is_unboxed(true);
|
|
field.set_guarded_list_length(dart::Field::kNoFixedLength);
|
|
field.set_guarded_list_length_in_object_offset(
|
|
dart::Field::kUnknownLengthOffset);
|
|
}
|
|
}
|
|
|
|
} // namespace target
|
|
} // namespace compiler
|
|
} // namespace dart
|
|
|
|
#else
|
|
|
|
namespace dart {
|
|
namespace compiler {
|
|
namespace target {
|
|
|
|
const word Array::kMaxElements = Array_kMaxElements;
|
|
const word Context::kMaxElements = Context_kMaxElements;
|
|
const word Record::kMaxElements = Record_kMaxElements;
|
|
|
|
const word RecordShape::kNumFieldsMask = RecordShape_kNumFieldsMask;
|
|
const word RecordShape::kMaxNumFields = RecordShape_kMaxNumFields;
|
|
const word RecordShape::kFieldNamesIndexShift =
|
|
RecordShape_kFieldNamesIndexShift;
|
|
const word RecordShape::kFieldNamesIndexMask = RecordShape_kFieldNamesIndexMask;
|
|
const word RecordShape::kMaxFieldNamesIndex = RecordShape_kMaxFieldNamesIndex;
|
|
|
|
} // namespace target
|
|
} // namespace compiler
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|