Add the align_to_end option to .bundle_lock in the MC implementation of aligned
bundling. The document describing this feature and the implementation has also
been updated:

https://sites.google.com/a/chromium.org/dev/nativeclient/pnacl/aligned-bundling-support-in-llvm

llvm-svn: 171797
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 9b7074d..e234dfe 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -260,7 +260,7 @@
   virtual void EmitInstruction(const MCInst &Inst);
 
   virtual void EmitBundleAlignMode(unsigned AlignPow2);
-  virtual void EmitBundleLock();
+  virtual void EmitBundleLock(bool AlignToEnd);
   virtual void EmitBundleUnlock();
 
   /// EmitRawText - If this file is backed by an assembly streamer, this dumps
@@ -1370,8 +1370,10 @@
   EmitEOL();
 }
 
-void MCAsmStreamer::EmitBundleLock() {
+void MCAsmStreamer::EmitBundleLock(bool AlignToEnd) {
   OS << "\t.bundle_lock";
+  if (AlignToEnd)
+    OS << " align_to_end";
   EmitEOL();
 }
 
diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp
index 20b64e2..d30dc7d 100644
--- a/llvm/lib/MC/MCAssembler.cpp
+++ b/llvm/lib/MC/MCAssembler.cpp
@@ -167,10 +167,34 @@
          "computeBundlePadding should only be called if bundling is enabled");
   uint64_t BundleMask = BundleSize - 1;
   uint64_t OffsetInBundle = FOffset & BundleMask;
+  uint64_t EndOfFragment = OffsetInBundle + FSize;
 
-  // If the fragment would cross a bundle boundary, add enough padding until
-  // the end of the current bundle.
-  if (OffsetInBundle + FSize > BundleSize)
+  // There are two kinds of bundling restrictions:
+  // 
+  // 1) For alignToBundleEnd(), add padding to ensure that the fragment will
+  //    *end* on a bundle boundary.
+  // 2) Otherwise, check if the fragment would cross a bundle boundary. If it
+  //    would, add padding until the end of the bundle so that the fragment
+  //    will start in a new one.
+  if (F->alignToBundleEnd()) {
+    // Three possibilities here:
+    //
+    // A) The fragment just happens to end at a bundle boundary, so we're good.
+    // B) The fragment ends before the current bundle boundary: pad it just
+    //    enough to reach the boundary.
+    // C) The fragment ends after the current bundle boundary: pad it until it
+    //    reaches the end of the next bundle boundary.
+    //
+    // Note: this code could be made shorter with some modulo trickery, but it's
+    // intentionally kept in its more explicit form for simplicity.
+    if (EndOfFragment == BundleSize)
+      return 0;
+    else if (EndOfFragment < BundleSize)
+      return BundleSize - EndOfFragment;
+    else { // EndOfFragment > BundleSize
+      return 2 * BundleSize - EndOfFragment;
+    }
+  } else if (EndOfFragment > BundleSize)
     return BundleSize - OffsetInBundle;
   else
     return 0;
@@ -204,7 +228,7 @@
   : Section(&_Section),
     Ordinal(~UINT32_C(0)),
     Alignment(1),
-    BundleLocked(false), BundleGroupBeforeFirstInst(false),
+    BundleLockState(NotBundleLocked), BundleGroupBeforeFirstInst(false),
     HasInstructions(false)
 {
   if (A)
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index e3acc44..e9afff6 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -379,8 +379,14 @@
     MCSectionData *SD = getCurrentSectionData();
     if (SD->isBundleLocked() && !SD->isBundleGroupBeforeFirstInst())
       DF = getOrCreateDataFragment();
-    else
+    else {
       DF = new MCDataFragment(SD);
+      if (SD->getBundleLockState() == MCSectionData::BundleLockedAlignToEnd) {
+        // If this is a new fragment created for a bundle-locked group, and the
+        // group was marked as "align_to_end", set a flag in the fragment.
+        DF->setAlignToBundleEnd(true);
+      }
+    }
 
     // We're now emitting an instruction in a bundle group, so this flag has
     // to be turned off.
@@ -407,7 +413,7 @@
     report_fatal_error(".bundle_align_mode should be only set once per file");
 }
 
-void MCELFStreamer::EmitBundleLock() {
+void MCELFStreamer::EmitBundleLock(bool AlignToEnd) {
   MCSectionData *SD = getCurrentSectionData();
 
   // Sanity checks
@@ -417,7 +423,8 @@
   else if (SD->isBundleLocked())
     report_fatal_error("Nesting of .bundle_lock is forbidden");
 
-  SD->setBundleLocked(true);
+  SD->setBundleLockState(AlignToEnd ? MCSectionData::BundleLockedAlignToEnd :
+                                      MCSectionData::BundleLocked);
   SD->setBundleGroupBeforeFirstInst(true);
 }
 
@@ -432,7 +439,7 @@
   else if (SD->isBundleGroupBeforeFirstInst())
     report_fatal_error("Empty bundle-locked group is forbidden");
 
-  SD->setBundleLocked(false);
+  SD->setBundleLockState(MCSectionData::NotBundleLocked);
 }
 
 void MCELFStreamer::FinishImpl() {
diff --git a/llvm/lib/MC/MCNullStreamer.cpp b/llvm/lib/MC/MCNullStreamer.cpp
index d1cd006..364c324 100644
--- a/llvm/lib/MC/MCNullStreamer.cpp
+++ b/llvm/lib/MC/MCNullStreamer.cpp
@@ -96,7 +96,7 @@
     virtual void EmitInstruction(const MCInst &Inst) {}
 
     virtual void EmitBundleAlignMode(unsigned AlignPow2) {}
-    virtual void EmitBundleLock() {}
+    virtual void EmitBundleLock(bool AlignToEnd) {}
     virtual void EmitBundleUnlock() {}
 
     virtual void FinishImpl() {}
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index 6d5c0a5..a848615 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -233,7 +233,7 @@
   llvm_unreachable(BundlingNotImplementedMsg);
 }
 
-void MCObjectStreamer::EmitBundleLock() {
+void MCObjectStreamer::EmitBundleLock(bool AlignToEnd) {
   llvm_unreachable(BundlingNotImplementedMsg);
 }
 
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 8a22ed5..7c3fea5 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -2473,15 +2473,31 @@
 }
 
 /// ParseDirectiveBundleLock
-/// ::= {.bundle_lock}
+/// ::= {.bundle_lock} [align_to_end]
 bool AsmParser::ParseDirectiveBundleLock() {
   CheckForValidSection();
+  bool AlignToEnd = false;
 
-  if (getLexer().isNot(AsmToken::EndOfStatement))
-    return TokError("unexpected token in '.bundle_lock' directive");
+  if (getLexer().isNot(AsmToken::EndOfStatement)) {
+    StringRef Option;
+    SMLoc Loc = getTok().getLoc();
+    const char *kInvalidOptionError =
+      "invalid option for '.bundle_lock' directive";
+
+    if (ParseIdentifier(Option))
+      return Error(Loc, kInvalidOptionError);
+
+    if (Option != "align_to_end")
+      return Error(Loc, kInvalidOptionError);
+    else if (getLexer().isNot(AsmToken::EndOfStatement))
+      return Error(Loc,
+                   "unexpected token after '.bundle_lock' directive option");
+    AlignToEnd = true;
+  }
+
   Lex();
 
-  getStreamer().EmitBundleLock();
+  getStreamer().EmitBundleLock(AlignToEnd);
   return false;
 }