1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "art_method-inl.h"
18 #include "dex/code_item_accessors.h"
19 #include "entrypoints/quick/callee_save_frame.h"
20 #include "interpreter/interpreter_mterp_impl.h"
21 #include "nterp_helpers.h"
22 #include "oat_quick_method_header.h"
23 #include "quick/quick_method_frame_info.h"
24 
25 namespace art {
26 
27 /**
28  * An nterp frame follows the optimizing compiler's ABI conventions, with
29  * int/long/reference parameters being passed in core registers / stack and
30  * float/double parameters being passed in floating point registers / stack.
31  *
32  * There are no ManagedStack transitions between compiler and nterp frames.
33  *
34  * On entry, nterp will copy its parameters to a dex register array allocated on
35  * the stack. There is a fast path when calling from nterp to nterp to not
36  * follow the ABI but just copy the parameters from the caller's dex registers
37  * to the callee's dex registers.
38  *
39  * The stack layout of an nterp frame is:
40  *    ----------------
41  *    |              |      All callee save registers of the platform
42  *    | callee-save  |      (core and floating point).
43  *    | registers    |      On x86 and x64 this includes the return address,
44  *    |              |      already spilled on entry.
45  *    ----------------
46  *    |  alignment   |      Stack aligment of kStackAlignment.
47  *    ----------------
48  *    |              |      Contains `registers_size` entries (of size 4) from
49  *    |    dex       |      the code item information of the method.
50  *    |  registers   |
51  *    |              |
52  *    ----------------
53  *    |              |      A copy of the dex registers above, but only
54  *    |  reference   |      containing references, used for GC.
55  *    |  registers   |
56  *    |              |
57  *    ----------------
58  *    |  caller fp   |      Frame pointer of caller. Stored below the reference
59  *    ----------------      registers array for easy access from nterp when returning.
60  *    |  dex_pc_ptr  |      Pointer to the dex instruction being executed.
61  *    ----------------      Stored whenever nterp goes into the runtime.
62  *    |              |      In case nterp calls compiled code, we reserve space
63  *    |     out      |      for out registers. This space will be used for
64  *    |   registers  |      arguments passed on stack.
65  *    |              |
66  *    ----------------
67  *    |  ArtMethod*  |      The method being currently executed.
68  *    ----------------
69  *
70  *    Exception handling:
71  *    Nterp follows the same convention than the compiler,
72  *    with the addition of:
73  *    - All catch handlers have the same landing pad.
74  *    - Before doing the longjmp for exception delivery, the register containing the
75  *      dex PC pointer must be updated.
76  *
77  *    Stack walking:
78  *    An nterp frame is walked like a compiled code frame. We add an
79  *    OatQuickMethodHeader prefix to the nterp entry point, which contains:
80  *    - vmap_table_offset=0 (nterp doesn't need one).
81  *    - code_size=NterpEnd-NterpStart
82  */
83 
84 static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize);
85 
NterpGetFrameEntrySize()86 static constexpr size_t NterpGetFrameEntrySize() {
87   uint32_t core_spills =
88       RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
89   uint32_t fp_spills =
90       RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
91   // Note: the return address is considered part of the callee saves.
92   return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * kPointerSize;
93 }
94 
NterpGetFrameSize(ArtMethod * method)95 size_t NterpGetFrameSize(ArtMethod* method) {
96   CodeItemDataAccessor accessor(method->DexInstructionData());
97   const uint16_t num_regs = accessor.RegistersSize();
98   const uint16_t out_regs = accessor.OutsSize();
99 
100   size_t frame_size =
101       NterpGetFrameEntrySize() +
102       (num_regs * kVRegSize) * 2 +  // dex registers and reference registers
103       kPointerSize +  // previous frame
104       kPointerSize +  // saved dex pc
105       (out_regs * kVRegSize) +  // out arguments
106       kPointerSize;  // method
107   return RoundUp(frame_size, kStackAlignment);
108 }
109 
NterpFrameInfo(ArtMethod ** frame)110 QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
111   uint32_t core_spills =
112       RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
113   uint32_t fp_spills =
114       RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
115   return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills);
116 }
117 
NterpGetRegistersArray(ArtMethod ** frame)118 uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
119   CodeItemDataAccessor accessor((*frame)->DexInstructionData());
120   const uint16_t num_regs = accessor.RegistersSize();
121   // The registers array is just above the reference array.
122   return NterpGetReferenceArray(frame) + (num_regs * kVRegSize);
123 }
124 
NterpGetReferenceArray(ArtMethod ** frame)125 uintptr_t NterpGetReferenceArray(ArtMethod** frame) {
126   CodeItemDataAccessor accessor((*frame)->DexInstructionData());
127   const uint16_t out_regs = accessor.OutsSize();
128   // The references array is just above the saved frame pointer.
129   return reinterpret_cast<uintptr_t>(frame) +
130       kPointerSize +  // method
131       (out_regs * kVRegSize) +  // out arguments
132       kPointerSize +  // saved dex pc
133       kPointerSize;  // previous frame.
134 }
135 
NterpGetDexPC(ArtMethod ** frame)136 uint32_t NterpGetDexPC(ArtMethod** frame) {
137   CodeItemDataAccessor accessor((*frame)->DexInstructionData());
138   const uint16_t out_regs = accessor.OutsSize();
139   uintptr_t dex_pc_ptr = reinterpret_cast<uintptr_t>(frame) +
140       kPointerSize +  // method
141       (out_regs * kVRegSize);  // out arguments
142   CodeItemInstructionAccessor instructions((*frame)->DexInstructions());
143   return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - instructions.Insns();
144 }
145 
NterpGetVReg(ArtMethod ** frame,uint16_t vreg)146 uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) {
147   return reinterpret_cast<uint32_t*>(NterpGetRegistersArray(frame))[vreg];
148 }
149 
NterpGetVRegReference(ArtMethod ** frame,uint16_t vreg)150 uint32_t NterpGetVRegReference(ArtMethod** frame, uint16_t vreg) {
151   return reinterpret_cast<uint32_t*>(NterpGetReferenceArray(frame))[vreg];
152 }
153 
NterpGetCatchHandler()154 uintptr_t NterpGetCatchHandler() {
155   // Nterp uses the same landing pad for all exceptions. The dex_pc_ptr set before
156   // longjmp will actually be used to jmp to the catch handler.
157   return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd);
158 }
159 
160 }  // namespace art
161