ARM IAS: support .movsp
authorSaleem Abdulrasool <compnerd@compnerd.org>
Thu, 30 Jan 2014 04:46:24 +0000 (04:46 +0000)
committerSaleem Abdulrasool <compnerd@compnerd.org>
Thu, 30 Jan 2014 04:46:24 +0000 (04:46 +0000)
.movsp is an ARM unwinding directive that indicates to the unwinder that a
register contains an offset from the current stack pointer.  If the offset is
unspecified, it defaults to zero.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@200449 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/MC/MCStreamer.h
lib/Target/ARM/ARMAsmPrinter.cpp
lib/Target/ARM/AsmParser/ARMAsmParser.cpp
lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
test/MC/ARM/eh-directive-movsp-diagnostics.s [new file with mode: 0644]
test/MC/ARM/eh-directive-movsp.s [new file with mode: 0644]

index bb4adfcf86233816f89d0d55767873c183265f79..f272572bb62535386ae064b12a02d7c9b8309472 100644 (file)
@@ -93,6 +93,7 @@ public:
   virtual void emitHandlerData() = 0;
   virtual void emitSetFP(unsigned FpReg, unsigned SpReg,
                          int64_t Offset = 0) = 0;
+  virtual void emitMovSP(unsigned Reg, int64_t Offset = 0) = 0;
   virtual void emitPad(int64_t Offset) = 0;
   virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
                            bool isVector) = 0;
index 46bd34fe39242d573d9a2f3d7c87f859c3fc990e..d0aad327c40f74256993bc9adebf36bd9c3deda5 100644 (file)
@@ -1091,11 +1091,11 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) {
         // instruction.
         ATS.emitPad(Offset);
       } else {
-        MI->dump();
-        llvm_unreachable("Unsupported opcode for unwinding information");
+        // Move of SP to a register.  Positive values correspond to an "add"
+        // instruction.
+        ATS.emitMovSP(DstReg, -Offset);
       }
     } else if (DstReg == ARM::SP) {
-      // FIXME: .movsp goes here
       MI->dump();
       llvm_unreachable("Unsupported opcode for unwinding information");
     }
index 2b4eaf0f15c90ce935980f4e77bf9f5230d8a214..531e2cc906aa153c74611c1e81504a1d26361627 100644 (file)
@@ -127,7 +127,7 @@ class UnwindContext {
   int FPReg;
 
 public:
-  UnwindContext(MCAsmParser &P) : Parser(P), FPReg(-1) {}
+  UnwindContext(MCAsmParser &P) : Parser(P), FPReg(ARM::SP) {}
 
   bool hasFnStart() const { return !FnStartLocs.empty(); }
   bool cantUnwind() const { return !CantUnwindLocs.empty(); }
@@ -182,7 +182,7 @@ public:
     PersonalityLocs = Locs();
     HandlerDataLocs = Locs();
     PersonalityIndexLocs = Locs();
-    FPReg = -1;
+    FPReg = ARM::SP;
   }
 };
 
@@ -297,6 +297,7 @@ class ARMAsmParser : public MCTargetAsmParser {
   bool parseDirectivePersonalityIndex(SMLoc L);
   bool parseDirectiveUnwindRaw(SMLoc L);
   bool parseDirectiveTLSDescSeq(SMLoc L);
+  bool parseDirectiveMovSP(SMLoc L);
 
   StringRef splitMnemonic(StringRef Mnemonic, unsigned &PredicationCode,
                           bool &CarrySetting, unsigned &ProcessorIMod,
@@ -8087,6 +8088,8 @@ bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) {
     return parseDirectiveUnwindRaw(DirectiveID.getLoc());
   else if (IDVal == ".tlsdescseq")
     return parseDirectiveTLSDescSeq(DirectiveID.getLoc());
+  else if (IDVal == ".movsp")
+    return parseDirectiveMovSP(DirectiveID.getLoc());
   return true;
 }
 
@@ -8616,7 +8619,7 @@ bool ARMAsmParser::parseDirectiveSetFP(SMLoc L) {
   }
 
   // Consume comma
-  if (!Parser.getTok().is(AsmToken::Comma)) {
+  if (Parser.getTok().isNot(AsmToken::Comma)) {
     Error(Parser.getTok().getLoc(), "comma expected");
     return false;
   }
@@ -9028,6 +9031,69 @@ bool ARMAsmParser::parseDirectiveTLSDescSeq(SMLoc L) {
   return false;
 }
 
+/// parseDirectiveMovSP
+///  ::= .movsp reg [, #offset]
+bool ARMAsmParser::parseDirectiveMovSP(SMLoc L) {
+  if (!UC.hasFnStart()) {
+    Parser.eatToEndOfStatement();
+    Error(L, ".fnstart must precede .movsp directives");
+    return false;
+  }
+  if (UC.getFPReg() != ARM::SP) {
+    Parser.eatToEndOfStatement();
+    Error(L, "unexpected .movsp directive");
+    return false;
+  }
+
+  SMLoc SPRegLoc = Parser.getTok().getLoc();
+  int SPReg = tryParseRegister();
+  if (SPReg == -1) {
+    Parser.eatToEndOfStatement();
+    Error(SPRegLoc, "register expected");
+    return false;
+  }
+
+  if (SPReg == ARM::SP || SPReg == ARM::PC) {
+    Parser.eatToEndOfStatement();
+    Error(SPRegLoc, "sp and pc are not permitted in .movsp directive");
+    return false;
+  }
+
+  int64_t Offset = 0;
+  if (Parser.getTok().is(AsmToken::Comma)) {
+    Parser.Lex();
+
+    if (Parser.getTok().isNot(AsmToken::Hash)) {
+      Error(Parser.getTok().getLoc(), "expected #constant");
+      Parser.eatToEndOfStatement();
+      return false;
+    }
+    Parser.Lex();
+
+    const MCExpr *OffsetExpr;
+    SMLoc OffsetLoc = Parser.getTok().getLoc();
+    if (Parser.parseExpression(OffsetExpr)) {
+      Parser.eatToEndOfStatement();
+      Error(OffsetLoc, "malformed offset expression");
+      return false;
+    }
+
+    const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(OffsetExpr);
+    if (!CE) {
+      Parser.eatToEndOfStatement();
+      Error(OffsetLoc, "offset must be an immediate constant");
+      return false;
+    }
+
+    Offset = CE->getValue();
+  }
+
+  getTargetStreamer().emitMovSP(SPReg, Offset);
+  UC.saveFPReg(SPReg);
+
+  return false;
+}
+
 /// Force static initialization.
 extern "C" void LLVMInitializeARMAsmParser() {
   RegisterMCAsmParser<ARMAsmParser> X(TheARMTarget);
index c2e3503278bba645c0a2a81ba03bf20da050f376..151d48df63e06d6ff37a224cc65d1830a25d3258 100644 (file)
@@ -123,6 +123,7 @@ class ARMTargetAsmStreamer : public ARMTargetStreamer {
   virtual void emitPersonalityIndex(unsigned Index);
   virtual void emitHandlerData();
   virtual void emitSetFP(unsigned FpReg, unsigned SpReg, int64_t Offset = 0);
+  virtual void emitMovSP(unsigned Reg, int64_t Offset = 0);
   virtual void emitPad(int64_t Offset);
   virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
                            bool isVector);
@@ -172,6 +173,16 @@ void ARMTargetAsmStreamer::emitSetFP(unsigned FpReg, unsigned SpReg,
     OS << ", #" << Offset;
   OS << '\n';
 }
+void ARMTargetAsmStreamer::emitMovSP(unsigned Reg, int64_t Offset) {
+  assert((Reg != ARM::SP && Reg != ARM::PC) &&
+         "the operand of .movsp cannot be either sp or pc");
+
+  OS << "\t.movsp\t";
+  InstPrinter.printRegName(OS, Reg);
+  if (Offset)
+    OS << ", #" << Offset;
+  OS << '\n';
+}
 void ARMTargetAsmStreamer::emitPad(int64_t Offset) {
   OS << "\t.pad\t#" << Offset << '\n';
 }
@@ -387,6 +398,7 @@ private:
   virtual void emitPersonalityIndex(unsigned Index);
   virtual void emitHandlerData();
   virtual void emitSetFP(unsigned FpReg, unsigned SpReg, int64_t Offset = 0);
+  virtual void emitMovSP(unsigned Reg, int64_t Offset = 0);
   virtual void emitPad(int64_t Offset);
   virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
                            bool isVector);
@@ -448,6 +460,7 @@ public:
   void emitPersonalityIndex(unsigned index);
   void emitHandlerData();
   void emitSetFP(unsigned NewFpReg, unsigned NewSpReg, int64_t Offset = 0);
+  void emitMovSP(unsigned Reg, int64_t Offset = 0);
   void emitPad(int64_t Offset);
   void emitRegSave(const SmallVectorImpl<unsigned> &RegList, bool isVector);
   void emitUnwindRaw(int64_t Offset, const SmallVectorImpl<uint8_t> &Opcodes);
@@ -657,6 +670,9 @@ void ARMTargetELFStreamer::emitSetFP(unsigned FpReg, unsigned SpReg,
                                      int64_t Offset) {
   getStreamer().emitSetFP(FpReg, SpReg, Offset);
 }
+void ARMTargetELFStreamer::emitMovSP(unsigned Reg, int64_t Offset) {
+  getStreamer().emitMovSP(Reg, Offset);
+}
 void ARMTargetELFStreamer::emitPad(int64_t Offset) {
   getStreamer().emitPad(Offset);
 }
@@ -1203,6 +1219,20 @@ void ARMELFStreamer::emitSetFP(unsigned NewFPReg, unsigned NewSPReg,
     FPOffset += Offset;
 }
 
+void ARMELFStreamer::emitMovSP(unsigned Reg, int64_t Offset) {
+  assert((Reg != ARM::SP && Reg != ARM::PC) &&
+         "the operand of .movsp cannot be either sp or pc");
+  assert(FPReg == ARM::SP && "current FP must be SP");
+
+  FlushPendingOffset();
+
+  FPReg = Reg;
+  FPOffset = SPOffset + Offset;
+
+  const MCRegisterInfo *MRI = getContext().getRegisterInfo();
+  UnwindOpAsm.EmitSetSP(MRI->getEncodingValue(FPReg));
+}
+
 void ARMELFStreamer::emitPad(int64_t Offset) {
   // Track the change of the $sp offset
   SPOffset -= Offset;
diff --git a/test/MC/ARM/eh-directive-movsp-diagnostics.s b/test/MC/ARM/eh-directive-movsp-diagnostics.s
new file mode 100644 (file)
index 0000000..519e7d7
--- /dev/null
@@ -0,0 +1,102 @@
+@ RUN: not llvm-mc -triple armv7-eabi -filetype asm -o /dev/null 2>&1 %s \
+@ RUN:  | FileCheck %s
+
+       .syntax unified
+       .thumb
+
+       .global false_start
+       .type false_start,%function
+       .thumb_func
+false_start:
+       .movsp r7
+
+@ CHECK: error: .fnstart must precede .movsp directive
+@ CHECK:       .movsp r7
+@ CHECK:        ^
+
+       .global beyond_saving
+       .type beyond_saving,%function
+       .thumb_func
+beyond_saving:
+       .fnstart
+       .setfp r11, sp, #8
+       add r11, sp, #8
+       .movsp r7
+       mov r7, r11
+       .fnend
+
+@ CHECK: error: unexpected .movsp directive
+@ CHECK:       .movsp r7
+@ CHECK:        ^
+
+
+       .global sp_invalid
+       .type sp_invalid,%function
+       .thumb_func
+sp_invalid:
+       .fnstart
+       .movsp r13
+       mov sp, sp
+       .fnend
+
+@ CHECK: error: sp and pc are not permitted in .movsp directive
+@ CHECK:       .movsp r13
+@ CHECK:               ^
+
+
+       .global pc_invalid
+       .type pc_invalid,%function
+       .thumb_func
+pc_invalid:
+       .fnstart
+       .movsp r15
+       mov sp, pc
+       .fnend
+
+@ CHECK: error: sp and pc are not permitted in .movsp directive
+@ CHECK:       .movsp r15
+@ CHECK:               ^
+
+
+       .global constant_required
+       .type constant_required,%function
+       .thumb_func
+constant_required:
+       .fnstart
+       .movsp r11,
+       mov sp, r11
+       .fnend
+
+@ CHECK: error: expected #constant
+@ CHECK:       .movsp r11,
+@ CHECK:                   ^
+
+
+       .global constant_constant
+       .type constant_constant,%function
+       .thumb_func
+constant_constant:
+       .fnstart
+       .movsp r11, #constant
+       mov sp, r11
+       .fnend
+
+@ CHECK: error: offset must be an immediate constant
+@ CHECK:       .movsp r11, #constant
+@ CHECK:                     ^
+
+
+       .arm
+
+       .global register_required
+       .type register_required,%function
+register_required:
+       .fnstart
+       .movsp #42
+       mov sp, #42
+       .fnend
+
+@ CHECK: error: register expected
+@ CHECK:       .movsp #42
+@ CHECK:               ^
+
diff --git a/test/MC/ARM/eh-directive-movsp.s b/test/MC/ARM/eh-directive-movsp.s
new file mode 100644 (file)
index 0000000..620f5b7
--- /dev/null
@@ -0,0 +1,44 @@
+@ RUN: llvm-mc -triple armv7-eabi -filetype obj -o - %s | llvm-readobj -s -sd \
+@ RUN:   | FileCheck %s
+
+       .syntax unified
+       .thumb
+
+       .section .duplicate
+
+       .global duplicate
+       .type duplicate,%function
+duplicate:
+       .fnstart
+       .setfp sp, sp, #8
+       add sp, sp, #8
+       .movsp r11
+       mov r11, sp
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.exidx.duplicate
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 B09B9B80
+@ CHECK:   )
+@ CHECK: }
+
+
+       .section .squash
+
+       .global squash
+       .type squash,%function
+squash:
+       .fnstart
+       .movsp ip
+       mov ip, sp
+       .save {fp, ip, lr}
+       stmfd sp!, {fp, ip, lr}
+       .fnend
+
+@ CHECK: Section {
+@ CHECK:   Name: .ARM.exidx.squash
+@ CHECK:   SectionData (
+@ CHECK:     0000: 00000000 9C808580
+@ CHECK:   )
+@ CHECK: }