[MC] Make bundle alignment mode setting idempotent and support nested bundles
authorDerek Schuff <dschuff@google.com>
Wed, 15 Oct 2014 17:10:04 +0000 (17:10 +0000)
committerDerek Schuff <dschuff@google.com>
Wed, 15 Oct 2014 17:10:04 +0000 (17:10 +0000)
Summary:
Currently an error is thrown if bundle alignment mode is set more than once
per module (either via the API or the .bundle_align_mode directive). This
change allows setting it multiple times as long as the alignment doesn't
change.

Also nested bundle_lock groups are currently not allowed. This change allows
them, with the effect that the group stays open until all nests are exited,
and if any of the bundle_lock directives has the align_to_end flag, the
group becomes align_to_end.

These changes make the bundle aligment simpler to use in the compiler, and
also better match the corresponding support in GNU as.

Reviewers: jvoung, eliben

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

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

include/llvm/MC/MCAssembler.h
lib/MC/MCAssembler.cpp
lib/MC/MCELFStreamer.cpp
test/MC/X86/AlignedBundling/nesting.s [new file with mode: 0644]

index b0a706d..681a317 100644 (file)
@@ -594,7 +594,10 @@ private:
   unsigned Alignment;
 
   /// \brief Keeping track of bundle-locked state.
-  BundleLockStateType BundleLockState; 
+  BundleLockStateType BundleLockState;
+
+  /// \brief Current nesting depth of bundle_lock directives.
+  unsigned BundleLockNestingDepth;
 
   /// \brief We've seen a bundle_lock directive but not its first instruction
   /// yet.
@@ -666,9 +669,7 @@ public:
     return BundleLockState;
   }
 
-  void setBundleLockState(BundleLockStateType NewState) {
-    BundleLockState = NewState;
-  }
+  void setBundleLockState(BundleLockStateType NewState);
 
   bool isBundleGroupBeforeFirstInst() const {
     return BundleGroupBeforeFirstInst;
index bc78a29..85d0c13 100644 (file)
@@ -291,7 +291,9 @@ MCSectionData::MCSectionData(const MCSection &_Section, MCAssembler *A)
   : Section(&_Section),
     Ordinal(~UINT32_C(0)),
     Alignment(1),
-    BundleLockState(NotBundleLocked), BundleGroupBeforeFirstInst(false),
+    BundleLockState(NotBundleLocked),
+    BundleLockNestingDepth(0),
+    BundleGroupBeforeFirstInst(false),
     HasInstructions(false)
 {
   if (A)
@@ -328,6 +330,25 @@ MCSectionData::getSubsectionInsertionPoint(unsigned Subsection) {
   return IP;
 }
 
+void MCSectionData::setBundleLockState(BundleLockStateType NewState) {
+  if (NewState == NotBundleLocked) {
+    if (BundleLockNestingDepth == 0) {
+      report_fatal_error("Mismatched bundle_lock/unlock directives");
+    }
+    if (--BundleLockNestingDepth == 0) {
+      BundleLockState = NotBundleLocked;
+    }
+    return;
+  }
+
+  // If any of the directives is an align_to_end directive, the whole nested
+  // group is align_to_end. So don't downgrade from align_to_end to just locked.
+  if (BundleLockState != BundleLockedAlignToEnd) {
+    BundleLockState = NewState;
+  }
+  ++BundleLockNestingDepth;
+}
+
 /* *** */
 
 MCSymbolData::MCSymbolData() : Symbol(nullptr) {}
index 34049b7..4ef22d0 100644 (file)
@@ -453,11 +453,13 @@ void MCELFStreamer::EmitInstToData(const MCInst &Inst,
     } else {
       DF = new MCDataFragment();
       insert(DF);
-      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);
-      }
+    }
+    if (SD->getBundleLockState() == MCSectionData::BundleLockedAlignToEnd) {
+      // If this fragment is for a group marked "align_to_end", set a flag
+      // in the fragment. This can happen after the fragment has already been
+      // created if there are nested bundle_align groups and an inner one
+      // is the one marked align_to_end.
+      DF->setAlignToBundleEnd(true);
     }
 
     // We're now emitting an instruction in a bundle group, so this flag has
@@ -479,10 +481,11 @@ void MCELFStreamer::EmitInstToData(const MCInst &Inst,
 void MCELFStreamer::EmitBundleAlignMode(unsigned AlignPow2) {
   assert(AlignPow2 <= 30 && "Invalid bundle alignment");
   MCAssembler &Assembler = getAssembler();
-  if (Assembler.getBundleAlignSize() == 0 && AlignPow2 > 0)
-    Assembler.setBundleAlignSize(1 << AlignPow2);
+  if (AlignPow2 > 0 && (Assembler.getBundleAlignSize() == 0 ||
+                        Assembler.getBundleAlignSize() == 1U << AlignPow2))
+    Assembler.setBundleAlignSize(1U << AlignPow2);
   else
-    report_fatal_error(".bundle_align_mode should be only set once per file");
+    report_fatal_error(".bundle_align_mode cannot be changed once set");
 }
 
 void MCELFStreamer::EmitBundleLock(bool AlignToEnd) {
@@ -492,12 +495,12 @@ void MCELFStreamer::EmitBundleLock(bool AlignToEnd) {
   //
   if (!getAssembler().isBundlingEnabled())
     report_fatal_error(".bundle_lock forbidden when bundling is disabled");
-  else if (SD->isBundleLocked())
-    report_fatal_error("Nesting of .bundle_lock is forbidden");
+
+  if (!SD->isBundleLocked())
+    SD->setBundleGroupBeforeFirstInst(true);
 
   SD->setBundleLockState(AlignToEnd ? MCSectionData::BundleLockedAlignToEnd :
                                       MCSectionData::BundleLocked);
-  SD->setBundleGroupBeforeFirstInst(true);
 }
 
 void MCELFStreamer::EmitBundleUnlock() {
diff --git a/test/MC/X86/AlignedBundling/nesting.s b/test/MC/X86/AlignedBundling/nesting.s
new file mode 100644 (file)
index 0000000..8996170
--- /dev/null
@@ -0,0 +1,67 @@
+# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - \
+# RUN:   | llvm-objdump -disassemble -no-show-raw-insn - | FileCheck %s
+
+# Will be bundle-aligning to 16 byte boundaries
+  .bundle_align_mode 4
+  .text
+# CHECK-LABEL: foo
+foo:
+# Test that bundle alignment mode can be set more than once.
+  .bundle_align_mode 4
+# Each of these callq instructions is 5 bytes long
+  callq bar
+  callq bar
+  .bundle_lock
+  .bundle_lock
+  callq bar
+  callq bar     
+  .bundle_unlock
+  .bundle_unlock
+# CHECK:      10: callq
+# CHECK-NEXT: 15: callq
+
+  .p2align 4
+# CHECK-LABEL: bar
+bar:
+  callq foo
+  callq foo
+# Check that the callqs get bundled together, and that the whole group is
+# align_to_end
+  .bundle_lock 
+  callq bar
+  .bundle_lock align_to_end
+  callq bar
+  .bundle_unlock
+  .bundle_unlock
+# CHECK:      36: callq
+# CHECK-NEXT: 3b: callq
+
+# CHECK-LABEL: baz
+baz:
+  callq foo
+  callq foo
+# Check that the callqs get bundled together, and that the whole group is
+# align_to_end (with the outer directive marked align_to_end)
+  .bundle_lock align_to_end
+  callq bar
+  .bundle_lock
+  callq bar
+  .bundle_unlock
+  .bundle_unlock
+# CHECK:      56: callq
+# CHECK-NEXT: 5b: callq
+
+# CHECK-LABEL: quux
+quux:
+  callq bar
+  callq bar
+  .bundle_lock
+  .bundle_lock
+  callq bar
+  .bundle_unlock
+  callq bar     
+  .bundle_unlock
+# Check that the calls are bundled together when the second one is after the
+# inner nest is closed.
+# CHECK:      70: callq
+# CHECK-NEXT: 75: callq