diff --git a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp --- a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp +++ b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp @@ -111,6 +111,10 @@ uint64_t Address, const void *Decoder); +/// Decode the register from a POPM instruction. +static DecodeStatus decodePOPMRegOperand(MCInst &MI, uint64_t Bits, + uint64_t Address, const void *Decoder); + #include "MSP430GenDisassemblerTables.inc" static DecodeStatus DecodeCGImm(MCInst &MI, uint64_t Bits, uint64_t Address, @@ -153,6 +157,23 @@ return MCDisassembler::Success; } +/// In a POPM instruction, the destination register is encoded as: +/// - + 1. +static DecodeStatus decodePOPMRegOperand(MCInst &MI, uint64_t Bits, + uint64_t Address, + const void *Decoder) { + assert(MI.getNumOperands() == 1 && "expected one already decoded operand"); + const MCOperand &MO = MI.getOperand(0); + assert(MO.isImm() && "expr operand expected"); + + int64_t Count = MO.getImm(); + assert(Count <= 15 && "invalid popm count value"); + unsigned Reg = Bits & 15; + Reg = Reg + Count - 1; + + return DecodeGR16RegisterClass(MI, Reg, Address, Decoder); +} + enum AddrMode { amInvalid = 0, amRegister, diff --git a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp --- a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp +++ b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp @@ -81,6 +81,11 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + /// Encode the register in a POPM instruction. + unsigned getPOPMRegOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + public: MSP430MCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) : Ctx(ctx), MCII(MCII) {} @@ -250,6 +255,26 @@ } } +/// In a POPM instruction, the destination register is encoded as: +/// - + 1. +unsigned +MSP430MCCodeEmitter::getPOPMRegOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(Op == 1 && "expected popmreg to be op1 of the inst"); + const MCOperand &MO1 = MI.getOperand(1); + assert(MO1.isReg() && "register operand expected"); + unsigned Reg = Ctx.getRegisterInfo()->getEncodingValue(MO1.getReg()); + + const MCOperand &MO = MI.getOperand(0); + assert(MO.isImm() && "expr operand expected"); + + int64_t Count = MO.getImm(); + assert((Count >= 1 && Count <= 16) && "invalid popm count"); + + return Reg - Count + 1; +} + MCCodeEmitter *createMSP430MCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { diff --git a/llvm/lib/Target/MSP430/MSP430FrameLowering.cpp b/llvm/lib/Target/MSP430/MSP430FrameLowering.cpp --- a/llvm/lib/Target/MSP430/MSP430FrameLowering.cpp +++ b/llvm/lib/Target/MSP430/MSP430FrameLowering.cpp @@ -64,11 +64,13 @@ // Save FP into the appropriate stack slot... BuildMI(MBB, MBBI, DL, TII.get(MSP430::PUSH16r)) - .addReg(MSP430::R4, RegState::Kill); + .addReg(MSP430::R4, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); // Update FP with the new base value... BuildMI(MBB, MBBI, DL, TII.get(MSP430::MOV16rr), MSP430::R4) - .addReg(MSP430::SP); + .addReg(MSP430::SP) + .setMIFlag(MachineInstr::FrameSetup); // Mark the FramePtr as live-in in every block except the entry. for (MachineBasicBlock &MBBJ : llvm::drop_begin(MF)) @@ -78,7 +80,7 @@ NumBytes = StackSize - MSP430FI->getCalleeSavedFrameSize(); // Skip the callee-saved push instructions. - while (MBBI != MBB.end() && (MBBI->getOpcode() == MSP430::PUSH16r)) + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) ++MBBI; if (MBBI != MBB.end()) @@ -131,15 +133,15 @@ NumBytes = FrameSize - CSSize; // pop FP. - BuildMI(MBB, MBBI, DL, TII.get(MSP430::POP16r), MSP430::R4); + BuildMI(MBB, MBBI, DL, TII.get(MSP430::POP16r), MSP430::R4) + .setMIFlag(MachineInstr::FrameDestroy); } else NumBytes = StackSize - CSSize; // Skip the callee-saved pop instructions. while (MBBI != MBB.begin()) { MachineBasicBlock::iterator PI = std::prev(MBBI); - unsigned Opc = PI->getOpcode(); - if (Opc != MSP430::POP16r && !PI->isTerminator()) + if (!PI->getFlag(MachineInstr::FrameDestroy) && !PI->isTerminator()) break; --MBBI; } @@ -189,13 +191,22 @@ MSP430MachineFunctionInfo *MFI = MF.getInfo(); MFI->setCalleeSavedFrameSize(CSI.size() * 2); + bool MSP430X = MF.getSubtarget().hasMSP430X(); for (unsigned i = CSI.size(); i != 0; --i) { unsigned Reg = CSI[i-1].getReg(); // Add the callee-saved register as live-in. It's killed at the spill. MBB.addLiveIn(Reg); - BuildMI(MBB, MI, DL, TII.get(MSP430::PUSH16r)) - .addReg(Reg, RegState::Kill); + if (!MSP430X) + BuildMI(MBB, MI, DL, TII.get(MSP430::PUSH16r)) + .addReg(Reg, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); } + + if (MSP430X) + BuildMI(MBB, MI, DL, TII.get(MSP430::PUSHM16r)) + .addImm(CSI.size()) + .addReg(CSI[CSI.size() - 1].getReg()) + .setMIFlag(MachineInstr::FrameSetup); return true; } @@ -211,8 +222,16 @@ MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); - for (unsigned i = 0, e = CSI.size(); i != e; ++i) - BuildMI(MBB, MI, DL, TII.get(MSP430::POP16r), CSI[i].getReg()); + if (MF.getSubtarget().hasMSP430X()) + BuildMI(MBB, MI, DL, TII.get(MSP430::POPM16r)) + .addImm(CSI.size()) + .addReg(CSI[CSI.size() - 1].getReg()) + .setMIFlag(MachineInstr::FrameDestroy); + else { + for (unsigned I = 0, E = CSI.size(); I != E; ++I) + BuildMI(MBB, MI, DL, TII.get(MSP430::POP16r), CSI[I].getReg()) + .setMIFlag(MachineInstr::FrameDestroy); + } return true; } diff --git a/llvm/lib/Target/MSP430/MSP430InstrFormats.td b/llvm/lib/Target/MSP430/MSP430InstrFormats.td --- a/llvm/lib/Target/MSP430/MSP430InstrFormats.td +++ b/llvm/lib/Target/MSP430/MSP430InstrFormats.td @@ -523,6 +523,25 @@ let Inst{63-48} = ExtensionWord; } +// MSP430 Extended Format II Instruction Format Exception - PUSHM/POPM. +class IIExtExcPushPopForm pattern> + : MSP430Inst { + let Pattern = pattern; + + bits<4> rs; + bits<4> cnt; + + let Inst{15-10} = 0b000101; + let Inst{9} = ispop; + let Inst{8} = al.Value; + let Inst{7-4} = cnt; + let Inst{3-0} = rs; +} + +class IIExtExcPushPopForm16 pattern> + : IIExtExcPushPopForm; + // Pseudo instructions class Pseudo pattern> : MSP430Inst { diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td --- a/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td +++ b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td @@ -26,6 +26,10 @@ def MSP430rlax : SDNode<"MSP430ISD::RLAX", SDTIntShiftOp>; def MSP430rrux : SDNode<"MSP430ISD::RRUX", SDTIntShiftOp>; +//===----------------------------------------------------------------------===// +// MSP430X Operand Definitions. +//===----------------------------------------------------------------------===// + // Differentiate between rpt2 and rpt4 to catch shift counts in IIExtExcRot // instructions that are too large. def Rpt2ImmAsmOperand : AsmOperandClass { @@ -52,6 +56,11 @@ let DecoderMethod = "decodeRptImm"; } +def popmreg : RegisterOperand { + let EncoderMethod = "getPOPMRegOpValue"; + let DecoderMethod = "decodePOPMRegOperand"; +} + //===----------------------------------------------------------------------===// // Shift Instructions // @@ -122,4 +131,9 @@ def : InstAlias<"rrax.b\t$rd, $cnt", (RRAX_8 GR8:$rd, rpt4imm:$cnt), 0>; def : InstAlias<"rrux.b\t$rd, $cnt", (RRUX_8 GR8:$rd, rpt4imm:$cnt), 0>; def : InstAlias<"rlax.b\t$rd, $cnt", (RLAX_8 GR8:$rd, rpt4imm:$cnt), 0>; + +let mayStore = 1 in { + def PUSHM16r : IIExtExcPushPopForm16<0, (outs), (ins rpt4imm:$cnt, GR16:$rs), "pushm\t$cnt, $rs", []>; + def POPM16r : IIExtExcPushPopForm16<1, (outs), (ins rpt4imm:$cnt, popmreg:$rs), "popm\t$cnt, $rs", []>; +} } // Predicates = [HasMSP430X] diff --git a/llvm/test/CodeGen/MSP430/callee-saved-fp.ll b/llvm/test/CodeGen/MSP430/callee-saved-fp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MSP430/callee-saved-fp.ll @@ -0,0 +1,57 @@ +; Test that saving/restoring the frame pointer in function prologues and +; epilogues does not affect saving/restoring callee-saved registers, or the +; stack pointer adjustment required by the function itself. +; +; RUN: llc -mcpu=msp430 < %s | FileCheck %s --check-prefixes=MSP430,BOTH +; RUN: llc -mcpu=msp430x < %s | FileCheck %s --check-prefixes=MSP430X,BOTH + +target datalayout = "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16" +target triple = "msp430" + +define dso_local double @foo(double %a, double %b, double %c, double %d) local_unnamed_addr #0 { +entry: +; CHECK-LABEL: foo: +; BOTH: push r4 +; BOTH-NEXT: mov r1, r4 + +; MSP430: push r10 +; MSP430-NEXT: push r9 +; MSP430-NEXT: push r8 +; MSP430-NEXT: push r7 +; MSP430-NEXT: push r6 +; MSP430-NEXT: push r5 +; MSP430X: pushm #6, r10 + +; BOTH: sub #{{[0-9]+}}, r1 + + call void asm sideeffect ";blah", "~{r5},~{r6},~{r7},~{r8},~{r9},~{r10}"() + %call = call i16 @boo(double %b, double %c, double %d, double %a) #0 + %0 = alloca i8, i16 %call, align 2 + %call1 = call double @bar(double %a, double %b, double %c, double %d, i8* nonnull %0) #0 + %1 = alloca i8, i16 %call, align 2 + %call2 = call double @bar(double %d, double %c, double %b, double %a, i8* nonnull %1) #0 + %add = fadd double %call1, %call2 + ret double %add + +; BOTH: mov r4, r1 +; BOTH-NEXT: sub #{{[0-9]+}}, r1 +; +; MSP430: pop r5 +; MSP430: pop r6 +; MSP430: pop r7 +; MSP430: pop r8 +; MSP430: pop r9 +; MSP430: pop r10 +; MSP430X: popm #6, r10 +; +; BOTH: pop r4 +; BOTH-NEXT: ret +} + +declare dso_local i16 @boo(double, double, double, double) local_unnamed_addr #1 +declare dso_local double @bar(double, double, double, double, i8*) local_unnamed_addr #1 + +attributes #0 = { nounwind "frame-pointer"="all" } + +!llvm.module.flags = !{!0} +!0 = !{i32 7, !"frame-pointer", i32 2} diff --git a/llvm/test/CodeGen/MSP430/pushm-popm.ll b/llvm/test/CodeGen/MSP430/pushm-popm.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MSP430/pushm-popm.ll @@ -0,0 +1,79 @@ +; Tests for the use of PUSHM/POPM in function prologue/epilogues, for +; the MSP430X CPU. +; +; RUN: llc -mcpu=msp430x < %s | FileCheck %s +target datalayout = "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16" +target triple = "msp430" + +define dso_local void @r4c1() { +entry: +; CHECK-LABEL: r4c1: +; CHECK: pushm #1, r4 +; CHECK: popm #1, r4 + call void asm sideeffect ";blah", "~{r4}"() + ret void +} + +define dso_local void @r5c2() { +entry: +; CHECK-LABEL: r5c2: +; CHECK: pushm #2, r5 +; CHECK: popm #2, r5 + call void asm sideeffect ";blah", "~{r4},~{r5}"() + ret void +} + +define dso_local void @r6c3() { +entry: +; CHECK-LABEL: r6c3: +; CHECK: pushm #3, r6 +; CHECK: popm #3, r6 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6}"() + ret void +} + +define dso_local void @r7c4() { +entry: +; CHECK-LABEL: r7c4: +; CHECK: pushm #4, r7 +; CHECK: popm #4, r7 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6},~{r7}"() + ret void +} + +define dso_local void @r8c5() { +entry: +; CHECK-LABEL: r8c5: +; CHECK: pushm #5, r8 +; CHECK: popm #5, r8 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6},~{r7},~{r8}"() + ret void +} + +define dso_local void @r9c6() { +entry: +; CHECK-LABEL: r9c6: +; CHECK: pushm #6, r9 +; CHECK: popm #6, r9 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9}"() + ret void +} + +define dso_local void @r10c7() { +entry: +; CHECK-LABEL: r10c7: +; CHECK: pushm #7, r10 +; CHECK: popm #7, r10 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10}"() + ret void +} + +; r11 is caller-saved so should not be spilled in the prologue. +define dso_local void @r11c8() { +entry: +; CHECK-LABEL: r11c8: +; CHECK: pushm #7, r10 +; CHECK: popm #7, r10 + call void asm sideeffect ";blah", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"() + ret void +} diff --git a/llvm/test/MC/MSP430/pushm-popm-invalid.s b/llvm/test/MC/MSP430/pushm-popm-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MSP430/pushm-popm-invalid.s @@ -0,0 +1,31 @@ +// Test for errors when PUSHM/POPM instructions are misused. + +// RUN: not llvm-mc -triple msp430 -mcpu=msp430x < %s 2>&1 | FileCheck %s +pushm: + pushm #0, r4 + pushm #17, r4 + pushm #17, r15 + pushm r4 + pushm #1, 0(r4) + pushm #1, &foo +// CHECK: :9: error: invalid operand for instruction +// CHECK: :9: error: invalid operand for instruction +// CHECK: :9: error: invalid operand for instruction +// CHECK: :9: error: invalid operand for instruction +// CHECK: :13: error: invalid operand for instruction +// CHECK: :13: error: invalid operand for instruction + + +popm: + popm #0, r4 + popm #17, r4 + popm #17, r15 + popm r4 + popm #1, 0(r4) + popm #1, &foo +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :12: error: invalid operand for instruction +// CHECK: :12: error: invalid operand for instruction diff --git a/llvm/test/MC/MSP430/pushm-popm.s b/llvm/test/MC/MSP430/pushm-popm.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MSP430/pushm-popm.s @@ -0,0 +1,110 @@ +// Test the encoding of PUSHM/POPM instructions for the MSP430X CPU. + +// RUN: llvm-mc -triple msp430 -mcpu=msp430x -show-encoding < %s | FileCheck %s +pushm: + pushm #1, r4 + pushm #2, r5 + pushm #3, r6 + pushm #4, r7 + pushm #5, r8 + pushm #6, r9 + pushm #7, r10 + pushm #8, r11 + pushm #9, r12 + pushm #10, r13 + pushm #11, r14 + pushm #12, r15 + pushm #16, r15 + + pushm #1, r15 + pushm #2, r15 + pushm #3, r15 + pushm #4, r15 + pushm #5, r15 + + pushm #1, r10 + pushm #2, r10 + pushm #3, r10 + pushm #4, r10 + pushm #5, r10 + +// CHECK-LABEL: pushm: +// CHECK-NEXT: pushm #1, r4 ; encoding: [0x04,0x15] +// CHECK: pushm #2, r5 ; encoding: [0x15,0x15] +// CHECK: pushm #3, r6 ; encoding: [0x26,0x15] +// CHECK: pushm #4, r7 ; encoding: [0x37,0x15] +// CHECK: pushm #5, r8 ; encoding: [0x48,0x15] +// CHECK: pushm #6, r9 ; encoding: [0x59,0x15] +// CHECK: pushm #7, r10 ; encoding: [0x6a,0x15] +// CHECK: pushm #8, r11 ; encoding: [0x7b,0x15] +// CHECK: pushm #9, r12 ; encoding: [0x8c,0x15] +// CHECK: pushm #10, r13 ; encoding: [0x9d,0x15] +// CHECK: pushm #11, r14 ; encoding: [0xae,0x15] +// CHECK: pushm #12, r15 ; encoding: [0xbf,0x15] +// CHECK: pushm #16, r15 ; encoding: [0xff,0x15] + +// CHECK: pushm #1, r15 ; encoding: [0x0f,0x15] +// CHECK: pushm #2, r15 ; encoding: [0x1f,0x15] +// CHECK: pushm #3, r15 ; encoding: [0x2f,0x15] +// CHECK: pushm #4, r15 ; encoding: [0x3f,0x15] +// CHECK: pushm #5, r15 ; encoding: [0x4f,0x15] + +// CHECK: pushm #1, r10 ; encoding: [0x0a,0x15] +// CHECK: pushm #2, r10 ; encoding: [0x1a,0x15] +// CHECK: pushm #3, r10 ; encoding: [0x2a,0x15] +// CHECK: pushm #4, r10 ; encoding: [0x3a,0x15] +// CHECK: pushm #5, r10 ; encoding: [0x4a,0x15] + +popm: + popm #1, r4 + popm #2, r5 + popm #3, r6 + popm #4, r7 + popm #5, r8 + popm #6, r9 + popm #7, r10 + popm #8, r11 + popm #9, r12 + popm #10, r13 + popm #11, r14 + popm #12, r15 + popm #16, r15 + + popm #1, r15 + popm #2, r15 + popm #3, r15 + popm #4, r15 + popm #5, r15 + + popm #1, r10 + popm #2, r10 + popm #3, r10 + popm #4, r10 + popm #5, r10 + +// CHECK-LABEL: popm: +// CHECK-NEXT: popm #1, r4 ; encoding: [0x04,0x17] +// CHECK: popm #2, r5 ; encoding: [0x14,0x17] +// CHECK: popm #3, r6 ; encoding: [0x24,0x17] +// CHECK: popm #4, r7 ; encoding: [0x34,0x17] +// CHECK: popm #5, r8 ; encoding: [0x44,0x17] +// CHECK: popm #6, r9 ; encoding: [0x54,0x17] +// CHECK: popm #7, r10 ; encoding: [0x64,0x17] +// CHECK: popm #8, r11 ; encoding: [0x74,0x17] +// CHECK: popm #9, r12 ; encoding: [0x84,0x17] +// CHECK: popm #10, r13 ; encoding: [0x94,0x17] +// CHECK: popm #11, r14 ; encoding: [0xa4,0x17] +// CHECK: popm #12, r15 ; encoding: [0xb4,0x17] +// CHECK: popm #16, r15 ; encoding: [0xf0,0x17] + +// CHECK: popm #1, r15 ; encoding: [0x0f,0x17] +// CHECK: popm #2, r15 ; encoding: [0x1e,0x17] +// CHECK: popm #3, r15 ; encoding: [0x2d,0x17] +// CHECK: popm #4, r15 ; encoding: [0x3c,0x17] +// CHECK: popm #5, r15 ; encoding: [0x4b,0x17] + +// CHECK: popm #1, r10 ; encoding: [0x0a,0x17] +// CHECK: popm #2, r10 ; encoding: [0x19,0x17] +// CHECK: popm #3, r10 ; encoding: [0x28,0x17] +// CHECK: popm #4, r10 ; encoding: [0x37,0x17] +// CHECK: popm #5, r10 ; encoding: [0x46,0x17]