utility_mips.cc revision 2c498d1f28e62e81fbdb477ff93ca7454e7493d7
1/*
2 * Copyright (C) 2012 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 "codegen_mips.h"
18#include "dex/quick/mir_to_lir-inl.h"
19#include "mips_lir.h"
20
21namespace art {
22
23/* This file contains codegen for the MIPS32 ISA. */
24LIR* MipsMir2Lir::OpFpRegCopy(int r_dest, int r_src) {
25  int opcode;
26  /* must be both DOUBLE or both not DOUBLE */
27  DCHECK_EQ(MIPS_DOUBLEREG(r_dest), MIPS_DOUBLEREG(r_src));
28  if (MIPS_DOUBLEREG(r_dest)) {
29    opcode = kMipsFmovd;
30  } else {
31    if (MIPS_SINGLEREG(r_dest)) {
32      if (MIPS_SINGLEREG(r_src)) {
33        opcode = kMipsFmovs;
34      } else {
35        /* note the operands are swapped for the mtc1 instr */
36        int t_opnd = r_src;
37        r_src = r_dest;
38        r_dest = t_opnd;
39        opcode = kMipsMtc1;
40      }
41    } else {
42      DCHECK(MIPS_SINGLEREG(r_src));
43      opcode = kMipsMfc1;
44    }
45  }
46  LIR* res = RawLIR(current_dalvik_offset_, opcode, r_src, r_dest);
47  if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
48    res->flags.is_nop = true;
49  }
50  return res;
51}
52
53bool MipsMir2Lir::InexpensiveConstantInt(int32_t value) {
54  return ((value == 0) || IsUint(16, value) || ((value < 0) && (value >= -32768)));
55}
56
57bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value) {
58  return false;  // TUNING
59}
60
61bool MipsMir2Lir::InexpensiveConstantLong(int64_t value) {
62  return false;  // TUNING
63}
64
65bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value) {
66  return false;  // TUNING
67}
68
69/*
70 * Load a immediate using a shortcut if possible; otherwise
71 * grab from the per-translation literal pool.  If target is
72 * a high register, build constant into a low register and copy.
73 *
74 * No additional register clobbering operation performed. Use this version when
75 * 1) r_dest is freshly returned from AllocTemp or
76 * 2) The codegen is under fixed register usage
77 */
78LIR* MipsMir2Lir::LoadConstantNoClobber(int r_dest, int value) {
79  LIR *res;
80
81  int r_dest_save = r_dest;
82  int is_fp_reg = MIPS_FPREG(r_dest);
83  if (is_fp_reg) {
84    DCHECK(MIPS_SINGLEREG(r_dest));
85    r_dest = AllocTemp();
86  }
87
88  /* See if the value can be constructed cheaply */
89  if (value == 0) {
90    res = NewLIR2(kMipsMove, r_dest, r_ZERO);
91  } else if ((value > 0) && (value <= 65535)) {
92    res = NewLIR3(kMipsOri, r_dest, r_ZERO, value);
93  } else if ((value < 0) && (value >= -32768)) {
94    res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value);
95  } else {
96    res = NewLIR2(kMipsLui, r_dest, value >> 16);
97    if (value & 0xffff)
98      NewLIR3(kMipsOri, r_dest, r_dest, value);
99  }
100
101  if (is_fp_reg) {
102    NewLIR2(kMipsMtc1, r_dest, r_dest_save);
103    FreeTemp(r_dest);
104  }
105
106  return res;
107}
108
109LIR* MipsMir2Lir::OpUnconditionalBranch(LIR* target) {
110  LIR* res = NewLIR1(kMipsB, 0 /* offset to be patched during assembly*/);
111  res->target = target;
112  return res;
113}
114
115LIR* MipsMir2Lir::OpReg(OpKind op, int r_dest_src) {
116  MipsOpCode opcode = kMipsNop;
117  switch (op) {
118    case kOpBlx:
119      opcode = kMipsJalr;
120      break;
121    case kOpBx:
122      return NewLIR1(kMipsJr, r_dest_src);
123      break;
124    default:
125      LOG(FATAL) << "Bad case in OpReg";
126  }
127  return NewLIR2(opcode, r_RA, r_dest_src);
128}
129
130LIR* MipsMir2Lir::OpRegImm(OpKind op, int r_dest_src1,
131          int value) {
132  LIR *res;
133  bool neg = (value < 0);
134  int abs_value = (neg) ? -value : value;
135  bool short_form = (abs_value & 0xff) == abs_value;
136  MipsOpCode opcode = kMipsNop;
137  switch (op) {
138    case kOpAdd:
139      return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
140      break;
141    case kOpSub:
142      return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
143      break;
144    default:
145      LOG(FATAL) << "Bad case in OpRegImm";
146      break;
147  }
148  if (short_form) {
149    res = NewLIR2(opcode, r_dest_src1, abs_value);
150  } else {
151    int r_scratch = AllocTemp();
152    res = LoadConstant(r_scratch, value);
153    if (op == kOpCmp)
154      NewLIR2(opcode, r_dest_src1, r_scratch);
155    else
156      NewLIR3(opcode, r_dest_src1, r_dest_src1, r_scratch);
157  }
158  return res;
159}
160
161LIR* MipsMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) {
162  MipsOpCode opcode = kMipsNop;
163  switch (op) {
164    case kOpAdd:
165      opcode = kMipsAddu;
166      break;
167    case kOpSub:
168      opcode = kMipsSubu;
169      break;
170    case kOpAnd:
171      opcode = kMipsAnd;
172      break;
173    case kOpMul:
174      opcode = kMipsMul;
175      break;
176    case kOpOr:
177      opcode = kMipsOr;
178      break;
179    case kOpXor:
180      opcode = kMipsXor;
181      break;
182    case kOpLsl:
183      opcode = kMipsSllv;
184      break;
185    case kOpLsr:
186      opcode = kMipsSrlv;
187      break;
188    case kOpAsr:
189      opcode = kMipsSrav;
190      break;
191    case kOpAdc:
192    case kOpSbc:
193      LOG(FATAL) << "No carry bit on MIPS";
194      break;
195    default:
196      LOG(FATAL) << "bad case in OpRegRegReg";
197      break;
198  }
199  return NewLIR3(opcode, r_dest, r_src1, r_src2);
200}
201
202LIR* MipsMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) {
203  LIR *res;
204  MipsOpCode opcode = kMipsNop;
205  bool short_form = true;
206
207  switch (op) {
208    case kOpAdd:
209      if (IS_SIMM16(value)) {
210        opcode = kMipsAddiu;
211      } else {
212        short_form = false;
213        opcode = kMipsAddu;
214      }
215      break;
216    case kOpSub:
217      if (IS_SIMM16((-value))) {
218        value = -value;
219        opcode = kMipsAddiu;
220      } else {
221        short_form = false;
222        opcode = kMipsSubu;
223      }
224      break;
225    case kOpLsl:
226        DCHECK(value >= 0 && value <= 31);
227        opcode = kMipsSll;
228        break;
229    case kOpLsr:
230        DCHECK(value >= 0 && value <= 31);
231        opcode = kMipsSrl;
232        break;
233    case kOpAsr:
234        DCHECK(value >= 0 && value <= 31);
235        opcode = kMipsSra;
236        break;
237    case kOpAnd:
238      if (IS_UIMM16((value))) {
239        opcode = kMipsAndi;
240      } else {
241        short_form = false;
242        opcode = kMipsAnd;
243      }
244      break;
245    case kOpOr:
246      if (IS_UIMM16((value))) {
247        opcode = kMipsOri;
248      } else {
249        short_form = false;
250        opcode = kMipsOr;
251      }
252      break;
253    case kOpXor:
254      if (IS_UIMM16((value))) {
255        opcode = kMipsXori;
256      } else {
257        short_form = false;
258        opcode = kMipsXor;
259      }
260      break;
261    case kOpMul:
262      short_form = false;
263      opcode = kMipsMul;
264      break;
265    default:
266      LOG(FATAL) << "Bad case in OpRegRegImm";
267      break;
268  }
269
270  if (short_form) {
271    res = NewLIR3(opcode, r_dest, r_src1, value);
272  } else {
273    if (r_dest != r_src1) {
274      res = LoadConstant(r_dest, value);
275      NewLIR3(opcode, r_dest, r_src1, r_dest);
276    } else {
277      int r_scratch = AllocTemp();
278      res = LoadConstant(r_scratch, value);
279      NewLIR3(opcode, r_dest, r_src1, r_scratch);
280    }
281  }
282  return res;
283}
284
285LIR* MipsMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) {
286  MipsOpCode opcode = kMipsNop;
287  LIR *res;
288  switch (op) {
289    case kOpMov:
290      opcode = kMipsMove;
291      break;
292    case kOpMvn:
293      return NewLIR3(kMipsNor, r_dest_src1, r_src2, r_ZERO);
294    case kOpNeg:
295      return NewLIR3(kMipsSubu, r_dest_src1, r_ZERO, r_src2);
296    case kOpAdd:
297    case kOpAnd:
298    case kOpMul:
299    case kOpOr:
300    case kOpSub:
301    case kOpXor:
302      return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2);
303    case kOp2Byte:
304#if __mips_isa_rev >= 2
305      res = NewLIR2(kMipsSeb, r_dest_src1, r_src2);
306#else
307      res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 24);
308      OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 24);
309#endif
310      return res;
311    case kOp2Short:
312#if __mips_isa_rev >= 2
313      res = NewLIR2(kMipsSeh, r_dest_src1, r_src2);
314#else
315      res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 16);
316      OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 16);
317#endif
318      return res;
319    case kOp2Char:
320       return NewLIR3(kMipsAndi, r_dest_src1, r_src2, 0xFFFF);
321    default:
322      LOG(FATAL) << "Bad case in OpRegReg";
323      break;
324  }
325  return NewLIR2(opcode, r_dest_src1, r_src2);
326}
327
328LIR* MipsMir2Lir::OpMovRegMem(int r_dest, int r_base, int offset, MoveType move_type) {
329  UNIMPLEMENTED(FATAL);
330  return nullptr;
331}
332
333LIR* MipsMir2Lir::OpMovMemReg(int r_base, int offset, int r_src, MoveType move_type) {
334  UNIMPLEMENTED(FATAL);
335  return nullptr;
336}
337
338LIR* MipsMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src) {
339  LOG(FATAL) << "Unexpected use of OpCondRegReg for MIPS";
340  return NULL;
341}
342
343LIR* MipsMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) {
344  LIR *res;
345  res = LoadConstantNoClobber(r_dest_lo, Low32Bits(value));
346  LoadConstantNoClobber(r_dest_hi, High32Bits(value));
347  return res;
348}
349
350/* Load value from base + scaled index. */
351LIR* MipsMir2Lir::LoadBaseIndexed(int rBase, int r_index, int r_dest,
352                                  int scale, OpSize size) {
353  LIR *first = NULL;
354  LIR *res;
355  MipsOpCode opcode = kMipsNop;
356  int t_reg = AllocTemp();
357
358  if (MIPS_FPREG(r_dest)) {
359    DCHECK(MIPS_SINGLEREG(r_dest));
360    DCHECK((size == kWord) || (size == kSingle));
361    size = kSingle;
362  } else {
363    if (size == kSingle)
364      size = kWord;
365  }
366
367  if (!scale) {
368    first = NewLIR3(kMipsAddu, t_reg , rBase, r_index);
369  } else {
370    first = OpRegRegImm(kOpLsl, t_reg, r_index, scale);
371    NewLIR3(kMipsAddu, t_reg , rBase, t_reg);
372  }
373
374  switch (size) {
375    case kSingle:
376      opcode = kMipsFlwc1;
377      break;
378    case kWord:
379      opcode = kMipsLw;
380      break;
381    case kUnsignedHalf:
382      opcode = kMipsLhu;
383      break;
384    case kSignedHalf:
385      opcode = kMipsLh;
386      break;
387    case kUnsignedByte:
388      opcode = kMipsLbu;
389      break;
390    case kSignedByte:
391      opcode = kMipsLb;
392      break;
393    default:
394      LOG(FATAL) << "Bad case in LoadBaseIndexed";
395  }
396
397  res = NewLIR3(opcode, r_dest, 0, t_reg);
398  FreeTemp(t_reg);
399  return (first) ? first : res;
400}
401
402/* store value base base + scaled index. */
403LIR* MipsMir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src,
404                                   int scale, OpSize size) {
405  LIR *first = NULL;
406  MipsOpCode opcode = kMipsNop;
407  int r_new_index = r_index;
408  int t_reg = AllocTemp();
409
410  if (MIPS_FPREG(r_src)) {
411    DCHECK(MIPS_SINGLEREG(r_src));
412    DCHECK((size == kWord) || (size == kSingle));
413    size = kSingle;
414  } else {
415    if (size == kSingle)
416      size = kWord;
417  }
418
419  if (!scale) {
420    first = NewLIR3(kMipsAddu, t_reg , rBase, r_index);
421  } else {
422    first = OpRegRegImm(kOpLsl, t_reg, r_index, scale);
423    NewLIR3(kMipsAddu, t_reg , rBase, t_reg);
424  }
425
426  switch (size) {
427    case kSingle:
428      opcode = kMipsFswc1;
429      break;
430    case kWord:
431      opcode = kMipsSw;
432      break;
433    case kUnsignedHalf:
434    case kSignedHalf:
435      opcode = kMipsSh;
436      break;
437    case kUnsignedByte:
438    case kSignedByte:
439      opcode = kMipsSb;
440      break;
441    default:
442      LOG(FATAL) << "Bad case in StoreBaseIndexed";
443  }
444  NewLIR3(opcode, r_src, 0, t_reg);
445  FreeTemp(r_new_index);
446  return first;
447}
448
449LIR* MipsMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest,
450                                   int r_dest_hi, OpSize size, int s_reg) {
451/*
452 * Load value from base + displacement.  Optionally perform null check
453 * on base (which must have an associated s_reg and MIR).  If not
454 * performing null check, incoming MIR can be null. IMPORTANT: this
455 * code must not allocate any new temps.  If a new register is needed
456 * and base and dest are the same, spill some other register to
457 * rlp and then restore.
458 */
459  LIR *res;
460  LIR *load = NULL;
461  LIR *load2 = NULL;
462  MipsOpCode opcode = kMipsNop;
463  bool short_form = IS_SIMM16(displacement);
464  bool pair = false;
465
466  switch (size) {
467    case kLong:
468    case kDouble:
469      pair = true;
470      opcode = kMipsLw;
471      if (MIPS_FPREG(r_dest)) {
472        opcode = kMipsFlwc1;
473        if (MIPS_DOUBLEREG(r_dest)) {
474          r_dest = r_dest - MIPS_FP_DOUBLE;
475        } else {
476          DCHECK(MIPS_FPREG(r_dest_hi));
477          DCHECK(r_dest == (r_dest_hi - 1));
478        }
479        r_dest_hi = r_dest + 1;
480      }
481      short_form = IS_SIMM16_2WORD(displacement);
482      DCHECK_EQ((displacement & 0x3), 0);
483      break;
484    case kWord:
485    case kSingle:
486      opcode = kMipsLw;
487      if (MIPS_FPREG(r_dest)) {
488        opcode = kMipsFlwc1;
489        DCHECK(MIPS_SINGLEREG(r_dest));
490      }
491      DCHECK_EQ((displacement & 0x3), 0);
492      break;
493    case kUnsignedHalf:
494      opcode = kMipsLhu;
495      DCHECK_EQ((displacement & 0x1), 0);
496      break;
497    case kSignedHalf:
498      opcode = kMipsLh;
499      DCHECK_EQ((displacement & 0x1), 0);
500      break;
501    case kUnsignedByte:
502      opcode = kMipsLbu;
503      break;
504    case kSignedByte:
505      opcode = kMipsLb;
506      break;
507    default:
508      LOG(FATAL) << "Bad case in LoadBaseIndexedBody";
509  }
510
511  if (short_form) {
512    if (!pair) {
513      load = res = NewLIR3(opcode, r_dest, displacement, rBase);
514    } else {
515      load = res = NewLIR3(opcode, r_dest,
516                           displacement + LOWORD_OFFSET, rBase);
517      load2 = NewLIR3(opcode, r_dest_hi,
518                      displacement + HIWORD_OFFSET, rBase);
519    }
520  } else {
521    if (pair) {
522      int r_tmp = AllocTemp();
523      res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement);
524      load = NewLIR3(opcode, r_dest, LOWORD_OFFSET, r_tmp);
525      load2 = NewLIR3(opcode, r_dest_hi, HIWORD_OFFSET, r_tmp);
526      FreeTemp(r_tmp);
527    } else {
528      int r_tmp = (rBase == r_dest) ? AllocTemp() : r_dest;
529      res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement);
530      load = NewLIR3(opcode, r_dest, 0, r_tmp);
531      if (r_tmp != r_dest)
532        FreeTemp(r_tmp);
533    }
534  }
535
536  if (rBase == rMIPS_SP) {
537    AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
538                            true /* is_load */, pair /* is64bit */);
539    if (pair) {
540      AnnotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
541                              true /* is_load */, pair /* is64bit */);
542    }
543  }
544  return load;
545}
546
547LIR* MipsMir2Lir::LoadBaseDisp(int rBase, int displacement, int r_dest,
548                               OpSize size, int s_reg) {
549  return LoadBaseDispBody(rBase, displacement, r_dest, -1,
550                          size, s_reg);
551}
552
553LIR* MipsMir2Lir::LoadBaseDispWide(int rBase, int displacement,
554                                   int r_dest_lo, int r_dest_hi, int s_reg) {
555  return LoadBaseDispBody(rBase, displacement, r_dest_lo, r_dest_hi, kLong, s_reg);
556}
557
558LIR* MipsMir2Lir::StoreBaseDispBody(int rBase, int displacement,
559                                    int r_src, int r_src_hi, OpSize size) {
560  LIR *res;
561  LIR *store = NULL;
562  LIR *store2 = NULL;
563  MipsOpCode opcode = kMipsNop;
564  bool short_form = IS_SIMM16(displacement);
565  bool pair = false;
566
567  switch (size) {
568    case kLong:
569    case kDouble:
570      pair = true;
571      opcode = kMipsSw;
572      if (MIPS_FPREG(r_src)) {
573        opcode = kMipsFswc1;
574        if (MIPS_DOUBLEREG(r_src)) {
575          r_src = r_src - MIPS_FP_DOUBLE;
576        } else {
577          DCHECK(MIPS_FPREG(r_src_hi));
578          DCHECK_EQ(r_src, (r_src_hi - 1));
579        }
580        r_src_hi = r_src + 1;
581      }
582      short_form = IS_SIMM16_2WORD(displacement);
583      DCHECK_EQ((displacement & 0x3), 0);
584      break;
585    case kWord:
586    case kSingle:
587      opcode = kMipsSw;
588      if (MIPS_FPREG(r_src)) {
589        opcode = kMipsFswc1;
590        DCHECK(MIPS_SINGLEREG(r_src));
591      }
592      DCHECK_EQ((displacement & 0x3), 0);
593      break;
594    case kUnsignedHalf:
595    case kSignedHalf:
596      opcode = kMipsSh;
597      DCHECK_EQ((displacement & 0x1), 0);
598      break;
599    case kUnsignedByte:
600    case kSignedByte:
601      opcode = kMipsSb;
602      break;
603    default:
604      LOG(FATAL) << "Bad case in StoreBaseIndexedBody";
605  }
606
607  if (short_form) {
608    if (!pair) {
609      store = res = NewLIR3(opcode, r_src, displacement, rBase);
610    } else {
611      store = res = NewLIR3(opcode, r_src, displacement + LOWORD_OFFSET,
612                            rBase);
613      store2 = NewLIR3(opcode, r_src_hi, displacement + HIWORD_OFFSET,
614                       rBase);
615    }
616  } else {
617    int r_scratch = AllocTemp();
618    res = OpRegRegImm(kOpAdd, r_scratch, rBase, displacement);
619    if (!pair) {
620      store =  NewLIR3(opcode, r_src, 0, r_scratch);
621    } else {
622      store =  NewLIR3(opcode, r_src, LOWORD_OFFSET, r_scratch);
623      store2 = NewLIR3(opcode, r_src_hi, HIWORD_OFFSET, r_scratch);
624    }
625    FreeTemp(r_scratch);
626  }
627
628  if (rBase == rMIPS_SP) {
629    AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
630                            false /* is_load */, pair /* is64bit */);
631    if (pair) {
632      AnnotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
633                              false /* is_load */, pair /* is64bit */);
634    }
635  }
636
637  return res;
638}
639
640LIR* MipsMir2Lir::StoreBaseDisp(int rBase, int displacement, int r_src,
641                                OpSize size) {
642  return StoreBaseDispBody(rBase, displacement, r_src, -1, size);
643}
644
645LIR* MipsMir2Lir::StoreBaseDispWide(int rBase, int displacement,
646                                    int r_src_lo, int r_src_hi) {
647  return StoreBaseDispBody(rBase, displacement, r_src_lo, r_src_hi, kLong);
648}
649
650LIR* MipsMir2Lir::OpThreadMem(OpKind op, ThreadOffset thread_offset) {
651  LOG(FATAL) << "Unexpected use of OpThreadMem for MIPS";
652  return NULL;
653}
654
655LIR* MipsMir2Lir::OpMem(OpKind op, int rBase, int disp) {
656  LOG(FATAL) << "Unexpected use of OpMem for MIPS";
657  return NULL;
658}
659
660LIR* MipsMir2Lir::StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
661                                       int r_src, int r_src_hi, OpSize size, int s_reg) {
662  LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for MIPS";
663  return NULL;
664}
665
666LIR* MipsMir2Lir::OpRegMem(OpKind op, int r_dest, int rBase,
667              int offset) {
668  LOG(FATAL) << "Unexpected use of OpRegMem for MIPS";
669  return NULL;
670}
671
672LIR* MipsMir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
673                                      int r_dest, int r_dest_hi, OpSize size, int s_reg) {
674  LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for MIPS";
675  return NULL;
676}
677
678LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) {
679  LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS";
680  return NULL;
681}
682
683}  // namespace art
684