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 "arch/instruction_set.h"
18 #include "art_method-inl.h"
19 #include "dex/code_item_accessors.h"
20 #include "entrypoints/quick/callee_save_frame.h"
21 #include "interpreter/mterp/nterp.h"
22 #include "nterp_helpers.h"
23 #include "oat/oat_quick_method_header.h"
24 #include "quick/quick_method_frame_info.h"
25
26 namespace art HIDDEN {
27
28 /**
29 * An nterp frame follows the optimizing compiler's ABI conventions, with
30 * int/long/reference parameters being passed in core registers / stack and
31 * float/double parameters being passed in floating point registers / stack.
32 *
33 * There are no ManagedStack transitions between compiler and nterp frames.
34 *
35 * On entry, nterp will copy its parameters to a dex register array allocated on
36 * the stack. There is a fast path when calling from nterp to nterp to not
37 * follow the ABI but just copy the parameters from the caller's dex registers
38 * to the callee's dex registers.
39 *
40 * The stack layout of an nterp frame is:
41 * ----------------
42 * | | All callee save registers of the platform
43 * | callee-save | (core and floating point).
44 * | registers | On x86 and x64 this includes the return address,
45 * | | already spilled on entry.
46 * ----------------
47 * | x86 args | x86 only: registers used for argument passing.
48 * ----------------
49 * | alignment | Stack aligment of kStackAlignment.
50 * ----------------
51 * | | Contains `registers_size` entries (of size 4) from
52 * | dex | the code item information of the method.
53 * | registers |
54 * | |
55 * ----------------
56 * | | A copy of the dex registers above, but only
57 * | reference | containing references, used for GC.
58 * | registers |
59 * | |
60 * ----------------
61 * | caller fp | Frame pointer of caller. Stored below the reference
62 * ---------------- registers array for easy access from nterp when returning.
63 * | dex_pc_ptr | Pointer to the dex instruction being executed.
64 * ---------------- Stored whenever nterp goes into the runtime.
65 * | alignment | Pointer aligment for dex_pc_ptr and caller_fp.
66 * ----------------
67 * | | In case nterp calls compiled code, we reserve space
68 * | out | for out registers. This space will be used for
69 * | registers | arguments passed on stack.
70 * | |
71 * ----------------
72 * | ArtMethod* | The method being currently executed.
73 * ----------------
74 *
75 * Exception handling:
76 * Nterp follows the same convention than the compiler,
77 * with the addition of:
78 * - All catch handlers have the same landing pad.
79 * - Before doing the longjmp for exception delivery, the register containing the
80 * dex PC pointer must be updated.
81 *
82 * Stack walking:
83 * An nterp frame is walked like a compiled code frame. We add an
84 * OatQuickMethodHeader prefix to the nterp entry point, which contains:
85 * - vmap_table_offset=0 (nterp doesn't need one).
86 * - code_size=NterpEnd-NterpStart
87 */
88
89 static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize);
90
NterpGetFrameEntrySize(InstructionSet isa)91 static constexpr size_t NterpGetFrameEntrySize(InstructionSet isa) {
92 uint32_t core_spills = 0;
93 uint32_t fp_spills = 0;
94 // Note: the return address is considered part of the callee saves.
95 switch (isa) {
96 case InstructionSet::kX86:
97 core_spills = x86::X86CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
98 fp_spills = x86::X86CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
99 // x86 also saves registers used for argument passing.
100 core_spills |= x86::kX86CalleeSaveEverythingSpills;
101 break;
102 case InstructionSet::kX86_64:
103 core_spills =
104 x86_64::X86_64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
105 fp_spills = x86_64::X86_64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
106 break;
107 case InstructionSet::kArm:
108 case InstructionSet::kThumb2:
109 core_spills = arm::ArmCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
110 fp_spills = arm::ArmCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
111 break;
112 case InstructionSet::kArm64:
113 core_spills = arm64::Arm64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
114 fp_spills = arm64::Arm64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
115 break;
116 case InstructionSet::kRiscv64:
117 core_spills =
118 riscv64::Riscv64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
119 fp_spills = riscv64::Riscv64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
120 break;
121 default:
122 InstructionSetAbort(isa);
123 }
124 // Note: the return address is considered part of the callee saves.
125 return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) *
126 static_cast<size_t>(InstructionSetPointerSize(isa));
127 }
128
GetNumberOfOutRegs(const CodeItemDataAccessor & accessor,InstructionSet isa)129 static uint16_t GetNumberOfOutRegs(const CodeItemDataAccessor& accessor, InstructionSet isa) {
130 uint16_t out_regs = accessor.OutsSize();
131 switch (isa) {
132 case InstructionSet::kX86: {
133 // On x86, we use three slots for temporaries.
134 out_regs = std::max(out_regs, static_cast<uint16_t>(3u));
135 break;
136 }
137 default:
138 break;
139 }
140 return out_regs;
141 }
142
GetNumberOfOutRegs(ArtMethod * method,InstructionSet isa)143 static uint16_t GetNumberOfOutRegs(ArtMethod* method, InstructionSet isa)
144 REQUIRES_SHARED(Locks::mutator_lock_) {
145 CodeItemDataAccessor accessor(method->DexInstructionData());
146 return GetNumberOfOutRegs(accessor, isa);
147 }
148
149 // Note: There may be two pieces of alignment but there is no need to align
150 // out args to `kPointerSize` separately before aligning to kStackAlignment.
151 // This allows using the size without padding for the maximum frame size check
152 // in `CanMethodUseNterp()`.
NterpGetFrameSizeWithoutPadding(ArtMethod * method,InstructionSet isa)153 static size_t NterpGetFrameSizeWithoutPadding(ArtMethod* method, InstructionSet isa)
154 REQUIRES_SHARED(Locks::mutator_lock_) {
155 CodeItemDataAccessor accessor(method->DexInstructionData());
156 const uint16_t num_regs = accessor.RegistersSize();
157 const uint16_t out_regs = GetNumberOfOutRegs(accessor, isa);
158 size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa));
159
160 DCHECK(IsAlignedParam(kStackAlignment, pointer_size));
161 DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size));
162 DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size));
163 size_t frame_size =
164 NterpGetFrameEntrySize(isa) +
165 (num_regs * kVRegSize) * 2 + // dex registers and reference registers
166 pointer_size + // previous frame
167 pointer_size + // saved dex pc
168 (out_regs * kVRegSize) + // out arguments
169 pointer_size; // method
170 return frame_size;
171 }
172
173 // The frame size nterp will use for the given method.
NterpGetFrameSize(ArtMethod * method,InstructionSet isa)174 static inline size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa)
175 REQUIRES_SHARED(Locks::mutator_lock_) {
176 return RoundUp(NterpGetFrameSizeWithoutPadding(method, isa), kStackAlignment);
177 }
178
NterpFrameInfo(ArtMethod ** frame)179 QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
180 uint32_t core_spills =
181 RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
182 uint32_t fp_spills =
183 RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
184 return QuickMethodFrameInfo(NterpGetFrameSize(*frame, kRuntimeISA), core_spills, fp_spills);
185 }
186
NterpGetRegistersArray(ArtMethod ** frame)187 uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
188 CodeItemDataAccessor accessor((*frame)->DexInstructionData());
189 const uint16_t num_regs = accessor.RegistersSize();
190 // The registers array is just above the reference array.
191 return NterpGetReferenceArray(frame) + (num_regs * kVRegSize);
192 }
193
NterpGetReferenceArray(ArtMethod ** frame)194 uintptr_t NterpGetReferenceArray(ArtMethod** frame) {
195 const uint16_t out_regs = GetNumberOfOutRegs(*frame, kRuntimeISA);
196 // The references array is just above the saved frame pointer.
197 return reinterpret_cast<uintptr_t>(frame) +
198 kPointerSize + // method
199 RoundUp(out_regs * kVRegSize, kPointerSize) + // out arguments and pointer alignment
200 kPointerSize + // saved dex pc
201 kPointerSize; // previous frame.
202 }
203
NterpGetDexPC(ArtMethod ** frame)204 uint32_t NterpGetDexPC(ArtMethod** frame) {
205 const uint16_t out_regs = GetNumberOfOutRegs(*frame, kRuntimeISA);
206 uintptr_t dex_pc_ptr = reinterpret_cast<uintptr_t>(frame) +
207 kPointerSize + // method
208 RoundUp(out_regs * kVRegSize, kPointerSize); // out arguments and pointer alignment
209 CodeItemInstructionAccessor instructions((*frame)->DexInstructions());
210 return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - instructions.Insns();
211 }
212
NterpGetVReg(ArtMethod ** frame,uint16_t vreg)213 uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) {
214 return reinterpret_cast<uint32_t*>(NterpGetRegistersArray(frame))[vreg];
215 }
216
NterpGetVRegReference(ArtMethod ** frame,uint16_t vreg)217 uint32_t NterpGetVRegReference(ArtMethod** frame, uint16_t vreg) {
218 return reinterpret_cast<uint32_t*>(NterpGetReferenceArray(frame))[vreg];
219 }
220
NterpGetCatchHandler()221 uintptr_t NterpGetCatchHandler() {
222 // Nterp uses the same landing pad for all exceptions. The dex_pc_ptr set before
223 // longjmp will actually be used to jmp to the catch handler.
224 return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd);
225 }
226
CanMethodUseNterp(ArtMethod * method,InstructionSet isa)227 bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) {
228 uint32_t access_flags = method->GetAccessFlags();
229 if (ArtMethod::IsNative(access_flags) ||
230 !ArtMethod::IsInvokable(access_flags) ||
231 ArtMethod::MustCountLocks(access_flags) ||
232 // Proxy methods do not go through the JIT like other methods, so we don't
233 // run them with nterp.
234 method->IsProxyMethod()) {
235 return false;
236 }
237 if (isa == InstructionSet::kRiscv64 && method->GetDexFile()->IsCompactDexFile()) {
238 return false; // Riscv64 nterp does not support compact dex yet.
239 }
240 // There is no need to add the alignment padding size for comparison with aligned limit.
241 size_t frame_size_without_padding = NterpGetFrameSizeWithoutPadding(method, isa);
242 DCHECK_EQ(NterpGetFrameSize(method, isa), RoundUp(frame_size_without_padding, kStackAlignment));
243 static_assert(IsAligned<kStackAlignment>(interpreter::kNterpMaxFrame));
244 return frame_size_without_padding <= interpreter::kNterpMaxFrame;
245 }
246
247 } // namespace art
248