utility_arm64.cc revision 2689fbad6b5ec1ae8f8c8791a80c6fd3cf24144d
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 */
16
17#include "arm64_lir.h"
18#include "codegen_arm64.h"
19#include "dex/quick/mir_to_lir-inl.h"
20
21namespace art {
22
23/* This file contains codegen for the A64 ISA. */
24
25static int32_t EncodeImmSingle(uint32_t bits) {
26  /*
27   * Valid values will have the form:
28   *
29   *   aBbb.bbbc.defg.h000.0000.0000.0000.0000
30   *
31   * where B = not(b). In other words, if b == 1, then B == 0 and viceversa.
32   */
33
34  // bits[19..0] are cleared.
35  if ((bits & 0x0007ffff) != 0)
36    return -1;
37
38  // bits[29..25] are all set or all cleared.
39  uint32_t b_pattern = (bits >> 16) & 0x3e00;
40  if (b_pattern != 0 && b_pattern != 0x3e00)
41    return -1;
42
43  // bit[30] and bit[29] are opposite.
44  if (((bits ^ (bits << 1)) & 0x40000000) == 0)
45    return -1;
46
47  // bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000
48  // bit7: a000.0000
49  uint32_t bit7 = ((bits >> 31) & 0x1) << 7;
50  // bit6: 0b00.0000
51  uint32_t bit6 = ((bits >> 29) & 0x1) << 6;
52  // bit5_to_0: 00cd.efgh
53  uint32_t bit5_to_0 = (bits >> 19) & 0x3f;
54  return (bit7 | bit6 | bit5_to_0);
55}
56
57static int32_t EncodeImmDouble(uint64_t bits) {
58  /*
59   * Valid values will have the form:
60   *
61   *   aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
62   *   0000.0000.0000.0000.0000.0000.0000.0000
63   *
64   * where B = not(b).
65   */
66
67  // bits[47..0] are cleared.
68  if ((bits & UINT64_C(0xffffffffffff)) != 0)
69    return -1;
70
71  // bits[61..54] are all set or all cleared.
72  uint32_t b_pattern = (bits >> 48) & 0x3fc0;
73  if (b_pattern != 0 && b_pattern != 0x3fc0)
74    return -1;
75
76  // bit[62] and bit[61] are opposite.
77  if (((bits ^ (bits << 1)) & UINT64_C(0x4000000000000000)) == 0)
78    return -1;
79
80  // bit7: a000.0000
81  uint32_t bit7 = ((bits >> 63) & 0x1) << 7;
82  // bit6: 0b00.0000
83  uint32_t bit6 = ((bits >> 61) & 0x1) << 6;
84  // bit5_to_0: 00cd.efgh
85  uint32_t bit5_to_0 = (bits >> 48) & 0x3f;
86  return (bit7 | bit6 | bit5_to_0);
87}
88
89LIR* Arm64Mir2Lir::LoadFPConstantValue(RegStorage r_dest, int32_t value) {
90  DCHECK(r_dest.IsSingle());
91  if (value == 0) {
92    return NewLIR2(kA64Fmov2sw, r_dest.GetReg(), rwzr);
93  } else {
94    int32_t encoded_imm = EncodeImmSingle((uint32_t)value);
95    if (encoded_imm >= 0) {
96      return NewLIR2(kA64Fmov2fI, r_dest.GetReg(), encoded_imm);
97    }
98  }
99
100  LIR* data_target = ScanLiteralPool(literal_list_, value, 0);
101  if (data_target == NULL) {
102    // Wide, as we need 8B alignment.
103    data_target = AddWideData(&literal_list_, value, 0);
104  }
105
106  ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
107  LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kA64Ldr2fp,
108                            r_dest.GetReg(), 0, 0, 0, 0, data_target);
109  AppendLIR(load_pc_rel);
110  return load_pc_rel;
111}
112
113LIR* Arm64Mir2Lir::LoadFPConstantValueWide(RegStorage r_dest, int64_t value) {
114  DCHECK(r_dest.IsDouble());
115  if (value == 0) {
116    return NewLIR2(kA64Fmov2Sx, r_dest.GetReg(), rxzr);
117  } else {
118    int32_t encoded_imm = EncodeImmDouble(value);
119    if (encoded_imm >= 0) {
120      return NewLIR2(FWIDE(kA64Fmov2fI), r_dest.GetReg(), encoded_imm);
121    }
122  }
123
124  // No short form - load from the literal pool.
125  int32_t val_lo = Low32Bits(value);
126  int32_t val_hi = High32Bits(value);
127  LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
128  if (data_target == NULL) {
129    data_target = AddWideData(&literal_list_, val_lo, val_hi);
130  }
131
132  ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
133  LIR* load_pc_rel = RawLIR(current_dalvik_offset_, FWIDE(kA64Ldr2fp),
134                            r_dest.GetReg(), 0, 0, 0, 0, data_target);
135  AppendLIR(load_pc_rel);
136  return load_pc_rel;
137}
138
139static int CountLeadingZeros(bool is_wide, uint64_t value) {
140  return (is_wide) ? __builtin_clzll(value) : __builtin_clz((uint32_t)value);
141}
142
143static int CountTrailingZeros(bool is_wide, uint64_t value) {
144  return (is_wide) ? __builtin_ctzll(value) : __builtin_ctz((uint32_t)value);
145}
146
147static int CountSetBits(bool is_wide, uint64_t value) {
148  return ((is_wide) ?
149          __builtin_popcountll(value) : __builtin_popcount((uint32_t)value));
150}
151
152/**
153 * @brief Try encoding an immediate in the form required by logical instructions.
154 *
155 * @param is_wide Whether @p value is a 64-bit (as opposed to 32-bit) value.
156 * @param value An integer to be encoded. This is interpreted as 64-bit if @p is_wide is true and as
157 *   32-bit if @p is_wide is false.
158 * @return A non-negative integer containing the encoded immediate or -1 if the encoding failed.
159 * @note This is the inverse of Arm64Mir2Lir::DecodeLogicalImmediate().
160 */
161int Arm64Mir2Lir::EncodeLogicalImmediate(bool is_wide, uint64_t value) {
162  unsigned n, imm_s, imm_r;
163
164  // Logical immediates are encoded using parameters n, imm_s and imm_r using
165  // the following table:
166  //
167  //  N   imms    immr    size        S             R
168  //  1  ssssss  rrrrrr    64    UInt(ssssss)  UInt(rrrrrr)
169  //  0  0sssss  xrrrrr    32    UInt(sssss)   UInt(rrrrr)
170  //  0  10ssss  xxrrrr    16    UInt(ssss)    UInt(rrrr)
171  //  0  110sss  xxxrrr     8    UInt(sss)     UInt(rrr)
172  //  0  1110ss  xxxxrr     4    UInt(ss)      UInt(rr)
173  //  0  11110s  xxxxxr     2    UInt(s)       UInt(r)
174  // (s bits must not be all set)
175  //
176  // A pattern is constructed of size bits, where the least significant S+1
177  // bits are set. The pattern is rotated right by R, and repeated across a
178  // 32 or 64-bit value, depending on destination register width.
179  //
180  // To test if an arbitary immediate can be encoded using this scheme, an
181  // iterative algorithm is used.
182  //
183
184  // 1. If the value has all set or all clear bits, it can't be encoded.
185  if (value == 0 || value == ~UINT64_C(0) ||
186      (!is_wide && (uint32_t)value == ~UINT32_C(0))) {
187    return -1;
188  }
189
190  unsigned lead_zero  = CountLeadingZeros(is_wide, value);
191  unsigned lead_one   = CountLeadingZeros(is_wide, ~value);
192  unsigned trail_zero = CountTrailingZeros(is_wide, value);
193  unsigned trail_one  = CountTrailingZeros(is_wide, ~value);
194  unsigned set_bits   = CountSetBits(is_wide, value);
195
196  // The fixed bits in the immediate s field.
197  // If width == 64 (X reg), start at 0xFFFFFF80.
198  // If width == 32 (W reg), start at 0xFFFFFFC0, as the iteration for 64-bit
199  // widths won't be executed.
200  unsigned width = (is_wide) ? 64 : 32;
201  int imm_s_fixed = (is_wide) ? -128 : -64;
202  int imm_s_mask = 0x3f;
203
204  for (;;) {
205    // 2. If the value is two bits wide, it can be encoded.
206    if (width == 2) {
207      n = 0;
208      imm_s = 0x3C;
209      imm_r = (value & 3) - 1;
210      break;
211    }
212
213    n = (width == 64) ? 1 : 0;
214    imm_s = ((imm_s_fixed | (set_bits - 1)) & imm_s_mask);
215    if ((lead_zero + set_bits) == width) {
216      imm_r = 0;
217    } else {
218      imm_r = (lead_zero > 0) ? (width - trail_zero) : lead_one;
219    }
220
221    // 3. If the sum of leading zeros, trailing zeros and set bits is
222    //    equal to the bit width of the value, it can be encoded.
223    if (lead_zero + trail_zero + set_bits == width) {
224      break;
225    }
226
227    // 4. If the sum of leading ones, trailing ones and unset bits in the
228    //    value is equal to the bit width of the value, it can be encoded.
229    if (lead_one + trail_one + (width - set_bits) == width) {
230      break;
231    }
232
233    // 5. If the most-significant half of the bitwise value is equal to
234    //    the least-significant half, return to step 2 using the
235    //    least-significant half of the value.
236    uint64_t mask = (UINT64_C(1) << (width >> 1)) - 1;
237    if ((value & mask) == ((value >> (width >> 1)) & mask)) {
238      width >>= 1;
239      set_bits >>= 1;
240      imm_s_fixed >>= 1;
241      continue;
242    }
243
244    // 6. Otherwise, the value can't be encoded.
245    return -1;
246  }
247
248  return (n << 12 | imm_r << 6 | imm_s);
249}
250
251bool Arm64Mir2Lir::InexpensiveConstantInt(int32_t value) {
252  return false;  // (ModifiedImmediate(value) >= 0) || (ModifiedImmediate(~value) >= 0);
253}
254
255bool Arm64Mir2Lir::InexpensiveConstantFloat(int32_t value) {
256  return EncodeImmSingle(value) >= 0;
257}
258
259bool Arm64Mir2Lir::InexpensiveConstantLong(int64_t value) {
260  return InexpensiveConstantInt(High32Bits(value)) && InexpensiveConstantInt(Low32Bits(value));
261}
262
263bool Arm64Mir2Lir::InexpensiveConstantDouble(int64_t value) {
264  return EncodeImmDouble(value) >= 0;
265}
266
267/*
268 * Load a immediate using one single instruction when possible; otherwise
269 * use a pair of movz and movk instructions.
270 *
271 * No additional register clobbering operation performed. Use this version when
272 * 1) r_dest is freshly returned from AllocTemp or
273 * 2) The codegen is under fixed register usage
274 */
275LIR* Arm64Mir2Lir::LoadConstantNoClobber(RegStorage r_dest, int value) {
276  LIR* res;
277
278  if (r_dest.IsFloat()) {
279    return LoadFPConstantValue(r_dest, value);
280  }
281
282  if (r_dest.Is64Bit()) {
283    return LoadConstantWide(r_dest, value);
284  }
285
286  // Loading SP/ZR with an immediate is not supported.
287  DCHECK(!A64_REG_IS_SP(r_dest.GetReg()));
288  DCHECK(!A64_REG_IS_ZR(r_dest.GetReg()));
289
290  // Compute how many movk, movz instructions are needed to load the value.
291  uint16_t high_bits = High16Bits(value);
292  uint16_t low_bits = Low16Bits(value);
293
294  bool low_fast = ((uint16_t)(low_bits + 1) <= 1);
295  bool high_fast = ((uint16_t)(high_bits + 1) <= 1);
296
297  if (LIKELY(low_fast || high_fast)) {
298    // 1 instruction is enough to load the immediate.
299    if (LIKELY(low_bits == high_bits)) {
300      // Value is either 0 or -1: we can just use wzr.
301      ArmOpcode opcode = LIKELY(low_bits == 0) ? kA64Mov2rr : kA64Mvn2rr;
302      res = NewLIR2(opcode, r_dest.GetReg(), rwzr);
303    } else {
304      uint16_t uniform_bits, useful_bits;
305      int shift;
306
307      if (LIKELY(high_fast)) {
308        shift = 0;
309        uniform_bits = high_bits;
310        useful_bits = low_bits;
311      } else {
312        shift = 1;
313        uniform_bits = low_bits;
314        useful_bits = high_bits;
315      }
316
317      if (UNLIKELY(uniform_bits != 0)) {
318        res = NewLIR3(kA64Movn3rdM, r_dest.GetReg(), ~useful_bits, shift);
319      } else {
320        res = NewLIR3(kA64Movz3rdM, r_dest.GetReg(), useful_bits, shift);
321      }
322    }
323  } else {
324    // movk, movz require 2 instructions. Try detecting logical immediates.
325    int log_imm = EncodeLogicalImmediate(/*is_wide=*/false, value);
326    if (log_imm >= 0) {
327      res = NewLIR3(kA64Orr3Rrl, r_dest.GetReg(), rwzr, log_imm);
328    } else {
329      // Use 2 instructions.
330      res = NewLIR3(kA64Movz3rdM, r_dest.GetReg(), low_bits, 0);
331      NewLIR3(kA64Movk3rdM, r_dest.GetReg(), high_bits, 1);
332    }
333  }
334
335  return res;
336}
337
338// TODO: clean up the names. LoadConstantWide() should really be LoadConstantNoClobberWide().
339LIR* Arm64Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) {
340  // Maximum number of instructions to use for encoding the immediate.
341  const int max_num_ops = 2;
342
343  if (r_dest.IsFloat()) {
344    return LoadFPConstantValueWide(r_dest, value);
345  }
346
347  DCHECK(r_dest.Is64Bit());
348
349  // Loading SP/ZR with an immediate is not supported.
350  DCHECK(!A64_REG_IS_SP(r_dest.GetReg()));
351  DCHECK(!A64_REG_IS_ZR(r_dest.GetReg()));
352
353  if (LIKELY(value == INT64_C(0) || value == INT64_C(-1))) {
354    // value is either 0 or -1: we can just use xzr.
355    ArmOpcode opcode = LIKELY(value == 0) ? WIDE(kA64Mov2rr) : WIDE(kA64Mvn2rr);
356    return NewLIR2(opcode, r_dest.GetReg(), rxzr);
357  }
358
359  // At least one in value's halfwords is not 0x0, nor 0xffff: find out how many.
360  int num_0000_halfwords = 0;
361  int num_ffff_halfwords = 0;
362  uint64_t uvalue = static_cast<uint64_t>(value);
363  for (int shift = 0; shift < 64; shift += 16) {
364    uint16_t halfword = static_cast<uint16_t>(uvalue >> shift);
365    if (halfword == 0)
366      num_0000_halfwords++;
367    else if (halfword == UINT16_C(0xffff))
368      num_ffff_halfwords++;
369  }
370  int num_fast_halfwords = std::max(num_0000_halfwords, num_ffff_halfwords);
371
372  if (num_fast_halfwords < 3) {
373    // A single movz/movn is not enough. Try the logical immediate route.
374    int log_imm = EncodeLogicalImmediate(/*is_wide=*/true, value);
375    if (log_imm >= 0) {
376      return NewLIR3(WIDE(kA64Orr3Rrl), r_dest.GetReg(), rxzr, log_imm);
377    }
378  }
379
380  if (num_fast_halfwords >= 4 - max_num_ops) {
381    // We can encode the number using a movz/movn followed by one or more movk.
382    ArmOpcode op;
383    uint16_t background;
384    LIR* res = nullptr;
385
386    // Decide whether to use a movz or a movn.
387    if (num_0000_halfwords >= num_ffff_halfwords) {
388      op = WIDE(kA64Movz3rdM);
389      background = 0;
390    } else {
391      op = WIDE(kA64Movn3rdM);
392      background = 0xffff;
393    }
394
395    // Emit the first instruction (movz, movn).
396    int shift;
397    for (shift = 0; shift < 4; shift++) {
398      uint16_t halfword = static_cast<uint16_t>(uvalue >> (shift << 4));
399      if (halfword != background) {
400        res = NewLIR3(op, r_dest.GetReg(), halfword ^ background, shift);
401        break;
402      }
403    }
404
405    // Emit the movk instructions.
406    for (shift++; shift < 4; shift++) {
407      uint16_t halfword = static_cast<uint16_t>(uvalue >> (shift << 4));
408      if (halfword != background) {
409        NewLIR3(WIDE(kA64Movk3rdM), r_dest.GetReg(), halfword, shift);
410      }
411    }
412    return res;
413  }
414
415  // Use the literal pool.
416  int32_t val_lo = Low32Bits(value);
417  int32_t val_hi = High32Bits(value);
418  LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
419  if (data_target == NULL) {
420    data_target = AddWideData(&literal_list_, val_lo, val_hi);
421  }
422
423  ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
424  LIR *res = RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2rp),
425                    r_dest.GetReg(), 0, 0, 0, 0, data_target);
426  AppendLIR(res);
427  return res;
428}
429
430LIR* Arm64Mir2Lir::OpUnconditionalBranch(LIR* target) {
431  LIR* res = NewLIR1(kA64B1t, 0 /* offset to be patched  during assembly */);
432  res->target = target;
433  return res;
434}
435
436LIR* Arm64Mir2Lir::OpCondBranch(ConditionCode cc, LIR* target) {
437  LIR* branch = NewLIR2(kA64B2ct, ArmConditionEncoding(cc),
438                        0 /* offset to be patched */);
439  branch->target = target;
440  return branch;
441}
442
443LIR* Arm64Mir2Lir::OpReg(OpKind op, RegStorage r_dest_src) {
444  ArmOpcode opcode = kA64Brk1d;
445  switch (op) {
446    case kOpBlx:
447      opcode = kA64Blr1x;
448      break;
449    // TODO(Arm64): port kThumbBx.
450    // case kOpBx:
451    //   opcode = kThumbBx;
452    //   break;
453    default:
454      LOG(FATAL) << "Bad opcode " << op;
455  }
456  return NewLIR1(opcode, r_dest_src.GetReg());
457}
458
459LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int shift) {
460  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
461  CHECK_EQ(r_dest_src1.Is64Bit(), r_src2.Is64Bit());
462  ArmOpcode opcode = kA64Brk1d;
463
464  switch (op) {
465    case kOpCmn:
466      opcode = kA64Cmn3rro;
467      break;
468    case kOpCmp:
469      opcode = kA64Cmp3rro;
470      break;
471    case kOpMov:
472      opcode = kA64Mov2rr;
473      break;
474    case kOpMvn:
475      opcode = kA64Mvn2rr;
476      break;
477    case kOpNeg:
478      opcode = kA64Neg3rro;
479      break;
480    case kOpTst:
481      opcode = kA64Tst3rro;
482      break;
483    case kOpRev:
484      DCHECK_EQ(shift, 0);
485      // Binary, but rm is encoded twice.
486      return NewLIR2(kA64Rev2rr | wide, r_dest_src1.GetReg(), r_src2.GetReg());
487      break;
488    case kOpRevsh:
489      // Binary, but rm is encoded twice.
490      return NewLIR2(kA64Rev162rr | wide, r_dest_src1.GetReg(), r_src2.GetReg());
491      break;
492    case kOp2Byte:
493      DCHECK_EQ(shift, ENCODE_NO_SHIFT);
494      // "sbfx r1, r2, #imm1, #imm2" is "sbfm r1, r2, #imm1, #(imm1 + imm2 - 1)".
495      // For now we use sbfm directly.
496      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 7);
497    case kOp2Short:
498      DCHECK_EQ(shift, ENCODE_NO_SHIFT);
499      // For now we use sbfm rather than its alias, sbfx.
500      return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 15);
501    case kOp2Char:
502      // "ubfx r1, r2, #imm1, #imm2" is "ubfm r1, r2, #imm1, #(imm1 + imm2 - 1)".
503      // For now we use ubfm directly.
504      DCHECK_EQ(shift, ENCODE_NO_SHIFT);
505      return NewLIR4(kA64Ubfm4rrdd | wide, r_dest_src1.GetReg(), r_src2.GetReg(), 0, 15);
506    default:
507      return OpRegRegRegShift(op, r_dest_src1, r_dest_src1, r_src2, shift);
508  }
509
510  DCHECK(!IsPseudoLirOp(opcode));
511  if (EncodingMap[opcode].flags & IS_BINARY_OP) {
512    DCHECK_EQ(shift, ENCODE_NO_SHIFT);
513    return NewLIR2(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg());
514  } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
515    ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
516    if (kind == kFmtShift) {
517      return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), shift);
518    }
519  }
520
521  LOG(FATAL) << "Unexpected encoding operand count";
522  return NULL;
523}
524
525LIR* Arm64Mir2Lir::OpRegRegExtend(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int extend) {
526  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
527  ArmOpcode opcode = kA64Brk1d;
528
529  switch (op) {
530    case kOpCmn:
531      opcode = kA64Cmn3Rre;
532      break;
533    case kOpCmp:
534      opcode = kA64Cmp3Rre;
535      break;
536    default:
537      LOG(FATAL) << "Bad Opcode: " << opcode;
538      break;
539  }
540
541  DCHECK(!IsPseudoLirOp(opcode));
542  if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
543    ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
544    if (kind == kFmtExtend) {
545      return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), extend);
546    }
547  }
548
549  LOG(FATAL) << "Unexpected encoding operand count";
550  return NULL;
551}
552
553LIR* Arm64Mir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) {
554  /* RegReg operations with SP in first parameter need extended register instruction form.
555   * Only CMN and CMP instructions are implemented.
556   */
557  if (r_dest_src1 == rs_rA64_SP) {
558    return OpRegRegExtend(op, r_dest_src1, r_src2, ENCODE_NO_EXTEND);
559  } else {
560    return OpRegRegShift(op, r_dest_src1, r_src2, ENCODE_NO_SHIFT);
561  }
562}
563
564LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type) {
565  UNIMPLEMENTED(FATAL);
566  return nullptr;
567}
568
569LIR* Arm64Mir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) {
570  UNIMPLEMENTED(FATAL);
571  return nullptr;
572}
573
574LIR* Arm64Mir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) {
575  LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm64";
576  return NULL;
577}
578
579LIR* Arm64Mir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_src1,
580                                    RegStorage r_src2, int shift) {
581  ArmOpcode opcode = kA64Brk1d;
582
583  switch (op) {
584    case kOpAdd:
585      opcode = kA64Add4rrro;
586      break;
587    case kOpSub:
588      opcode = kA64Sub4rrro;
589      break;
590    // case kOpRsub:
591    //   opcode = kA64RsubWWW;
592    //   break;
593    case kOpAdc:
594      opcode = kA64Adc3rrr;
595      break;
596    case kOpAnd:
597      opcode = kA64And4rrro;
598      break;
599    case kOpXor:
600      opcode = kA64Eor4rrro;
601      break;
602    case kOpMul:
603      opcode = kA64Mul3rrr;
604      break;
605    case kOpDiv:
606      opcode = kA64Sdiv3rrr;
607      break;
608    case kOpOr:
609      opcode = kA64Orr4rrro;
610      break;
611    case kOpSbc:
612      opcode = kA64Sbc3rrr;
613      break;
614    case kOpLsl:
615      opcode = kA64Lsl3rrr;
616      break;
617    case kOpLsr:
618      opcode = kA64Lsr3rrr;
619      break;
620    case kOpAsr:
621      opcode = kA64Asr3rrr;
622      break;
623    case kOpRor:
624      opcode = kA64Ror3rrr;
625      break;
626    default:
627      LOG(FATAL) << "Bad opcode: " << op;
628      break;
629  }
630
631  // The instructions above belong to two kinds:
632  // - 4-operands instructions, where the last operand is a shift/extend immediate,
633  // - 3-operands instructions with no shift/extend.
634  ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
635  CHECK_EQ(r_dest.Is64Bit(), r_src1.Is64Bit());
636  CHECK_EQ(r_dest.Is64Bit(), r_src2.Is64Bit());
637  if (EncodingMap[opcode].flags & IS_QUAD_OP) {
638    DCHECK(!IsExtendEncoding(shift));
639    return NewLIR4(widened_opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg(), shift);
640  } else {
641    DCHECK(EncodingMap[opcode].flags & IS_TERTIARY_OP);
642    DCHECK_EQ(shift, ENCODE_NO_SHIFT);
643    return NewLIR3(widened_opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg());
644  }
645}
646
647LIR* Arm64Mir2Lir::OpRegRegRegExtend(OpKind op, RegStorage r_dest, RegStorage r_src1,
648                                     RegStorage r_src2, A64RegExtEncodings ext, uint8_t amount) {
649  ArmOpcode opcode = kA64Brk1d;
650
651  switch (op) {
652    case kOpAdd:
653      opcode = kA64Add4RRre;
654      break;
655    case kOpSub:
656      opcode = kA64Sub4RRre;
657      break;
658    default:
659      LOG(FATAL) << "Unimplemented opcode: " << op;
660      break;
661  }
662  ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
663
664  if (r_dest.Is64Bit()) {
665    CHECK(r_src1.Is64Bit());
666
667    // dest determines whether the op is wide or not. Up-convert src2 when necessary.
668    // Note: this is not according to aarch64 specifications, but our encoding.
669    if (!r_src2.Is64Bit()) {
670      r_src2 = As64BitReg(r_src2);
671    }
672  } else {
673    CHECK(!r_src1.Is64Bit());
674    CHECK(!r_src2.Is64Bit());
675  }
676
677  // Sanity checks.
678  //    1) Amount is in the range 0..4
679  CHECK_LE(amount, 4);
680
681  return NewLIR4(widened_opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg(),
682                 EncodeExtend(ext, amount));
683}
684
685LIR* Arm64Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2) {
686  return OpRegRegRegShift(op, r_dest, r_src1, r_src2, ENCODE_NO_SHIFT);
687}
688
689LIR* Arm64Mir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value) {
690  return OpRegRegImm64(op, r_dest, r_src1, static_cast<int64_t>(value));
691}
692
693LIR* Arm64Mir2Lir::OpRegRegImm64(OpKind op, RegStorage r_dest, RegStorage r_src1, int64_t value) {
694  LIR* res;
695  bool neg = (value < 0);
696  int64_t abs_value = (neg) ? -value : value;
697  ArmOpcode opcode = kA64Brk1d;
698  ArmOpcode alt_opcode = kA64Brk1d;
699  int32_t log_imm = -1;
700  bool is_wide = r_dest.Is64Bit();
701  ArmOpcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
702  int info = 0;
703
704  switch (op) {
705    case kOpLsl: {
706      // "lsl w1, w2, #imm" is an alias of "ubfm w1, w2, #(-imm MOD 32), #(31-imm)"
707      // and "lsl x1, x2, #imm" of "ubfm x1, x2, #(-imm MOD 64), #(63-imm)".
708      // For now, we just use ubfm directly.
709      int max_value = (is_wide) ? 63 : 31;
710      return NewLIR4(kA64Ubfm4rrdd | wide, r_dest.GetReg(), r_src1.GetReg(),
711                     (-value) & max_value, max_value - value);
712    }
713    case kOpLsr:
714      return NewLIR3(kA64Lsr3rrd | wide, r_dest.GetReg(), r_src1.GetReg(), value);
715    case kOpAsr:
716      return NewLIR3(kA64Asr3rrd | wide, r_dest.GetReg(), r_src1.GetReg(), value);
717    case kOpRor:
718      // "ror r1, r2, #imm" is an alias of "extr r1, r2, r2, #imm".
719      // For now, we just use extr directly.
720      return NewLIR4(kA64Extr4rrrd | wide, r_dest.GetReg(), r_src1.GetReg(), r_src1.GetReg(),
721                     value);
722    case kOpAdd:
723      neg = !neg;
724      // Note: intentional fallthrough
725    case kOpSub:
726      // Add and sub below read/write sp rather than xzr.
727      if (abs_value < 0x1000) {
728        opcode = (neg) ? kA64Add4RRdT : kA64Sub4RRdT;
729        return NewLIR4(opcode | wide, r_dest.GetReg(), r_src1.GetReg(), abs_value, 0);
730      } else if ((abs_value & UINT64_C(0xfff)) == 0 && ((abs_value >> 12) < 0x1000)) {
731        opcode = (neg) ? kA64Add4RRdT : kA64Sub4RRdT;
732        return NewLIR4(opcode | wide, r_dest.GetReg(), r_src1.GetReg(), abs_value >> 12, 1);
733      } else {
734        log_imm = -1;
735        alt_opcode = (neg) ? kA64Add4RRre : kA64Sub4RRre;
736        info = EncodeExtend(is_wide ? kA64Uxtx : kA64Uxtw, 0);
737      }
738      break;
739    // case kOpRsub:
740    //   opcode = kThumb2RsubRRI8M;
741    //   alt_opcode = kThumb2RsubRRR;
742    //   break;
743    case kOpAdc:
744      log_imm = -1;
745      alt_opcode = kA64Adc3rrr;
746      break;
747    case kOpSbc:
748      log_imm = -1;
749      alt_opcode = kA64Sbc3rrr;
750      break;
751    case kOpOr:
752      log_imm = EncodeLogicalImmediate(is_wide, value);
753      opcode = kA64Orr3Rrl;
754      alt_opcode = kA64Orr4rrro;
755      break;
756    case kOpAnd:
757      log_imm = EncodeLogicalImmediate(is_wide, value);
758      opcode = kA64And3Rrl;
759      alt_opcode = kA64And4rrro;
760      break;
761    case kOpXor:
762      log_imm = EncodeLogicalImmediate(is_wide, value);
763      opcode = kA64Eor3Rrl;
764      alt_opcode = kA64Eor4rrro;
765      break;
766    case kOpMul:
767      // TUNING: power of 2, shift & add
768      log_imm = -1;
769      alt_opcode = kA64Mul3rrr;
770      break;
771    default:
772      LOG(FATAL) << "Bad opcode: " << op;
773  }
774
775  if (log_imm >= 0) {
776    return NewLIR3(opcode | wide, r_dest.GetReg(), r_src1.GetReg(), log_imm);
777  } else {
778    RegStorage r_scratch;
779    if (is_wide) {
780      r_scratch = AllocTempWide();
781      LoadConstantWide(r_scratch, value);
782    } else {
783      r_scratch = AllocTemp();
784      LoadConstant(r_scratch, value);
785    }
786    if (EncodingMap[alt_opcode].flags & IS_QUAD_OP)
787      res = NewLIR4(alt_opcode | wide, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg(), info);
788    else
789      res = NewLIR3(alt_opcode | wide, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg());
790    FreeTemp(r_scratch);
791    return res;
792  }
793}
794
795LIR* Arm64Mir2Lir::OpRegImm(OpKind op, RegStorage r_dest_src1, int value) {
796  return OpRegImm64(op, r_dest_src1, static_cast<int64_t>(value));
797}
798
799LIR* Arm64Mir2Lir::OpRegImm64(OpKind op, RegStorage r_dest_src1, int64_t value) {
800  ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
801  ArmOpcode opcode = kA64Brk1d;
802  ArmOpcode neg_opcode = kA64Brk1d;
803  bool shift;
804  bool neg = (value < 0);
805  uint64_t abs_value = (neg) ? -value : value;
806
807  if (LIKELY(abs_value < 0x1000)) {
808    // abs_value is a 12-bit immediate.
809    shift = false;
810  } else if ((abs_value & UINT64_C(0xfff)) == 0 && ((abs_value >> 12) < 0x1000)) {
811    // abs_value is a shifted 12-bit immediate.
812    shift = true;
813    abs_value >>= 12;
814  } else if (LIKELY(abs_value < 0x1000000 && (op == kOpAdd || op == kOpSub))) {
815    // Note: It is better to use two ADD/SUB instead of loading a number to a temp register.
816    // This works for both normal registers and SP.
817    // For a frame size == 0x2468, it will be encoded as:
818    //   sub sp, #0x2000
819    //   sub sp, #0x468
820    if (neg) {
821      op = (op == kOpAdd) ? kOpSub : kOpAdd;
822    }
823    OpRegImm64(op, r_dest_src1, abs_value & (~INT64_C(0xfff)));
824    return OpRegImm64(op, r_dest_src1, abs_value & 0xfff);
825  } else if (LIKELY(A64_REG_IS_SP(r_dest_src1.GetReg()) && (op == kOpAdd || op == kOpSub))) {
826    // Note: "sub sp, sp, Xm" is not correct on arm64.
827    // We need special instructions for SP.
828    // Also operation on 32-bit SP should be avoided.
829    DCHECK(IS_WIDE(wide));
830    RegStorage r_tmp = AllocTempWide();
831    OpRegRegImm(kOpAdd, r_tmp, r_dest_src1, 0);
832    OpRegImm64(op, r_tmp, value);
833    return OpRegRegImm(kOpAdd, r_dest_src1, r_tmp, 0);
834  } else {
835    RegStorage r_tmp;
836    LIR* res;
837    if (IS_WIDE(wide)) {
838      r_tmp = AllocTempWide();
839      res = LoadConstantWide(r_tmp, value);
840    } else {
841      r_tmp = AllocTemp();
842      res = LoadConstant(r_tmp, value);
843    }
844    OpRegReg(op, r_dest_src1, r_tmp);
845    FreeTemp(r_tmp);
846    return res;
847  }
848
849  switch (op) {
850    case kOpAdd:
851      neg_opcode = kA64Sub4RRdT;
852      opcode = kA64Add4RRdT;
853      break;
854    case kOpSub:
855      neg_opcode = kA64Add4RRdT;
856      opcode = kA64Sub4RRdT;
857      break;
858    case kOpCmp:
859      neg_opcode = kA64Cmn3RdT;
860      opcode = kA64Cmp3RdT;
861      break;
862    default:
863      LOG(FATAL) << "Bad op-kind in OpRegImm: " << op;
864      break;
865  }
866
867  if (UNLIKELY(neg))
868    opcode = neg_opcode;
869
870  if (EncodingMap[opcode].flags & IS_QUAD_OP)
871    return NewLIR4(opcode | wide, r_dest_src1.GetReg(), r_dest_src1.GetReg(), abs_value,
872                   (shift) ? 1 : 0);
873  else
874    return NewLIR3(opcode | wide, r_dest_src1.GetReg(), abs_value, (shift) ? 1 : 0);
875}
876
877int Arm64Mir2Lir::EncodeShift(int shift_type, int amount) {
878  return ((shift_type & 0x3) << 7) | (amount & 0x1f);
879}
880
881int Arm64Mir2Lir::EncodeExtend(int extend_type, int amount) {
882  return  (1 << 6) | ((extend_type & 0x7) << 3) | (amount & 0x7);
883}
884
885bool Arm64Mir2Lir::IsExtendEncoding(int encoded_value) {
886  return ((1 << 6) & encoded_value) != 0;
887}
888
889LIR* Arm64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
890                                   int scale, OpSize size) {
891  LIR* load;
892  int expected_scale = 0;
893  ArmOpcode opcode = kA64Brk1d;
894  DCHECK(r_base.Is64Bit());
895  // TODO: need a cleaner handling of index registers here and throughout.
896  r_index = Check32BitReg(r_index);
897
898  if (r_dest.IsFloat()) {
899    if (r_dest.IsDouble()) {
900      DCHECK(size == k64 || size == kDouble);
901      expected_scale = 3;
902      opcode = FWIDE(kA64Ldr4fXxG);
903    } else {
904      DCHECK(r_dest.IsSingle());
905      DCHECK(size == k32 || size == kSingle);
906      expected_scale = 2;
907      opcode = kA64Ldr4fXxG;
908    }
909
910    DCHECK(scale == 0 || scale == expected_scale);
911    return NewLIR4(opcode, r_dest.GetReg(), r_base.GetReg(), r_index.GetReg(),
912                   (scale != 0) ? 1 : 0);
913  }
914
915  switch (size) {
916    case kDouble:
917    case kWord:
918    case k64:
919      r_dest = Check64BitReg(r_dest);
920      opcode = WIDE(kA64Ldr4rXxG);
921      expected_scale = 3;
922      break;
923    case kSingle:
924    case k32:
925    case kReference:
926      r_dest = Check32BitReg(r_dest);
927      opcode = kA64Ldr4rXxG;
928      expected_scale = 2;
929      break;
930    case kUnsignedHalf:
931      opcode = kA64Ldrh4wXxd;
932      expected_scale = 1;
933      break;
934    case kSignedHalf:
935      opcode = kA64Ldrsh4rXxd;
936      expected_scale = 1;
937      break;
938    case kUnsignedByte:
939      opcode = kA64Ldrb3wXx;
940      break;
941    case kSignedByte:
942      opcode = kA64Ldrsb3rXx;
943      break;
944    default:
945      LOG(FATAL) << "Bad size: " << size;
946  }
947
948  if (UNLIKELY(expected_scale == 0)) {
949    // This is a tertiary op (e.g. ldrb, ldrsb), it does not not support scale.
950    DCHECK_NE(EncodingMap[UNWIDE(opcode)].flags & IS_TERTIARY_OP, 0U);
951    DCHECK_EQ(scale, 0);
952    load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), r_index.GetReg());
953  } else {
954    DCHECK(scale == 0 || scale == expected_scale);
955    load = NewLIR4(opcode, r_dest.GetReg(), r_base.GetReg(), r_index.GetReg(),
956                   (scale != 0) ? 1 : 0);
957  }
958
959  return load;
960}
961
962LIR* Arm64Mir2Lir::LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest) {
963  return LoadBaseIndexed(r_base, r_index, As32BitReg(r_dest), 2, kReference);
964}
965
966LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
967                                    int scale, OpSize size) {
968  LIR* store;
969  int expected_scale = 0;
970  ArmOpcode opcode = kA64Brk1d;
971  DCHECK(r_base.Is64Bit());
972  // TODO: need a cleaner handling of index registers here and throughout.
973  r_index = Check32BitReg(r_index);
974
975  if (r_src.IsFloat()) {
976    if (r_src.IsDouble()) {
977      DCHECK(size == k64 || size == kDouble);
978      expected_scale = 3;
979      opcode = FWIDE(kA64Str4fXxG);
980    } else {
981      DCHECK(r_src.IsSingle());
982      DCHECK(size == k32 || size == kSingle);
983      expected_scale = 2;
984      opcode = kA64Str4fXxG;
985    }
986
987    DCHECK(scale == 0 || scale == expected_scale);
988    return NewLIR4(opcode, r_src.GetReg(), r_base.GetReg(), r_index.GetReg(),
989                   (scale != 0) ? 1 : 0);
990  }
991
992  switch (size) {
993    case kDouble:     // Intentional fall-trough.
994    case kWord:       // Intentional fall-trough.
995    case k64:
996      r_src = Check64BitReg(r_src);
997      opcode = WIDE(kA64Str4rXxG);
998      expected_scale = 3;
999      break;
1000    case kSingle:     // Intentional fall-trough.
1001    case k32:         // Intentional fall-trough.
1002    case kReference:
1003      r_src = Check32BitReg(r_src);
1004      opcode = kA64Str4rXxG;
1005      expected_scale = 2;
1006      break;
1007    case kUnsignedHalf:
1008    case kSignedHalf:
1009      opcode = kA64Strh4wXxd;
1010      expected_scale = 1;
1011      break;
1012    case kUnsignedByte:
1013    case kSignedByte:
1014      opcode = kA64Strb3wXx;
1015      break;
1016    default:
1017      LOG(FATAL) << "Bad size: " << size;
1018  }
1019
1020  if (UNLIKELY(expected_scale == 0)) {
1021    // This is a tertiary op (e.g. strb), it does not not support scale.
1022    DCHECK_NE(EncodingMap[UNWIDE(opcode)].flags & IS_TERTIARY_OP, 0U);
1023    DCHECK_EQ(scale, 0);
1024    store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), r_index.GetReg());
1025  } else {
1026    store = NewLIR4(opcode, r_src.GetReg(), r_base.GetReg(), r_index.GetReg(),
1027                    (scale != 0) ? 1 : 0);
1028  }
1029
1030  return store;
1031}
1032
1033LIR* Arm64Mir2Lir::StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src) {
1034  return StoreBaseIndexed(r_base, r_index, As32BitReg(r_src), 2, kReference);
1035}
1036
1037/*
1038 * Load value from base + displacement.  Optionally perform null check
1039 * on base (which must have an associated s_reg and MIR).  If not
1040 * performing null check, incoming MIR can be null.
1041 */
1042LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest,
1043                                    OpSize size) {
1044  LIR* load = NULL;
1045  ArmOpcode opcode = kA64Brk1d;
1046  ArmOpcode alt_opcode = kA64Brk1d;
1047  int scale = 0;
1048
1049  switch (size) {
1050    case kDouble:     // Intentional fall-through.
1051    case kWord:       // Intentional fall-through.
1052    case k64:
1053      r_dest = Check64BitReg(r_dest);
1054      scale = 3;
1055      if (r_dest.IsFloat()) {
1056        DCHECK(r_dest.IsDouble());
1057        opcode = FWIDE(kA64Ldr3fXD);
1058        alt_opcode = FWIDE(kA64Ldur3fXd);
1059      } else {
1060        opcode = WIDE(kA64Ldr3rXD);
1061        alt_opcode = WIDE(kA64Ldur3rXd);
1062      }
1063      break;
1064    case kSingle:     // Intentional fall-through.
1065    case k32:         // Intentional fall-trough.
1066    case kReference:
1067      r_dest = Check32BitReg(r_dest);
1068      scale = 2;
1069      if (r_dest.IsFloat()) {
1070        DCHECK(r_dest.IsSingle());
1071        opcode = kA64Ldr3fXD;
1072      } else {
1073        opcode = kA64Ldr3rXD;
1074      }
1075      break;
1076    case kUnsignedHalf:
1077      scale = 1;
1078      opcode = kA64Ldrh3wXF;
1079      break;
1080    case kSignedHalf:
1081      scale = 1;
1082      opcode = kA64Ldrsh3rXF;
1083      break;
1084    case kUnsignedByte:
1085      opcode = kA64Ldrb3wXd;
1086      break;
1087    case kSignedByte:
1088      opcode = kA64Ldrsb3rXd;
1089      break;
1090    default:
1091      LOG(FATAL) << "Bad size: " << size;
1092  }
1093
1094  bool displacement_is_aligned = (displacement & ((1 << scale) - 1)) == 0;
1095  int scaled_disp = displacement >> scale;
1096  if (displacement_is_aligned && scaled_disp >= 0 && scaled_disp < 4096) {
1097    // Can use scaled load.
1098    load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), scaled_disp);
1099  } else if (alt_opcode != kA64Brk1d && IS_SIGNED_IMM9(displacement)) {
1100    // Can use unscaled load.
1101    load = NewLIR3(alt_opcode, r_dest.GetReg(), r_base.GetReg(), displacement);
1102  } else {
1103    // Use long sequence.
1104    // TODO: cleaner support for index/displacement registers?  Not a reference, but must match width.
1105    RegStorage r_scratch = AllocTempWide();
1106    LoadConstantWide(r_scratch, displacement);
1107    load = LoadBaseIndexed(r_base, r_scratch, r_dest, 0, size);
1108    FreeTemp(r_scratch);
1109  }
1110
1111  // TODO: in future may need to differentiate Dalvik accesses w/ spills
1112  if (mem_ref_type_ == ResourceMask::kDalvikReg) {
1113    DCHECK(r_base == rs_rA64_SP);
1114    AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit());
1115  }
1116  return load;
1117}
1118
1119LIR* Arm64Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
1120                                OpSize size, VolatileKind is_volatile) {
1121  // LoadBaseDisp() will emit correct insn for atomic load on arm64
1122  // assuming r_dest is correctly prepared using RegClassForFieldLoadStore().
1123
1124  LIR* load = LoadBaseDispBody(r_base, displacement, r_dest, size);
1125
1126  if (UNLIKELY(is_volatile == kVolatile)) {
1127    // Without context sensitive analysis, we must issue the most conservative barriers.
1128    // In this case, either a load or store may follow so we issue both barriers.
1129    GenMemBarrier(kLoadLoad);
1130    GenMemBarrier(kLoadStore);
1131  }
1132
1133  return load;
1134}
1135
1136LIR* Arm64Mir2Lir::LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
1137                               VolatileKind is_volatile) {
1138  return LoadBaseDisp(r_base, displacement, As32BitReg(r_dest), kReference, is_volatile);
1139}
1140
1141LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
1142                                     OpSize size) {
1143  LIR* store = NULL;
1144  ArmOpcode opcode = kA64Brk1d;
1145  ArmOpcode alt_opcode = kA64Brk1d;
1146  int scale = 0;
1147
1148  switch (size) {
1149    case kDouble:     // Intentional fall-through.
1150    case kWord:       // Intentional fall-through.
1151    case k64:
1152      r_src = Check64BitReg(r_src);
1153      scale = 3;
1154      if (r_src.IsFloat()) {
1155        DCHECK(r_src.IsDouble());
1156        opcode = FWIDE(kA64Str3fXD);
1157        alt_opcode = FWIDE(kA64Stur3fXd);
1158      } else {
1159        opcode = FWIDE(kA64Str3rXD);
1160        alt_opcode = FWIDE(kA64Stur3rXd);
1161      }
1162      break;
1163    case kSingle:     // Intentional fall-through.
1164    case k32:         // Intentional fall-trough.
1165    case kReference:
1166      r_src = Check32BitReg(r_src);
1167      scale = 2;
1168      if (r_src.IsFloat()) {
1169        DCHECK(r_src.IsSingle());
1170        opcode = kA64Str3fXD;
1171      } else {
1172        opcode = kA64Str3rXD;
1173      }
1174      break;
1175    case kUnsignedHalf:
1176    case kSignedHalf:
1177      scale = 1;
1178      opcode = kA64Strh3wXF;
1179      break;
1180    case kUnsignedByte:
1181    case kSignedByte:
1182      opcode = kA64Strb3wXd;
1183      break;
1184    default:
1185      LOG(FATAL) << "Bad size: " << size;
1186  }
1187
1188  bool displacement_is_aligned = (displacement & ((1 << scale) - 1)) == 0;
1189  int scaled_disp = displacement >> scale;
1190  if (displacement_is_aligned && scaled_disp >= 0 && scaled_disp < 4096) {
1191    // Can use scaled store.
1192    store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), scaled_disp);
1193  } else if (alt_opcode != kA64Brk1d && IS_SIGNED_IMM9(displacement)) {
1194    // Can use unscaled store.
1195    store = NewLIR3(alt_opcode, r_src.GetReg(), r_base.GetReg(), displacement);
1196  } else {
1197    // Use long sequence.
1198    RegStorage r_scratch = AllocTempWide();
1199    LoadConstantWide(r_scratch, displacement);
1200    store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
1201    FreeTemp(r_scratch);
1202  }
1203
1204  // TODO: In future, may need to differentiate Dalvik & spill accesses.
1205  if (mem_ref_type_ == ResourceMask::kDalvikReg) {
1206    DCHECK(r_base == rs_rA64_SP);
1207    AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit());
1208  }
1209  return store;
1210}
1211
1212LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src,
1213                                 OpSize size, VolatileKind is_volatile) {
1214  if (UNLIKELY(is_volatile == kVolatile)) {
1215    // There might have been a store before this volatile one so insert StoreStore barrier.
1216    GenMemBarrier(kStoreStore);
1217  }
1218
1219  // StoreBaseDisp() will emit correct insn for atomic store on arm64
1220  // assuming r_dest is correctly prepared using RegClassForFieldLoadStore().
1221
1222  LIR* store = StoreBaseDispBody(r_base, displacement, r_src, size);
1223
1224  if (UNLIKELY(is_volatile == kVolatile)) {
1225    // A load might follow the volatile store so insert a StoreLoad barrier.
1226    GenMemBarrier(kStoreLoad);
1227  }
1228
1229  return store;
1230}
1231
1232LIR* Arm64Mir2Lir::StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
1233                                VolatileKind is_volatile) {
1234  return StoreBaseDisp(r_base, displacement, As32BitReg(r_src), kReference, is_volatile);
1235}
1236
1237LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
1238  LOG(FATAL) << "Unexpected use of OpFpRegCopy for Arm64";
1239  return NULL;
1240}
1241
1242LIR* Arm64Mir2Lir::OpThreadMem(OpKind op, ThreadOffset<4> thread_offset) {
1243  UNIMPLEMENTED(FATAL) << "Should not be used.";
1244  return nullptr;
1245}
1246
1247LIR* Arm64Mir2Lir::OpThreadMem(OpKind op, ThreadOffset<8> thread_offset) {
1248  LOG(FATAL) << "Unexpected use of OpThreadMem for Arm64";
1249  return NULL;
1250}
1251
1252LIR* Arm64Mir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) {
1253  LOG(FATAL) << "Unexpected use of OpMem for Arm64";
1254  return NULL;
1255}
1256
1257LIR* Arm64Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale,
1258                                        int displacement, RegStorage r_src, OpSize size) {
1259  LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for Arm64";
1260  return NULL;
1261}
1262
1263LIR* Arm64Mir2Lir::OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset) {
1264  LOG(FATAL) << "Unexpected use of OpRegMem for Arm64";
1265  return NULL;
1266}
1267
1268LIR* Arm64Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale,
1269                                       int displacement, RegStorage r_dest, OpSize size) {
1270  LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for Arm64";
1271  return NULL;
1272}
1273
1274}  // namespace art
1275