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