summaryrefslogtreecommitdiff
path: root/runtime/nterp_helpers.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/nterp_helpers.cc')
-rw-r--r--runtime/nterp_helpers.cc152
1 files changed, 152 insertions, 0 deletions
diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc
new file mode 100644
index 0000000000..df0eb7305a
--- /dev/null
+++ b/runtime/nterp_helpers.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "art_method-inl.h"
+#include "dex/code_item_accessors.h"
+#include "entrypoints/quick/callee_save_frame.h"
+#include "interpreter/interpreter_mterp_impl.h"
+#include "nterp_helpers.h"
+#include "oat_quick_method_header.h"
+#include "quick/quick_method_frame_info.h"
+
+namespace art {
+
+/**
+ * An nterp frame follows the optimizing compiler's ABI conventions, with
+ * int/long/reference parameters being passed in core registers / stack and
+ * float/double parameters being passed in floating point registers / stack.
+ *
+ * There are no ManagedStack transitions between compiler and nterp frames.
+ *
+ * On entry, nterp will copy its parameters to a dex register array allocated on
+ * the stack. There is a fast path when calling from nterp to nterp to not
+ * follow the ABI but just copy the parameters from the caller's dex registers
+ * to the callee's dex registers.
+ *
+ * The stack layout of an nterp frame is:
+ * ----------------
+ * | | All callee save registers of the platform
+ * | callee-save | (core and floating point).
+ * | registers | On x86 and x64 this includes the return address,
+ * | | already spilled on entry.
+ * ----------------
+ * | | Contains `registers_size` entries (of size 4) from
+ * | dex | the code item information of the method.
+ * | registers |
+ * | |
+ * ----------------
+ * | | A copy of the dex registers above, but only
+ * | reference | containing references, used for GC.
+ * | registers |
+ * | |
+ * ----------------
+ * | caller fp | Frame pointer of caller. Stored below the reference
+ * ---------------- registers array for easy access from nterp when returning.
+ * | dex_pc_ptr | Pointer to the dex instruction being executed.
+ * ---------------- Stored whenever nterp goes into the runtime.
+ * | alignment | Stack aligment of kStackAlignment. TODO: try to move
+ * ---------------- this below the callee-save registers.
+ * | | In case nterp calls compiled code, we reserve space
+ * | out | for out registers. This space will be used for
+ * | registers | arguments passed on stack.
+ * | |
+ * ----------------
+ * | ArtMethod* | The method being currently executed.
+ * ----------------
+ *
+ * Exception handling:
+ * Nterp follows the same convention than the compiler,
+ * with the addition of:
+ * - All catch handlers have the same landing pad.
+ * - Before doing the longjmp for exception delivery, the register containing the
+ * dex PC pointer must be updated.
+ *
+ * Stack walking:
+ * An nterp frame is walked like a compiled code frame. We add an
+ * OatQuickMethodHeader prefix to the nterp entry point, which contains:
+ * - vmap_table_offset=0 (nterp doesn't need one).
+ * - code_size=NterpEnd-NterpStart
+ */
+
+static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize);
+
+static constexpr size_t NterpGetFrameEntrySize() {
+ uint32_t core_spills =
+ RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+ uint32_t fp_spills =
+ RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+ // Note: the return address is considered part of the callee saves.
+ return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * kPointerSize;
+}
+
+static size_t NterpGetFrameSize(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+ const uint16_t num_regs = accessor.RegistersSize();
+ const uint16_t out_regs = accessor.OutsSize();
+
+ size_t frame_size =
+ NterpGetFrameEntrySize() +
+ (num_regs * kVRegSize) * 2 + // dex registers and reference registers
+ kPointerSize + // previous frame
+ kPointerSize + // saved dex pc
+ (out_regs * kVRegSize) + // out arguments
+ kPointerSize; // method
+ return RoundUp(frame_size, kStackAlignment);
+}
+
+QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
+ uint32_t core_spills =
+ RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
+ uint32_t fp_spills =
+ RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
+ return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills);
+}
+
+uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
+ CodeItemDataAccessor accessor((*frame)->DexInstructionData());
+ const uint16_t num_regs = accessor.RegistersSize();
+ // The registers array is just below the frame entry.
+ return reinterpret_cast<uintptr_t>(frame) + NterpGetFrameSize(*frame) -
+ NterpGetFrameEntrySize() -
+ (num_regs * kVRegSize);
+}
+
+uintptr_t NterpGetReferenceArray(ArtMethod** frame) {
+ CodeItemDataAccessor accessor((*frame)->DexInstructionData());
+ const uint16_t num_regs = accessor.RegistersSize();
+ // The references array is just below the registers array.
+ return NterpGetRegistersArray(frame) - (num_regs * kVRegSize);
+}
+
+uint32_t NterpGetDexPC(ArtMethod** frame) {
+ uintptr_t dex_pc_ptr = NterpGetReferenceArray(frame) -
+ kPointerSize - // saved previous frame
+ kPointerSize; // saved dex pc
+ CodeItemInstructionAccessor accessor((*frame)->DexInstructions());
+ return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - accessor.Insns();
+}
+
+uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) {
+ return reinterpret_cast<uint32_t*>(NterpGetRegistersArray(frame))[vreg];
+}
+
+uintptr_t NterpGetCatchHandler() {
+ // Nterp uses the same landing pad for all exceptions. The dex_pc_ptr set before
+ // longjmp will actually be used to jmp to the catch handler.
+ return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd);
+}
+
+} // namespace art