1 /*
2  * Copyright (C) 2014 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 "base/logging.h"
18 #include "calling_convention_arm64.h"
19 #include "handle_scope-inl.h"
20 #include "utils/arm64/managed_register_arm64.h"
21 
22 namespace art {
23 namespace arm64 {
24 
25 static const XRegister kXArgumentRegisters[] = {
26   X0, X1, X2, X3, X4, X5, X6, X7
27 };
28 
29 static const WRegister kWArgumentRegisters[] = {
30   W0, W1, W2, W3, W4, W5, W6, W7
31 };
32 
33 static const DRegister kDArgumentRegisters[] = {
34   D0, D1, D2, D3, D4, D5, D6, D7
35 };
36 
37 static const SRegister kSArgumentRegisters[] = {
38   S0, S1, S2, S3, S4, S5, S6, S7
39 };
40 
41 static const DRegister kDCalleeSaveRegisters[] = {
42   D8, D9, D10, D11, D12, D13, D14, D15
43 };
44 
45 // Calling convention
InterproceduralScratchRegister()46 ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
47   return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
48 }
49 
InterproceduralScratchRegister()50 ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() {
51   return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
52 }
53 
ReturnRegisterForShorty(const char * shorty)54 static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
55   if (shorty[0] == 'F') {
56     return Arm64ManagedRegister::FromSRegister(S0);
57   } else if (shorty[0] == 'D') {
58     return Arm64ManagedRegister::FromDRegister(D0);
59   } else if (shorty[0] == 'J') {
60     return Arm64ManagedRegister::FromXRegister(X0);
61   } else if (shorty[0] == 'V') {
62     return Arm64ManagedRegister::NoRegister();
63   } else {
64     return Arm64ManagedRegister::FromWRegister(W0);
65   }
66 }
67 
ReturnRegister()68 ManagedRegister Arm64ManagedRuntimeCallingConvention::ReturnRegister() {
69   return ReturnRegisterForShorty(GetShorty());
70 }
71 
ReturnRegister()72 ManagedRegister Arm64JniCallingConvention::ReturnRegister() {
73   return ReturnRegisterForShorty(GetShorty());
74 }
75 
IntReturnRegister()76 ManagedRegister Arm64JniCallingConvention::IntReturnRegister() {
77   return Arm64ManagedRegister::FromWRegister(W0);
78 }
79 
80 // Managed runtime calling convention
81 
MethodRegister()82 ManagedRegister Arm64ManagedRuntimeCallingConvention::MethodRegister() {
83   return Arm64ManagedRegister::FromXRegister(X0);
84 }
85 
IsCurrentParamInRegister()86 bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
87   return false;  // Everything moved to stack on entry.
88 }
89 
IsCurrentParamOnStack()90 bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
91   return true;
92 }
93 
CurrentParamRegister()94 ManagedRegister Arm64ManagedRuntimeCallingConvention::CurrentParamRegister() {
95   LOG(FATAL) << "Should not reach here";
96   return ManagedRegister::NoRegister();
97 }
98 
CurrentParamStackOffset()99 FrameOffset Arm64ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
100   CHECK(IsCurrentParamOnStack());
101   FrameOffset result =
102       FrameOffset(displacement_.Int32Value() +  // displacement
103                   kFramePointerSize +  // Method ref
104                   (itr_slots_ * sizeof(uint32_t)));  // offset into in args
105   return result;
106 }
107 
EntrySpills()108 const ManagedRegisterEntrySpills& Arm64ManagedRuntimeCallingConvention::EntrySpills() {
109   // We spill the argument registers on ARM64 to free them up for scratch use, we then assume
110   // all arguments are on the stack.
111   if ((entry_spills_.size() == 0) && (NumArgs() > 0)) {
112     int gp_reg_index = 1;   // we start from X1/W1, X0 holds ArtMethod*.
113     int fp_reg_index = 0;   // D0/S0.
114 
115     // We need to choose the correct register (D/S or X/W) since the managed
116     // stack uses 32bit stack slots.
117     ResetIterator(FrameOffset(0));
118     while (HasNext()) {
119       if (IsCurrentParamAFloatOrDouble()) {  // FP regs.
120           if (fp_reg_index < 8) {
121             if (!IsCurrentParamADouble()) {
122               entry_spills_.push_back(Arm64ManagedRegister::FromSRegister(kSArgumentRegisters[fp_reg_index]));
123             } else {
124               entry_spills_.push_back(Arm64ManagedRegister::FromDRegister(kDArgumentRegisters[fp_reg_index]));
125             }
126             fp_reg_index++;
127           } else {  // just increase the stack offset.
128             if (!IsCurrentParamADouble()) {
129               entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
130             } else {
131               entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
132             }
133           }
134       } else {  // GP regs.
135         if (gp_reg_index < 8) {
136           if (IsCurrentParamALong() && (!IsCurrentParamAReference())) {
137             entry_spills_.push_back(Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg_index]));
138           } else {
139             entry_spills_.push_back(Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg_index]));
140           }
141           gp_reg_index++;
142         } else {  // just increase the stack offset.
143           if (IsCurrentParamALong() && (!IsCurrentParamAReference())) {
144               entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
145           } else {
146               entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
147           }
148         }
149       }
150       Next();
151     }
152   }
153   return entry_spills_;
154 }
155 
156 // JNI calling convention
Arm64JniCallingConvention(bool is_static,bool is_synchronized,const char * shorty)157 Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized,
158                                                      const char* shorty)
159     : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
160   uint32_t core_spill_mask = CoreSpillMask();
161   DCHECK_EQ(XZR, kNumberOfXRegisters - 1);  // Exclude XZR from the loop (avoid 1 << 32).
162   for (int x_reg = 0; x_reg < kNumberOfXRegisters - 1; ++x_reg) {
163     if (((1 << x_reg) & core_spill_mask) != 0) {
164       callee_save_regs_.push_back(
165           Arm64ManagedRegister::FromXRegister(static_cast<XRegister>(x_reg)));
166     }
167   }
168 
169   uint32_t fp_spill_mask = FpSpillMask();
170   for (int d_reg = 0; d_reg < kNumberOfDRegisters; ++d_reg) {
171     if (((1 << d_reg) & fp_spill_mask) != 0) {
172       callee_save_regs_.push_back(
173           Arm64ManagedRegister::FromDRegister(static_cast<DRegister>(d_reg)));
174     }
175   }
176 }
177 
CoreSpillMask() const178 uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
179   // Compute spill mask to agree with callee saves initialized in the constructor.
180   // Note: The native jni function may call to some VM runtime functions which may suspend
181   // or trigger GC. And the jni method frame will become top quick frame in those cases.
182   // So we need to satisfy GC to save LR and callee-save registers which is similar to
183   // CalleeSaveMethod(RefOnly) frame.
184   // Jni function is the native function which the java code wants to call.
185   // Jni method is the method that compiled by jni compiler.
186   // Call chain: managed code(java) --> jni method --> jni function.
187   // Thread register(X18, scratched by aapcs64) is not saved on stack, it is saved in ETR(X21).
188   return 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 |
189          1 << X25 | 1 << X26 | 1 << X27 | 1 << X28 | 1 << X29 | 1 << LR;
190 }
191 
FpSpillMask() const192 uint32_t Arm64JniCallingConvention::FpSpillMask() const {
193   // Considering the case, java_method_1 --> jni method --> jni function --> java_method_2, we may
194   // break on java_method_2 and we still need to find out the values of DEX registers in
195   // java_method_1. So all callee-saves(in managed code) need to be saved.
196   uint32_t result = 0;
197   for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) {
198     result |= (1 << kDCalleeSaveRegisters[i]);
199   }
200   return result;
201 }
202 
ReturnScratchRegister() const203 ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const {
204   return ManagedRegister::NoRegister();
205 }
206 
FrameSize()207 size_t Arm64JniCallingConvention::FrameSize() {
208   // Method*, callee save area size, local reference segment state
209   size_t frame_data_size = kFramePointerSize +
210       CalleeSaveRegisters().size() * kFramePointerSize + sizeof(uint32_t);
211   // References plus 2 words for HandleScope header
212   size_t handle_scope_size = HandleScope::SizeOf(kFramePointerSize, ReferenceCount());
213   // Plus return value spill area size
214   return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
215 }
216 
OutArgSize()217 size_t Arm64JniCallingConvention::OutArgSize() {
218   return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment);
219 }
220 
IsCurrentParamInRegister()221 bool Arm64JniCallingConvention::IsCurrentParamInRegister() {
222   if (IsCurrentParamAFloatOrDouble()) {
223     return (itr_float_and_doubles_ < 8);
224   } else {
225     return ((itr_args_ - itr_float_and_doubles_) < 8);
226   }
227 }
228 
IsCurrentParamOnStack()229 bool Arm64JniCallingConvention::IsCurrentParamOnStack() {
230   return !IsCurrentParamInRegister();
231 }
232 
CurrentParamRegister()233 ManagedRegister Arm64JniCallingConvention::CurrentParamRegister() {
234   CHECK(IsCurrentParamInRegister());
235   if (IsCurrentParamAFloatOrDouble()) {
236     CHECK_LT(itr_float_and_doubles_, 8u);
237     if (IsCurrentParamADouble()) {
238       return Arm64ManagedRegister::FromDRegister(kDArgumentRegisters[itr_float_and_doubles_]);
239     } else {
240       return Arm64ManagedRegister::FromSRegister(kSArgumentRegisters[itr_float_and_doubles_]);
241     }
242   } else {
243     int gp_reg = itr_args_ - itr_float_and_doubles_;
244     CHECK_LT(static_cast<unsigned int>(gp_reg), 8u);
245     if (IsCurrentParamALong() || IsCurrentParamAReference() || IsCurrentParamJniEnv())  {
246       return Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg]);
247     } else {
248       return Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg]);
249     }
250   }
251 }
252 
CurrentParamStackOffset()253 FrameOffset Arm64JniCallingConvention::CurrentParamStackOffset() {
254   CHECK(IsCurrentParamOnStack());
255   size_t args_on_stack = itr_args_
256                   - std::min(8u, itr_float_and_doubles_)
257                   - std::min(8u, (itr_args_ - itr_float_and_doubles_));
258   size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize);
259   CHECK_LT(offset, OutArgSize());
260   return FrameOffset(offset);
261 }
262 
NumberOfOutgoingStackArgs()263 size_t Arm64JniCallingConvention::NumberOfOutgoingStackArgs() {
264   // all arguments including JNI args
265   size_t all_args = NumArgs() + NumberOfExtraArgumentsForJni();
266 
267   size_t all_stack_args = all_args -
268             std::min(8u, static_cast<unsigned int>(NumFloatOrDoubleArgs())) -
269             std::min(8u, static_cast<unsigned int>((all_args - NumFloatOrDoubleArgs())));
270 
271   return all_stack_args;
272 }
273 
274 }  // namespace arm64
275 }  // namespace art
276