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 "calling_convention_x86_64.h" 18 19 #include "base/bit_utils.h" 20 #include "base/logging.h" 21 #include "handle_scope-inl.h" 22 #include "utils/x86_64/managed_register_x86_64.h" 23 24 namespace art { 25 namespace x86_64 { 26 27 constexpr size_t kFramePointerSize = static_cast<size_t>(PointerSize::k64); 28 static_assert(kX86_64PointerSize == PointerSize::k64, "Unexpected x86_64 pointer size"); 29 static_assert(kStackAlignment >= 16u, "System V AMD64 ABI requires at least 16 byte stack alignment"); 30 31 // XMM0..XMM7 can be used to pass the first 8 floating args. The rest must go on the stack. 32 // -- Managed and JNI calling conventions. 33 constexpr size_t kMaxFloatOrDoubleRegisterArguments = 8u; 34 // Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be 35 // enregistered. The rest of the args must go on the stack. 36 // -- JNI calling convention only (Managed excludes RDI, so it's actually 5). 37 constexpr size_t kMaxIntLikeRegisterArguments = 6u; 38 39 static constexpr ManagedRegister kCalleeSaveRegisters[] = { 40 // Core registers. 41 X86_64ManagedRegister::FromCpuRegister(RBX), 42 X86_64ManagedRegister::FromCpuRegister(RBP), 43 X86_64ManagedRegister::FromCpuRegister(R12), 44 X86_64ManagedRegister::FromCpuRegister(R13), 45 X86_64ManagedRegister::FromCpuRegister(R14), 46 X86_64ManagedRegister::FromCpuRegister(R15), 47 // Hard float registers. 48 X86_64ManagedRegister::FromXmmRegister(XMM12), 49 X86_64ManagedRegister::FromXmmRegister(XMM13), 50 X86_64ManagedRegister::FromXmmRegister(XMM14), 51 X86_64ManagedRegister::FromXmmRegister(XMM15), 52 }; 53 54 static constexpr uint32_t CalculateCoreCalleeSpillMask() { 55 // The spilled PC gets a special marker. 56 uint32_t result = 1 << kNumberOfCpuRegisters; 57 for (auto&& r : kCalleeSaveRegisters) { 58 if (r.AsX86_64().IsCpuRegister()) { 59 result |= (1 << r.AsX86_64().AsCpuRegister().AsRegister()); 60 } 61 } 62 return result; 63 } 64 65 static constexpr uint32_t CalculateFpCalleeSpillMask() { 66 uint32_t result = 0; 67 for (auto&& r : kCalleeSaveRegisters) { 68 if (r.AsX86_64().IsXmmRegister()) { 69 result |= (1 << r.AsX86_64().AsXmmRegister().AsFloatRegister()); 70 } 71 } 72 return result; 73 } 74 75 static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask(); 76 static constexpr uint32_t kFpCalleeSpillMask = CalculateFpCalleeSpillMask(); 77 78 // Calling convention 79 80 ManagedRegister X86_64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { 81 return X86_64ManagedRegister::FromCpuRegister(RAX); 82 } 83 84 ManagedRegister X86_64JniCallingConvention::InterproceduralScratchRegister() { 85 return X86_64ManagedRegister::FromCpuRegister(RAX); 86 } 87 88 ManagedRegister X86_64JniCallingConvention::ReturnScratchRegister() const { 89 return ManagedRegister::NoRegister(); // No free regs, so assembler uses push/pop 90 } 91 92 static ManagedRegister ReturnRegisterForShorty(const char* shorty, bool jni ATTRIBUTE_UNUSED) { 93 if (shorty[0] == 'F' || shorty[0] == 'D') { 94 return X86_64ManagedRegister::FromXmmRegister(XMM0); 95 } else if (shorty[0] == 'J') { 96 return X86_64ManagedRegister::FromCpuRegister(RAX); 97 } else if (shorty[0] == 'V') { 98 return ManagedRegister::NoRegister(); 99 } else { 100 return X86_64ManagedRegister::FromCpuRegister(RAX); 101 } 102 } 103 104 ManagedRegister X86_64ManagedRuntimeCallingConvention::ReturnRegister() { 105 return ReturnRegisterForShorty(GetShorty(), false); 106 } 107 108 ManagedRegister X86_64JniCallingConvention::ReturnRegister() { 109 return ReturnRegisterForShorty(GetShorty(), true); 110 } 111 112 ManagedRegister X86_64JniCallingConvention::IntReturnRegister() { 113 return X86_64ManagedRegister::FromCpuRegister(RAX); 114 } 115 116 // Managed runtime calling convention 117 118 ManagedRegister X86_64ManagedRuntimeCallingConvention::MethodRegister() { 119 return X86_64ManagedRegister::FromCpuRegister(RDI); 120 } 121 122 bool X86_64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() { 123 return !IsCurrentParamOnStack(); 124 } 125 126 bool X86_64ManagedRuntimeCallingConvention::IsCurrentParamOnStack() { 127 // We assume all parameters are on stack, args coming via registers are spilled as entry_spills 128 return true; 129 } 130 131 ManagedRegister X86_64ManagedRuntimeCallingConvention::CurrentParamRegister() { 132 ManagedRegister res = ManagedRegister::NoRegister(); 133 if (!IsCurrentParamAFloatOrDouble()) { 134 switch (itr_args_ - itr_float_and_doubles_) { 135 case 0: res = X86_64ManagedRegister::FromCpuRegister(RSI); break; 136 case 1: res = X86_64ManagedRegister::FromCpuRegister(RDX); break; 137 case 2: res = X86_64ManagedRegister::FromCpuRegister(RCX); break; 138 case 3: res = X86_64ManagedRegister::FromCpuRegister(R8); break; 139 case 4: res = X86_64ManagedRegister::FromCpuRegister(R9); break; 140 } 141 } else if (itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments) { 142 // First eight float parameters are passed via XMM0..XMM7 143 res = X86_64ManagedRegister::FromXmmRegister( 144 static_cast<FloatRegister>(XMM0 + itr_float_and_doubles_)); 145 } 146 return res; 147 } 148 149 FrameOffset X86_64ManagedRuntimeCallingConvention::CurrentParamStackOffset() { 150 return FrameOffset(displacement_.Int32Value() + // displacement 151 static_cast<size_t>(kX86_64PointerSize) + // Method ref 152 itr_slots_ * sizeof(uint32_t)); // offset into in args 153 } 154 155 const ManagedRegisterEntrySpills& X86_64ManagedRuntimeCallingConvention::EntrySpills() { 156 // We spill the argument registers on X86 to free them up for scratch use, we then assume 157 // all arguments are on the stack. 158 if (entry_spills_.size() == 0) { 159 ResetIterator(FrameOffset(0)); 160 while (HasNext()) { 161 ManagedRegister in_reg = CurrentParamRegister(); 162 if (!in_reg.IsNoRegister()) { 163 int32_t size = IsParamALongOrDouble(itr_args_) ? 8 : 4; 164 int32_t spill_offset = CurrentParamStackOffset().Uint32Value(); 165 ManagedRegisterSpill spill(in_reg, size, spill_offset); 166 entry_spills_.push_back(spill); 167 } 168 Next(); 169 } 170 } 171 return entry_spills_; 172 } 173 174 // JNI calling convention 175 176 X86_64JniCallingConvention::X86_64JniCallingConvention(bool is_static, 177 bool is_synchronized, 178 bool is_critical_native, 179 const char* shorty) 180 : JniCallingConvention(is_static, 181 is_synchronized, 182 is_critical_native, 183 shorty, 184 kX86_64PointerSize) { 185 } 186 187 uint32_t X86_64JniCallingConvention::CoreSpillMask() const { 188 return kCoreCalleeSpillMask; 189 } 190 191 uint32_t X86_64JniCallingConvention::FpSpillMask() const { 192 return kFpCalleeSpillMask; 193 } 194 195 size_t X86_64JniCallingConvention::FrameSize() { 196 // Method*, PC return address and callee save area size, local reference segment state 197 const size_t method_ptr_size = static_cast<size_t>(kX86_64PointerSize); 198 const size_t pc_return_addr_size = kFramePointerSize; 199 const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize; 200 size_t frame_data_size = method_ptr_size + pc_return_addr_size + callee_save_area_size; 201 202 if (LIKELY(HasLocalReferenceSegmentState())) { // local ref. segment state 203 // Local reference segment state is sometimes excluded. 204 frame_data_size += kFramePointerSize; 205 } 206 207 // References plus link_ (pointer) and number_of_references_ (uint32_t) for HandleScope header 208 const size_t handle_scope_size = HandleScope::SizeOf(kX86_64PointerSize, ReferenceCount()); 209 210 size_t total_size = frame_data_size; 211 if (LIKELY(HasHandleScope())) { 212 // HandleScope is sometimes excluded. 213 total_size += handle_scope_size; // handle scope size 214 } 215 216 // Plus return value spill area size 217 total_size += SizeOfReturnValue(); 218 219 return RoundUp(total_size, kStackAlignment); 220 } 221 222 size_t X86_64JniCallingConvention::OutArgSize() { 223 return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment); 224 } 225 226 ArrayRef<const ManagedRegister> X86_64JniCallingConvention::CalleeSaveRegisters() const { 227 return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters); 228 } 229 230 bool X86_64JniCallingConvention::IsCurrentParamInRegister() { 231 return !IsCurrentParamOnStack(); 232 } 233 234 bool X86_64JniCallingConvention::IsCurrentParamOnStack() { 235 return CurrentParamRegister().IsNoRegister(); 236 } 237 238 ManagedRegister X86_64JniCallingConvention::CurrentParamRegister() { 239 ManagedRegister res = ManagedRegister::NoRegister(); 240 if (!IsCurrentParamAFloatOrDouble()) { 241 switch (itr_args_ - itr_float_and_doubles_) { 242 case 0: res = X86_64ManagedRegister::FromCpuRegister(RDI); break; 243 case 1: res = X86_64ManagedRegister::FromCpuRegister(RSI); break; 244 case 2: res = X86_64ManagedRegister::FromCpuRegister(RDX); break; 245 case 3: res = X86_64ManagedRegister::FromCpuRegister(RCX); break; 246 case 4: res = X86_64ManagedRegister::FromCpuRegister(R8); break; 247 case 5: res = X86_64ManagedRegister::FromCpuRegister(R9); break; 248 static_assert(5u == kMaxIntLikeRegisterArguments - 1, "Missing case statement(s)"); 249 } 250 } else if (itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments) { 251 // First eight float parameters are passed via XMM0..XMM7 252 res = X86_64ManagedRegister::FromXmmRegister( 253 static_cast<FloatRegister>(XMM0 + itr_float_and_doubles_)); 254 } 255 return res; 256 } 257 258 FrameOffset X86_64JniCallingConvention::CurrentParamStackOffset() { 259 CHECK(IsCurrentParamOnStack()); 260 size_t args_on_stack = itr_args_ 261 - std::min(kMaxFloatOrDoubleRegisterArguments, 262 static_cast<size_t>(itr_float_and_doubles_)) 263 // Float arguments passed through Xmm0..Xmm7 264 - std::min(kMaxIntLikeRegisterArguments, 265 static_cast<size_t>(itr_args_ - itr_float_and_doubles_)); 266 // Integer arguments passed through GPR 267 size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize); 268 CHECK_LT(offset, OutArgSize()); 269 return FrameOffset(offset); 270 } 271 272 // TODO: Calling this "NumberArgs" is misleading. 273 // It's really more like NumberSlots (like itr_slots_) 274 // because doubles/longs get counted twice. 275 size_t X86_64JniCallingConvention::NumberOfOutgoingStackArgs() { 276 size_t static_args = HasSelfClass() ? 1 : 0; // count jclass 277 // regular argument parameters and this 278 size_t param_args = NumArgs() + NumLongOrDoubleArgs(); 279 // count JNIEnv* and return pc (pushed after Method*) 280 size_t internal_args = 1 /* return pc */ + (HasJniEnv() ? 1 : 0 /* jni env */); 281 size_t total_args = static_args + param_args + internal_args; 282 283 // Float arguments passed through Xmm0..Xmm7 284 // Other (integer) arguments passed through GPR (RDI, RSI, RDX, RCX, R8, R9) 285 size_t total_stack_args = total_args 286 - std::min(kMaxFloatOrDoubleRegisterArguments, static_cast<size_t>(NumFloatOrDoubleArgs())) 287 - std::min(kMaxIntLikeRegisterArguments, static_cast<size_t>(NumArgs() - NumFloatOrDoubleArgs())); 288 289 return total_stack_args; 290 } 291 292 } // namespace x86_64 293 } // namespace art 294