text: /*
   * Copyright (C) 2009 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 "../../CompilerInternals.h"
  #include "libdex/DexOpcodes.h"
  #include "MipsLIR.h"
  
  /* For dumping instructions */
  #define MIPS_REG_COUNT 32
  static const char *mipsRegName[MIPS_REG_COUNT] = {
      "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
      "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
      "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
      "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
  };
  
  /*
   * Interpret a format string and build a string no longer than size
   * See format key in Assemble.c.
   */
  static void buildInsnString(const char *fmt, MipsLIR *lir, char* buf,
                              unsigned char *baseAddr, int size)
  {
      int i;
      char *bufEnd = &buf[size-1];
      const char *fmtEnd = &fmt[strlen(fmt)];
      char tbuf[256];
      char nc;
      while (fmt < fmtEnd) {
          int operand;
          if (*fmt == '!') {
              fmt++;
              assert(fmt < fmtEnd);
              nc = *fmt++;
              if (nc=='!') {
                  strcpy(tbuf, "!");
              } else {
                 assert(fmt < fmtEnd);
                 assert((unsigned)(nc-'0') < 4);
                 operand = lir->operands[nc-'0'];
                 switch(*fmt++) {
                     case 'b':
                         strcpy(tbuf,"0000");
                         for (i=3; i>= 0; i--) {
                             tbuf[i] += operand & 1;
                             operand >>= 1;
                         }
                         break;
                     case 's':
                         sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
                         break;
                     case 'S':
  		       assert(((operand & FP_REG_MASK) & 1) == 0);
                         sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
                         break;
                     case 'h':
                         sprintf(tbuf,"%04x", operand);
                         break;
                     case 'M':
                     case 'd':
                         sprintf(tbuf,"%d", operand);
                         break;
                     case 'D':
                         sprintf(tbuf,"%d", operand+1);
                         break;
                     case 'E':
                         sprintf(tbuf,"%d", operand*4);
                         break;
                     case 'F':
                         sprintf(tbuf,"%d", operand*2);
                         break;
                     case 'c':
                         switch (operand) {
                             case kMipsCondEq:
                                 strcpy(tbuf, "eq");
                                 break;
                             case kMipsCondNe:
                                 strcpy(tbuf, "ne");
                                 break;
                             case kMipsCondLt:
                                 strcpy(tbuf, "lt");
                                 break;
                             case kMipsCondGe:
                                 strcpy(tbuf, "ge");
                                 break;
                             case kMipsCondGt:
                                 strcpy(tbuf, "gt");
                                 break;
                             case kMipsCondLe:
                                 strcpy(tbuf, "le");
                                 break;
                             case kMipsCondCs:
                                 strcpy(tbuf, "cs");
                                 break;
                             case kMipsCondMi:
                                 strcpy(tbuf, "mi");
                                 break;
                             default:
                                 strcpy(tbuf, "");
                                 break;
                         }
                         break;
                     case 't':
                         sprintf(tbuf,"0x%08x (L%p)",
                                 (int) baseAddr + lir->generic.offset + 4 +
                                 (operand << 2),
                                 lir->generic.target);
                         break;
                     case 'T':
                         sprintf(tbuf,"0x%08x",
                                 (int) (operand << 2));
                         break;
                     case 'u': {
                         int offset_1 = lir->operands[0];
                         int offset_2 = NEXT_LIR(lir)->operands[0];
                         intptr_t target =
                             ((((intptr_t) baseAddr + lir->generic.offset + 4) &
                              ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
                             0xfffffffc;
                         sprintf(tbuf, "%p", (void *) target);
                         break;
                      }
  
                     /* Nothing to print for BLX_2 */
                     case 'v':
                         strcpy(tbuf, "see above");
                         break;
                     case 'r':
                         assert(operand >= 0 && operand < MIPS_REG_COUNT);
                         strcpy(tbuf, mipsRegName[operand]);
                         break;
                     default:
                         strcpy(tbuf,"DecodeError");
                         break;
                 }
                 if (buf+strlen(tbuf) <= bufEnd) {
                     strcpy(buf, tbuf);
                     buf += strlen(tbuf);
                 } else {
                     break;
                 }
              }
          } else {
             *buf++ = *fmt++;
          }
          if (buf == bufEnd)
              break;
      }
      *buf = 0;
  }
  
  void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
  {
      char buf[256];
      buf[0] = 0;
      MipsLIR *mipsLIR = (MipsLIR *) lir;
  
      if (mask == ENCODE_ALL) {
          strcpy(buf, "all");
      } else {
          char num[8];
          int i;
  
          for (i = 0; i < kRegEnd; i++) {
              if (mask & (1ULL << i)) {
                  sprintf(num, "%d ", i);
                  strcat(buf, num);
              }
          }
  
          if (mask & ENCODE_CCODE) {
              strcat(buf, "cc ");
          }
          if (mask & ENCODE_FP_STATUS) {
              strcat(buf, "fpcc ");
          }
          /* Memory bits */
          if (mipsLIR && (mask & ENCODE_DALVIK_REG)) {
              sprintf(buf + strlen(buf), "dr%d%s", mipsLIR->aliasInfo & 0xffff,
                      (mipsLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
          }
          if (mask & ENCODE_LITERAL) {
              strcat(buf, "lit ");
          }
  
          if (mask & ENCODE_HEAP_REF) {
              strcat(buf, "heap ");
          }
          if (mask & ENCODE_MUST_NOT_ALIAS) {
              strcat(buf, "noalias ");
          }
      }
      if (buf[0]) {
          ALOGD("%s: %s", prefix, buf);
      }
  }
  
  /*
   * Debugging macros
   */
  #define DUMP_RESOURCE_MASK(X)
  #define DUMP_SSA_REP(X)
  
  /* Pretty-print a LIR instruction */
  void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
  {
      MipsLIR *lir = (MipsLIR *) arg;
      char buf[256];
      char opName[256];
      int offset = lir->generic.offset;
      int dest = lir->operands[0];
      const bool dumpNop = false;
  
      /* Handle pseudo-ops individually, and all regular insns as a group */
      switch(lir->opcode) {
          case kMipsChainingCellBottom:
              ALOGD("-------- end of chaining cells (0x%04x)", offset);
              break;
          case kMipsPseudoBarrier:
              ALOGD("-------- BARRIER");
              break;
          case kMipsPseudoExtended:
              /* intentional fallthrough */
          case kMipsPseudoSSARep:
              DUMP_SSA_REP(ALOGD("-------- %s", (char *) dest));
              break;
          case kMipsPseudoChainingCellBackwardBranch:
              ALOGD("L%p:", lir);
              ALOGD("-------- chaining cell (backward branch): 0x%04x", dest);
              break;
          case kMipsPseudoChainingCellNormal:
              ALOGD("L%p:", lir);
              ALOGD("-------- chaining cell (normal): 0x%04x", dest);
              break;
          case kMipsPseudoChainingCellHot:
              ALOGD("L%p:", lir);
              ALOGD("-------- chaining cell (hot): 0x%04x", dest);
              break;
          case kMipsPseudoChainingCellInvokePredicted:
              ALOGD("L%p:", lir);
              ALOGD("-------- chaining cell (predicted): %s%s",
                   dest ? ((Method *) dest)->clazz->descriptor : "",
                   dest ? ((Method *) dest)->name : "N/A");
              break;
          case kMipsPseudoChainingCellInvokeSingleton:
              ALOGD("L%p:", lir);
              ALOGD("-------- chaining cell (invoke singleton): %s%s/%p",
                   ((Method *)dest)->clazz->descriptor,
                   ((Method *)dest)->name,
                   ((Method *)dest)->insns);
              break;
          case kMipsPseudoEntryBlock:
              ALOGD("-------- entry offset: 0x%04x", dest);
              break;
          case kMipsPseudoDalvikByteCodeBoundary:
              ALOGD("-------- dalvik offset: 0x%04x @ %s", dest,
                   (char *) lir->operands[1]);
              break;
          case kMipsPseudoExitBlock:
              ALOGD("-------- exit offset: 0x%04x", dest);
              break;
          case kMipsPseudoPseudoAlign4:
              ALOGD("%p (%04x): .align4", baseAddr + offset, offset);
              break;
          case kMipsPseudoPCReconstructionCell:
              ALOGD("L%p:", lir);
              ALOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x", dest,
                   lir->operands[1]);
              break;
          case kMipsPseudoPCReconstructionBlockLabel:
              /* Do nothing */
              break;
          case kMipsPseudoEHBlockLabel:
              ALOGD("Exception_Handling:");
              break;
          case kMipsPseudoTargetLabel:
          case kMipsPseudoNormalBlockLabel:
              ALOGD("L%p:", lir);
              break;
          default:
              if (lir->flags.isNop && !dumpNop) {
                  break;
              }
              buildInsnString(EncodingMap[lir->opcode].name, lir, opName,
                              baseAddr, 256);
              buildInsnString(EncodingMap[lir->opcode].fmt, lir, buf, baseAddr,
                              256);
              ALOGD("%p (%04x): %08x %-9s%s%s",
                   baseAddr + offset, offset, *(u4 *)(baseAddr + offset), opName, buf,
                   lir->flags.isNop ? "(nop)" : "");
              break;
      }
  
      if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
          DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
                                                 lir->useMask, "use"));
      }
      if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
          DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
                                                 lir->defMask, "def"));
      }
  }
  
  /* Dump instructions and constant pool contents */
  void dvmCompilerCodegenDump(CompilationUnit *cUnit)
  {
      ALOGD("Dumping LIR insns");
      LIR *lirInsn;
      MipsLIR *mipsLIR;
  
      ALOGD("installed code is at %p", cUnit->baseAddr);
      ALOGD("total size is %d bytes", cUnit->totalSize);
      for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
          dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
      }
      for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
          mipsLIR = (MipsLIR *) lirInsn;
          ALOGD("%p (%04x): .class (%s)",
               (char*)cUnit->baseAddr + mipsLIR->generic.offset,
               mipsLIR->generic.offset,
               ((CallsiteInfo *) mipsLIR->operands[0])->classDescriptor);
      }
      for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
          mipsLIR = (MipsLIR *) lirInsn;
          ALOGD("%p (%04x): .word (%#x)",
               (char*)cUnit->baseAddr + mipsLIR->generic.offset,
               mipsLIR->generic.offset,
               mipsLIR->operands[0]);
      }
  }
  
  /* Target-specific cache flushing */
  void dvmCompilerCacheFlush(long start, long end, long flags)
  {
      cacheflush(start, end, flags);
  }
  
  /* Target-specific cache clearing */
  void dvmCompilerCacheClear(char *start, size_t size)
  {
      /* 0x66 is an invalid opcode for mips. */
      memset(start, 0x66, size);
  }
  
  unsigned int dvmArchSpecGetNumberOfScratch (void)
  {
      //Non-x86 don't have scratch registers in their frames
      return 0;
  }
  
  int dvmArchSpecGetPureLocalScratchRegister (const Method * method, unsigned int idx, int registerWindowShift)
  {
      //Non-x86 don't have scratch registers in their frames
      return -1;
  }
  
  bool dvmArchIsPureLocalScratchRegister (const Method * method, int virtualReg, int registerWindowShift)
  {
      //Non-x86 don't support scratch registers and therefore no VR is scratch
      return false;
  }
  