From 4fc29848a8e1a858037240889eb991ffe04b58a6 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 14 Dec 2018 16:03:04 +0000 Subject: [PATCH] [VM] Bare instructions - Part 4: Add --use-bare-instructions flag to AOT compiler & runtime This is the final CL which adds a new --use-bare-instructions flag to the VM. If this flag is set during AOT compilation, we will: * Build one global object pool (abbr: GOP) which all code objects share. This gop will be stored in the object store. The PP register is populated in the enter dart stub and it is restored when returning from native calls. * Gets rid of the CODE_REG/PP slots from the dart frames. Instead the compiled code uses the global object pool, which is always in PP. * Starts emitting pc-relative calls for calls between two dart functions or when invoking a stub. Limitation: We only emit pc-relative calls between two code objects in the same isolate (this is because the image writer is writing instruction objects for vm-isolate/main-isolate seperately) * We do compile-time relocation of those static calls after the precompiler has finished its work, but before writing the snapshot. This patches all the instruction objects with pc-relative calls to have the right .text distance. * We emit a sorted list of code objects in ObjectStore::reverse_code_table, which will be used by the AOT runtime to go back from PC to Code objects (where all metadata, e.g. stack maps, catch entry moves, pc descriptors are available). Issue https://github.com/dart-lang/sdk/issues/33274 Change-Id: I6c5dd2b1571e3a889b27e804a24c2986c71e03b6 Reviewed-on: https://dart-review.googlesource.com/c/85769 Commit-Queue: Martin Kustermann Reviewed-by: Ryan Macnak Reviewed-by: Vyacheslav Egorov --- assembler.cc | 53 ++++++++++++++++++++++++++++++++++++++++++++++ assembler.h | 38 +++++++++++++++++++++++++++++++++ assembler_arm.cc | 25 ++++++++++++++++------ assembler_arm64.cc | 35 ++++++++++++++++++------------ assembler_x64.cc | 29 +++++++++++++++---------- disassembler.cc | 31 ++++++++++++++++++--------- 6 files changed, 169 insertions(+), 42 deletions(-) diff --git a/assembler.cc b/assembler.cc index df4e886fcfbe..f945e3ff508c 100644 --- a/assembler.cc +++ b/assembler.cc @@ -250,6 +250,46 @@ intptr_t ObjIndexPair::Hashcode(Key key) { // Unlikely. return key.obj_->GetClassId(); } +void ObjectPoolWrapper::Reset() { + // Null out the handles we've accumulated. + for (intptr_t i = 0; i < object_pool_.length(); ++i) { + if (object_pool_[i].type() == ObjectPool::kTaggedObject) { + *const_cast(object_pool_[i].obj_) = Object::null(); + *const_cast(object_pool_[i].equivalence_) = Object::null(); + } + } + + object_pool_.Clear(); + object_pool_index_table_.Clear(); +} + +void ObjectPoolWrapper::InitializeFrom(const ObjectPool& other) { + ASSERT(object_pool_.length() == 0); + + for (intptr_t i = 0; i < other.Length(); i++) { + auto type = other.TypeAt(i); + auto patchable = other.PatchableAt(i); + switch (type) { + case ObjectPool::kTaggedObject: { + ObjectPoolWrapperEntry entry(&Object::ZoneHandle(other.ObjectAt(i)), + patchable); + AddObject(entry); + break; + } + case ObjectPool::kImmediate: + case ObjectPool::kNativeFunction: + case ObjectPool::kNativeFunctionWrapper: { + ObjectPoolWrapperEntry entry(other.RawValueAt(i), type, patchable); + AddObject(entry); + break; + } + default: + UNREACHABLE(); + } + } + + ASSERT(CurrentLength() == other.Length()); +} intptr_t ObjectPoolWrapper::AddObject(const Object& obj, ObjectPool::Patchability patchable) { @@ -267,6 +307,19 @@ intptr_t ObjectPoolWrapper::AddObject(ObjectPoolWrapperEntry entry) { (entry.obj_->IsNotTemporaryScopedHandle() && (entry.equivalence_ == NULL || entry.equivalence_->IsNotTemporaryScopedHandle()))); + + if (entry.type() == ObjectPool::kTaggedObject) { + // If the owner of the object pool wrapper specified a specific zone we + // shoulld use we'll do so. + if (zone_ != NULL) { + entry.obj_ = &Object::ZoneHandle(zone_, entry.obj_->raw()); + if (entry.equivalence_ != NULL) { + entry.equivalence_ = + &Object::ZoneHandle(zone_, entry.equivalence_->raw()); + } + } + } + object_pool_.Add(entry); if (entry.patchable() == ObjectPool::kNotPatchable) { // The object isn't patchable. Record the index for fast lookup. diff --git a/assembler.h b/assembler.h index 7b25cf169a3b..8d600d5279cc 100644 --- a/assembler.h +++ b/assembler.h @@ -370,10 +370,40 @@ class ObjIndexPair { class ObjectPoolWrapper : public ValueObject { public: + ObjectPoolWrapper() : zone_(nullptr) {} + ~ObjectPoolWrapper() { + if (zone_ != nullptr) { + Reset(); + zone_ = nullptr; + } + } + + // Clears all existing entries in this object pool builder. + // + // Note: Any code which has been compiled via this builder might use offsets + // into the pool which are not correct anymore. + void Reset(); + + // Initializes this object pool builder from [other]. + // + // All entries from [other] will be populated, including their + // kind/patchability bits. + void InitializeFrom(const ObjectPool& other); + + // Initialize this object pool builder with a [zone]. + // + // Any objects added later on will be referenced using handles from [zone]. + void InitializeWithZone(Zone* zone) { + ASSERT(object_pool_.length() == 0); + ASSERT(zone_ == nullptr && zone != nullptr); + zone_ = zone; + } + intptr_t AddObject( const Object& obj, ObjectPool::Patchability patchable = ObjectPool::kNotPatchable); intptr_t AddImmediate(uword imm); + intptr_t FindObject( const Object& obj, ObjectPool::Patchability patchable = ObjectPool::kNotPatchable); @@ -386,6 +416,9 @@ class ObjectPoolWrapper : public ValueObject { RawObjectPool* MakeObjectPool(); + intptr_t CurrentLength() { return object_pool_.length(); } + ObjectPoolWrapperEntry& EntryAt(intptr_t i) { return object_pool_[i]; } + private: intptr_t AddObject(ObjectPoolWrapperEntry entry); intptr_t FindObject(ObjectPoolWrapperEntry entry); @@ -395,6 +428,11 @@ class ObjectPoolWrapper : public ValueObject { // Hashmap for fast lookup in object pool. DirectChainedHashMap object_pool_index_table_; + + // The zone used for allocating the handles we keep in the map and array (or + // NULL, in which case allocations happen using the zone active at the point + // of insertion). + Zone* zone_; }; enum RestorePP { kRestoreCallerPP, kKeepCalleePP }; diff --git a/assembler_arm.cc b/assembler_arm.cc index 188955423ea1..53de88ba8d6f 100644 --- a/assembler_arm.cc +++ b/assembler_arm.cc @@ -24,6 +24,7 @@ namespace dart { DECLARE_FLAG(bool, check_code_pointer); DECLARE_FLAG(bool, inline_alloc); +DECLARE_FLAG(bool, precompiled_mode); uint32_t Address::encoding3() const { if (kind_ == Immediate) { @@ -3162,10 +3163,16 @@ void Assembler::EnterDartFrame(intptr_t frame_size) { COMPILE_ASSERT(PP < CODE_REG); COMPILE_ASSERT(CODE_REG < FP); COMPILE_ASSERT(FP < LR); - EnterFrame((1 << PP) | (1 << CODE_REG) | (1 << FP) | (1 << LR), 0); - // Setup pool pointer for this dart function. - LoadPoolPointer(); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + EnterFrame((1 << PP) | (1 << CODE_REG) | (1 << FP) | (1 << LR), 0); + + // Setup pool pointer for this dart function. + LoadPoolPointer(); + } else { + EnterFrame((1 << FP) | (1 << LR), 0); + } + set_constant_pool_allowed(true); // Reserve space for locals. AddImmediate(SP, -frame_size); @@ -3186,8 +3193,10 @@ void Assembler::EnterOsrFrame(intptr_t extra_size) { } void Assembler::LeaveDartFrame() { - ldr(PP, - Address(FP, compiler_frame_layout.saved_caller_pp_from_fp * kWordSize)); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + ldr(PP, + Address(FP, compiler_frame_layout.saved_caller_pp_from_fp * kWordSize)); + } set_constant_pool_allowed(false); // This will implicitly drop saved PP, PC marker due to restoring SP from FP @@ -3196,8 +3205,10 @@ void Assembler::LeaveDartFrame() { } void Assembler::LeaveDartFrameAndReturn() { - ldr(PP, - Address(FP, compiler_frame_layout.saved_caller_pp_from_fp * kWordSize)); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + ldr(PP, + Address(FP, compiler_frame_layout.saved_caller_pp_from_fp * kWordSize)); + } set_constant_pool_allowed(false); // This will implicitly drop saved PP, PC marker due to restoring SP from FP diff --git a/assembler_arm64.cc b/assembler_arm64.cc index 1f44fdd976ff..7ca178ca58d7 100644 --- a/assembler_arm64.cc +++ b/assembler_arm64.cc @@ -18,6 +18,7 @@ namespace dart { DECLARE_FLAG(bool, check_code_pointer); DECLARE_FLAG(bool, inline_alloc); +DECLARE_FLAG(bool, precompiled_mode); DEFINE_FLAG(bool, use_far_branches, false, "Always use far branches"); @@ -1250,15 +1251,18 @@ void Assembler::EnterDartFrame(intptr_t frame_size, Register new_pp) { ASSERT(!constant_pool_allowed()); // Setup the frame. EnterFrame(0); - TagAndPushPPAndPcMarker(); // Save PP and PC marker. - // Load the pool pointer. - if (new_pp == kNoRegister) { - LoadPoolPointer(); - } else { - mov(PP, new_pp); - set_constant_pool_allowed(true); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + TagAndPushPPAndPcMarker(); // Save PP and PC marker. + + // Load the pool pointer. + if (new_pp == kNoRegister) { + LoadPoolPointer(); + } else { + mov(PP, new_pp); + } } + set_constant_pool_allowed(true); // Reserve space. if (frame_size > 0) { @@ -1283,13 +1287,15 @@ void Assembler::EnterOsrFrame(intptr_t extra_size, Register new_pp) { } void Assembler::LeaveDartFrame(RestorePP restore_pp) { - if (restore_pp == kRestoreCallerPP) { - set_constant_pool_allowed(false); - // Restore and untag PP. - LoadFromOffset(PP, FP, - compiler_frame_layout.saved_caller_pp_from_fp * kWordSize); - sub(PP, PP, Operand(kHeapObjectTag)); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + if (restore_pp == kRestoreCallerPP) { + // Restore and untag PP. + LoadFromOffset(PP, FP, + compiler_frame_layout.saved_caller_pp_from_fp * kWordSize); + sub(PP, PP, Operand(kHeapObjectTag)); + } } + set_constant_pool_allowed(false); LeaveFrame(); } @@ -1325,7 +1331,8 @@ void Assembler::LeaveCallRuntimeFrame() { const intptr_t kPushedRegistersSize = kDartVolatileCpuRegCount * kWordSize + kDartVolatileFpuRegCount * kWordSize + - 2 * kWordSize; // PP and pc marker from EnterStubFrame. + (compiler_frame_layout.dart_fixed_frame_size - 2) * + kWordSize; // From EnterStubFrame (excluding PC / FP) AddImmediate(SP, FP, -kPushedRegistersSize); for (int i = kDartLastVolatileCpuReg; i >= kDartFirstVolatileCpuReg; i--) { const Register reg = static_cast(i); diff --git a/assembler_x64.cc b/assembler_x64.cc index f070f4437ad0..1d6be0161919 100644 --- a/assembler_x64.cc +++ b/assembler_x64.cc @@ -21,6 +21,7 @@ namespace dart { DECLARE_FLAG(bool, check_code_pointer); DECLARE_FLAG(bool, inline_alloc); +DECLARE_FLAG(bool, precompiled_mode); Assembler::Assembler(ObjectPoolWrapper* object_pool_wrapper, bool use_far_branches) @@ -1531,7 +1532,9 @@ void Assembler::LeaveCallRuntimeFrame() { const intptr_t kPushedRegistersSize = kPushedCpuRegistersCount * kWordSize + kPushedXmmRegistersCount * kFpuRegisterSize + - 2 * kWordSize; // PP, pc marker from EnterStubFrame + (compiler_frame_layout.dart_fixed_frame_size - 2) * + kWordSize; // From EnterStubFrame (excluding PC / FP) + leaq(RSP, Address(RBP, -kPushedRegistersSize)); // TODO(vegorov): avoid saving FpuTMP, it is used only as scratch. @@ -1568,12 +1571,14 @@ void Assembler::LoadPoolPointer(Register pp) { void Assembler::EnterDartFrame(intptr_t frame_size, Register new_pp) { ASSERT(!constant_pool_allowed()); EnterFrame(0); - pushq(CODE_REG); - pushq(PP); - if (new_pp == kNoRegister) { - LoadPoolPointer(PP); - } else { - movq(PP, new_pp); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + pushq(CODE_REG); + pushq(PP); + if (new_pp == kNoRegister) { + LoadPoolPointer(PP); + } else { + movq(PP, new_pp); + } } set_constant_pool_allowed(true); if (frame_size != 0) { @@ -1583,11 +1588,13 @@ void Assembler::EnterDartFrame(intptr_t frame_size, Register new_pp) { void Assembler::LeaveDartFrame(RestorePP restore_pp) { // Restore caller's PP register that was pushed in EnterDartFrame. - if (restore_pp == kRestoreCallerPP) { - movq(PP, Address(RBP, (compiler_frame_layout.saved_caller_pp_from_fp * - kWordSize))); - set_constant_pool_allowed(false); + if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) { + if (restore_pp == kRestoreCallerPP) { + movq(PP, Address(RBP, (compiler_frame_layout.saved_caller_pp_from_fp * + kWordSize))); + } } + set_constant_pool_allowed(false); LeaveFrame(); } diff --git a/disassembler.cc b/disassembler.cc index 11138e60b7ec..8cf7adcb88c4 100644 --- a/disassembler.cc +++ b/disassembler.cc @@ -329,19 +329,23 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname, THR_Print("Static call target functions {\n"); const auto& table = Array::Handle(zone, code.static_calls_target_table()); auto& cls = Class::Handle(zone); - auto& kind_and_offset = Smi::Handle(zone); + auto& kind_type_and_offset = Smi::Handle(zone); auto& function = Function::Handle(zone); auto& code = Code::Handle(zone); if (!table.IsNull()) { StaticCallsTable static_calls(table); for (auto& call : static_calls) { - kind_and_offset = call.Get(); + kind_type_and_offset = call.Get(); function = call.Get(); code = call.Get(); - auto kind = Code::KindField::decode(kind_and_offset.Value()); - auto offset = Code::OffsetField::decode(kind_and_offset.Value()); + auto kind = Code::KindField::decode(kind_type_and_offset.Value()); + auto offset = Code::OffsetField::decode(kind_type_and_offset.Value()); + auto entry_point = + Code::EntryPointField::decode(kind_type_and_offset.Value()); + const char* s_entry_point = + entry_point == Code::kUncheckedEntry ? " " : ""; const char* skind = nullptr; switch (kind) { case Code::kPcRelativeCall: @@ -359,15 +363,17 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname, if (function.IsNull()) { cls ^= code.owner(); if (cls.IsNull()) { - THR_Print(" 0x%" Px ": %s, %p (%s)\n", start + offset, - code.QualifiedName(), code.raw(), skind); + THR_Print(" 0x%" Px ": %s, %p (%s)%s\n", start + offset, + code.QualifiedName(), code.raw(), skind, s_entry_point); } else { - THR_Print(" 0x%" Px ": allocation stub for %s, %p (%s)\n", - start + offset, cls.ToCString(), code.raw(), skind); + THR_Print(" 0x%" Px ": allocation stub for %s, %p (%s)%s\n", + start + offset, cls.ToCString(), code.raw(), skind, + s_entry_point); } } else { - THR_Print(" 0x%" Px ": %s, %p (%s)\n", start + offset, - function.ToFullyQualifiedCString(), code.raw(), skind); + THR_Print(" 0x%" Px ": %s, %p (%s)%s\n", start + offset, + function.ToFullyQualifiedCString(), code.raw(), skind, + s_entry_point); } } } @@ -389,6 +395,11 @@ void Disassembler::DisassembleCode(const Function& function, DisassembleCodeHelper(function_fullname, code, optimized); } +#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) + +void Disassembler::DisassembleCode(const Function& function, + const Code& code, + bool optimized) {} #endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) } // namespace dart