Skip to content

[NFC][TableGen] DecoderEmitter optimize scope stack in Filter::emitTableEntry #135693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2025

Conversation

jurahul
Copy link
Contributor

@jurahul jurahul commented Apr 14, 2025

  • Create a new stack scope only in the fallthrough case.
  • For the non-fallthrough cases, any fixup entries will naturally be added to the existing scope without needing to copy them manually.
  • Verified that the generated GenDisassembler files are identical with and without this change.

Copy link

github-actions bot commented Apr 14, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@jurahul jurahul force-pushed the nfc_decoder_emitter_stack_opt branch from 57bbec2 to ff135c1 Compare April 14, 2025 23:25
@jurahul jurahul changed the title [NFC][TableGen] DecoderEmitter optimizer scope stack [NFC][TableGen] DecoderEmitter optimize scope stack in Filter::emitTableEntry Apr 14, 2025
@jurahul jurahul marked this pull request as ready for review April 15, 2025 00:58
@jurahul jurahul requested a review from topperc April 15, 2025 00:58
@llvmbot
Copy link
Member

llvmbot commented Apr 15, 2025

@llvm/pr-subscribers-tablegen

Author: Rahul Joshi (jurahul)

Changes
  • Create a new stack scope only in the fallthrough case.
  • For the non-fallthrough cases, any fixup entries will naturally be added to the existing scope without needing to copy them manually.
  • Verified that the generated GenDisassembler files are identical with and without this change.

Full diff: https://github.com/llvm/llvm-project/pull/135693.diff

1 Files Affected:

  • (modified) llvm/utils/TableGen/DecoderEmitter.cpp (+22-20)
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 14fb96bdfcfbf..9c6015cc24576 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -714,30 +714,39 @@ void Filter::emitTableEntry(DecoderTableInfo &TableInfo) const {
   TableInfo.Table.insertULEB128(StartBit);
   TableInfo.Table.push_back(NumBits);
 
-  // A new filter entry begins a new scope for fixup resolution.
-  TableInfo.FixupStack.emplace_back();
+  // If the NO_FIXED_SEGMENTS_SENTINEL is present, we need to add a new scope
+  // for this filter. Otherwise, we can skip adding a new scope and any
+  // patching added will automatically be added to the enclosing scope.
+
+  // If NO_FIXED_SEGMENTS_SENTINEL is present, it will be last entry in
+  // FilterChooserMap.
+
+  const uint64_t LastFilter = FilterChooserMap.rbegin()->first;
+  bool HasFallthrough = LastFilter == NO_FIXED_SEGMENTS_SENTINEL;
+  if (HasFallthrough)
+    TableInfo.FixupStack.emplace_back();
 
   DecoderTable &Table = TableInfo.Table;
 
   size_t PrevFilter = 0;
-  bool HasFallthrough = false;
-  for (const auto &Filter : FilterChooserMap) {
-    // Field value -1 implies a non-empty set of variable instructions.
-    // See also recurse().
-    if (Filter.first == NO_FIXED_SEGMENTS_SENTINEL) {
-      HasFallthrough = true;
-
+  for (const auto &[FilterVal, Delegate] : FilterChooserMap) {
+    // Field value NO_FIXED_SEGMENTS_SENTINEL implies a non-empty set of
+    // variable instructions. See also recurse().
+    if (FilterVal == NO_FIXED_SEGMENTS_SENTINEL) {
       // Each scope should always have at least one filter value to check
       // for.
       assert(PrevFilter != 0 && "empty filter set!");
       FixupList &CurScope = TableInfo.FixupStack.back();
       // Resolve any NumToSkip fixups in the current scope.
       resolveTableFixups(Table, CurScope, Table.size());
-      CurScope.clear();
+
+      // Delete the scope we have added here.
+      TableInfo.FixupStack.pop_back();
+
       PrevFilter = 0; // Don't re-process the filter's fallthrough.
     } else {
       Table.push_back(MCD::OPC_FilterValue);
-      Table.insertULEB128(Filter.first);
+      Table.insertULEB128(FilterVal);
       // Reserve space for the NumToSkip entry. We'll backpatch the value
       // later.
       PrevFilter = Table.insertNumToSkip();
@@ -747,11 +756,11 @@ void Filter::emitTableEntry(DecoderTableInfo &TableInfo) const {
     // Now delegate to the sub filter chooser for further decodings.
     // The case may fallthrough, which happens if the remaining well-known
     // encoding bits do not match exactly.
-    Filter.second->emitTableEntries(TableInfo);
+    Delegate->emitTableEntries(TableInfo);
 
     // Now that we've emitted the body of the handler, update the NumToSkip
     // of the filter itself to be able to skip forward when false. Subtract
-    // two as to account for the width of the NumToSkip field itself.
+    // three as to account for the width of the NumToSkip field itself.
     if (PrevFilter) {
       uint32_t NumToSkip = Table.size() - PrevFilter - 3;
       assert(isUInt<24>(NumToSkip) && "disassembler decoding table too large!");
@@ -761,13 +770,6 @@ void Filter::emitTableEntry(DecoderTableInfo &TableInfo) const {
     }
   }
 
-  // Any remaining unresolved fixups bubble up to the parent fixup scope.
-  assert(TableInfo.FixupStack.size() > 1 && "fixup stack underflow!");
-  FixupScopeList::iterator Source = TableInfo.FixupStack.end() - 1;
-  FixupScopeList::iterator Dest = Source - 1;
-  llvm::append_range(*Dest, *Source);
-  TableInfo.FixupStack.pop_back();
-
   // If there is no fallthrough, then the final filter should get fixed
   // up according to the enclosing scope rather than the current position.
   if (!HasFallthrough)

@jurahul
Copy link
Contributor Author

jurahul commented Apr 15, 2025

Note, this can go by itself, but its really in preparation for another follow-on change to optimize the encoding of the decoder ops that end up jumping to the "Op_Fail" case, by adding "OrFail" variants of these ops (for ex, OPC_FilterValueOrFail) which do not need to encode the 24-bit NumToSkip that just jump to Op_Fail in the decoder table and will directly fail.

I am hoping that this will help reduce the size of the decoder table by doing 2 things: (a) avoid the 3 byte encoding of NumToSkip for such ops, and (2) I suspect the NumToSkip was bumped from 2 bytes to 3 to accommodate these "long" jumps to the end of the table. If we can avoid encoding them, maybe we can go back to 16-bits for NumToSkip.

The way to detect if we can generate the OpFail variants is if we are at the outermost scope in the scope stack (i.e., scope stack size == 1) which is where this change is relevant

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jurahul jurahul merged commit 4780658 into llvm:main Apr 15, 2025
13 of 14 checks passed
@jurahul jurahul deleted the nfc_decoder_emitter_stack_opt branch April 15, 2025 14:49
var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
…ableEntry` (llvm#135693)

- Create a new stack scope only in the fallthrough case.
- For the non-fallthrough cases, any fixup entries will naturally be
added to the existing scope without needing to copy them manually.
- Verified that the generated `GenDisassembler` files are identical with
and without this change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants