Index: llvm/trunk/lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ llvm/trunk/lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -201,7 +201,13 @@ const MCSubtargetInfo *STI); void expandMemInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, - const MCSubtargetInfo *STI, bool isLoad, bool isImmOpnd); + const MCSubtargetInfo *STI, bool IsLoad, bool IsImmOpnd); + + void expandLoadInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd); + + void expandStoreInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd); bool expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI); @@ -2526,78 +2532,97 @@ } void MipsAsmParser::expandMemInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, - const MCSubtargetInfo *STI, bool isLoad, - bool isImmOpnd) { + const MCSubtargetInfo *STI, bool IsLoad, + bool IsImmOpnd) { + if (IsLoad) { + expandLoadInst(Inst, IDLoc, Out, STI, IsImmOpnd); + return; + } + expandStoreInst(Inst, IDLoc, Out, STI, IsImmOpnd); +} + +void MipsAsmParser::expandLoadInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd) { MipsTargetStreamer &TOut = getTargetStreamer(); - MCOperand HiOperand, LoOperand; - unsigned TmpRegNum; - // 1st operand is either the source or destination register. - assert(Inst.getOperand(0).isReg() && "expected register operand kind"); - unsigned RegOpNum = Inst.getOperand(0).getReg(); - // 2nd operand is the base register. - assert(Inst.getOperand(1).isReg() && "expected register operand kind"); - unsigned BaseRegNum = Inst.getOperand(1).getReg(); - // 3rd operand is either an immediate or expression. - if (isImmOpnd) { - assert(Inst.getOperand(2).isImm() && "expected immediate operand kind"); - unsigned ImmOffset = Inst.getOperand(2).getImm(); - unsigned LoOffset = ImmOffset & 0x0000ffff; - unsigned HiOffset = (ImmOffset & 0xffff0000) >> 16; - // If msb of LoOffset is 1(negative number) we must increment HiOffset. - if (LoOffset & 0x8000) - HiOffset++; - LoOperand = MCOperand::createImm(LoOffset); - HiOperand = MCOperand::createImm(HiOffset); - } else { - const MCExpr *ExprOffset = Inst.getOperand(2).getExpr(); - LoOperand = MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "lo")); - HiOperand = MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "hi")); - } - // These are some of the types of expansions we perform here: - // 1) lw $8, sym => lui $8, %hi(sym) - // lw $8, %lo(sym)($8) - // 2) lw $8, offset($9) => lui $8, %hi(offset) - // add $8, $8, $9 - // lw $8, %lo(offset)($9) - // 3) lw $8, offset($8) => lui $at, %hi(offset) - // add $at, $at, $8 - // lw $8, %lo(offset)($at) - // 4) sw $8, sym => lui $at, %hi(sym) - // sw $8, %lo(sym)($at) - // 5) sw $8, offset($8) => lui $at, %hi(offset) - // add $at, $at, $8 - // sw $8, %lo(offset)($at) - // 6) ldc1 $f0, sym => lui $at, %hi(sym) - // ldc1 $f0, %lo(sym)($at) - // - // For load instructions we can use the destination register as a temporary - // if base and dst are different (examples 1 and 2) and if the base register - // is general purpose otherwise we must use $at (example 6) and error if it's - // not available. For stores we must use $at (examples 4 and 5) because we - // must not clobber the source register setting up the offset. + + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned BaseReg = Inst.getOperand(1).getReg(); + const MCInstrDesc &Desc = getInstDesc(Inst.getOpcode()); - int16_t RegClassOp0 = Desc.OpInfo[0].RegClass; - unsigned RegClassIDOp0 = - getContext().getRegisterInfo()->getRegClass(RegClassOp0).getID(); - bool IsGPR = (RegClassIDOp0 == Mips::GPR32RegClassID) || - (RegClassIDOp0 == Mips::GPR64RegClassID); - if (isLoad && IsGPR && (BaseRegNum != RegOpNum)) - TmpRegNum = RegOpNum; - else { + int16_t DstRegClass = Desc.OpInfo[0].RegClass; + unsigned DstRegClassID = + getContext().getRegisterInfo()->getRegClass(DstRegClass).getID(); + bool IsGPR = (DstRegClassID == Mips::GPR32RegClassID) || + (DstRegClassID == Mips::GPR64RegClassID); + + if (IsImmOpnd) { + // Try to use DstReg as the temporary. + if (IsGPR && (BaseReg != DstReg)) { + TOut.emitLoadWithImmOffset(Inst.getOpcode(), DstReg, BaseReg, + Inst.getOperand(2).getImm(), DstReg, IDLoc, + STI); + return; + } + // At this point we need AT to perform the expansions and we exit if it is // not available. - TmpRegNum = getATReg(IDLoc); - if (!TmpRegNum) + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) return; + + TOut.emitLoadWithImmOffset(Inst.getOpcode(), DstReg, BaseReg, + Inst.getOperand(2).getImm(), ATReg, IDLoc, STI); + return; + } + + const MCExpr *ExprOffset = Inst.getOperand(2).getExpr(); + MCOperand LoOperand = + MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "lo")); + MCOperand HiOperand = + MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "hi")); + + // Try to use DstReg as the temporary. + if (IsGPR && (BaseReg != DstReg)) { + TOut.emitLoadWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, + LoOperand, DstReg, IDLoc, STI); + return; + } + + // At this point we need AT to perform the expansions and we exit if it is + // not available. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return; + + TOut.emitLoadWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, + LoOperand, ATReg, IDLoc, STI); +} + +void MipsAsmParser::expandStoreInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, + bool IsImmOpnd) { + MipsTargetStreamer &TOut = getTargetStreamer(); + + unsigned SrcReg = Inst.getOperand(0).getReg(); + unsigned BaseReg = Inst.getOperand(1).getReg(); + + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return; + + if (IsImmOpnd) { + TOut.emitStoreWithImmOffset(Inst.getOpcode(), SrcReg, BaseReg, + Inst.getOperand(2).getImm(), ATReg, IDLoc, STI); + return; } - TOut.emitRX(Mips::LUi, TmpRegNum, HiOperand, IDLoc, STI); - // Add temp register to base. - if (BaseRegNum != Mips::ZERO) - TOut.emitRRR(Mips::ADDu, TmpRegNum, TmpRegNum, BaseRegNum, IDLoc, STI); - // And finally, create original instruction with low part - // of offset and new base. - TOut.emitRRX(Inst.getOpcode(), RegOpNum, TmpRegNum, LoOperand, IDLoc, STI); + const MCExpr *ExprOffset = Inst.getOperand(2).getExpr(); + MCOperand LoOperand = + MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "lo")); + MCOperand HiOperand = + MCOperand::createExpr(evaluateRelocExpr(ExprOffset, "hi")); + TOut.emitStoreWithSymOffset(Inst.getOpcode(), SrcReg, BaseReg, HiOperand, + LoOperand, ATReg, IDLoc, STI); } bool MipsAsmParser::expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, Index: llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp +++ llvm/trunk/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp @@ -207,6 +207,106 @@ emitRRI(Mips::SLL, Mips::ZERO, Mips::ZERO, 0, IDLoc, STI); } +/// Emit a store instruction with an immediate offset. The immediate is +/// expected to be out-of-range for a simm16 and will be expanded to +/// appropriate instructions. +void MipsTargetStreamer::emitStoreWithImmOffset( + unsigned Opcode, unsigned SrcReg, unsigned BaseReg, int64_t Offset, + unsigned ATReg, SMLoc IDLoc, const MCSubtargetInfo *STI) { + // sw $8, offset($8) => lui $at, %hi(offset) + // add $at, $at, $8 + // sw $8, %lo(offset)($at) + + unsigned LoOffset = Offset & 0x0000ffff; + unsigned HiOffset = (Offset & 0xffff0000) >> 16; + + // If msb of LoOffset is 1(negative number) we must increment HiOffset + // to account for the sign-extension of the low part. + if (LoOffset & 0x8000) + HiOffset++; + + // Generate the base address in ATReg. + emitRI(Mips::LUi, ATReg, HiOffset, IDLoc, STI); + if (BaseReg != Mips::ZERO) + emitRRR(Mips::ADDu, ATReg, ATReg, BaseReg, IDLoc, STI); + // Emit the store with the adjusted base and offset. + emitRRI(Opcode, SrcReg, ATReg, LoOffset, IDLoc, STI); +} + +/// Emit a store instruction with an symbol offset. Symbols are assumed to be +/// out of range for a simm16 will be expanded to appropriate instructions. +void MipsTargetStreamer::emitStoreWithSymOffset( + unsigned Opcode, unsigned SrcReg, unsigned BaseReg, MCOperand &HiOperand, + MCOperand &LoOperand, unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + // sw $8, sym => lui $at, %hi(sym) + // sw $8, %lo(sym)($at) + + // Generate the base address in ATReg. + emitRX(Mips::LUi, ATReg, HiOperand, IDLoc, STI); + if (BaseReg != Mips::ZERO) + emitRRR(Mips::ADDu, ATReg, ATReg, BaseReg, IDLoc, STI); + // Emit the store with the adjusted base and offset. + emitRRX(Opcode, SrcReg, ATReg, LoOperand, IDLoc, STI); +} + +/// Emit a load instruction with an immediate offset. The immediate is expected +/// to be out-of-range for a simm16 and will be expanded to appropriate +/// instructions. DstReg and TmpReg are permitted to be the same register iff +/// DstReg is distinct from BaseReg and DstReg is a GPR. It is the callers +/// responsibility to identify such cases and pass the appropriate register in +/// TmpReg. +void MipsTargetStreamer::emitLoadWithImmOffset(unsigned Opcode, unsigned DstReg, + unsigned BaseReg, int64_t Offset, + unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + // 1) lw $8, offset($9) => lui $8, %hi(offset) + // add $8, $8, $9 + // lw $8, %lo(offset)($9) + // 2) lw $8, offset($8) => lui $at, %hi(offset) + // add $at, $at, $8 + // lw $8, %lo(offset)($at) + + unsigned LoOffset = Offset & 0x0000ffff; + unsigned HiOffset = (Offset & 0xffff0000) >> 16; + + // If msb of LoOffset is 1(negative number) we must increment HiOffset + // to account for the sign-extension of the low part. + if (LoOffset & 0x8000) + HiOffset++; + + // Generate the base address in TmpReg. + emitRI(Mips::LUi, TmpReg, HiOffset, IDLoc, STI); + if (BaseReg != Mips::ZERO) + emitRRR(Mips::ADDu, TmpReg, TmpReg, BaseReg, IDLoc, STI); + // Emit the load with the adjusted base and offset. + emitRRI(Opcode, DstReg, TmpReg, LoOffset, IDLoc, STI); +} + +/// Emit a load instruction with an symbol offset. Symbols are assumed to be +/// out of range for a simm16 will be expanded to appropriate instructions. +/// DstReg and TmpReg are permitted to be the same register iff DstReg is a +/// GPR. It is the callers responsibility to identify such cases and pass the +/// appropriate register in TmpReg. +void MipsTargetStreamer::emitLoadWithSymOffset(unsigned Opcode, unsigned DstReg, + unsigned BaseReg, + MCOperand &HiOperand, + MCOperand &LoOperand, + unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + // 1) lw $8, sym => lui $8, %hi(sym) + // lw $8, %lo(sym)($8) + // 2) ldc1 $f0, sym => lui $at, %hi(sym) + // ldc1 $f0, %lo(sym)($at) + + // Generate the base address in TmpReg. + emitRX(Mips::LUi, TmpReg, HiOperand, IDLoc, STI); + if (BaseReg != Mips::ZERO) + emitRRR(Mips::ADDu, TmpReg, TmpReg, BaseReg, IDLoc, STI); + // Emit the load with the adjusted base and offset. + emitRRX(Opcode, DstReg, TmpReg, LoOperand, IDLoc, STI); +} + MipsTargetAsmStreamer::MipsTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS) : MipsTargetStreamer(S), OS(OS) {} Index: llvm/trunk/lib/Target/Mips/MipsTargetStreamer.h =================================================================== --- llvm/trunk/lib/Target/Mips/MipsTargetStreamer.h +++ llvm/trunk/lib/Target/Mips/MipsTargetStreamer.h @@ -116,6 +116,20 @@ void emitEmptyDelaySlot(bool hasShortDelaySlot, SMLoc IDLoc, const MCSubtargetInfo *STI); void emitNop(SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitStoreWithImmOffset(unsigned Opcode, unsigned SrcReg, + unsigned BaseReg, int64_t Offset, unsigned ATReg, + SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitStoreWithSymOffset(unsigned Opcode, unsigned SrcReg, + unsigned BaseReg, MCOperand &HiOperand, + MCOperand &LoOperand, unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitLoadWithImmOffset(unsigned Opcode, unsigned DstReg, unsigned BaseReg, + int64_t Offset, unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitLoadWithSymOffset(unsigned Opcode, unsigned DstReg, unsigned BaseReg, + MCOperand &HiOperand, MCOperand &LoOperand, + unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); void forbidModuleDirective() { ModuleDirectiveAllowed = false; } void reallowModuleDirective() { ModuleDirectiveAllowed = true; } Index: llvm/trunk/test/MC/Mips/micromips-expansions.s =================================================================== --- llvm/trunk/test/MC/Mips/micromips-expansions.s +++ llvm/trunk/test/MC/Mips/micromips-expansions.s @@ -39,7 +39,7 @@ # CHECK: lw $10, 123($10) # encoding: [0x4a,0xfd,0x7b,0x00] # CHECK: lui $1, 2 # encoding: [0xa1,0x41,0x02,0x00] # CHECK: addu $1, $1, $9 # encoding: [0x21,0x01,0x50,0x09] -# CHECK: sw $10, 57920($1) # encoding: [0x41,0xf9,0x40,0xe2] +# CHECK: sw $10, -7616($1) # encoding: [0x41,0xf9,0x40,0xe2] li $5,123 li $6,-2345 Index: llvm/trunk/test/MC/Mips/mips-expansions.s =================================================================== --- llvm/trunk/test/MC/Mips/mips-expansions.s +++ llvm/trunk/test/MC/Mips/mips-expansions.s @@ -47,7 +47,7 @@ sw $10, 123456($9) # CHECK-LE: lui $1, 2 # encoding: [0x02,0x00,0x01,0x3c] # CHECK-LE: addu $1, $1, $9 # encoding: [0x21,0x08,0x29,0x00] -# CHECK-LE: sw $10, 57920($1) # encoding: [0x40,0xe2,0x2a,0xac] +# CHECK-LE: sw $10, -7616($1) # encoding: [0x40,0xe2,0x2a,0xac] lw $8, symbol # CHECK-LE: lui $8, %hi(symbol) # encoding: [A,A,0x08,0x3c]