huangcm
2025-04-22 c8cf547b11f2c03565d8fb8b8bcdc69860d0ed08
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include "calling_convention_mips.h"
 
#include <android-base/logging.h>
 
#include "arch/instruction_set.h"
#include "handle_scope-inl.h"
#include "utils/mips/managed_register_mips.h"
 
namespace art {
namespace mips {
 
//
// JNI calling convention constants.
//
 
// Up to how many float-like (float, double) args can be enregistered in floating-point registers.
// The rest of the args must go in integer registers or on the stack.
constexpr size_t kMaxFloatOrDoubleRegisterArguments = 2u;
// Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be
// enregistered. The rest of the args must go on the stack.
constexpr size_t kMaxIntLikeRegisterArguments = 4u;
 
static const Register kJniCoreArgumentRegisters[] = { A0, A1, A2, A3 };
static const FRegister kJniFArgumentRegisters[] = { F12, F14 };
static const DRegister kJniDArgumentRegisters[] = { D6, D7 };
 
//
// Managed calling convention constants.
//
 
static const Register kManagedCoreArgumentRegisters[] = { A0, A1, A2, A3, T0, T1 };
static const FRegister kManagedFArgumentRegisters[] = { F8, F10, F12, F14, F16, F18 };
static const DRegister kManagedDArgumentRegisters[] = { D4, D5, D6, D7, D8, D9 };
 
static constexpr ManagedRegister kCalleeSaveRegisters[] = {
    // Core registers.
    MipsManagedRegister::FromCoreRegister(S2),
    MipsManagedRegister::FromCoreRegister(S3),
    MipsManagedRegister::FromCoreRegister(S4),
    MipsManagedRegister::FromCoreRegister(S5),
    MipsManagedRegister::FromCoreRegister(S6),
    MipsManagedRegister::FromCoreRegister(S7),
    MipsManagedRegister::FromCoreRegister(FP),
    // No hard float callee saves.
};
 
static constexpr uint32_t CalculateCoreCalleeSpillMask() {
  // RA is a special callee save which is not reported by CalleeSaveRegisters().
  uint32_t result = 1 << RA;
  for (auto&& r : kCalleeSaveRegisters) {
    if (r.AsMips().IsCoreRegister()) {
      result |= (1 << r.AsMips().AsCoreRegister());
    }
  }
  return result;
}
 
static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask();
static constexpr uint32_t kFpCalleeSpillMask = 0u;
 
// Calling convention
ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
  return MipsManagedRegister::FromCoreRegister(T9);
}
 
ManagedRegister MipsJniCallingConvention::InterproceduralScratchRegister() {
  return MipsManagedRegister::FromCoreRegister(T9);
}
 
static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
  if (shorty[0] == 'F') {
    return MipsManagedRegister::FromFRegister(F0);
  } else if (shorty[0] == 'D') {
    return MipsManagedRegister::FromDRegister(D0);
  } else if (shorty[0] == 'J') {
    return MipsManagedRegister::FromRegisterPair(V0_V1);
  } else if (shorty[0] == 'V') {
    return MipsManagedRegister::NoRegister();
  } else {
    return MipsManagedRegister::FromCoreRegister(V0);
  }
}
 
ManagedRegister MipsManagedRuntimeCallingConvention::ReturnRegister() {
  return ReturnRegisterForShorty(GetShorty());
}
 
ManagedRegister MipsJniCallingConvention::ReturnRegister() {
  return ReturnRegisterForShorty(GetShorty());
}
 
ManagedRegister MipsJniCallingConvention::IntReturnRegister() {
  return MipsManagedRegister::FromCoreRegister(V0);
}
 
// Managed runtime calling convention
 
ManagedRegister MipsManagedRuntimeCallingConvention::MethodRegister() {
  return MipsManagedRegister::FromCoreRegister(A0);
}
 
bool MipsManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
  return false;  // Everything moved to stack on entry.
}
 
bool MipsManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
  return true;
}
 
ManagedRegister MipsManagedRuntimeCallingConvention::CurrentParamRegister() {
  LOG(FATAL) << "Should not reach here";
  UNREACHABLE();
}
 
FrameOffset MipsManagedRuntimeCallingConvention::CurrentParamStackOffset() {
  CHECK(IsCurrentParamOnStack());
  FrameOffset result =
      FrameOffset(displacement_.Int32Value() +        // displacement
                  kFramePointerSize +                 // Method*
                  (itr_slots_ * kFramePointerSize));  // offset into in args
  return result;
}
 
const ManagedRegisterEntrySpills& MipsManagedRuntimeCallingConvention::EntrySpills() {
  // We spill the argument registers on MIPS to free them up for scratch use, we then assume
  // all arguments are on the stack.
  if ((entry_spills_.size() == 0) && (NumArgs() > 0)) {
    uint32_t gpr_index = 1;  // Skip A0, it is used for ArtMethod*.
    uint32_t fpr_index = 0;
 
    for (ResetIterator(FrameOffset(0)); HasNext(); Next()) {
      if (IsCurrentParamAFloatOrDouble()) {
        if (IsCurrentParamADouble()) {
          if (fpr_index < arraysize(kManagedDArgumentRegisters)) {
            entry_spills_.push_back(
                MipsManagedRegister::FromDRegister(kManagedDArgumentRegisters[fpr_index++]));
          } else {
            entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
          }
        } else {
          if (fpr_index < arraysize(kManagedFArgumentRegisters)) {
            entry_spills_.push_back(
                MipsManagedRegister::FromFRegister(kManagedFArgumentRegisters[fpr_index++]));
          } else {
            entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
          }
        }
      } else {
        if (IsCurrentParamALong() && !IsCurrentParamAReference()) {
          if (gpr_index == 1 || gpr_index == 3) {
            // Don't use A1-A2(A3-T0) as a register pair, move to A2-A3(T0-T1) instead.
            gpr_index++;
          }
          if (gpr_index < arraysize(kManagedCoreArgumentRegisters) - 1) {
            entry_spills_.push_back(
                MipsManagedRegister::FromCoreRegister(kManagedCoreArgumentRegisters[gpr_index++]));
          } else if (gpr_index == arraysize(kManagedCoreArgumentRegisters) - 1) {
            gpr_index++;
            entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
          } else {
            entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
          }
        }
 
        if (gpr_index < arraysize(kManagedCoreArgumentRegisters)) {
          entry_spills_.push_back(
              MipsManagedRegister::FromCoreRegister(kManagedCoreArgumentRegisters[gpr_index++]));
        } else {
          entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
        }
      }
    }
  }
  return entry_spills_;
}
 
// JNI calling convention
 
MipsJniCallingConvention::MipsJniCallingConvention(bool is_static,
                                                   bool is_synchronized,
                                                   bool is_critical_native,
                                                   const char* shorty)
    : JniCallingConvention(is_static,
                           is_synchronized,
                           is_critical_native,
                           shorty,
                           kMipsPointerSize) {
  // SYSTEM V - Application Binary Interface (MIPS RISC Processor):
  // Data Representation - Fundamental Types (3-4) specifies fundamental alignments for each type.
  //   "Each member is assigned to the lowest available offset with the appropriate alignment. This
  // may require internal padding, depending on the previous member."
  //
  // All of our stack arguments are usually 4-byte aligned, however longs and doubles must be 8
  // bytes aligned. Add padding to maintain 8-byte alignment invariant.
  //
  // Compute padding to ensure longs and doubles are not split in o32.
  size_t padding = 0;
  size_t cur_arg, cur_reg;
  if (LIKELY(HasExtraArgumentsForJni())) {
    // Ignore the 'this' jobject or jclass for static methods and the JNIEnv.
    // We start at the aligned register A2.
    //
    // Ignore the first 2 parameters because they are guaranteed to be aligned.
    cur_arg = NumImplicitArgs();  // Skip the "this" argument.
    cur_reg = 2;  // Skip {A0=JNIEnv, A1=jobject} / {A0=JNIEnv, A1=jclass} parameters (start at A2).
  } else {
    // Check every parameter.
    cur_arg = 0;
    cur_reg = 0;
  }
 
  // Shift across a logical register mapping that looks like:
  //
  //   | A0 | A1 | A2 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 |
  //
  //   or some of variants with floating-point registers (F12 and F14), for example
  //
  //   | F12     | F14 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 |
  //
  //   (where SP is the stack pointer at the start of called function).
  //
  // Any time there would normally be a long/double in an odd logical register,
  // we have to push out the rest of the mappings by 4 bytes to maintain an 8-byte alignment.
  //
  // This works for both physical register pairs {A0, A1}, {A2, A3},
  // floating-point registers F12, F14 and for when the value is on the stack.
  //
  // For example:
  // (a) long would normally go into A1, but we shift it into A2
  //  | INT | (PAD) | LONG    |
  //  | A0  |  A1   | A2 | A3 |
  //
  // (b) long would normally go into A3, but we shift it into SP
  //  | INT | INT | INT | (PAD) | LONG        |
  //  | A0  | A1  | A2  |  A3   | SP+16 SP+20 |
  //
  // where INT is any <=4 byte arg, and LONG is any 8-byte arg.
  for (; cur_arg < NumArgs(); cur_arg++) {
    if (IsParamALongOrDouble(cur_arg)) {
      if ((cur_reg & 1) != 0) {
        padding += 4;
        cur_reg++;   // Additional bump to ensure alignment.
      }
      cur_reg += 2;  // Bump the iterator twice for every long argument.
    } else {
      cur_reg++;     // Bump the iterator for every argument.
    }
  }
  if (cur_reg < kMaxIntLikeRegisterArguments) {
    // As a special case when, as a result of shifting (or not) there are no arguments on the stack,
    // we actually have 0 stack padding.
    //
    // For example with @CriticalNative and:
    // (int, long) -> shifts the long but doesn't need to pad the stack
    //
    //          shift
    //           \/
    //  | INT | (PAD) | LONG      | (EMPTY) ...
    //  | r0  |  r1   |  r2  | r3 |   SP    ...
    //                                /\
    //                          no stack padding
    padding_ = 0;
  } else {
    padding_ = padding;
  }
 
  // Argument Passing (3-17):
  //   "When the first argument is integral, the remaining arguments are passed in the integer
  // registers."
  //
  //   "The rules that determine which arguments go into registers and which ones must be passed on
  // the stack are most easily explained by considering the list of arguments as a structure,
  // aligned according to normal structure rules. Mapping of this structure into the combination of
  // stack and registers is as follows: up to two leading floating-point arguments can be passed in
  // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on
  // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset.
  // Holes left in the structure for alignment are unused, whether in registers or in the stack."
  //
  // For example with @CriticalNative and:
  // (a) first argument is not floating-point, so all go into integer registers
  //  | INT | FLOAT | DOUBLE  |
  //  | A0  |  A1   | A2 | A3 |
  // (b) first argument is floating-point, but 2nd is integer
  //  | FLOAT | INT | DOUBLE  |
  //  |  F12  | A1  | A2 | A3 |
  // (c) first two arguments are floating-point (float, double)
  //  | FLOAT | (PAD) | DOUBLE |  INT  |
  //  |  F12  |       |  F14   | SP+16 |
  // (d) first two arguments are floating-point (double, float)
  //  | DOUBLE | FLOAT | INT |
  //  |  F12   |  F14  | A3  |
  // (e) first three arguments are floating-point, but just first two will go into fp registers
  //  | DOUBLE | FLOAT | FLOAT |
  //  |  F12   |  F14  |  A3   |
  //
  // Find out if the first argument is a floating-point. In that case, floating-point registers will
  // be used for up to two leading floating-point arguments. Otherwise, all arguments will be passed
  // using integer registers.
  use_fp_arg_registers_ = false;
  if (is_critical_native) {
    if (NumArgs() > 0) {
      if (IsParamAFloatOrDouble(0)) {
        use_fp_arg_registers_ = true;
      }
    }
  }
}
 
uint32_t MipsJniCallingConvention::CoreSpillMask() const {
  return kCoreCalleeSpillMask;
}
 
uint32_t MipsJniCallingConvention::FpSpillMask() const {
  return kFpCalleeSpillMask;
}
 
ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
  return MipsManagedRegister::FromCoreRegister(AT);
}
 
size_t MipsJniCallingConvention::FrameSize() {
  // ArtMethod*, RA and callee save area size, local reference segment state.
  const size_t method_ptr_size = static_cast<size_t>(kMipsPointerSize);
  const size_t ra_return_addr_size = kFramePointerSize;
  const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
 
  size_t frame_data_size = method_ptr_size + ra_return_addr_size + callee_save_area_size;
 
  if (LIKELY(HasLocalReferenceSegmentState())) {
    // Local reference segment state.
    frame_data_size += kFramePointerSize;
  }
 
  // References plus 2 words for HandleScope header.
  const size_t handle_scope_size = HandleScope::SizeOf(kMipsPointerSize, ReferenceCount());
 
  size_t total_size = frame_data_size;
  if (LIKELY(HasHandleScope())) {
    // HandleScope is sometimes excluded.
    total_size += handle_scope_size;    // Handle scope size.
  }
 
  // Plus return value spill area size.
  total_size += SizeOfReturnValue();
 
  return RoundUp(total_size, kStackAlignment);
}
 
size_t MipsJniCallingConvention::OutArgSize() {
  // Argument Passing (3-17):
  //   "Despite the fact that some or all of the arguments to a function are passed in registers,
  // always allocate space on the stack for all arguments. This stack space should be a structure
  // large enough to contain all the arguments, aligned according to normal structure rules (after
  // promotion and structure return pointer insertion). The locations within the stack frame used
  // for arguments are called the home locations."
  //
  // Allocate 16 bytes for home locations + space needed for stack arguments.
  return RoundUp(
      (kMaxIntLikeRegisterArguments + NumberOfOutgoingStackArgs()) * kFramePointerSize + padding_,
      kStackAlignment);
}
 
ArrayRef<const ManagedRegister> MipsJniCallingConvention::CalleeSaveRegisters() const {
  return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
}
 
// JniCallingConvention ABI follows o32 where longs and doubles must occur
// in even register numbers and stack slots.
void MipsJniCallingConvention::Next() {
  JniCallingConvention::Next();
 
  if (LIKELY(HasNext())) {  // Avoid CHECK failure for IsCurrentParam
    // Ensure slot is 8-byte aligned for longs/doubles (o32).
    if (IsCurrentParamALongOrDouble() && ((itr_slots_ & 0x1u) != 0)) {
      // itr_slots_ needs to be an even number, according to o32.
      itr_slots_++;
    }
  }
}
 
bool MipsJniCallingConvention::IsCurrentParamInRegister() {
  // Argument Passing (3-17):
  //   "The rules that determine which arguments go into registers and which ones must be passed on
  // the stack are most easily explained by considering the list of arguments as a structure,
  // aligned according to normal structure rules. Mapping of this structure into the combination of
  // stack and registers is as follows: up to two leading floating-point arguments can be passed in
  // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on
  // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset.
  // Holes left in the structure for alignment are unused, whether in registers or in the stack."
  //
  // Even when floating-point registers are used, there can be up to 4 arguments passed in
  // registers.
  return itr_slots_ < kMaxIntLikeRegisterArguments;
}
 
bool MipsJniCallingConvention::IsCurrentParamOnStack() {
  return !IsCurrentParamInRegister();
}
 
ManagedRegister MipsJniCallingConvention::CurrentParamRegister() {
  CHECK_LT(itr_slots_, kMaxIntLikeRegisterArguments);
  // Up to two leading floating-point arguments can be passed in floating-point registers.
  if (use_fp_arg_registers_ && (itr_args_ < kMaxFloatOrDoubleRegisterArguments)) {
    if (IsCurrentParamAFloatOrDouble()) {
      if (IsCurrentParamADouble()) {
        return MipsManagedRegister::FromDRegister(kJniDArgumentRegisters[itr_args_]);
      } else {
        return MipsManagedRegister::FromFRegister(kJniFArgumentRegisters[itr_args_]);
      }
    }
  }
  // All other arguments (including other floating-point arguments) will be passed in integer
  // registers.
  if (IsCurrentParamALongOrDouble()) {
    if (itr_slots_ == 0u) {
      return MipsManagedRegister::FromRegisterPair(A0_A1);
    } else {
      CHECK_EQ(itr_slots_, 2u);
      return MipsManagedRegister::FromRegisterPair(A2_A3);
    }
  } else {
    return MipsManagedRegister::FromCoreRegister(kJniCoreArgumentRegisters[itr_slots_]);
  }
}
 
FrameOffset MipsJniCallingConvention::CurrentParamStackOffset() {
  CHECK_GE(itr_slots_, kMaxIntLikeRegisterArguments);
  size_t offset = displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize);
  CHECK_LT(offset, OutArgSize());
  return FrameOffset(offset);
}
 
size_t MipsJniCallingConvention::NumberOfOutgoingStackArgs() {
  size_t static_args = HasSelfClass() ? 1 : 0;            // Count jclass.
  // Regular argument parameters and this.
  size_t param_args = NumArgs() + NumLongOrDoubleArgs();  // Twice count 8-byte args.
  // Count JNIEnv* less arguments in registers.
  size_t internal_args = (HasJniEnv() ? 1 : 0);
  size_t total_args = static_args + param_args + internal_args;
 
  return total_args - std::min(kMaxIntLikeRegisterArguments, static_cast<size_t>(total_args));
}
 
}  // namespace mips
}  // namespace art