[mips] Implement .ent, .end, .frame, .mask and .fmask assembler directives
authorDaniel Sanders <daniel.sanders@imgtec.com>
Mon, 11 Aug 2014 15:28:56 +0000 (15:28 +0000)
committerDaniel Sanders <daniel.sanders@imgtec.com>
Mon, 11 Aug 2014 15:28:56 +0000 (15:28 +0000)
Patch by Matheus Almeida and Toma Tabacu

Differential Revision: http://reviews.llvm.org/D4179

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

lib/Target/Mips/AsmParser/MipsAsmParser.cpp
lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
lib/Target/Mips/MipsTargetStreamer.h
test/MC/Mips/mips-pdr-bad.s [new file with mode: 0644]
test/MC/Mips/mips-pdr.s [new file with mode: 0644]

index ca2032b11c6c9de354d746a129bc12c7b24e0804..eb68c52745eb9aa7f009aabbe0fea186e5fa2fd9 100644 (file)
@@ -82,6 +82,10 @@ class MipsAsmParser : public MCTargetAsmParser {
   MCSubtargetInfo &STI;
   MCAsmParser &Parser;
   MipsAssemblerOptions Options;
+  MCSymbol *CurrentFn; // Pointer to the function being parsed. It may be a
+                       // nullptr, which indicates that no function is currently
+                       // selected. This usually happens after an '.end func'
+                       // directive.
 
 #define GET_ASSEMBLER_HEADER
 #include "MipsGenAsmMatcher.inc"
@@ -285,6 +289,8 @@ public:
 
     if (!isABI_O32() && !useOddSPReg() != 0)
       report_fatal_error("-mno-odd-spreg requires the O32 ABI");
+
+    CurrentFn = nullptr;
   }
 
   MCAsmParser &getParser() const { return Parser; }
@@ -3055,22 +3061,151 @@ bool MipsAsmParser::ParseDirective(AsmToken DirectiveID) {
     parseDataDirective(8, DirectiveID.getLoc());
     return false;
   }
-
   if (IDVal == ".ent") {
-    // Ignore this directive for now.
-    Parser.Lex();
+    StringRef SymbolName;
+
+    if (Parser.parseIdentifier(SymbolName)) {
+      reportParseError("expected identifier after .ent");
+      return false;
+    }
+
+    // There's an undocumented extension that allows an integer to
+    // follow the name of the procedure which AFAICS is ignored by GAS.
+    // Example: .ent foo,2
+    if (getLexer().isNot(AsmToken::EndOfStatement)) {
+      if (getLexer().isNot(AsmToken::Comma)) {
+        // Even though we accept this undocumented extension for compatibility
+        // reasons, the additional integer argument does not actually change
+        // the behaviour of the '.ent' directive, so we would like to discourage
+        // its use. We do this by not referring to the extended version in
+        // error messages which are not directly related to its use.
+        reportParseError("unexpected token, expected end of statement");
+        return false;
+      }
+      Parser.Lex(); // Eat the comma.
+      const MCExpr *DummyNumber;
+      int64_t DummyNumberVal;
+      // If the user was explicitly trying to use the extended version,
+      // we still give helpful extension-related error messages.
+      if (Parser.parseExpression(DummyNumber)) {
+        reportParseError("expected number after comma");
+        return false;
+      }
+      if (!DummyNumber->EvaluateAsAbsolute(DummyNumberVal)) {
+        reportParseError("expected an absolute expression after comma");
+        return false;
+      }
+    }
+
+    // If this is not the end of the statement, report an error.
+    if (getLexer().isNot(AsmToken::EndOfStatement)) {
+      reportParseError("unexpected token, expected end of statement");
+      return false;
+    }
+
+    MCSymbol *Sym = getContext().GetOrCreateSymbol(SymbolName);
+
+    getTargetStreamer().emitDirectiveEnt(*Sym);
+    CurrentFn = Sym;
     return false;
   }
 
   if (IDVal == ".end") {
-    // Ignore this directive for now.
-    Parser.Lex();
+    StringRef SymbolName;
+
+    if (Parser.parseIdentifier(SymbolName)) {
+      reportParseError("expected identifier after .end");
+      return false;
+    }
+
+    if (getLexer().isNot(AsmToken::EndOfStatement)) {
+      reportParseError("unexpected token, expected end of statement");
+      return false;
+    }
+
+    if (CurrentFn == nullptr) {
+      reportParseError(".end used without .ent");
+      return false;
+    }
+
+    if ((SymbolName != CurrentFn->getName())) {
+      reportParseError(".end symbol does not match .ent symbol");
+      return false;
+    }
+
+    getTargetStreamer().emitDirectiveEnd(SymbolName);
+    CurrentFn = nullptr;
     return false;
   }
 
   if (IDVal == ".frame") {
-    // Ignore this directive for now.
-    Parser.eatToEndOfStatement();
+    // .frame $stack_reg, frame_size_in_bytes, $return_reg
+    SmallVector<std::unique_ptr<MCParsedAsmOperand>, 1> TmpReg;
+    OperandMatchResultTy ResTy = ParseAnyRegister(TmpReg);
+    if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) {
+      reportParseError("expected stack register");
+      return false;
+    }
+
+    MipsOperand &StackRegOpnd = static_cast<MipsOperand &>(*TmpReg[0]);
+    if (!StackRegOpnd.isGPRAsmReg()) {
+      reportParseError(StackRegOpnd.getStartLoc(),
+                       "expected general purpose register");
+      return false;
+    }
+    unsigned StackReg = StackRegOpnd.getGPR32Reg();
+
+    if (Parser.getTok().is(AsmToken::Comma))
+      Parser.Lex();
+    else {
+      reportParseError("unexpected token, expected comma");
+      return false;
+    }
+
+    // Parse the frame size.
+    const MCExpr *FrameSize;
+    int64_t FrameSizeVal;
+
+    if (Parser.parseExpression(FrameSize)) {
+      reportParseError("expected frame size value");
+      return false;
+    }
+
+    if (!FrameSize->EvaluateAsAbsolute(FrameSizeVal)) {
+      reportParseError("frame size not an absolute expression");
+      return false;
+    }
+
+    if (Parser.getTok().is(AsmToken::Comma))
+      Parser.Lex();
+    else {
+      reportParseError("unexpected token, expected comma");
+      return false;
+    }
+
+    // Parse the return register.
+    TmpReg.clear();
+    ResTy = ParseAnyRegister(TmpReg);
+    if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) {
+      reportParseError("expected return register");
+      return false;
+    }
+
+    MipsOperand &ReturnRegOpnd = static_cast<MipsOperand &>(*TmpReg[0]);
+    if (!ReturnRegOpnd.isGPRAsmReg()) {
+      reportParseError(ReturnRegOpnd.getStartLoc(),
+                       "expected general purpose register");
+      return false;
+    }
+
+    // If this is not the end of the statement, report an error.
+    if (getLexer().isNot(AsmToken::EndOfStatement)) {
+      reportParseError("unexpected token, expected end of statement");
+      return false;
+    }
+
+    getTargetStreamer().emitFrame(StackReg, FrameSizeVal,
+                                  ReturnRegOpnd.getGPR32Reg());
     return false;
   }
 
@@ -3078,15 +3213,61 @@ bool MipsAsmParser::ParseDirective(AsmToken DirectiveID) {
     return parseDirectiveSet();
   }
 
-  if (IDVal == ".fmask") {
-    // Ignore this directive for now.
-    Parser.eatToEndOfStatement();
-    return false;
-  }
+  if (IDVal == ".mask" || IDVal == ".fmask") {
+    // .mask bitmask, frame_offset
+    // bitmask: One bit for each register used.
+    // frame_offset: Offset from Canonical Frame Address ($sp on entry) where
+    //               first register is expected to be saved.
+    // Examples:
+    //   .mask 0x80000000, -4
+    //   .fmask 0x80000000, -4
+    //
 
-  if (IDVal == ".mask") {
-    // Ignore this directive for now.
-    Parser.eatToEndOfStatement();
+    // Parse the bitmask
+    const MCExpr *BitMask;
+    int64_t BitMaskVal;
+
+    if (Parser.parseExpression(BitMask)) {
+      reportParseError("expected bitmask value");
+      return false;
+    }
+
+    if (!BitMask->EvaluateAsAbsolute(BitMaskVal)) {
+      reportParseError("bitmask not an absolute expression");
+      return false;
+    }
+
+    if (Parser.getTok().is(AsmToken::Comma))
+      Parser.Lex();
+    else {
+      reportParseError("unexpected token, expected comma");
+      return false;
+    }
+
+    // Parse the frame_offset
+    const MCExpr *FrameOffset;
+    int64_t FrameOffsetVal;
+
+    if (Parser.parseExpression(FrameOffset)) {
+      reportParseError("expected frame offset value");
+      return false;
+    }
+
+    if (!FrameOffset->EvaluateAsAbsolute(FrameOffsetVal)) {
+      reportParseError("frame offset not an absolute expression");
+      return false;
+    }
+
+    // If this is not the end of the statement, report an error.
+    if (getLexer().isNot(AsmToken::EndOfStatement)) {
+      reportParseError("unexpected token, expected end of statement");
+      return false;
+    }
+
+    if (IDVal == ".mask")
+      getTargetStreamer().emitMask(BitMaskVal, FrameOffsetVal);
+    else
+      getTargetStreamer().emitFMask(BitMaskVal, FrameOffsetVal);
     return false;
   }
 
index 7a8230fdebf8db52e5c2cc800d94fa3a9190b187..23e0d90dc0b618dedb064433422ebd9d1e8ac40a 100644 (file)
@@ -29,7 +29,9 @@
 using namespace llvm;
 
 MipsTargetStreamer::MipsTargetStreamer(MCStreamer &S)
-    : MCTargetStreamer(S), canHaveModuleDirective(true) {}
+    : MCTargetStreamer(S), canHaveModuleDirective(true) {
+  GPRInfoSet = FPRInfoSet = FrameInfoSet = false;
+}
 void MipsTargetStreamer::emitDirectiveSetMicroMips() {}
 void MipsTargetStreamer::emitDirectiveSetNoMicroMips() {}
 void MipsTargetStreamer::emitDirectiveSetMips16() {}
@@ -492,11 +494,45 @@ void MipsTargetELFStreamer::emitDirectiveSetNoAt() {
 }
 
 void MipsTargetELFStreamer::emitDirectiveEnd(StringRef Name) {
-  // FIXME: implement.
+  MCAssembler &MCA = getStreamer().getAssembler();
+  MCContext &Context = MCA.getContext();
+  MCStreamer &OS = getStreamer();
+
+  const MCSectionELF *Sec = Context.getELFSection(".pdr", ELF::SHT_PROGBITS,
+                                                  ELF::SHF_ALLOC | ELF::SHT_REL,
+                                                  SectionKind::getMetadata());
+
+  const MCSymbolRefExpr *ExprRef =
+      MCSymbolRefExpr::Create(Name, MCSymbolRefExpr::VK_None, Context);
+
+  MCSectionData &SecData = MCA.getOrCreateSectionData(*Sec);
+  SecData.setAlignment(4);
+
+  OS.PushSection();
+
+  OS.SwitchSection(Sec);
+
+  OS.EmitValueImpl(ExprRef, 4);
+
+  OS.EmitIntValue(GPRInfoSet ? GPRBitMask : 0, 4); // reg_mask
+  OS.EmitIntValue(GPRInfoSet ? GPROffset : 0, 4);  // reg_offset
+
+  OS.EmitIntValue(FPRInfoSet ? FPRBitMask : 0, 4); // fpreg_mask
+  OS.EmitIntValue(FPRInfoSet ? FPROffset : 0, 4);  // fpreg_offset
+
+  OS.EmitIntValue(FrameInfoSet ? FrameOffset : 0, 4); // frame_offset
+  OS.EmitIntValue(FrameInfoSet ? FrameReg : 0, 4);    // frame_reg
+  OS.EmitIntValue(FrameInfoSet ? ReturnReg : 0, 4);   // return_reg
+
+  // The .end directive marks the end of a procedure. Invalidate
+  // the information gathered up until this point.
+  GPRInfoSet = FPRInfoSet = FrameInfoSet = false;
+
+  OS.PopSection();
 }
 
 void MipsTargetELFStreamer::emitDirectiveEnt(const MCSymbol &Symbol) {
-  // FIXME: implement.
+  GPRInfoSet = FPRInfoSet = FrameInfoSet = false;
 }
 
 void MipsTargetELFStreamer::emitDirectiveAbiCalls() {
@@ -542,18 +578,28 @@ void MipsTargetELFStreamer::emitDirectiveOptionPic2() {
 }
 
 void MipsTargetELFStreamer::emitFrame(unsigned StackReg, unsigned StackSize,
-                                      unsigned ReturnReg) {
-  // FIXME: implement.
+                                      unsigned ReturnReg_) {
+  MCContext &Context = getStreamer().getAssembler().getContext();
+  const MCRegisterInfo *RegInfo = Context.getRegisterInfo();
+
+  FrameInfoSet = true;
+  FrameReg = RegInfo->getEncodingValue(StackReg);
+  FrameOffset = StackSize;
+  ReturnReg = RegInfo->getEncodingValue(ReturnReg_);
 }
 
 void MipsTargetELFStreamer::emitMask(unsigned CPUBitmask,
                                      int CPUTopSavedRegOff) {
-  // FIXME: implement.
+  GPRInfoSet = true;
+  GPRBitMask = CPUBitmask;
+  GPROffset = CPUTopSavedRegOff;
 }
 
 void MipsTargetELFStreamer::emitFMask(unsigned FPUBitmask,
                                       int FPUTopSavedRegOff) {
-  // FIXME: implement.
+  FPRInfoSet = true;
+  FPRBitMask = FPUBitmask;
+  FPROffset = FPUTopSavedRegOff;
 }
 
 void MipsTargetELFStreamer::emitDirectiveSetMips1() {
index 8253c6225639d044299f2096248551c77a792470..fbea033eee9d6e679d1993dc6a191669a617aee8 100644 (file)
@@ -11,6 +11,7 @@
 #define MIPSTARGETSTREAMER_H
 
 #include "llvm/MC/MCELFStreamer.h"
+#include "llvm/MC/MCRegisterInfo.h"
 #include "llvm/MC/MCStreamer.h"
 #include "MCTargetDesc/MipsABIFlagsSection.h"
 
@@ -97,6 +98,19 @@ public:
 protected:
   MipsABIFlagsSection ABIFlagsSection;
 
+  bool GPRInfoSet;
+  unsigned GPRBitMask;
+  int GPROffset;
+
+  bool FPRInfoSet;
+  unsigned FPRBitMask;
+  int FPROffset;
+
+  bool FrameInfoSet;
+  int FrameOffset;
+  unsigned FrameReg;
+  unsigned ReturnReg;
+
 private:
   bool canHaveModuleDirective;
 };
diff --git a/test/MC/Mips/mips-pdr-bad.s b/test/MC/Mips/mips-pdr-bad.s
new file mode 100644 (file)
index 0000000..40c6ba2
--- /dev/null
@@ -0,0 +1,42 @@
+# RUN: not llvm-mc %s -arch=mips -mcpu=mips32r2 2>%t1
+# RUN: FileCheck %s < %t1 -check-prefix=ASM
+
+        .text
+        
+        .ent # ASM: :[[@LINE]]:14: error: expected identifier after .ent
+        .ent bar, # ASM: :[[@LINE]]:19: error: expected number after comma
+        .ent foo, bar # AMS: :[[@LINE]]:23: error: expected an absolute expression after comma
+        .ent foo, 5, bar # AMS: :[[@LINE]]:20: error: unexpected token, expected end of statement
+        .frame # ASM: :[[@LINE]]:16: error: expected stack register
+        .frame bar # ASM: :[[@LINE]]:16: error: expected stack register
+        .frame $f1, 8, # ASM: :[[@LINE]]:16: error: expected general purpose register
+        .frame $sp # ASM: :[[@LINE]]:20: error: unexpected token, expected comma
+        .frame $sp, # ASM: :[[@LINE]]:21: error: expected frame size value
+        .frame $sp, bar # ASM: :[[@LINE]]:25: error: frame size not an absolute expression
+        .frame $sp, 8 # ASM: :[[@LINE]]:23: error: unexpected token, expected comma
+        .frame $sp, 8, # ASM: :[[@LINE]]:24: error: expected return register
+        .frame $sp, 8, $f1 # ASM: :[[@LINE]]:24: error: expected general purpose register
+        .frame $sp, 8, $ra, foo # ASM: :[[@LINE]]:27: error: unexpected token, expected end of statement
+
+        .mask  # ASM: :[[@LINE]]:16: error: expected bitmask value
+        .mask foo # ASM: :[[@LINE]]:19: error: bitmask not an absolute expression
+        .mask 0x80000000 # ASM: :[[@LINE]]:26: error: unexpected token, expected comma
+        .mask 0x80000000, # ASM: :[[@LINE]]:27: error: expected frame offset value
+        .mask 0x80000000, foo # ASM: :[[@LINE]]:31: error: frame offset not an absolute expression
+        .mask 0x80000000, -4, bar # ASM: :[[@LINE]]:29: error: unexpected token, expected end of statement
+        
+        .fmask  # ASM: :[[@LINE]]:17: error: expected bitmask value
+        .fmask foo # ASM: :[[@LINE]]:20: error: bitmask not an absolute expression
+        .fmask 0x80000000 # ASM: :[[@LINE]]:27: error: unexpected token, expected comma
+        .fmask 0x80000000, # ASM: :[[@LINE]]:28: error: expected frame offset value
+        .fmask 0x80000000, foo # ASM: :[[@LINE]]:32: error: frame offset not an absolute expression
+        .fmask 0x80000000, -4, bar # ASM: :[[@LINE]]:30: error: unexpected token, expected end of statement
+
+        .end # ASM: :[[@LINE]]:14: error: expected identifier after .end
+        .ent _local_foo_bar
+        .end _local_foo_bar, foo # ASM: :[[@LINE]]:28: error: unexpected token, expected end of statement
+        .end _local_foo_bar
+        .end _local_foo # ASM: :[[@LINE]]:25: error: .end used without .ent
+        .ent _local_foo, 2
+        .end _local_foo_bar # ASM: :[[@LINE]]:29: error: .end symbol does not match .ent symbol
diff --git a/test/MC/Mips/mips-pdr.s b/test/MC/Mips/mips-pdr.s
new file mode 100644 (file)
index 0000000..372c259
--- /dev/null
@@ -0,0 +1,64 @@
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 -filetype=asm | \
+# RUN:   FileCheck %s -check-prefix=ASMOUT
+
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 -filetype=obj -o - | \
+# RUN:   llvm-readobj -s -section-data | \
+# RUN:     FileCheck %s -check-prefix=OBJOUT
+
+# ASMOUT: .text
+# ASMOUT:        .type _local_foo,@function
+# ASMOUT:        .ent _local_foo
+# ASMOUT:_local_foo:
+# ASMOUT:        .frame $fp,16,$ra
+# ASMOUT:        .mask 0x10101010,-4
+# ASMOUT:        .fmask 0x01010101,-8
+# ASMOUT:        .end _local_foo
+# ASMOUT:        .size local_foo,
+
+# OBJOUT: Section {
+# OBJOUT:     Name: .pdr
+# OBJOUT:     Type: SHT_PROGBITS (0x1)
+# OBJOUT:     Flags [ (0xB)
+# OBJOUT:       SHF_ALLOC (0x2)
+# OBJOUT:       SHF_WRITE (0x1)
+# OBJOUT:     ]
+# OBJOUT:     Size: 64
+# OBJOUT:     SectionData (
+# OBJOUT:       0000: 00000000 10101010 FFFFFFFC 01010101
+# OBJOUT:       0010: FFFFFFF8 00000010 0000001E 0000001F
+# OBJOUT:       0020: 00000000 10101010 FFFFFFFC 01010101
+# OBJOUT:       0030: FFFFFFF8 00000010 0000001E 0000001F
+# OBJOUT:     )
+# OBJOUT:   }
+
+# We should also check if relocation information was correctly generated.
+# OBJOUT:   Section {
+# OBJOUT:     Name: .rel.pdr
+# OBJOUT:     Type: SHT_REL (0x9)
+# OBJOUT:     Flags [ (0x0)
+# OBJOUT:     ]
+# OBJOUT:     Size: 16
+# OBJOUT:     SectionData (
+# OBJOUT:       0000: 00000000 00000202 00000020 00000802
+# OBJOUT:     )
+# OBJOUT:   }
+
+.text
+        .type _local_foo,@function
+        .ent _local_foo
+_local_foo:
+        .frame $fp,16,$ra
+        .mask 0x10101010,-4
+        .fmask 0x01010101,-8
+        .end _local_foo
+        .size local_foo,.-_local_foo
+
+        .globl _global_foo
+        .type _global_foo,@function
+        .ent _global_foo
+_global_foo:
+        .frame $fp,16,$ra
+        .mask 0x10101010,-4
+        .fmask 0x01010101,-8
+        .end _global_foo
+        .size global_foo,.-_global_foo