bool MipsAsmParser::processInstruction()

in llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp [1871:2369]


bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
                                       MCStreamer &Out,
                                       const MCSubtargetInfo *STI) {
  MipsTargetStreamer &TOut = getTargetStreamer();
  const unsigned Opcode = Inst.getOpcode();
  const MCInstrDesc &MCID = getInstDesc(Opcode);
  bool ExpandedJalSym = false;

  Inst.setLoc(IDLoc);

  if (MCID.isBranch() || MCID.isCall()) {
    MCOperand Offset;

    switch (Opcode) {
    default:
      break;
    case Mips::BBIT0:
    case Mips::BBIT032:
    case Mips::BBIT1:
    case Mips::BBIT132:
      assert(hasCnMips() && "instruction only valid for octeon cpus");
      LLVM_FALLTHROUGH;

    case Mips::BEQ:
    case Mips::BNE:
    case Mips::BEQ_MM:
    case Mips::BNE_MM:
      assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
      Offset = Inst.getOperand(2);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isIntN(inMicroMipsMode() ? 17 : 18, Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(),
                            (inMicroMipsMode() ? Align(2) : Align(4))))
        return Error(IDLoc, "branch to misaligned address");
      break;
    case Mips::BGEZ:
    case Mips::BGTZ:
    case Mips::BLEZ:
    case Mips::BLTZ:
    case Mips::BGEZAL:
    case Mips::BLTZAL:
    case Mips::BC1F:
    case Mips::BC1T:
    case Mips::BGEZ_MM:
    case Mips::BGTZ_MM:
    case Mips::BLEZ_MM:
    case Mips::BLTZ_MM:
    case Mips::BGEZAL_MM:
    case Mips::BLTZAL_MM:
    case Mips::BC1F_MM:
    case Mips::BC1T_MM:
    case Mips::BC1EQZC_MMR6:
    case Mips::BC1NEZC_MMR6:
    case Mips::BC2EQZC_MMR6:
    case Mips::BC2NEZC_MMR6:
      assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
      Offset = Inst.getOperand(1);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isIntN(inMicroMipsMode() ? 17 : 18, Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(),
                            (inMicroMipsMode() ? Align(2) : Align(4))))
        return Error(IDLoc, "branch to misaligned address");
      break;
    case Mips::BGEC:    case Mips::BGEC_MMR6:
    case Mips::BLTC:    case Mips::BLTC_MMR6:
    case Mips::BGEUC:   case Mips::BGEUC_MMR6:
    case Mips::BLTUC:   case Mips::BLTUC_MMR6:
    case Mips::BEQC:    case Mips::BEQC_MMR6:
    case Mips::BNEC:    case Mips::BNEC_MMR6:
      assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
      Offset = Inst.getOperand(2);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isIntN(18, Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(), Align(4)))
        return Error(IDLoc, "branch to misaligned address");
      break;
    case Mips::BLEZC:   case Mips::BLEZC_MMR6:
    case Mips::BGEZC:   case Mips::BGEZC_MMR6:
    case Mips::BGTZC:   case Mips::BGTZC_MMR6:
    case Mips::BLTZC:   case Mips::BLTZC_MMR6:
      assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
      Offset = Inst.getOperand(1);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isIntN(18, Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(), Align(4)))
        return Error(IDLoc, "branch to misaligned address");
      break;
    case Mips::BEQZC:   case Mips::BEQZC_MMR6:
    case Mips::BNEZC:   case Mips::BNEZC_MMR6:
      assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
      Offset = Inst.getOperand(1);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isIntN(23, Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(), Align(4)))
        return Error(IDLoc, "branch to misaligned address");
      break;
    case Mips::BEQZ16_MM:
    case Mips::BEQZC16_MMR6:
    case Mips::BNEZ16_MM:
    case Mips::BNEZC16_MMR6:
      assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
      Offset = Inst.getOperand(1);
      if (!Offset.isImm())
        break; // We'll deal with this situation later on when applying fixups.
      if (!isInt<8>(Offset.getImm()))
        return Error(IDLoc, "branch target out of range");
      if (offsetToAlignment(Offset.getImm(), Align(2)))
        return Error(IDLoc, "branch to misaligned address");
      break;
    }
  }

  // SSNOP is deprecated on MIPS32r6/MIPS64r6
  // We still accept it but it is a normal nop.
  if (hasMips32r6() && Opcode == Mips::SSNOP) {
    std::string ISA = hasMips64r6() ? "MIPS64r6" : "MIPS32r6";
    Warning(IDLoc, "ssnop is deprecated for " + ISA + " and is equivalent to a "
                                                      "nop instruction");
  }

  if (hasCnMips()) {
    MCOperand Opnd;
    int Imm;

    switch (Opcode) {
      default:
        break;

      case Mips::BBIT0:
      case Mips::BBIT032:
      case Mips::BBIT1:
      case Mips::BBIT132:
        assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
        // The offset is handled above
        Opnd = Inst.getOperand(1);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < 0 || Imm > (Opcode == Mips::BBIT0 ||
                              Opcode == Mips::BBIT1 ? 63 : 31))
          return Error(IDLoc, "immediate operand value out of range");
        if (Imm > 31) {
          Inst.setOpcode(Opcode == Mips::BBIT0 ? Mips::BBIT032
                                               : Mips::BBIT132);
          Inst.getOperand(1).setImm(Imm - 32);
        }
        break;

      case Mips::SEQi:
      case Mips::SNEi:
        assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (!isInt<10>(Imm))
          return Error(IDLoc, "immediate operand value out of range");
        break;
    }
  }

  // Warn on division by zero. We're checking here as all instructions get
  // processed here, not just the macros that need expansion.
  //
  // The MIPS backend models most of the divison instructions and macros as
  // three operand instructions. The pre-R6 divide instructions however have
  // two operands and explicitly define HI/LO as part of the instruction,
  // not in the operands.
  unsigned FirstOp = 1;
  unsigned SecondOp = 2;
  switch (Opcode) {
  default:
    break;
  case Mips::SDivIMacro:
  case Mips::UDivIMacro:
  case Mips::DSDivIMacro:
  case Mips::DUDivIMacro:
    if (Inst.getOperand(2).getImm() == 0) {
      if (Inst.getOperand(1).getReg() == Mips::ZERO ||
          Inst.getOperand(1).getReg() == Mips::ZERO_64)
        Warning(IDLoc, "dividing zero by zero");
      else
        Warning(IDLoc, "division by zero");
    }
    break;
  case Mips::DSDIV:
  case Mips::SDIV:
  case Mips::UDIV:
  case Mips::DUDIV:
  case Mips::UDIV_MM:
  case Mips::SDIV_MM:
    FirstOp = 0;
    SecondOp = 1;
    LLVM_FALLTHROUGH;
  case Mips::SDivMacro:
  case Mips::DSDivMacro:
  case Mips::UDivMacro:
  case Mips::DUDivMacro:
  case Mips::DIV:
  case Mips::DIVU:
  case Mips::DDIV:
  case Mips::DDIVU:
  case Mips::DIVU_MMR6:
  case Mips::DIV_MMR6:
    if (Inst.getOperand(SecondOp).getReg() == Mips::ZERO ||
        Inst.getOperand(SecondOp).getReg() == Mips::ZERO_64) {
      if (Inst.getOperand(FirstOp).getReg() == Mips::ZERO ||
          Inst.getOperand(FirstOp).getReg() == Mips::ZERO_64)
        Warning(IDLoc, "dividing zero by zero");
      else
        Warning(IDLoc, "division by zero");
    }
    break;
  }

  // For PIC code convert unconditional jump to unconditional branch.
  if ((Opcode == Mips::J || Opcode == Mips::J_MM) && inPicMode()) {
    MCInst BInst;
    BInst.setOpcode(inMicroMipsMode() ? Mips::BEQ_MM : Mips::BEQ);
    BInst.addOperand(MCOperand::createReg(Mips::ZERO));
    BInst.addOperand(MCOperand::createReg(Mips::ZERO));
    BInst.addOperand(Inst.getOperand(0));
    Inst = BInst;
  }

  // This expansion is not in a function called by tryExpandInstruction()
  // because the pseudo-instruction doesn't have a distinct opcode.
  if ((Opcode == Mips::JAL || Opcode == Mips::JAL_MM) && inPicMode()) {
    warnIfNoMacro(IDLoc);

    const MCExpr *JalExpr = Inst.getOperand(0).getExpr();

    // We can do this expansion if there's only 1 symbol in the argument
    // expression.
    if (countMCSymbolRefExpr(JalExpr) > 1)
      return Error(IDLoc, "jal doesn't support multiple symbols in PIC mode");

    // FIXME: This is checking the expression can be handled by the later stages
    //        of the assembler. We ought to leave it to those later stages.
    const MCSymbol *JalSym = getSingleMCSymbol(JalExpr);

    if (expandLoadAddress(Mips::T9, Mips::NoRegister, Inst.getOperand(0),
                          !isGP64bit(), IDLoc, Out, STI))
      return true;

    MCInst JalrInst;
    if (inMicroMipsMode())
      JalrInst.setOpcode(IsCpRestoreSet ? Mips::JALRS_MM : Mips::JALR_MM);
    else
      JalrInst.setOpcode(Mips::JALR);
    JalrInst.addOperand(MCOperand::createReg(Mips::RA));
    JalrInst.addOperand(MCOperand::createReg(Mips::T9));

    if (isJalrRelocAvailable(JalExpr)) {
      // As an optimization hint for the linker, before the JALR we add:
      // .reloc tmplabel, R_{MICRO}MIPS_JALR, symbol
      // tmplabel:
      MCSymbol *TmpLabel = getContext().createTempSymbol();
      const MCExpr *TmpExpr = MCSymbolRefExpr::create(TmpLabel, getContext());
      const MCExpr *RelocJalrExpr =
          MCSymbolRefExpr::create(JalSym, MCSymbolRefExpr::VK_None,
                                  getContext(), IDLoc);

      TOut.getStreamer().emitRelocDirective(
          *TmpExpr, inMicroMipsMode() ? "R_MICROMIPS_JALR" : "R_MIPS_JALR",
          RelocJalrExpr, IDLoc, *STI);
      TOut.getStreamer().emitLabel(TmpLabel);
    }

    Inst = JalrInst;
    ExpandedJalSym = true;
  }

  if (MCID.mayLoad() || MCID.mayStore()) {
    // Check the offset of memory operand, if it is a symbol
    // reference or immediate we may have to expand instructions.
    if (needsExpandMemInst(Inst)) {
      const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode());
      switch (MCID.OpInfo[MCID.getNumOperands() - 1].OperandType) {
      case MipsII::OPERAND_MEM_SIMM9:
        expandMem9Inst(Inst, IDLoc, Out, STI, MCID.mayLoad());
        break;
      default:
        expandMem16Inst(Inst, IDLoc, Out, STI, MCID.mayLoad());
        break;
      }
      return getParser().hasPendingError();
    }
  }

  if (inMicroMipsMode()) {
    if (MCID.mayLoad() && Opcode != Mips::LWP_MM) {
      // Try to create 16-bit GP relative load instruction.
      for (unsigned i = 0; i < MCID.getNumOperands(); i++) {
        const MCOperandInfo &OpInfo = MCID.OpInfo[i];
        if ((OpInfo.OperandType == MCOI::OPERAND_MEMORY) ||
            (OpInfo.OperandType == MCOI::OPERAND_UNKNOWN)) {
          MCOperand &Op = Inst.getOperand(i);
          if (Op.isImm()) {
            int MemOffset = Op.getImm();
            MCOperand &DstReg = Inst.getOperand(0);
            MCOperand &BaseReg = Inst.getOperand(1);
            if (isInt<9>(MemOffset) && (MemOffset % 4 == 0) &&
                getContext().getRegisterInfo()->getRegClass(
                  Mips::GPRMM16RegClassID).contains(DstReg.getReg()) &&
                (BaseReg.getReg() == Mips::GP ||
                BaseReg.getReg() == Mips::GP_64)) {

              TOut.emitRRI(Mips::LWGP_MM, DstReg.getReg(), Mips::GP, MemOffset,
                           IDLoc, STI);
              return false;
            }
          }
        }
      } // for
    }   // if load

    // TODO: Handle this with the AsmOperandClass.PredicateMethod.

    MCOperand Opnd;
    int Imm;

    switch (Opcode) {
      default:
        break;
      case Mips::ADDIUSP_MM:
        Opnd = Inst.getOperand(0);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < -1032 || Imm > 1028 || (Imm < 8 && Imm > -12) ||
            Imm % 4 != 0)
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::SLL16_MM:
      case Mips::SRL16_MM:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < 1 || Imm > 8)
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::LI16_MM:
        Opnd = Inst.getOperand(1);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < -1 || Imm > 126)
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::ADDIUR2_MM:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (!(Imm == 1 || Imm == -1 ||
              ((Imm % 4 == 0) && Imm < 28 && Imm > 0)))
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::ANDI16_MM:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (!(Imm == 128 || (Imm >= 1 && Imm <= 4) || Imm == 7 || Imm == 8 ||
              Imm == 15 || Imm == 16 || Imm == 31 || Imm == 32 || Imm == 63 ||
              Imm == 64 || Imm == 255 || Imm == 32768 || Imm == 65535))
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::LBU16_MM:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < -1 || Imm > 14)
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::SB16_MM:
      case Mips::SB16_MMR6:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < 0 || Imm > 15)
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::LHU16_MM:
      case Mips::SH16_MM:
      case Mips::SH16_MMR6:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < 0 || Imm > 30 || (Imm % 2 != 0))
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::LW16_MM:
      case Mips::SW16_MM:
      case Mips::SW16_MMR6:
        Opnd = Inst.getOperand(2);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if (Imm < 0 || Imm > 60 || (Imm % 4 != 0))
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::ADDIUPC_MM:
        Opnd = Inst.getOperand(1);
        if (!Opnd.isImm())
          return Error(IDLoc, "expected immediate operand kind");
        Imm = Opnd.getImm();
        if ((Imm % 4 != 0) || !isInt<25>(Imm))
          return Error(IDLoc, "immediate operand value out of range");
        break;
      case Mips::LWP_MM:
      case Mips::SWP_MM:
        if (Inst.getOperand(0).getReg() == Mips::RA)
          return Error(IDLoc, "invalid operand for instruction");
        break;
      case Mips::MOVEP_MM:
      case Mips::MOVEP_MMR6: {
        unsigned R0 = Inst.getOperand(0).getReg();
        unsigned R1 = Inst.getOperand(1).getReg();
        bool RegPair = ((R0 == Mips::A1 && R1 == Mips::A2) ||
                        (R0 == Mips::A1 && R1 == Mips::A3) ||
                        (R0 == Mips::A2 && R1 == Mips::A3) ||
                        (R0 == Mips::A0 && R1 == Mips::S5) ||
                        (R0 == Mips::A0 && R1 == Mips::S6) ||
                        (R0 == Mips::A0 && R1 == Mips::A1) ||
                        (R0 == Mips::A0 && R1 == Mips::A2) ||
                        (R0 == Mips::A0 && R1 == Mips::A3));
        if (!RegPair)
          return Error(IDLoc, "invalid operand for instruction");
        break;
      }
    }
  }

  bool FillDelaySlot =
      MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder();
  if (FillDelaySlot)
    TOut.emitDirectiveSetNoReorder();

  MacroExpanderResultTy ExpandResult =
      tryExpandInstruction(Inst, IDLoc, Out, STI);
  switch (ExpandResult) {
  case MER_NotAMacro:
    Out.emitInstruction(Inst, *STI);
    break;
  case MER_Success:
    break;
  case MER_Fail:
    return true;
  }

  // We know we emitted an instruction on the MER_NotAMacro or MER_Success path.
  // If we're in microMIPS mode then we must also set EF_MIPS_MICROMIPS.
  if (inMicroMipsMode()) {
    TOut.setUsesMicroMips();
    TOut.updateABIInfo(*this);
  }

  // If this instruction has a delay slot and .set reorder is active,
  // emit a NOP after it.
  if (FillDelaySlot) {
    TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst), IDLoc, STI);
    TOut.emitDirectiveSetReorder();
  }

  if ((Opcode == Mips::JalOneReg || Opcode == Mips::JalTwoReg ||
       ExpandedJalSym) &&
      isPicAndNotNxxAbi()) {
    if (IsCpRestoreSet) {
      // We need a NOP between the JALR and the LW:
      // If .set reorder has been used, we've already emitted a NOP.
      // If .set noreorder has been used, we need to emit a NOP at this point.
      if (!AssemblerOptions.back()->isReorder())
        TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst), IDLoc,
                                STI);

      // Load the $gp from the stack.
      TOut.emitGPRestore(CpRestoreOffset, IDLoc, STI);
    } else
      Warning(IDLoc, "no .cprestore used in PIC mode");
  }

  return false;
}