Elliott Hughes | 2faa5f1 | 2012-01-30 14:42:07 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 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 | */ |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 16 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 17 | #include "calling_convention_x86.h" |
Ian Rogers | 57b86d4 | 2012-03-27 16:05:41 -0700 | [diff] [blame] | 18 | |
Elliott Hughes | 07ed66b | 2012-12-12 18:34:25 -0800 | [diff] [blame] | 19 | #include "base/logging.h" |
Mathieu Chartier | 3e0acf6 | 2015-01-08 09:41:25 -0800 | [diff] [blame] | 20 | #include "handle_scope-inl.h" |
Ian Rogers | 166db04 | 2013-07-26 12:05:57 -0700 | [diff] [blame] | 21 | #include "utils/x86/managed_register_x86.h" |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 22 | |
| 23 | namespace art { |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 24 | namespace x86 { |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 25 | |
Andreas Gampe | 542451c | 2016-07-26 09:02:02 -0700 | [diff] [blame] | 26 | static_assert(kX86PointerSize == PointerSize::k32, "Unexpected x86 pointer size"); |
| 27 | |
Vladimir Marko | 3224838 | 2016-05-19 10:37:24 +0100 | [diff] [blame] | 28 | static constexpr ManagedRegister kCalleeSaveRegisters[] = { |
| 29 | // Core registers. |
| 30 | X86ManagedRegister::FromCpuRegister(EBP), |
| 31 | X86ManagedRegister::FromCpuRegister(ESI), |
| 32 | X86ManagedRegister::FromCpuRegister(EDI), |
| 33 | // No hard float callee saves. |
| 34 | }; |
| 35 | |
| 36 | static constexpr uint32_t CalculateCoreCalleeSpillMask() { |
| 37 | // The spilled PC gets a special marker. |
| 38 | uint32_t result = 1 << kNumberOfCpuRegisters; |
| 39 | for (auto&& r : kCalleeSaveRegisters) { |
| 40 | if (r.AsX86().IsCpuRegister()) { |
| 41 | result |= (1 << r.AsX86().AsCpuRegister()); |
| 42 | } |
| 43 | } |
| 44 | return result; |
| 45 | } |
| 46 | |
| 47 | static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask(); |
| 48 | static constexpr uint32_t kFpCalleeSpillMask = 0u; |
| 49 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 50 | // Calling convention |
| 51 | |
| 52 | ManagedRegister X86ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { |
| 53 | return X86ManagedRegister::FromCpuRegister(ECX); |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 54 | } |
| 55 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 56 | ManagedRegister X86JniCallingConvention::InterproceduralScratchRegister() { |
| 57 | return X86ManagedRegister::FromCpuRegister(ECX); |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 58 | } |
| 59 | |
Ian Rogers | dc51b79 | 2011-09-22 20:41:37 -0700 | [diff] [blame] | 60 | ManagedRegister X86JniCallingConvention::ReturnScratchRegister() const { |
| 61 | return ManagedRegister::NoRegister(); // No free regs, so assembler uses push/pop |
| 62 | } |
| 63 | |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 64 | static ManagedRegister ReturnRegisterForShorty(const char* shorty, bool jni) { |
Ian Rogers | 169c9a7 | 2011-11-13 20:13:17 -0800 | [diff] [blame] | 65 | if (shorty[0] == 'F' || shorty[0] == 'D') { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 66 | if (jni) { |
| 67 | return X86ManagedRegister::FromX87Register(ST0); |
| 68 | } else { |
| 69 | return X86ManagedRegister::FromXmmRegister(XMM0); |
| 70 | } |
Ian Rogers | 169c9a7 | 2011-11-13 20:13:17 -0800 | [diff] [blame] | 71 | } else if (shorty[0] == 'J') { |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 72 | return X86ManagedRegister::FromRegisterPair(EAX_EDX); |
Ian Rogers | 169c9a7 | 2011-11-13 20:13:17 -0800 | [diff] [blame] | 73 | } else if (shorty[0] == 'V') { |
Ian Rogers | 45a76cb | 2011-07-21 22:00:15 -0700 | [diff] [blame] | 74 | return ManagedRegister::NoRegister(); |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 75 | } else { |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 76 | return X86ManagedRegister::FromCpuRegister(EAX); |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 77 | } |
| 78 | } |
| 79 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 80 | ManagedRegister X86ManagedRuntimeCallingConvention::ReturnRegister() { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 81 | return ReturnRegisterForShorty(GetShorty(), false); |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | ManagedRegister X86JniCallingConvention::ReturnRegister() { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 85 | return ReturnRegisterForShorty(GetShorty(), true); |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 86 | } |
| 87 | |
Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 88 | ManagedRegister X86JniCallingConvention::IntReturnRegister() { |
| 89 | return X86ManagedRegister::FromCpuRegister(EAX); |
| 90 | } |
| 91 | |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 92 | // Managed runtime calling convention |
| 93 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 94 | ManagedRegister X86ManagedRuntimeCallingConvention::MethodRegister() { |
Ian Rogers | 67375ac | 2011-09-14 00:55:44 -0700 | [diff] [blame] | 95 | return X86ManagedRegister::FromCpuRegister(EAX); |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 96 | } |
| 97 | |
| 98 | bool X86ManagedRuntimeCallingConvention::IsCurrentParamInRegister() { |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 99 | return false; // Everything is passed by stack |
| 100 | } |
| 101 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 102 | bool X86ManagedRuntimeCallingConvention::IsCurrentParamOnStack() { |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 103 | // We assume all parameters are on stack, args coming via registers are spilled as entry_spills. |
| 104 | return true; |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 105 | } |
| 106 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 107 | ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamRegister() { |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 108 | ManagedRegister res = ManagedRegister::NoRegister(); |
| 109 | if (!IsCurrentParamAFloatOrDouble()) { |
| 110 | switch (gpr_arg_count_) { |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 111 | case 0: |
| 112 | res = X86ManagedRegister::FromCpuRegister(ECX); |
| 113 | break; |
| 114 | case 1: |
| 115 | res = X86ManagedRegister::FromCpuRegister(EDX); |
| 116 | break; |
| 117 | case 2: |
| 118 | // Don't split a long between the last register and the stack. |
| 119 | if (IsCurrentParamALong()) { |
| 120 | return ManagedRegister::NoRegister(); |
| 121 | } |
| 122 | res = X86ManagedRegister::FromCpuRegister(EBX); |
| 123 | break; |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 124 | } |
| 125 | } else if (itr_float_and_doubles_ < 4) { |
| 126 | // First four float parameters are passed via XMM0..XMM3 |
| 127 | res = X86ManagedRegister::FromXmmRegister( |
| 128 | static_cast<XmmRegister>(XMM0 + itr_float_and_doubles_)); |
| 129 | } |
| 130 | return res; |
| 131 | } |
| 132 | |
| 133 | ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamHighLongRegister() { |
| 134 | ManagedRegister res = ManagedRegister::NoRegister(); |
| 135 | DCHECK(IsCurrentParamALong()); |
| 136 | switch (gpr_arg_count_) { |
| 137 | case 0: res = X86ManagedRegister::FromCpuRegister(EDX); break; |
| 138 | case 1: res = X86ManagedRegister::FromCpuRegister(EBX); break; |
| 139 | } |
| 140 | return res; |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 141 | } |
| 142 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 143 | FrameOffset X86ManagedRuntimeCallingConvention::CurrentParamStackOffset() { |
Ian Rogers | cdd1d2d | 2011-08-18 09:58:17 -0700 | [diff] [blame] | 144 | return FrameOffset(displacement_.Int32Value() + // displacement |
Ian Rogers | 790a6b7 | 2014-04-01 10:36:00 -0700 | [diff] [blame] | 145 | kFramePointerSize + // Method* |
| 146 | (itr_slots_ * kFramePointerSize)); // offset into in args |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 147 | } |
| 148 | |
Dmitry Petrochenko | fca8220 | 2014-03-21 11:21:37 +0700 | [diff] [blame] | 149 | const ManagedRegisterEntrySpills& X86ManagedRuntimeCallingConvention::EntrySpills() { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 150 | // We spill the argument registers on X86 to free them up for scratch use, we then assume |
| 151 | // all arguments are on the stack. |
| 152 | if (entry_spills_.size() == 0) { |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 153 | ResetIterator(FrameOffset(0)); |
| 154 | while (HasNext()) { |
| 155 | ManagedRegister in_reg = CurrentParamRegister(); |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 156 | bool is_long = IsCurrentParamALong(); |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 157 | if (!in_reg.IsNoRegister()) { |
| 158 | int32_t size = IsParamADouble(itr_args_) ? 8 : 4; |
| 159 | int32_t spill_offset = CurrentParamStackOffset().Uint32Value(); |
| 160 | ManagedRegisterSpill spill(in_reg, size, spill_offset); |
| 161 | entry_spills_.push_back(spill); |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 162 | if (is_long) { |
| 163 | // special case, as we need a second register here. |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 164 | in_reg = CurrentParamHighLongRegister(); |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 165 | DCHECK(!in_reg.IsNoRegister()); |
| 166 | // We have to spill the second half of the long. |
| 167 | ManagedRegisterSpill spill2(in_reg, size, spill_offset + 4); |
| 168 | entry_spills_.push_back(spill2); |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | // Keep track of the number of GPRs allocated. |
| 172 | if (!IsCurrentParamAFloatOrDouble()) { |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 173 | if (is_long) { |
| 174 | // Long was allocated in 2 registers. |
| 175 | gpr_arg_count_ += 2; |
| 176 | } else { |
| 177 | gpr_arg_count_++; |
| 178 | } |
Ian Rogers | b41b33b | 2012-03-20 14:22:54 -0700 | [diff] [blame] | 179 | } |
Mark Mendell | 3e6a3bf | 2015-01-19 14:09:22 -0500 | [diff] [blame] | 180 | } else if (is_long) { |
| 181 | // We need to skip the unused last register, which is empty. |
| 182 | // If we are already out of registers, this is harmless. |
| 183 | gpr_arg_count_ += 2; |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 184 | } |
Mark P Mendell | 966c3ae | 2015-01-27 15:45:27 +0000 | [diff] [blame] | 185 | Next(); |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 186 | } |
| 187 | } |
| 188 | return entry_spills_; |
| 189 | } |
| 190 | |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 191 | // JNI calling convention |
| 192 | |
jeffhao | 703f2cd | 2012-07-13 17:25:52 -0700 | [diff] [blame] | 193 | X86JniCallingConvention::X86JniCallingConvention(bool is_static, bool is_synchronized, |
| 194 | const char* shorty) |
Andreas Gampe | 542451c | 2016-07-26 09:02:02 -0700 | [diff] [blame] | 195 | : JniCallingConvention(is_static, is_synchronized, shorty, kX86PointerSize) { |
jeffhao | 703f2cd | 2012-07-13 17:25:52 -0700 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | uint32_t X86JniCallingConvention::CoreSpillMask() const { |
Vladimir Marko | 3224838 | 2016-05-19 10:37:24 +0100 | [diff] [blame] | 199 | return kCoreCalleeSpillMask; |
| 200 | } |
| 201 | |
| 202 | uint32_t X86JniCallingConvention::FpSpillMask() const { |
| 203 | return kFpCalleeSpillMask; |
jeffhao | 703f2cd | 2012-07-13 17:25:52 -0700 | [diff] [blame] | 204 | } |
Ian Rogers | bdb0391 | 2011-09-14 00:55:44 -0700 | [diff] [blame] | 205 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 206 | size_t X86JniCallingConvention::FrameSize() { |
jeffhao | 703f2cd | 2012-07-13 17:25:52 -0700 | [diff] [blame] | 207 | // Method*, return address and callee save area size, local reference segment state |
Andreas Gampe | 542451c | 2016-07-26 09:02:02 -0700 | [diff] [blame] | 208 | size_t frame_data_size = static_cast<size_t>(kX86PointerSize) + |
Andreas Gampe | cf4035a | 2014-05-28 22:43:01 -0700 | [diff] [blame] | 209 | (2 + CalleeSaveRegisters().size()) * kFramePointerSize; |
Mathieu Chartier | eb8167a | 2014-05-07 15:43:14 -0700 | [diff] [blame] | 210 | // References plus 2 words for HandleScope header |
Andreas Gampe | 542451c | 2016-07-26 09:02:02 -0700 | [diff] [blame] | 211 | size_t handle_scope_size = HandleScope::SizeOf(kX86PointerSize, ReferenceCount()); |
Ian Rogers | 0d666d8 | 2011-08-14 16:03:46 -0700 | [diff] [blame] | 212 | // Plus return value spill area size |
Mathieu Chartier | eb8167a | 2014-05-07 15:43:14 -0700 | [diff] [blame] | 213 | return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment); |
Ian Rogers | 0d666d8 | 2011-08-14 16:03:46 -0700 | [diff] [blame] | 214 | } |
| 215 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 216 | size_t X86JniCallingConvention::OutArgSize() { |
Ian Rogers | 790a6b7 | 2014-04-01 10:36:00 -0700 | [diff] [blame] | 217 | return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment); |
Ian Rogers | 7a99c11 | 2011-09-07 12:48:27 -0700 | [diff] [blame] | 218 | } |
| 219 | |
Vladimir Marko | 3224838 | 2016-05-19 10:37:24 +0100 | [diff] [blame] | 220 | ArrayRef<const ManagedRegister> X86JniCallingConvention::CalleeSaveRegisters() const { |
| 221 | return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters); |
| 222 | } |
| 223 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 224 | bool X86JniCallingConvention::IsCurrentParamInRegister() { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 225 | return false; // Everything is passed by stack. |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 226 | } |
| 227 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 228 | bool X86JniCallingConvention::IsCurrentParamOnStack() { |
Ian Rogers | b5d09b2 | 2012-03-06 22:14:17 -0800 | [diff] [blame] | 229 | return true; // Everything is passed by stack. |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 230 | } |
| 231 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 232 | ManagedRegister X86JniCallingConvention::CurrentParamRegister() { |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 233 | LOG(FATAL) << "Should not reach here"; |
| 234 | return ManagedRegister::NoRegister(); |
| 235 | } |
| 236 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 237 | FrameOffset X86JniCallingConvention::CurrentParamStackOffset() { |
Ian Rogers | 790a6b7 | 2014-04-01 10:36:00 -0700 | [diff] [blame] | 238 | return FrameOffset(displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize)); |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 239 | } |
| 240 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 241 | size_t X86JniCallingConvention::NumberOfOutgoingStackArgs() { |
Ian Rogers | 169c9a7 | 2011-11-13 20:13:17 -0800 | [diff] [blame] | 242 | size_t static_args = IsStatic() ? 1 : 0; // count jclass |
Ian Rogers | 7a99c11 | 2011-09-07 12:48:27 -0700 | [diff] [blame] | 243 | // regular argument parameters and this |
Ian Rogers | 169c9a7 | 2011-11-13 20:13:17 -0800 | [diff] [blame] | 244 | size_t param_args = NumArgs() + NumLongOrDoubleArgs(); |
Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 245 | // count JNIEnv* and return pc (pushed after Method*) |
| 246 | size_t total_args = static_args + param_args + 2; |
| 247 | return total_args; |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 248 | } |
| 249 | |
Ian Rogers | 2c8f653 | 2011-09-02 17:16:34 -0700 | [diff] [blame] | 250 | } // namespace x86 |
Ian Rogers | b033c75 | 2011-07-20 12:22:35 -0700 | [diff] [blame] | 251 | } // namespace art |