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 * | 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 * | alignment | Pointer aligment for dex_pc_ptr and caller_fp.
63 * ----------------
64 * | | In case nterp calls compiled code, we reserve space
65 * | out | for out registers. This space will be used for
66 * | registers | arguments passed on stack.
67 * | |
68 * ----------------
69 * | ArtMethod* | The method being currently executed.
70 * ----------------
71 *
72 * Exception handling:
73 * Nterp follows the same convention than the compiler,
74 * with the addition of:
75 * - All catch handlers have the same landing pad.
76 * - Before doing the longjmp for exception delivery, the register containing the
77 * dex PC pointer must be updated.
78 *
79 * Stack walking:
80 * An nterp frame is walked like a compiled code frame. We add an
81 * OatQuickMethodHeader prefix to the nterp entry point, which contains:
82 * - vmap_table_offset=0 (nterp doesn't need one).
83 * - code_size=NterpEnd-NterpStart
84 */
85
86 static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize);
87
NterpGetFrameEntrySize(InstructionSet isa)88 static constexpr size_t NterpGetFrameEntrySize(InstructionSet isa) {
89 uint32_t core_spills = 0;
90 uint32_t fp_spills = 0;
91 // Note: the return address is considered part of the callee saves.
92 switch (isa) {
93 case InstructionSet::kX86:
94 core_spills = x86::X86CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
95 fp_spills = x86::X86CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
96 break;
97 case InstructionSet::kX86_64:
98 core_spills =
99 x86_64::X86_64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
100 fp_spills = x86_64::X86_64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
101 break;
102 case InstructionSet::kArm:
103 case InstructionSet::kThumb2:
104 core_spills = arm::ArmCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
105 fp_spills = arm::ArmCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
106 break;
107 case InstructionSet::kArm64:
108 core_spills = arm64::Arm64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
109 fp_spills = arm64::Arm64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
110 break;
111 default:
112 InstructionSetAbort(isa);
113 }
114 // Note: the return address is considered part of the callee saves.
115 return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) *
116 static_cast<size_t>(InstructionSetPointerSize(isa));
117 }
118
NterpGetFrameSize(ArtMethod * method,InstructionSet isa)119 size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa) {
120 CodeItemDataAccessor accessor(method->DexInstructionData());
121 const uint16_t num_regs = accessor.RegistersSize();
122 const uint16_t out_regs = accessor.OutsSize();
123 size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa));
124
125 // Note: There may be two pieces of alignment but there is no need to align
126 // out args to `kPointerSize` separately before aligning to kStackAlignment.
127 DCHECK(IsAlignedParam(kStackAlignment, pointer_size));
128 DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size));
129 DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size));
130 size_t frame_size =
131 NterpGetFrameEntrySize(isa) +
132 (num_regs * kVRegSize) * 2 + // dex registers and reference registers
133 pointer_size + // previous frame
134 pointer_size + // saved dex pc
135 (out_regs * kVRegSize) + // out arguments
136 pointer_size; // method
137 return RoundUp(frame_size, kStackAlignment);
138 }
139
NterpFrameInfo(ArtMethod ** frame)140 QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
141 uint32_t core_spills =
142 RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
143 uint32_t fp_spills =
144 RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
145 return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills);
146 }
147
NterpGetRegistersArray(ArtMethod ** frame)148 uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
149 CodeItemDataAccessor accessor((*frame)->DexInstructionData());
150 const uint16_t num_regs = accessor.RegistersSize();
151 // The registers array is just above the reference array.
152 return NterpGetReferenceArray(frame) + (num_regs * kVRegSize);
153 }
154
NterpGetReferenceArray(ArtMethod ** frame)155 uintptr_t NterpGetReferenceArray(ArtMethod** frame) {
156 CodeItemDataAccessor accessor((*frame)->DexInstructionData());
157 const uint16_t out_regs = accessor.OutsSize();
158 // The references array is just above the saved frame pointer.
159 return reinterpret_cast<uintptr_t>(frame) +
160 kPointerSize + // method
161 RoundUp(out_regs * kVRegSize, kPointerSize) + // out arguments and pointer alignment
162 kPointerSize + // saved dex pc
163 kPointerSize; // previous frame.
164 }
165
NterpGetDexPC(ArtMethod ** frame)166 uint32_t NterpGetDexPC(ArtMethod** frame) {
167 CodeItemDataAccessor accessor((*frame)->DexInstructionData());
168 const uint16_t out_regs = accessor.OutsSize();
169 uintptr_t dex_pc_ptr = reinterpret_cast<uintptr_t>(frame) +
170 kPointerSize + // method
171 RoundUp(out_regs * kVRegSize, kPointerSize); // out arguments and pointer alignment
172 CodeItemInstructionAccessor instructions((*frame)->DexInstructions());
173 return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - instructions.Insns();
174 }
175
NterpGetVReg(ArtMethod ** frame,uint16_t vreg)176 uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) {
177 return reinterpret_cast<uint32_t*>(NterpGetRegistersArray(frame))[vreg];
178 }
179
NterpGetVRegReference(ArtMethod ** frame,uint16_t vreg)180 uint32_t NterpGetVRegReference(ArtMethod** frame, uint16_t vreg) {
181 return reinterpret_cast<uint32_t*>(NterpGetReferenceArray(frame))[vreg];
182 }
183
NterpGetCatchHandler()184 uintptr_t NterpGetCatchHandler() {
185 // Nterp uses the same landing pad for all exceptions. The dex_pc_ptr set before
186 // longjmp will actually be used to jmp to the catch handler.
187 return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd);
188 }
189
CanMethodUseNterp(ArtMethod * method,InstructionSet isa)190 bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) {
191 return !method->IsNative() &&
192 method->IsInvokable() &&
193 // Nterp supports the same methods the compiler supports.
194 method->IsCompilable() &&
195 !method->MustCountLocks() &&
196 // Proxy methods do not go through the JIT like other methods, so we don't
197 // run them with nterp.
198 !method->IsProxyMethod() &&
199 NterpGetFrameSize(method, isa) <= interpreter::kNterpMaxFrame;
200 }
201
202 } // namespace art
203