+bool MipsAsmParser::expandCondBranches(MCInst &Inst, SMLoc IDLoc,
+ SmallVectorImpl<MCInst> &Instructions) {
+ unsigned PseudoOpcode = Inst.getOpcode();
+ unsigned SrcReg = Inst.getOperand(0).getReg();
+ unsigned TrgReg = Inst.getOperand(1).getReg();
+ const MCExpr *OffsetExpr = Inst.getOperand(2).getExpr();
+
+ unsigned ZeroSrcOpcode, ZeroTrgOpcode;
+ bool ReverseOrderSLT, IsUnsigned, AcceptsEquality;
+
+ switch (PseudoOpcode) {
+ case Mips::BLT:
+ case Mips::BLTU:
+ AcceptsEquality = false;
+ ReverseOrderSLT = false;
+ IsUnsigned = (PseudoOpcode == Mips::BLTU);
+ ZeroSrcOpcode = Mips::BGTZ;
+ ZeroTrgOpcode = Mips::BLTZ;
+ break;
+ case Mips::BLE:
+ case Mips::BLEU:
+ AcceptsEquality = true;
+ ReverseOrderSLT = true;
+ IsUnsigned = (PseudoOpcode == Mips::BLEU);
+ ZeroSrcOpcode = Mips::BGEZ;
+ ZeroTrgOpcode = Mips::BLEZ;
+ break;
+ case Mips::BGE:
+ case Mips::BGEU:
+ AcceptsEquality = true;
+ ReverseOrderSLT = false;
+ IsUnsigned = (PseudoOpcode == Mips::BGEU);
+ ZeroSrcOpcode = Mips::BLEZ;
+ ZeroTrgOpcode = Mips::BGEZ;
+ break;
+ case Mips::BGT:
+ case Mips::BGTU:
+ AcceptsEquality = false;
+ ReverseOrderSLT = true;
+ IsUnsigned = (PseudoOpcode == Mips::BGTU);
+ ZeroSrcOpcode = Mips::BLTZ;
+ ZeroTrgOpcode = Mips::BGTZ;
+ break;
+ default:
+ llvm_unreachable("unknown opcode for branch pseudo-instruction");
+ }
+
+ MCInst BranchInst;
+ bool IsTrgRegZero = (TrgReg == Mips::ZERO);
+ bool IsSrcRegZero = (SrcReg == Mips::ZERO);
+ if (IsSrcRegZero && IsTrgRegZero) {
+ // FIXME: All of these Opcode-specific if's are needed for compatibility
+ // with GAS' behaviour. However, they may not generate the most efficient
+ // code in some circumstances.
+ if (PseudoOpcode == Mips::BLT) {
+ BranchInst.setOpcode(Mips::BLTZ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+ }
+ if (PseudoOpcode == Mips::BLE) {
+ BranchInst.setOpcode(Mips::BLEZ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ Warning(IDLoc, "branch is always taken");
+ return false;
+ }
+ if (PseudoOpcode == Mips::BGE) {
+ BranchInst.setOpcode(Mips::BGEZ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ Warning(IDLoc, "branch is always taken");
+ return false;
+ }
+ if (PseudoOpcode == Mips::BGT) {
+ BranchInst.setOpcode(Mips::BGTZ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+ }
+ if (PseudoOpcode == Mips::BGTU) {
+ BranchInst.setOpcode(Mips::BNE);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+ }
+ if (AcceptsEquality) {
+ // If both registers are $0 and the pseudo-branch accepts equality, it
+ // will always be taken, so we emit an unconditional branch.
+ BranchInst.setOpcode(Mips::BEQ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ Warning(IDLoc, "branch is always taken");
+ return false;
+ }
+ // If both registers are $0 and the pseudo-branch does not accept
+ // equality, it will never be taken, so we don't have to emit anything.
+ return false;
+ }
+ if (IsSrcRegZero || IsTrgRegZero) {
+ if ((IsSrcRegZero && PseudoOpcode == Mips::BGTU) ||
+ (IsTrgRegZero && PseudoOpcode == Mips::BLTU)) {
+ // If the $rs is $0 and the pseudo-branch is BGTU (0 > x) or
+ // if the $rt is $0 and the pseudo-branch is BLTU (x < 0),
+ // the pseudo-branch will never be taken, so we don't emit anything.
+ // This only applies to unsigned pseudo-branches.
+ return false;
+ }
+ if ((IsSrcRegZero && PseudoOpcode == Mips::BLEU) ||
+ (IsTrgRegZero && PseudoOpcode == Mips::BGEU)) {
+ // If the $rs is $0 and the pseudo-branch is BLEU (0 <= x) or
+ // if the $rt is $0 and the pseudo-branch is BGEU (x >= 0),
+ // the pseudo-branch will always be taken, so we emit an unconditional
+ // branch.
+ // This only applies to unsigned pseudo-branches.
+ BranchInst.setOpcode(Mips::BEQ);
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ Warning(IDLoc, "branch is always taken");
+ return false;
+ }
+ if (IsUnsigned) {
+ // If the $rs is $0 and the pseudo-branch is BLTU (0 < x) or
+ // if the $rt is $0 and the pseudo-branch is BGTU (x > 0),
+ // the pseudo-branch will be taken only when the non-zero register is
+ // different from 0, so we emit a BNEZ.
+ //
+ // If the $rs is $0 and the pseudo-branch is BGEU (0 >= x) or
+ // if the $rt is $0 and the pseudo-branch is BLEU (x <= 0),
+ // the pseudo-branch will be taken only when the non-zero register is
+ // equal to 0, so we emit a BEQZ.
+ //
+ // Because only BLEU and BGEU branch on equality, we can use the
+ // AcceptsEquality variable to decide when to emit the BEQZ.
+ BranchInst.setOpcode(AcceptsEquality ? Mips::BEQ : Mips::BNE);
+ BranchInst.addOperand(
+ MCOperand::createReg(IsSrcRegZero ? TrgReg : SrcReg));
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+ }
+ // If we have a signed pseudo-branch and one of the registers is $0,
+ // we can use an appropriate compare-to-zero branch. We select which one
+ // to use in the switch statement above.
+ BranchInst.setOpcode(IsSrcRegZero ? ZeroSrcOpcode : ZeroTrgOpcode);
+ BranchInst.addOperand(MCOperand::createReg(IsSrcRegZero ? TrgReg : SrcReg));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+ }
+
+ // If neither the SrcReg nor the TrgReg are $0, we need AT to perform the
+ // expansions. If it is not available, we return.
+ unsigned ATRegNum = getATReg(IDLoc);
+ if (!ATRegNum)
+ return true;
+
+ warnIfNoMacro(IDLoc);
+
+ // SLT fits well with 2 of our 4 pseudo-branches:
+ // BLT, where $rs < $rt, translates into "slt $at, $rs, $rt" and
+ // BGT, where $rs > $rt, translates into "slt $at, $rt, $rs".
+ // If the result of the SLT is 1, we branch, and if it's 0, we don't.
+ // This is accomplished by using a BNEZ with the result of the SLT.
+ //
+ // The other 2 pseudo-branches are opposites of the above 2 (BGE with BLT
+ // and BLE with BGT), so we change the BNEZ into a a BEQZ.
+ // Because only BGE and BLE branch on equality, we can use the
+ // AcceptsEquality variable to decide when to emit the BEQZ.
+ // Note that the order of the SLT arguments doesn't change between
+ // opposites.
+ //
+ // The same applies to the unsigned variants, except that SLTu is used
+ // instead of SLT.
+ MCInst SetInst;
+ SetInst.setOpcode(IsUnsigned ? Mips::SLTu : Mips::SLT);
+ SetInst.addOperand(MCOperand::createReg(ATRegNum));
+ SetInst.addOperand(MCOperand::createReg(ReverseOrderSLT ? TrgReg : SrcReg));
+ SetInst.addOperand(MCOperand::createReg(ReverseOrderSLT ? SrcReg : TrgReg));
+ Instructions.push_back(SetInst);
+
+ BranchInst.setOpcode(AcceptsEquality ? Mips::BEQ : Mips::BNE);
+ BranchInst.addOperand(MCOperand::createReg(ATRegNum));
+ BranchInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ BranchInst.addOperand(MCOperand::createExpr(OffsetExpr));
+ Instructions.push_back(BranchInst);
+ return false;
+}
+
+bool MipsAsmParser::expandDiv(MCInst &Inst, SMLoc IDLoc,
+ SmallVectorImpl<MCInst> &Instructions,
+ const bool IsMips64, const bool Signed) {
+ if (hasMips32r6()) {
+ Error(IDLoc, "instruction not supported on mips32r6 or mips64r6");
+ return false;
+ }
+
+ warnIfNoMacro(IDLoc);
+
+ const MCOperand &RsRegOp = Inst.getOperand(0);
+ assert(RsRegOp.isReg() && "expected register operand kind");
+ unsigned RsReg = RsRegOp.getReg();
+
+ const MCOperand &RtRegOp = Inst.getOperand(1);
+ assert(RtRegOp.isReg() && "expected register operand kind");
+ unsigned RtReg = RtRegOp.getReg();
+ unsigned DivOp;
+ unsigned ZeroReg;
+
+ if (IsMips64) {
+ DivOp = Signed ? Mips::DSDIV : Mips::DUDIV;
+ ZeroReg = Mips::ZERO_64;
+ } else {
+ DivOp = Signed ? Mips::SDIV : Mips::UDIV;
+ ZeroReg = Mips::ZERO;
+ }
+
+ bool UseTraps = useTraps();
+
+ if (RsReg == Mips::ZERO || RsReg == Mips::ZERO_64) {
+ if (RtReg == Mips::ZERO || RtReg == Mips::ZERO_64)
+ Warning(IDLoc, "dividing zero by zero");
+ if (IsMips64) {
+ if (Signed && (RtReg == Mips::ZERO || RtReg == Mips::ZERO_64)) {
+ if (UseTraps) {
+ emitRRI(Mips::TEQ, RtReg, ZeroReg, 0x7, IDLoc, Instructions);
+ return false;
+ }
+
+ emitII(Mips::BREAK, 0x7, 0, IDLoc, Instructions);
+ return false;
+ }
+ } else {
+ emitRR(DivOp, RsReg, RtReg, IDLoc, Instructions);
+ return false;
+ }
+ }
+
+ if (RtReg == Mips::ZERO || RtReg == Mips::ZERO_64) {
+ Warning(IDLoc, "division by zero");
+ if (Signed) {
+ if (UseTraps) {
+ emitRRI(Mips::TEQ, RtReg, ZeroReg, 0x7, IDLoc, Instructions);
+ return false;
+ }
+
+ emitII(Mips::BREAK, 0x7, 0, IDLoc, Instructions);
+ return false;
+ }
+ }
+
+ // FIXME: The values for these two BranchTarget variables may be different in
+ // micromips. These magic numbers need to be removed.
+ unsigned BranchTargetNoTraps;
+ unsigned BranchTarget;
+
+ if (UseTraps) {
+ BranchTarget = IsMips64 ? 12 : 8;
+ emitRRI(Mips::TEQ, RtReg, ZeroReg, 0x7, IDLoc, Instructions);
+ } else {
+ BranchTarget = IsMips64 ? 20 : 16;
+ BranchTargetNoTraps = 8;
+ // Branch to the li instruction.
+ emitRRI(Mips::BNE, RtReg, ZeroReg, BranchTargetNoTraps, IDLoc,
+ Instructions);
+ }
+
+ emitRR(DivOp, RsReg, RtReg, IDLoc, Instructions);
+
+ if (!UseTraps)
+ emitII(Mips::BREAK, 0x7, 0, IDLoc, Instructions);
+
+ if (!Signed) {
+ emitR(Mips::MFLO, RsReg, IDLoc, Instructions);
+ return false;
+ }
+
+ unsigned ATReg = getATReg(IDLoc);
+ if (!ATReg)
+ return true;
+
+ emitRRI(Mips::ADDiu, ATReg, ZeroReg, -1, IDLoc, Instructions);
+ if (IsMips64) {
+ // Branch to the mflo instruction.
+ emitRRI(Mips::BNE, RtReg, ATReg, BranchTarget, IDLoc, Instructions);
+ emitRRI(Mips::ADDiu, ATReg, ZeroReg, 1, IDLoc, Instructions);
+ emitRRI(Mips::DSLL32, ATReg, ATReg, 0x1f, IDLoc, Instructions);
+ } else {
+ // Branch to the mflo instruction.
+ emitRRI(Mips::BNE, RtReg, ATReg, BranchTarget, IDLoc, Instructions);
+ emitRI(Mips::LUi, ATReg, (uint16_t)0x8000, IDLoc, Instructions);
+ }
+
+ if (UseTraps)
+ emitRRI(Mips::TEQ, RsReg, ATReg, 0x6, IDLoc, Instructions);
+ else {
+ // Branch to the mflo instruction.
+ emitRRI(Mips::BNE, RsReg, ATReg, BranchTargetNoTraps, IDLoc, Instructions);
+ emitRRI(Mips::SLL, ZeroReg, ZeroReg, 0, IDLoc, Instructions);
+ emitII(Mips::BREAK, 0x6, 0, IDLoc, Instructions);
+ }
+ emitR(Mips::MFLO, RsReg, IDLoc, Instructions);
+ return false;
+}
+
+bool MipsAsmParser::expandUlhu(MCInst &Inst, SMLoc IDLoc,
+ SmallVectorImpl<MCInst> &Instructions) {
+ if (hasMips32r6() || hasMips64r6()) {
+ Error(IDLoc, "instruction not supported on mips32r6 or mips64r6");
+ return false;
+ }
+
+ warnIfNoMacro(IDLoc);
+
+ const MCOperand &DstRegOp = Inst.getOperand(0);
+ assert(DstRegOp.isReg() && "expected register operand kind");
+
+ const MCOperand &SrcRegOp = Inst.getOperand(1);
+ assert(SrcRegOp.isReg() && "expected register operand kind");
+
+ const MCOperand &OffsetImmOp = Inst.getOperand(2);
+ assert(OffsetImmOp.isImm() && "expected immediate operand kind");
+
+ unsigned DstReg = DstRegOp.getReg();
+ unsigned SrcReg = SrcRegOp.getReg();
+ int64_t OffsetValue = OffsetImmOp.getImm();
+
+ // NOTE: We always need AT for ULHU, as it is always used as the source
+ // register for one of the LBu's.
+ unsigned ATReg = getATReg(IDLoc);
+ if (!ATReg)
+ return true;
+
+ // When the value of offset+1 does not fit in 16 bits, we have to load the
+ // offset in AT, (D)ADDu the original source register (if there was one), and
+ // then use AT as the source register for the 2 generated LBu's.
+ bool LoadedOffsetInAT = false;
+ if (!isInt<16>(OffsetValue + 1) || !isInt<16>(OffsetValue)) {
+ LoadedOffsetInAT = true;
+
+ if (loadImmediate(OffsetValue, ATReg, Mips::NoRegister, !ABI.ArePtrs64bit(),
+ true, IDLoc, Instructions))
+ return true;
+
+ // NOTE: We do this (D)ADDu here instead of doing it in loadImmediate()
+ // because it will make our output more similar to GAS'. For example,
+ // generating an "ori $1, $zero, 32768" followed by an "addu $1, $1, $9",
+ // instead of just an "ori $1, $9, 32768".
+ // NOTE: If there is no source register specified in the ULHU, the parser
+ // will interpret it as $0.
+ if (SrcReg != Mips::ZERO && SrcReg != Mips::ZERO_64)
+ createAddu(ATReg, ATReg, SrcReg, ABI.ArePtrs64bit(), Instructions);
+ }
+
+ unsigned FirstLbuDstReg = LoadedOffsetInAT ? DstReg : ATReg;
+ unsigned SecondLbuDstReg = LoadedOffsetInAT ? ATReg : DstReg;
+ unsigned LbuSrcReg = LoadedOffsetInAT ? ATReg : SrcReg;
+
+ int64_t FirstLbuOffset = 0, SecondLbuOffset = 0;
+ if (isLittle()) {
+ FirstLbuOffset = LoadedOffsetInAT ? 1 : (OffsetValue + 1);
+ SecondLbuOffset = LoadedOffsetInAT ? 0 : OffsetValue;
+ } else {
+ FirstLbuOffset = LoadedOffsetInAT ? 0 : OffsetValue;
+ SecondLbuOffset = LoadedOffsetInAT ? 1 : (OffsetValue + 1);
+ }
+
+ unsigned SllReg = LoadedOffsetInAT ? DstReg : ATReg;
+
+ MCInst TmpInst;
+ TmpInst.setOpcode(Mips::LBu);
+ TmpInst.addOperand(MCOperand::createReg(FirstLbuDstReg));
+ TmpInst.addOperand(MCOperand::createReg(LbuSrcReg));
+ TmpInst.addOperand(MCOperand::createImm(FirstLbuOffset));
+ Instructions.push_back(TmpInst);
+
+ TmpInst.clear();
+ TmpInst.setOpcode(Mips::LBu);
+ TmpInst.addOperand(MCOperand::createReg(SecondLbuDstReg));
+ TmpInst.addOperand(MCOperand::createReg(LbuSrcReg));
+ TmpInst.addOperand(MCOperand::createImm(SecondLbuOffset));
+ Instructions.push_back(TmpInst);
+
+ TmpInst.clear();
+ TmpInst.setOpcode(Mips::SLL);
+ TmpInst.addOperand(MCOperand::createReg(SllReg));
+ TmpInst.addOperand(MCOperand::createReg(SllReg));
+ TmpInst.addOperand(MCOperand::createImm(8));
+ Instructions.push_back(TmpInst);
+
+ TmpInst.clear();
+ TmpInst.setOpcode(Mips::OR);
+ TmpInst.addOperand(MCOperand::createReg(DstReg));
+ TmpInst.addOperand(MCOperand::createReg(DstReg));
+ TmpInst.addOperand(MCOperand::createReg(ATReg));
+ Instructions.push_back(TmpInst);
+
+ return false;
+}
+
+bool MipsAsmParser::expandUlw(MCInst &Inst, SMLoc IDLoc,
+ SmallVectorImpl<MCInst> &Instructions) {
+ if (hasMips32r6() || hasMips64r6()) {
+ Error(IDLoc, "instruction not supported on mips32r6 or mips64r6");
+ return false;
+ }
+
+ const MCOperand &DstRegOp = Inst.getOperand(0);
+ assert(DstRegOp.isReg() && "expected register operand kind");
+
+ const MCOperand &SrcRegOp = Inst.getOperand(1);
+ assert(SrcRegOp.isReg() && "expected register operand kind");
+
+ const MCOperand &OffsetImmOp = Inst.getOperand(2);
+ assert(OffsetImmOp.isImm() && "expected immediate operand kind");
+
+ unsigned SrcReg = SrcRegOp.getReg();
+ int64_t OffsetValue = OffsetImmOp.getImm();
+ unsigned ATReg = 0;
+
+ // When the value of offset+3 does not fit in 16 bits, we have to load the
+ // offset in AT, (D)ADDu the original source register (if there was one), and
+ // then use AT as the source register for the generated LWL and LWR.
+ bool LoadedOffsetInAT = false;
+ if (!isInt<16>(OffsetValue + 3) || !isInt<16>(OffsetValue)) {
+ ATReg = getATReg(IDLoc);
+ if (!ATReg)
+ return true;
+ LoadedOffsetInAT = true;
+
+ warnIfNoMacro(IDLoc);
+
+ if (loadImmediate(OffsetValue, ATReg, Mips::NoRegister, !ABI.ArePtrs64bit(),
+ true, IDLoc, Instructions))
+ return true;
+
+ // NOTE: We do this (D)ADDu here instead of doing it in loadImmediate()
+ // because it will make our output more similar to GAS'. For example,
+ // generating an "ori $1, $zero, 32768" followed by an "addu $1, $1, $9",
+ // instead of just an "ori $1, $9, 32768".
+ // NOTE: If there is no source register specified in the ULW, the parser
+ // will interpret it as $0.
+ if (SrcReg != Mips::ZERO && SrcReg != Mips::ZERO_64)
+ createAddu(ATReg, ATReg, SrcReg, ABI.ArePtrs64bit(), Instructions);
+ }
+
+ unsigned FinalSrcReg = LoadedOffsetInAT ? ATReg : SrcReg;
+ int64_t LeftLoadOffset = 0, RightLoadOffset = 0;
+ if (isLittle()) {
+ LeftLoadOffset = LoadedOffsetInAT ? 3 : (OffsetValue + 3);
+ RightLoadOffset = LoadedOffsetInAT ? 0 : OffsetValue;
+ } else {
+ LeftLoadOffset = LoadedOffsetInAT ? 0 : OffsetValue;
+ RightLoadOffset = LoadedOffsetInAT ? 3 : (OffsetValue + 3);
+ }
+
+ MCInst LeftLoadInst;
+ LeftLoadInst.setOpcode(Mips::LWL);
+ LeftLoadInst.addOperand(DstRegOp);
+ LeftLoadInst.addOperand(MCOperand::createReg(FinalSrcReg));
+ LeftLoadInst.addOperand(MCOperand::createImm(LeftLoadOffset));
+ Instructions.push_back(LeftLoadInst);
+
+ MCInst RightLoadInst;
+ RightLoadInst.setOpcode(Mips::LWR);
+ RightLoadInst.addOperand(DstRegOp);
+ RightLoadInst.addOperand(MCOperand::createReg(FinalSrcReg));
+ RightLoadInst.addOperand(MCOperand::createImm(RightLoadOffset ));
+ Instructions.push_back(RightLoadInst);
+
+ return false;
+}
+
+void MipsAsmParser::createNop(bool hasShortDelaySlot, SMLoc IDLoc,
+ SmallVectorImpl<MCInst> &Instructions) {
+ MCInst NopInst;
+ if (hasShortDelaySlot) {
+ NopInst.setOpcode(Mips::MOVE16_MM);
+ NopInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ NopInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ } else {
+ NopInst.setOpcode(Mips::SLL);
+ NopInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ NopInst.addOperand(MCOperand::createReg(Mips::ZERO));
+ NopInst.addOperand(MCOperand::createImm(0));
+ }
+ Instructions.push_back(NopInst);
+}
+
+void MipsAsmParser::createAddu(unsigned DstReg, unsigned SrcReg,
+ unsigned TrgReg, bool Is64Bit,
+ SmallVectorImpl<MCInst> &Instructions) {
+ emitRRR(Is64Bit ? Mips::DADDu : Mips::ADDu, DstReg, SrcReg, TrgReg, SMLoc(),
+ Instructions);
+}
+