Re-commit: [mips] Implement .ent, .end, .frame, .mask and .fmask.
authorDaniel Sanders <daniel.sanders@imgtec.com>
Wed, 13 Aug 2014 10:07:34 +0000 (10:07 +0000)
committerDaniel Sanders <daniel.sanders@imgtec.com>
Wed, 13 Aug 2014 10:07:34 +0000 (10:07 +0000)
Patch by Matheus Almeida and Toma Tabacu

The lld test failure on the previous attempt to commit was caused by the
addition of the .pdr section causing the offsets it was checking to change.
This has been fixed by removing the .ent/.end directives from that test since
they weren't really needed.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215535 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 ca2032b..eb68c52 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 7a8230f..23e0d90 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 8253c62..fbea033 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