[Mlir-commits] [mlir] [mlir][ODS] Name obfuscation for private / internal dialects (PR #198083)

Matthias Springer llvmlistbot at llvm.org
Sat May 16 03:51:48 PDT 2026


https://github.com/matthias-springer created https://github.com/llvm/llvm-project/pull/198083

None

>From 7027fd5c33075b079257d5d05c07aab489d5ad69 Mon Sep 17 00:00:00 2001
From: Matthias Springer <me at m-sp.org>
Date: Sat, 16 May 2026 09:36:32 +0000
Subject: [PATCH] strip op and pass names

---
 mlir/CMakeLists.txt                           |  49 ++++++
 mlir/docs/PrivateNameObfuscation.md           | 151 ++++++++++++++++
 mlir/include/mlir/IR/AttrTypeBase.td          |  13 ++
 mlir/include/mlir/IR/DialectBase.td           |   8 +
 mlir/include/mlir/IR/OpBase.td                |   7 +
 mlir/include/mlir/Pass/PassBase.td            |  12 ++
 mlir/include/mlir/TableGen/AttrOrTypeDef.h    |   5 +
 mlir/include/mlir/TableGen/Dialect.h          |   5 +
 mlir/include/mlir/TableGen/Operator.h         |   5 +
 mlir/include/mlir/TableGen/Pass.h             |   7 +
 mlir/include/mlir/TableGen/PrivateName.h      |  68 ++++++++
 mlir/lib/TableGen/AttrOrTypeDef.cpp           |   4 +
 mlir/lib/TableGen/CMakeLists.txt              |   1 +
 mlir/lib/TableGen/Dialect.cpp                 |   4 +
 mlir/lib/TableGen/Operator.cpp                |   2 +
 mlir/lib/TableGen/Pass.cpp                    |   2 +
 mlir/lib/TableGen/PrivateName.cpp             | 144 +++++++++++++++
 mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp |  34 ++++
 .../mlir-tblgen/private-name-obfuscation.td   | 164 ++++++++++++++++++
 mlir/test/mlir-tblgen/private-pass-strip.td   |  75 ++++++++
 mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp   |  18 +-
 mlir/tools/mlir-tblgen/DialectGen.cpp         |   7 +-
 mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp   |  28 ++-
 mlir/tools/mlir-tblgen/PassCAPIGen.cpp        |  13 ++
 mlir/tools/mlir-tblgen/PassGen.cpp            |  68 ++++++--
 mlir/tools/mlir-tblgen/RewriterGen.cpp        |  30 ++--
 26 files changed, 890 insertions(+), 34 deletions(-)
 create mode 100644 mlir/docs/PrivateNameObfuscation.md
 create mode 100644 mlir/include/mlir/TableGen/PrivateName.h
 create mode 100644 mlir/lib/TableGen/PrivateName.cpp
 create mode 100644 mlir/test/mlir-tblgen/private-name-obfuscation.td
 create mode 100644 mlir/test/mlir-tblgen/private-pass-strip.td

diff --git a/mlir/CMakeLists.txt b/mlir/CMakeLists.txt
index 6d05fa50ecd05..75e961ecee804 100644
--- a/mlir/CMakeLists.txt
+++ b/mlir/CMakeLists.txt
@@ -152,6 +152,55 @@ set(MLIR_ENABLE_NVPTXCOMPILER 0 CACHE BOOL
 
 set(MLIR_ENABLE_PDL_IN_PATTERNMATCH 1 CACHE BOOL "Enable PDL in PatternMatch")
 
+#-------------------------------------------------------------------------------
+# Private name obfuscation / stripping
+#
+# When MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION is ON, dialects, ops, attributes,
+# types, and passes whose TableGen definitions set `let isPrivate = 1;` have
+# their mnemonics replaced with deterministic opaque hashes derived from a
+# build-time salt (SipHash-2-4-64, base32-truncated). This makes it harder to
+# reverse engineer the resulting binary while keeping registration, pattern
+# matching, bytecode, and IR printing internally consistent.
+#
+# When MLIR_STRIP_PASS_METADATA is ON, private passes additionally lose their
+# command-line description, per-option descriptions, and per-pass
+# `register*Pass()` / `mlirRegister*` registration helpers. They can still be
+# constructed and run from C++, but cannot be invoked via a textual pass
+# pipeline or through the C API.
+#
+# MLIR_PRIVATE_NAME_SALT can be set to a hex string to pin the salt across
+# builds; otherwise a random per-build salt is generated and stored in
+# `${CMAKE_BINARY_DIR}/mlir-obfuscation.salt`. Note that bytecode produced by
+# a build with a different salt is unreadable by any other build.
+#-------------------------------------------------------------------------------
+option(MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION
+       "Replace TableGen `isPrivate` dialect/op/attribute/type/pass mnemonics with opaque hashes."
+       OFF)
+set(MLIR_PRIVATE_NAME_SALT "" CACHE STRING
+    "Hex salt fed to mlir-tblgen for private-name obfuscation; if empty and obfuscation is enabled, a random per-build salt is generated.")
+option(MLIR_STRIP_PASS_METADATA
+       "For passes marked `isPrivate`, drop description, per-option `cl::desc` text, and CLI/C-API registration helpers."
+       OFF)
+
+if(MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION)
+  if(NOT MLIR_PRIVATE_NAME_SALT)
+    set(_mlir_salt_file "${CMAKE_BINARY_DIR}/mlir-obfuscation.salt")
+    if(EXISTS "${_mlir_salt_file}")
+      file(READ "${_mlir_salt_file}" MLIR_PRIVATE_NAME_SALT)
+      string(STRIP "${MLIR_PRIVATE_NAME_SALT}" MLIR_PRIVATE_NAME_SALT)
+    else()
+      string(RANDOM LENGTH 32 ALPHABET "0123456789abcdef" MLIR_PRIVATE_NAME_SALT)
+      file(WRITE "${_mlir_salt_file}" "${MLIR_PRIVATE_NAME_SALT}\n")
+      message(STATUS "MLIR: generated obfuscation salt at ${_mlir_salt_file}")
+    endif()
+  endif()
+  list(APPEND LLVM_TABLEGEN_FLAGS "--mlir-obfuscate-private")
+  list(APPEND LLVM_TABLEGEN_FLAGS "--mlir-obfuscation-salt=${MLIR_PRIVATE_NAME_SALT}")
+endif()
+if(MLIR_STRIP_PASS_METADATA)
+  list(APPEND LLVM_TABLEGEN_FLAGS "--mlir-strip-private-pass-metadata")
+endif()
+
 option(MLIR_INCLUDE_TESTS
        "Generate build targets for the MLIR unit tests."
        ${LLVM_INCLUDE_TESTS})
diff --git a/mlir/docs/PrivateNameObfuscation.md b/mlir/docs/PrivateNameObfuscation.md
new file mode 100644
index 0000000000000..3d0d3af803997
--- /dev/null
+++ b/mlir/docs/PrivateNameObfuscation.md
@@ -0,0 +1,151 @@
+# Private Name Obfuscation and Stripping
+
+[TOC]
+
+This page documents an opt-in TableGen + CMake mechanism for replacing the
+human-readable names of dialects, operations, attributes, types, and passes
+in a built MLIR binary with deterministic opaque identifiers, and for
+dropping pass description / CLI registration metadata. The intent is to make
+it harder to reverse-engineer a release binary while preserving a
+fully-readable internal/test build from the same source tree.
+
+## What gets obfuscated
+
+When `-DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON` is set and a TableGen
+definition declares `let isPrivate = 1;`, mlir-tblgen emits the following
+literals using a deterministic 12-character base32 hash of the original
+identifier (computed with SipHash-2-4-64 and a build-time salt) instead of
+the original spelling:
+
+| Source                     | Affected literal(s)                                          |
+| -------------------------- | ------------------------------------------------------------ |
+| `Dialect`                  | `getDialectNamespace()`, dialect constructor                 |
+| `Op`                       | `getOperationName()`, adaptor `odsOpName`, error prefixes    |
+| `AttrDef`, `TypeDef`       | `getMnemonic()`, `name`, `dialectName`, alias `getAlias`     |
+| `PassBase` / `Pass`        | `getArgument()`, `getArgumentName()`, `getName()`, `getPassName()` |
+
+The hash is split at the dot for op / attribute / type names, so the
+dialect prefix and the mnemonic are obfuscated independently and the
+runtime helper `OperationName::getDialectNamespace()` (which splits at the
+first `.`) keeps working.
+
+Hashes are **deterministic** for a given salt, so all translation units
+within one build agree on the obfuscated spelling. Pattern matching,
+`ConversionTarget`, the bytecode reader/writer (which uses whatever
+`getDialectNamespace()` / `getOperationName()` return), and dialect
+registration all keep working without source changes.
+
+Private ops also behave as if no `assemblyFormat` or
+`hasCustomAssemblyFormat` was specified when private-name obfuscation is
+enabled. ODS does not generate the custom/declarative `parse` and `print`
+methods for those ops. They still print in generic form, using the
+obfuscated operation name, and their custom textual syntax is rejected.
+
+## What gets stripped (passes only)
+
+When `-DMLIR_STRIP_PASS_METADATA=ON` is also set, passes marked
+`isPrivate` additionally lose:
+
+* `getDescription()` — emitted as the empty string.
+* The argument key returned by `getArgument()` / `getArgumentName()` —
+  emitted as the empty string.
+* Per-option `cl::desc(...)` text — emitted as the empty string.
+* Per-statistic description text — emitted as the empty string.
+* The per-pass `register{PassName}()` and `register{PassName}Pass()`
+  helpers, plus inclusion in the `register{Group}Passes()` aggregator.
+* The per-pass C-API entry points
+  `mlirCreate{Group}{PassName}` / `mlirRegister{Group}{PassName}`.
+
+A private + stripped pass can still be constructed from C++ via the
+generated `create{PassName}()` factory. It cannot be invoked via a textual
+pass pipeline (`--pass-pipeline=...`), via the per-pass CLI flag, or via
+the C API.
+
+## Build configuration
+
+```cmake
+# Public/test/dev build: no obfuscation, no stripping (default).
+cmake -G Ninja path/to/llvm \
+  -DLLVM_ENABLE_PROJECTS=mlir
+
+# Release build: obfuscate names of every TableGen item marked
+# `isPrivate`, and strip CLI/CAPI exposure for private passes.
+cmake -G Ninja path/to/llvm \
+  -DLLVM_ENABLE_PROJECTS=mlir \
+  -DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON \
+  -DMLIR_STRIP_PASS_METADATA=ON \
+  -DMLIR_PRIVATE_NAME_SALT=<32-hex-char salt>
+```
+
+If `MLIR_PRIVATE_NAME_SALT` is left empty when obfuscation is enabled,
+CMake generates a random 16-byte salt at first configure and stores it in
+`${CMAKE_BINARY_DIR}/mlir-obfuscation.salt`. Subsequent reconfigurations
+of the same build directory reuse that file. Wiping the build directory
+generates a new salt.
+
+The salt feeds directly into the `mlir-tblgen` command line as
+`--mlir-obfuscation-salt=<hex>` and is **not** embedded in the resulting
+binary. Bytecode produced by a build with one salt cannot be read by a
+build with a different salt, so pin the salt across releases that need
+binary compatibility.
+
+## Marking items as private
+
+```tablegen
+def MyDialect : Dialect {
+  let name = "mydialect";
+  let cppNamespace = "::my";
+  let isPrivate = 1;        // dialect, plus all of its ops/attrs/types
+}
+
+def MyOp : Op<MyDialect, "do_thing", []>;       // inherits isPrivate = 1
+def MyOtherOp : Op<MyDialect, "do_thing2", []> {
+  let isPrivate = 0;        // override: keep this op visible
+}
+
+def MyPass : Pass<"my-pass"> {
+  let isPrivate = 1;        // hash the argument and name, drop description
+                            // and CLI/CAPI registration when stripping
+}
+```
+
+`AttrDef` and `TypeDef` inherit `isPrivate` from their owning dialect by
+default and can be overridden the same way as ops.
+
+## Caveats
+
+* Hand-written code that compares `op->getName().getStringRef()` against a
+  spelled-out op name (e.g. `== "mydialect.do_thing"`) breaks under
+  obfuscation. Migrate such checks to `isa<my::DoThingOp>()` (which uses
+  TypeID and is unaffected) or compare against
+  `my::DoThingOp::getOperationName()` (which is itself obfuscated, so the
+  comparison still works).
+* Diagnostics and verifier messages naturally print the obfuscated names
+  because they use runtime `getName()` / `getDialect()->getNamespace()`
+  calls. The English skeleton text around the names is not stripped.
+* Python bindings emitted by `gen-python-op-bindings` are not adjusted by
+  this mechanism. Do not generate Python bindings for private dialects or
+  ops.
+* `LLVM_DEBUG`, `LDBG`, statistics (`LLVM_ENABLE_STATS`), and `--debug`
+  paths are already removed from a release build (`NDEBUG`); they don't
+  need separate handling.
+
+## Audit checklist for downstream trees
+
+Before enabling private-name obfuscation in a downstream compiler, audit for
+hand-written string comparisons and textual pipeline dependencies. These
+patterns should generally be rewritten to use TypeID-based APIs, concrete op
+classes, or the generated `::getOperationName()` accessors:
+
+```sh
+rg 'getName\\(\\)\\.getStringRef\\(\\).*==|==.*getName\\(\\)\\.getStringRef\\(\\)' path/to/downstream
+rg 'OperationName\\("[^"]+"' path/to/downstream
+rg 'RegisteredOperationName::lookup\\("[^"]+"' path/to/downstream
+rg 'getOrLoadDialect\\("[^"]+"' path/to/downstream
+rg 'PassInfo::lookup\\("[^"]+"' path/to/downstream
+rg 'parsePassPipeline|--pass-pipeline|register.*Passes' path/to/downstream
+```
+
+Comparisons against generated names, such as
+`my::DoThingOp::getOperationName()`, remain valid because the generated
+method returns the obfuscated spelling in release builds.
diff --git a/mlir/include/mlir/IR/AttrTypeBase.td b/mlir/include/mlir/IR/AttrTypeBase.td
index ac7ac8fdb3039..e2ec32bd350df 100644
--- a/mlir/include/mlir/IR/AttrTypeBase.td
+++ b/mlir/include/mlir/IR/AttrTypeBase.td
@@ -252,6 +252,13 @@ class AttrOrTypeDef<string valueType, string name, list<Trait> defTraits,
 
   // Generate a default 'getAlias' method for OpAsm{Type,Attr}Interface.
   bit genMnemonicAlias = 0;
+
+  // Marks this attribute or type as "private". Subclasses (`AttrDef` and
+  // `TypeDef`) default this to the owning dialect's `isPrivate` bit. When the
+  // build is configured with `-DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON`, the
+  // mnemonic and the dialect-qualified name of this def are replaced with
+  // deterministic opaque identifiers derived from a build-time salt.
+  bit isPrivate = 0;
 }
 
 // Define a new attribute, named `name`, belonging to `dialect` that inherits
@@ -273,6 +280,9 @@ class AttrDef<Dialect dialect, string name, list<Trait> traits = [],
   // The unique attribute name.
   string attrName = dialect.name # "." # mnemonic;
 
+  // Inherit privacy from the owning dialect by default.
+  let isPrivate = dialect.isPrivate;
+
   // The call expression to convert from the storage type to the return
   // type. For example, an enum can be stored as an int but returned as an
   // enum class.
@@ -308,6 +318,9 @@ class TypeDef<Dialect dialect, string name, list<Trait> traits = [],
   // The unique type name.
   string typeName = dialect.name # "." # mnemonic;
 
+  // Inherit privacy from the owning dialect by default.
+  let isPrivate = dialect.isPrivate;
+
   // A constant builder provided when the type has no parameters.
   let builderCall = !if(!empty(parameters),
                            "$_builder.getType<" # cppType # ">()",
diff --git a/mlir/include/mlir/IR/DialectBase.td b/mlir/include/mlir/IR/DialectBase.td
index efa09a43ec581..fe195ebf4e81f 100644
--- a/mlir/include/mlir/IR/DialectBase.td
+++ b/mlir/include/mlir/IR/DialectBase.td
@@ -92,6 +92,14 @@ class Dialect {
 
   // If this dialect can be extended at runtime with new operations or types.
   bit isExtensible = 0;
+
+  // Marks the dialect as "private". When the build is configured with
+  // `-DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON`, mlir-tblgen replaces the
+  // dialect's namespace and the mnemonics of all of its ops/attributes/types
+  // with deterministic opaque identifiers derived from a build-time salt.
+  // Operations, attributes, and types defined on this dialect inherit this
+  // bit by default and can override it individually if needed.
+  bit isPrivate = 0;
 }
 
 #endif // DIALECTBASE_TD
diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td
index 1e34959d0d557..ab5b044d238c5 100644
--- a/mlir/include/mlir/IR/OpBase.td
+++ b/mlir/include/mlir/IR/OpBase.td
@@ -335,6 +335,13 @@ class Op<Dialect dialect, string mnemonic, list<Trait> props = []> {
   // The mnemonic of the op.
   string opName = mnemonic;
 
+  // Marks this op as "private". Defaults to `dialect.isPrivate` and can be
+  // overridden per op. When the build is configured with
+  // `-DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON`, the op's mnemonic (and the
+  // dialect prefix in its registered name) is replaced with a deterministic
+  // opaque identifier derived from a build-time salt.
+  bit isPrivate = dialect.isPrivate;
+
   // The C++ namespace to use for this op.
   string cppNamespace = dialect.cppNamespace;
 
diff --git a/mlir/include/mlir/Pass/PassBase.td b/mlir/include/mlir/Pass/PassBase.td
index e37f9735e2241..50f591d0480af 100644
--- a/mlir/include/mlir/Pass/PassBase.td
+++ b/mlir/include/mlir/Pass/PassBase.td
@@ -89,6 +89,18 @@ class PassBase<string passArg, string base> {
 
   // A set of statistics provided by this pass.
   list<Statistic> statistics = [];
+
+  // Marks this pass as "private". When the build is configured with
+  // `-DMLIR_ENABLE_PRIVATE_NAME_OBFUSCATION=ON`, the pass argument and pass
+  // name are replaced with deterministic opaque identifiers derived from a
+  // build-time salt. When the build is configured with
+  // `-DMLIR_STRIP_PASS_METADATA=ON`, the description, the per-option
+  // descriptions, and the per-statistic descriptions are emitted as empty
+  // strings. Additionally, the pass is omitted from the generated
+  // `register*Passes()` aggregate registration function and from the
+  // generated C-API `mlirRegister*` registration entry points, so private
+  // passes cannot be invoked via `--pass-pipeline=...` or via the C API.
+  bit isPrivate = 0;
 }
 
 // This class represents an mlir::OperationPass.
diff --git a/mlir/include/mlir/TableGen/AttrOrTypeDef.h b/mlir/include/mlir/TableGen/AttrOrTypeDef.h
index 65992f9fef5e9..8bf5681190a07 100644
--- a/mlir/include/mlir/TableGen/AttrOrTypeDef.h
+++ b/mlir/include/mlir/TableGen/AttrOrTypeDef.h
@@ -220,6 +220,11 @@ class AttrOrTypeDef {
   /// using the mnemonic.
   bool genMnemonicAlias() const;
 
+  /// Returns true if this attribute or type has been marked private. Private
+  /// def names are eligible for obfuscation when
+  /// MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION is enabled in mlir-tblgen.
+  bool isPrivate() const;
+
   /// Get the code location (for error printing).
   ArrayRef<SMLoc> getLoc() const;
 
diff --git a/mlir/include/mlir/TableGen/Dialect.h b/mlir/include/mlir/TableGen/Dialect.h
index 30f9d690b678d..ba181191da2b7 100644
--- a/mlir/include/mlir/TableGen/Dialect.h
+++ b/mlir/include/mlir/TableGen/Dialect.h
@@ -88,6 +88,11 @@ class Dialect {
   /// operations or types.
   bool isExtensible() const;
 
+  /// Returns true if this dialect has been marked private. Private dialect,
+  /// op, attribute, and type names are eligible for obfuscation when
+  /// MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION is enabled in mlir-tblgen.
+  bool isPrivate() const;
+
   const llvm::DagInit *getDiscardableAttributes() const;
 
   const llvm::Record *getDef() const { return def; }
diff --git a/mlir/include/mlir/TableGen/Operator.h b/mlir/include/mlir/TableGen/Operator.h
index f0514d8e61748..04e8369ef615b 100644
--- a/mlir/include/mlir/TableGen/Operator.h
+++ b/mlir/include/mlir/TableGen/Operator.h
@@ -86,6 +86,11 @@ class Operator {
   /// format if its dialect name is not empty.
   std::string getOperationName() const;
 
+  /// Returns true if this op has been marked private. When private and
+  /// MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION is enabled in mlir-tblgen, the op's
+  /// dialect prefix and mnemonic in its registered name are obfuscated.
+  bool isPrivate() const;
+
   /// Returns this op's C++ class name.
   StringRef getCppClassName() const;
 
diff --git a/mlir/include/mlir/TableGen/Pass.h b/mlir/include/mlir/TableGen/Pass.h
index d2bb6e5304ee3..60c1e3f23ccf4 100644
--- a/mlir/include/mlir/TableGen/Pass.h
+++ b/mlir/include/mlir/TableGen/Pass.h
@@ -103,6 +103,13 @@ class Pass {
   /// Return the statistics provided by this pass.
   ArrayRef<PassStatistic> getStatistics() const;
 
+  /// Return true if this pass has been marked private. When private and
+  /// MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION is enabled in mlir-tblgen, the pass
+  /// argument and pass name are obfuscated; when MLIR_STRIP_PASS_METADATA is
+  /// enabled in mlir-tblgen, descriptions are dropped and registration helpers
+  /// are omitted.
+  bool isPrivate() const;
+
   const llvm::Record *getDef() const { return def; }
 
 private:
diff --git a/mlir/include/mlir/TableGen/PrivateName.h b/mlir/include/mlir/TableGen/PrivateName.h
new file mode 100644
index 0000000000000..f22a89d885035
--- /dev/null
+++ b/mlir/include/mlir/TableGen/PrivateName.h
@@ -0,0 +1,68 @@
+//===- PrivateName.h - Private name obfuscation for ODS ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers for ODS-driven obfuscation and stripping of "private" dialect,
+// operation, attribute, type, and pass names. The mlir-tblgen tool driver
+// configures the helper via the command-line flags `--mlir-obfuscate-private`,
+// `--mlir-obfuscation-salt`, and `--mlir-strip-private-pass-metadata`.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TABLEGEN_PRIVATENAME_H_
+#define MLIR_TABLEGEN_PRIVATENAME_H_
+
+#include "mlir/Support/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace mlir {
+namespace tblgen {
+
+/// Configuration setters. The mlir-tblgen tool driver calls these after
+/// parsing the command line; downstream tools that embed this library can
+/// also set these directly.
+void setObfuscatePrivateNames(bool enabled);
+void setStripPrivatePassMetadata(bool enabled);
+
+/// Sets the SipHash key used for obfuscation. `hexSalt` is parsed as up to
+/// 32 hex characters (16 bytes); missing nibbles are zero-padded; extra
+/// nibbles are truncated. A leading "0x"/"0X" is allowed.
+void setObfuscationSalt(StringRef hexSalt);
+
+/// Returns true if private-name obfuscation is enabled.
+bool obfuscatePrivateNamesEnabled();
+
+/// Returns true if private-pass metadata stripping is enabled.
+bool stripPrivatePassMetadataEnabled();
+
+/// Returns the obfuscated form of `name`, computed deterministically from
+/// the configured salt using SipHash-2-4-64 truncated to 60 bits and base32
+/// encoded with a leading underscore (so the result is a valid identifier
+/// and a valid MLIR mnemonic). The returned StringRef is stable for the
+/// lifetime of the process.
+StringRef obfuscatePrivateName(StringRef name);
+
+/// Returns either `name` (when not private or obfuscation is disabled) or
+/// `obfuscatePrivateName(name)`.
+inline StringRef maybeObfuscate(StringRef name, bool isPrivate) {
+  if (!isPrivate || !obfuscatePrivateNamesEnabled())
+    return name;
+  return obfuscatePrivateName(name);
+}
+
+/// For a dotted name "dialect.mnemonic", obfuscates the dialect prefix and
+/// the mnemonic suffix independently and rejoins them with a dot. This keeps
+/// runtime parsing of the dialect-prefix in `OperationName` working. Names
+/// without a '.' are obfuscated as-is.
+std::string maybeObfuscateDotted(StringRef name, bool isPrivate);
+
+} // namespace tblgen
+} // namespace mlir
+
+#endif // MLIR_TABLEGEN_PRIVATENAME_H_
diff --git a/mlir/lib/TableGen/AttrOrTypeDef.cpp b/mlir/lib/TableGen/AttrOrTypeDef.cpp
index bf835a860cd5b..4cd47e6490741 100644
--- a/mlir/lib/TableGen/AttrOrTypeDef.cpp
+++ b/mlir/lib/TableGen/AttrOrTypeDef.cpp
@@ -211,6 +211,10 @@ bool AttrOrTypeDef::genMnemonicAlias() const {
   return def->getValueAsBit("genMnemonicAlias");
 }
 
+bool AttrOrTypeDef::isPrivate() const {
+  return def->getValueAsBit("isPrivate");
+}
+
 ArrayRef<SMLoc> AttrOrTypeDef::getLoc() const { return def->getLoc(); }
 
 bool AttrOrTypeDef::skipDefaultBuilders() const {
diff --git a/mlir/lib/TableGen/CMakeLists.txt b/mlir/lib/TableGen/CMakeLists.txt
index a90c55847718e..cd079ac8a1a5d 100644
--- a/mlir/lib/TableGen/CMakeLists.txt
+++ b/mlir/lib/TableGen/CMakeLists.txt
@@ -28,6 +28,7 @@ llvm_add_library(MLIRTableGen STATIC
   Pass.cpp
   Pattern.cpp
   Predicate.cpp
+  PrivateName.cpp
   Property.cpp
   Region.cpp
   SideEffects.cpp
diff --git a/mlir/lib/TableGen/Dialect.cpp b/mlir/lib/TableGen/Dialect.cpp
index 7aaf4dd57c50e..e58e8bf13e986 100644
--- a/mlir/lib/TableGen/Dialect.cpp
+++ b/mlir/lib/TableGen/Dialect.cpp
@@ -102,6 +102,10 @@ bool Dialect::isExtensible() const {
   return def->getValueAsBit("isExtensible");
 }
 
+bool Dialect::isPrivate() const {
+  return def->getValueAsBit("isPrivate");
+}
+
 const llvm::DagInit *Dialect::getDiscardableAttributes() const {
   return def->getValueAsDag("discardableAttrs");
 }
diff --git a/mlir/lib/TableGen/Operator.cpp b/mlir/lib/TableGen/Operator.cpp
index 82dfbcbfa4d4f..9aa396b252c87 100644
--- a/mlir/lib/TableGen/Operator.cpp
+++ b/mlir/lib/TableGen/Operator.cpp
@@ -68,6 +68,8 @@ std::string Operator::getOperationName() const {
   return std::string(llvm::formatv("{0}.{1}", prefix, opName));
 }
 
+bool Operator::isPrivate() const { return def.getValueAsBit("isPrivate"); }
+
 std::string Operator::getAdaptorName() const {
   return std::string(llvm::formatv("{0}Adaptor", getCppClassName()));
 }
diff --git a/mlir/lib/TableGen/Pass.cpp b/mlir/lib/TableGen/Pass.cpp
index 271332d1125e5..bb0b2250760e8 100644
--- a/mlir/lib/TableGen/Pass.cpp
+++ b/mlir/lib/TableGen/Pass.cpp
@@ -98,3 +98,5 @@ ArrayRef<StringRef> Pass::getDependentDialects() const {
 ArrayRef<PassOption> Pass::getOptions() const { return options; }
 
 ArrayRef<PassStatistic> Pass::getStatistics() const { return statistics; }
+
+bool Pass::isPrivate() const { return def->getValueAsBit("isPrivate"); }
diff --git a/mlir/lib/TableGen/PrivateName.cpp b/mlir/lib/TableGen/PrivateName.cpp
new file mode 100644
index 0000000000000..3ffb69c1cb919
--- /dev/null
+++ b/mlir/lib/TableGen/PrivateName.cpp
@@ -0,0 +1,144 @@
+//===- PrivateName.cpp - Private name obfuscation for ODS -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/TableGen/PrivateName.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/SipHash.h"
+
+#include <array>
+#include <cstdint>
+#include <string>
+
+using namespace mlir;
+using namespace mlir::tblgen;
+
+namespace {
+/// Lazy state for private-name obfuscation. Wrapped in a function-local
+/// static so we don't introduce any global constructors (the MLIRTableGen
+/// library is built with `-Werror=global-constructors` in some
+/// configurations).
+struct State {
+  bool obfuscate = false;
+  bool strip = false;
+  std::array<uint8_t, 16> salt{};
+  llvm::StringMap<std::string> cache;
+};
+
+State &state() {
+  static State s;
+  return s;
+}
+
+uint8_t hexDigit(char c, bool &ok) {
+  if (c >= '0' && c <= '9')
+    return static_cast<uint8_t>(c - '0');
+  if (c >= 'a' && c <= 'f')
+    return static_cast<uint8_t>(10 + (c - 'a'));
+  if (c >= 'A' && c <= 'F')
+    return static_cast<uint8_t>(10 + (c - 'A'));
+  ok = false;
+  return 0;
+}
+
+/// Base32 alphabet (RFC 4648 lowercase variant). Each character encodes 5
+/// bits. The result is restricted to `[a-z0-9]` so the obfuscated mnemonic
+/// is a valid C++ identifier suffix and a valid MLIR mnemonic.
+constexpr char kBase32Alphabet[] = "abcdefghijklmnopqrstuvwxyz012345";
+
+/// Encodes the low `numChars * 5` bits of `hash` as base32. The result is
+/// always prefixed with `_` so that the encoded form is a valid bare
+/// identifier (which must start with a letter or underscore).
+std::string encodeBase32(uint64_t hash, unsigned numChars) {
+  std::string out;
+  out.reserve(1 + numChars);
+  out.push_back('_');
+  for (unsigned i = 0; i < numChars; ++i) {
+    out.push_back(kBase32Alphabet[hash & 0x1f]);
+    hash >>= 5;
+  }
+  return out;
+}
+} // namespace
+
+void mlir::tblgen::setObfuscatePrivateNames(bool enabled) {
+  state().obfuscate = enabled;
+}
+
+void mlir::tblgen::setStripPrivatePassMetadata(bool enabled) {
+  state().strip = enabled;
+}
+
+void mlir::tblgen::setObfuscationSalt(StringRef hex) {
+  if (hex.starts_with("0x") || hex.starts_with("0X"))
+    hex = hex.drop_front(2);
+
+  auto &s = state();
+  s.salt.fill(0);
+
+  bool ok = true;
+  for (size_t i = 0, e = hex.size(); i < e && (i / 2) < s.salt.size(); ++i) {
+    uint8_t nibble = hexDigit(hex[i], ok);
+    if ((i % 2) == 0)
+      s.salt[i / 2] = static_cast<uint8_t>(nibble << 4);
+    else
+      s.salt[i / 2] = static_cast<uint8_t>(s.salt[i / 2] | nibble);
+  }
+  if (!ok)
+    llvm::report_fatal_error(
+        "--mlir-obfuscation-salt must be a hex-encoded string");
+
+  // Salt change invalidates any previously cached obfuscations.
+  s.cache.clear();
+}
+
+bool mlir::tblgen::obfuscatePrivateNamesEnabled() { return state().obfuscate; }
+
+bool mlir::tblgen::stripPrivatePassMetadataEnabled() { return state().strip; }
+
+StringRef mlir::tblgen::obfuscatePrivateName(StringRef name) {
+  if (name.empty())
+    return name;
+
+  auto &s = state();
+  if (auto it = s.cache.find(name); it != s.cache.end())
+    return it->second;
+
+  uint8_t out[8] = {};
+  uint8_t (&saltKey)[16] =
+      *reinterpret_cast<uint8_t (*)[16]>(s.salt.data());
+  llvm::getSipHash_2_4_64(
+      llvm::ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(name.data()),
+                              name.size()),
+      saltKey, out);
+  uint64_t hash = 0;
+  for (unsigned i = 0; i < 8; ++i)
+    hash |= static_cast<uint64_t>(out[i]) << (i * 8);
+
+  // 12 chars * 5 bits = 60 bits of entropy. Plenty of headroom against
+  // collisions for the typical few-thousand-mnemonic dialect.
+  std::string obf = encodeBase32(hash, 12);
+  auto inserted = s.cache.try_emplace(name, std::move(obf));
+  return inserted.first->second;
+}
+
+std::string mlir::tblgen::maybeObfuscateDotted(StringRef name, bool isPrivate) {
+  if (!isPrivate || !obfuscatePrivateNamesEnabled())
+    return std::string(name);
+  size_t dot = name.find('.');
+  if (dot == StringRef::npos)
+    return obfuscatePrivateName(name).str();
+  StringRef dialect = name.substr(0, dot);
+  StringRef rest = name.substr(dot + 1);
+  std::string result = obfuscatePrivateName(dialect).str();
+  result.push_back('.');
+  result += obfuscatePrivateName(rest).str();
+  return result;
+}
diff --git a/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp b/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp
index 64e86f2a62073..3b643b33aeed9 100644
--- a/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp
+++ b/mlir/lib/Tools/mlir-tblgen/MlirTblgenMain.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/GenNameParser.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/Signals.h"
@@ -151,8 +152,41 @@ int mlir::MlirTblgenMain(int argc, char **argv) {
   llvm::cl::opt<const mlir::GenInfo *, true, mlir::GenNameParser> generator(
       "", llvm::cl::desc("Generator to run"), cl::location(::generator));
 
+  // Options driving private-name obfuscation and pass-metadata stripping.
+  // See `mlir/include/mlir/TableGen/PrivateName.h` and the
+  // `MLIR_ENABLE_PRIVATE_NAME_OBFUSCATION` / `MLIR_STRIP_PASS_METADATA`
+  // CMake variables for the user-facing entry points.
+  llvm::cl::opt<bool> obfuscatePrivateNames(
+      "mlir-obfuscate-private",
+      llvm::cl::desc(
+          "Replace mnemonics of dialects/ops/attributes/types/passes that "
+          "are marked `isPrivate` in their TableGen definitions with "
+          "deterministic opaque hashes."),
+      llvm::cl::init(false));
+  llvm::cl::opt<std::string> obfuscationSalt(
+      "mlir-obfuscation-salt",
+      llvm::cl::desc(
+          "Hex-encoded (up to 32 hex chars / 16 bytes) salt used as the "
+          "SipHash-2-4 key for `--mlir-obfuscate-private`. If empty, the "
+          "all-zero key is used."),
+      llvm::cl::init(""));
+  llvm::cl::opt<bool> stripPrivatePassMetadata(
+      "mlir-strip-private-pass-metadata",
+      llvm::cl::desc(
+          "For passes whose TableGen definition is marked `isPrivate`, "
+          "drop the pass description, per-option `cl::desc` strings, and "
+          "the per-pass `registerXxxPass()` and `mlirRegisterXxx` "
+          "registration helpers, so that the pass cannot be invoked via "
+          "`--pass-pipeline=...` or via the C API."),
+      llvm::cl::init(false));
+
   cl::ParseCommandLineOptions(argc, argv);
 
+  // Push parsed flag values into the helper's lazy state.
+  mlir::tblgen::setObfuscatePrivateNames(obfuscatePrivateNames);
+  mlir::tblgen::setStripPrivatePassMetadata(stripPrivatePassMetadata);
+  mlir::tblgen::setObfuscationSalt(obfuscationSalt);
+
   return TableGenMain(
       argv[0], [](TableGenOutputFiles &OutFiles, const RecordKeeper &RK) {
         std::string S;
diff --git a/mlir/test/mlir-tblgen/private-name-obfuscation.td b/mlir/test/mlir-tblgen/private-name-obfuscation.td
new file mode 100644
index 0000000000000..b4d076810e176
--- /dev/null
+++ b/mlir/test/mlir-tblgen/private-name-obfuscation.td
@@ -0,0 +1,164 @@
+// Verifies that --mlir-obfuscate-private replaces names of TableGen items
+// marked `let isPrivate = 1;` with deterministic opaque hashes, and leaves
+// public items unchanged.
+//
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=publicd -I %S/../../include %s \
+// RUN:   | FileCheck %s --check-prefix=PUBDD-OFF
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=publicd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=PUBDD-ON
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=privd -I %S/../../include %s \
+// RUN:   | FileCheck %s --check-prefix=PRIVDD-OFF
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=PRIVDD-ON
+// RUN: mlir-tblgen -gen-op-decls -dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=PRIVOP-ON
+// RUN: mlir-tblgen -gen-op-decls -dialect=publicd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=PUBOP-ON
+// RUN: mlir-tblgen -gen-op-decls -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=PRIVFORMAT-ON
+// RUN: mlir-tblgen -gen-attrdef-decls -attrdefs-dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=ATTR-PRIV-ON
+// RUN: mlir-tblgen -gen-attrdef-decls -attrdefs-dialect=publicd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=ATTR-PUB-ON
+// RUN: mlir-tblgen -gen-attrdef-defs -attrdefs-dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=ATTR-PRIV-DEF-ON
+// RUN: mlir-tblgen -gen-typedef-decls -typedefs-dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=TYPE-PRIV-ON
+// RUN: mlir-tblgen -gen-typedef-decls -typedefs-dialect=publicd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=TYPE-PUB-ON
+// RUN: mlir-tblgen -gen-typedef-defs -typedefs-dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=TYPE-PRIV-DEF-ON
+//
+// Determinism: rerunning produces identical output for the same salt.
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private --mlir-obfuscation-salt=cafebabe > %t.first
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private --mlir-obfuscation-salt=cafebabe > %t.second
+// RUN: cmp %t.first %t.second
+//
+// Different salt produces different output.
+// RUN: mlir-tblgen -gen-dialect-decls -dialect=privd -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private --mlir-obfuscation-salt=00112233 > %t.alt
+// RUN: not cmp %t.first %t.alt
+
+include "mlir/IR/OpBase.td"
+include "mlir/IR/AttrTypeBase.td"
+
+def PublicDialect : Dialect {
+  let name = "publicd";
+  let cppNamespace = "::publicd";
+}
+
+def PrivateDialect : Dialect {
+  let name = "privd";
+  let cppNamespace = "::privd";
+  let isPrivate = 1;
+}
+
+def Public_AddOp : Op<PublicDialect, "add", []>;
+def Public_PublicFormatOp : Op<PublicDialect, "format", []> {
+  let assemblyFormat = "attr-dict";
+}
+def Private_MulOp : Op<PrivateDialect, "mul", []>;
+def Private_PrivateFormatOp : Op<PrivateDialect, "format", []> {
+  let assemblyFormat = "attr-dict";
+}
+
+def Public_PublicAttr : AttrDef<PublicDialect, "Public"> {
+  let mnemonic = "public_attr";
+  let genMnemonicAlias = 1;
+}
+def Private_PrivateAttr : AttrDef<PrivateDialect, "Private"> {
+  let mnemonic = "private_attr";
+  let genMnemonicAlias = 1;
+}
+
+def Public_PublicType : TypeDef<PublicDialect, "Public"> {
+  let mnemonic = "public_type";
+  let genMnemonicAlias = 1;
+}
+def Private_PrivateType : TypeDef<PrivateDialect, "Private"> {
+  let mnemonic = "private_type";
+  let genMnemonicAlias = 1;
+}
+
+// Public dialect, obfuscation off: original namespace.
+// PUBDD-OFF: getDialectNamespace()
+// PUBDD-OFF: StringLiteral("publicd")
+
+// Public dialect, obfuscation on: still original namespace.
+// PUBDD-ON: getDialectNamespace()
+// PUBDD-ON: StringLiteral("publicd")
+
+// Private dialect, obfuscation off: original namespace.
+// PRIVDD-OFF: getDialectNamespace()
+// PRIVDD-OFF: StringLiteral("privd")
+
+// Private dialect, obfuscation on: opaque underscore-prefixed identifier.
+// PRIVDD-ON: getDialectNamespace()
+// PRIVDD-ON: StringLiteral("_{{[a-z0-9]+}}")
+// PRIVDD-ON-NOT: StringLiteral("privd")
+
+// Op of private dialect, obfuscation on: both halves obfuscated.
+// PRIVOP-ON: getOperationName()
+// PRIVOP-ON: StringLiteral("_{{[a-z0-9]+}}._{{[a-z0-9]+}}")
+// PRIVOP-ON-NOT: StringLiteral("privd.mul")
+
+// Op of public dialect, obfuscation on: unchanged.
+// PUBOP-ON: getOperationName()
+// PUBOP-ON: StringLiteral("publicd.add")
+
+// Declarative assembly formats of private ops are not generated under private
+// obfuscation. They therefore fall back to generic printing and reject the
+// custom textual syntax.
+// PRIVFORMAT-ON-LABEL: class PrivateFormatOp : public
+// PRIVFORMAT-ON: _{{[a-z0-9]+}}._{{[a-z0-9]+}}
+// PRIVFORMAT-ON-NOT: static ::mlir::ParseResult parse
+// PRIVFORMAT-ON-NOT: void print
+// PRIVFORMAT-ON-LABEL: class PublicFormatOp : public
+// PRIVFORMAT-ON: publicd.format
+// PRIVFORMAT-ON: static ::mlir::ParseResult parse
+// PRIVFORMAT-ON: void print
+
+// Attribute names and mnemonics of private dialects are obfuscated, while
+// public attributes remain unchanged.
+// ATTR-PRIV-ON-LABEL: class PrivateAttr : public
+// ATTR-PRIV-ON: static constexpr ::llvm::StringLiteral name = "_{{[a-z0-9]+}}._{{[a-z0-9]+}}";
+// ATTR-PRIV-ON: static constexpr ::llvm::StringLiteral dialectName = "_{{[a-z0-9]+}}";
+// ATTR-PRIV-ON: getMnemonic()
+// ATTR-PRIV-ON: return {"_{{[a-z0-9]+}}"};
+// ATTR-PRIV-ON-NOT: private_attr
+// ATTR-PRIV-DEF-ON: os << "_{{[a-z0-9]+}}";
+// ATTR-PRIV-DEF-ON-NOT: private_attr
+// ATTR-PUB-ON-LABEL: class PublicAttr : public
+// ATTR-PUB-ON: static constexpr ::llvm::StringLiteral name = "publicd.public_attr";
+// ATTR-PUB-ON: static constexpr ::llvm::StringLiteral dialectName = "publicd";
+// ATTR-PUB-ON: getMnemonic()
+// ATTR-PUB-ON: return {"public_attr"};
+
+// Type names and mnemonics of private dialects are obfuscated, while public
+// types remain unchanged.
+// TYPE-PRIV-ON-LABEL: class PrivateType : public
+// TYPE-PRIV-ON: static constexpr ::llvm::StringLiteral name = "_{{[a-z0-9]+}}._{{[a-z0-9]+}}";
+// TYPE-PRIV-ON: static constexpr ::llvm::StringLiteral dialectName = "_{{[a-z0-9]+}}";
+// TYPE-PRIV-ON: getMnemonic()
+// TYPE-PRIV-ON: return {"_{{[a-z0-9]+}}"};
+// TYPE-PRIV-ON-NOT: private_type
+// TYPE-PRIV-DEF-ON: os << "_{{[a-z0-9]+}}";
+// TYPE-PRIV-DEF-ON-NOT: private_type
+// TYPE-PUB-ON-LABEL: class PublicType : public
+// TYPE-PUB-ON: static constexpr ::llvm::StringLiteral name = "publicd.public_type";
+// TYPE-PUB-ON: static constexpr ::llvm::StringLiteral dialectName = "publicd";
+// TYPE-PUB-ON: getMnemonic()
+// TYPE-PUB-ON: return {"public_type"};
diff --git a/mlir/test/mlir-tblgen/private-pass-strip.td b/mlir/test/mlir-tblgen/private-pass-strip.td
new file mode 100644
index 0000000000000..4a7f6913face1
--- /dev/null
+++ b/mlir/test/mlir-tblgen/private-pass-strip.td
@@ -0,0 +1,75 @@
+// Verifies that --mlir-obfuscate-private and --mlir-strip-private-pass-metadata
+// rewrite or drop pass metadata for passes marked `let isPrivate = 1;`.
+//
+// RUN: mlir-tblgen -gen-pass-decls -name=Test -I %S/../../include %s \
+// RUN:   | FileCheck %s --check-prefix=BASELINE
+// RUN: mlir-tblgen -gen-pass-decls -name=Test -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private \
+// RUN:   | FileCheck %s --check-prefix=OBFUSCATED
+// RUN: mlir-tblgen -gen-pass-decls -name=Test -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private --mlir-strip-private-pass-metadata \
+// RUN:   | FileCheck %s --check-prefix=STRIPPED
+// RUN: mlir-tblgen -gen-pass-capi-header -prefix=Test -I %S/../../include %s \
+// RUN:     --mlir-obfuscate-private --mlir-strip-private-pass-metadata \
+// RUN:   | FileCheck %s --check-prefix=CAPI
+
+include "mlir/Pass/PassBase.td"
+
+def PublicTestPass : Pass<"public-test-pass"> {
+  let summary = "Public pass summary";
+}
+
+def PrivateTestPass : Pass<"private-test-pass"> {
+  let summary = "Private pass summary";
+  let isPrivate = 1;
+  let options = [
+    Option<"opt", "private-opt", "int", "0", "Private option description.">
+  ];
+}
+
+// Per-pass blocks emerge in alphabetical order (Private then Public), and
+// registration helpers follow the same order.
+//
+// Baseline (no flags): names and descriptions are emitted verbatim, both
+// passes are registered.
+// BASELINE: getArgument() const override { return "private-test-pass"; }
+// BASELINE: getDescription() const override { return R"PD(Private pass summary)PD"; }
+// BASELINE: getName() const override { return "PrivateTestPass"; }
+// BASELINE: ::llvm::cl::desc(R"PO(Private option description.)PO")
+// BASELINE: getArgument() const override { return "public-test-pass"; }
+// BASELINE: getDescription() const override { return R"PD(Public pass summary)PD"; }
+// BASELINE: getName() const override { return "PublicTestPass"; }
+// BASELINE: inline void registerPrivateTestPass()
+// BASELINE: inline void registerPublicTestPass()
+
+// Obfuscated only: public pass unchanged; private pass keeps its description
+// and registration, but its argument and name are opaque.
+// OBFUSCATED: getArgument() const override { return "_{{[a-z0-9]+}}"; }
+// OBFUSCATED: getDescription() const override { return R"PD(Private pass summary)PD"; }
+// OBFUSCATED: getName() const override { return "_{{[a-z0-9]+}}"; }
+// OBFUSCATED: getArgument() const override { return "public-test-pass"; }
+// OBFUSCATED: getName() const override { return "PublicTestPass"; }
+// OBFUSCATED-NOT: return "private-test-pass"
+// OBFUSCATED-NOT: return "PrivateTestPass"
+// OBFUSCATED: inline void registerPrivateTestPass()
+// OBFUSCATED: inline void registerPublicTestPass()
+
+// Stripped + obfuscated: public unchanged; private description, option
+// description, argument string, and registration helper are all gone.
+// STRIPPED: getArgument() const override { return ""; }
+// STRIPPED: getDescription() const override { return R"PD()PD"; }
+// STRIPPED: getName() const override { return "_{{[a-z0-9]+}}"; }
+// STRIPPED: ::llvm::cl::desc(R"PO()PO")
+// STRIPPED: getArgument() const override { return "public-test-pass"; }
+// STRIPPED: getName() const override { return "PublicTestPass"; }
+// STRIPPED-NOT: Private pass summary
+// STRIPPED-NOT: Private option description.
+// STRIPPED-NOT: registerPrivateTestPass
+// STRIPPED: inline void registerPublicTestPass()
+
+// With stripping, the public C-API entry points are still emitted while
+// the private ones disappear from the generated header.
+// CAPI: mlirCreateTestPublicTestPass
+// CAPI: mlirRegisterTestPublicTestPass
+// CAPI-NOT: mlirCreateTestPrivateTestPass
+// CAPI-NOT: mlirRegisterTestPrivateTestPass
diff --git a/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp b/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
index 64f35e7fef6d3..fcd3b81de8df3 100644
--- a/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
+++ b/mlir/tools/mlir-tblgen/AttrOrTypeDefGen.cpp
@@ -13,6 +13,7 @@
 #include "mlir/TableGen/Format.h"
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/Interfaces.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/CommandLine.h"
@@ -328,15 +329,20 @@ void DefGen::emitName() {
     auto *typeDef = cast<TypeDef>(&def);
     name = typeDef->getTypeName();
   }
+  // The full name is "dialect.mnemonic"; obfuscate dialect and mnemonic
+  // halves independently so that runtime parsing of the dialect prefix still
+  // works.
   std::string nameDecl =
-      strfmt("static constexpr ::llvm::StringLiteral name = \"{0}\";\n", name);
+      strfmt("static constexpr ::llvm::StringLiteral name = \"{0}\";\n",
+             tblgen::maybeObfuscateDotted(name, def.isPrivate()));
   defCls.declare<ExtraClassDeclaration>(std::move(nameDecl));
 }
 
 void DefGen::emitDialectName() {
   std::string decl =
       strfmt("static constexpr ::llvm::StringLiteral dialectName = \"{0}\";\n",
-             def.getDialect().getName());
+             tblgen::maybeObfuscate(def.getDialect().getName(),
+                                    def.getDialect().isPrivate()));
   defCls.declare<ExtraClassDeclaration>(std::move(decl));
 }
 
@@ -448,7 +454,9 @@ void DefGen::emitInvariantsVerifier(bool hasImpl, bool hasCustomVerifier) {
 void DefGen::emitParserPrinter() {
   auto *mnemonic = defCls.addStaticMethod<Method::Constexpr>(
       "::llvm::StringLiteral", "getMnemonic");
-  mnemonic->body().indent() << strfmt("return {\"{0}\"};", *def.getMnemonic());
+  mnemonic->body().indent() << strfmt(
+      "return {\"{0}\"};",
+      tblgen::maybeObfuscate(*def.getMnemonic(), def.isPrivate()));
 
   // Declare the parser and printer, if needed.
   bool hasAssemblyFormat = def.getAssemblyFormat().has_value();
@@ -705,7 +713,9 @@ void DefGen::emitMnemonicAliasMethod() {
   SmallVector<MethodParameter> params{{"::llvm::raw_ostream &", "os"}};
   Method *m = defCls.addMethod<Method::Const>("::mlir::OpAsmAliasResult",
                                               "getAlias", std::move(params));
-  m->body().indent() << strfmt("os << \"{0}\";\n", *def.getMnemonic())
+  m->body().indent() << strfmt(
+      "os << \"{0}\";\n",
+      tblgen::maybeObfuscate(*def.getMnemonic(), def.isPrivate()))
                      << "return ::mlir::OpAsmAliasResult::OverridableAlias;\n";
 }
 
diff --git a/mlir/tools/mlir-tblgen/DialectGen.cpp b/mlir/tools/mlir-tblgen/DialectGen.cpp
index 8eecad39f49f3..dd90efc731aeb 100644
--- a/mlir/tools/mlir-tblgen/DialectGen.cpp
+++ b/mlir/tools/mlir-tblgen/DialectGen.cpp
@@ -18,6 +18,7 @@
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/Interfaces.h"
 #include "mlir/TableGen/Operator.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "mlir/TableGen/Trait.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/StringExtras.h"
@@ -250,7 +251,9 @@ static void emitDialectDecl(Dialect &dialect, raw_ostream &os) {
     tblgen::emitSummaryAndDescComments(os, dialect.getSummary(),
                                        dialect.getDescription(),
                                        /*terminateCmment=*/false);
-    os << llvm::formatv(dialectDeclBeginStr, cppName, dialect.getName(),
+    StringRef emittedDialectName =
+        maybeObfuscate(dialect.getName(), dialect.isPrivate());
+    os << llvm::formatv(dialectDeclBeginStr, cppName, emittedDialectName,
                         superClassName);
 
     // If the dialect requested the default attribute printer and parser, emit
@@ -289,7 +292,7 @@ static void emitDialectDecl(Dialect &dialect, raw_ostream &os) {
           attrPair.first, /*capitalizeFirst=*/false);
       os << llvm::formatv(discardableAttrHelperDecl, camelNameUpper,
                           attrPair.first, attrPair.second, camelName,
-                          dialect.getName());
+                          emittedDialectName);
     }
 
     if (std::optional<StringRef> extraDecl = dialect.getExtraClassDeclaration())
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index 2cb47d084ce69..0a0a25c8cb021 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -24,6 +24,7 @@
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/Interfaces.h"
 #include "mlir/TableGen/Operator.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "mlir/TableGen/Property.h"
 #include "mlir/TableGen/Region.h"
 #include "mlir/TableGen/SideEffects.h"
@@ -273,6 +274,14 @@ static std::string getArgumentName(const Operator &op, int index) {
   return std::string(formatv("{0}_{1}", generatedArgName, index));
 }
 
+/// Returns true if a private op should behave as if it did not specify any
+/// custom or declarative assembly format. The op remains registered and can
+/// still be printed in generic form, but its custom textual syntax is not
+/// accepted in private-name obfuscation builds.
+static bool shouldStripPrivateAssemblyFormat(const Operator &op) {
+  return op.isPrivate() && tblgen::obfuscatePrivateNamesEnabled();
+}
+
 // Returns true if we can use unwrapped value for the given `attr` in builders.
 static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {
   return attr.getReturnType() != attr.getStorageType() &&
@@ -381,7 +390,9 @@ class OpOrAdaptorHelper {
     return [this](raw_ostream &os) -> raw_ostream & {
       if (emitForOp)
         return os << "emitOpError(\"";
-      return os << formatv("emitError(loc, \"'{0}' op ", op.getOperationName());
+      return os << formatv("emitError(loc, \"'{0}' op ",
+                           tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                                        op.isPrivate()));
     };
   }
 
@@ -1168,7 +1179,8 @@ OpEmitter::OpEmitter(const Operator &op,
   genFolderDecls();
   genTypeInterfaceMethods();
   genOpInterfaceMethods();
-  generateOpFormat(op, opClass, emitHelper.hasProperties());
+  if (!shouldStripPrivateAssemblyFormat(op))
+    generateOpFormat(op, opClass, emitHelper.hasProperties());
   genSideEffectInterfaceMethods();
 }
 void OpEmitter::emitDecl(
@@ -3835,6 +3847,9 @@ void OpEmitter::genTypeInterfaceMethods() {
 }
 
 void OpEmitter::genParser() {
+  if (shouldStripPrivateAssemblyFormat(op))
+    return;
+
   if (hasStringAttribute(def, "assemblyFormat"))
     return;
 
@@ -3851,6 +3866,9 @@ void OpEmitter::genParser() {
 }
 
 void OpEmitter::genPrinter() {
+  if (shouldStripPrivateAssemblyFormat(op))
+    return;
+
   if (hasStringAttribute(def, "assemblyFormat"))
     return;
 
@@ -4160,7 +4178,9 @@ void OpEmitter::genOpNameGetter() {
   auto *method = opClass.addStaticMethod<Method::Constexpr>(
       "::llvm::StringLiteral", "getOperationName");
   ERROR_IF_PRUNED(method, "getOperationName", op);
-  method->body() << "  return ::llvm::StringLiteral(\"" << op.getOperationName()
+  method->body() << "  return ::llvm::StringLiteral(\""
+                 << tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                                 op.isPrivate())
                  << "\");";
 }
 
@@ -4427,7 +4447,7 @@ OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(
     body.indent() << "if (odsAttrs)\n";
     body.indent() << formatv(
         "odsOpName.emplace(\"{0}\", odsAttrs.getContext());\n",
-        op.getOperationName());
+        tblgen::maybeObfuscateDotted(op.getOperationName(), op.isPrivate()));
 
     paramList.insert(paramList.begin(), MethodParameter("RangeT", "values"));
     auto *constructor = genericAdaptor.addConstructor(paramList);
diff --git a/mlir/tools/mlir-tblgen/PassCAPIGen.cpp b/mlir/tools/mlir-tblgen/PassCAPIGen.cpp
index 8c13c9b031335..ac123597c13a8 100644
--- a/mlir/tools/mlir-tblgen/PassCAPIGen.cpp
+++ b/mlir/tools/mlir-tblgen/PassCAPIGen.cpp
@@ -12,6 +12,7 @@
 
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/Pass.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -57,6 +58,14 @@ const char *const fileFooter = R"(
 #endif
 )";
 
+/// Returns true if `pass` should not be exposed via the C API. Private passes
+/// are skipped from the per-pass `mlirCreate*`/`mlirRegister*` entry points
+/// when `--mlir-strip-private-pass-metadata` is set, so they cannot be
+/// invoked from outside C++ code.
+static bool shouldSkipCAPI(const Pass &pass) {
+  return pass.isPrivate() && stripPrivatePassMetadataEnabled();
+}
+
 /// Emit TODO
 static bool emitCAPIHeader(const RecordKeeper &records, raw_ostream &os) {
   os << fileHeader;
@@ -65,6 +74,8 @@ static bool emitCAPIHeader(const RecordKeeper &records, raw_ostream &os) {
      << "Passes(void);\n\n";
   for (const auto *def : records.getAllDerivedDefinitions("PassBase")) {
     Pass pass(def);
+    if (shouldSkipCAPI(pass))
+      continue;
     StringRef defName = pass.getDef()->getName();
     os << formatv(passDecl, groupName, defName);
   }
@@ -99,6 +110,8 @@ static bool emitCAPIImpl(const RecordKeeper &records, raw_ostream &os) {
 
   for (const auto *def : records.getAllDerivedDefinitions("PassBase")) {
     Pass pass(def);
+    if (shouldSkipCAPI(pass))
+      continue;
     StringRef defName = pass.getDef()->getName();
 
     std::string constructorCall;
diff --git a/mlir/tools/mlir-tblgen/PassGen.cpp b/mlir/tools/mlir-tblgen/PassGen.cpp
index e4ae78f022405..63de05eda4ea0 100644
--- a/mlir/tools/mlir-tblgen/PassGen.cpp
+++ b/mlir/tools/mlir-tblgen/PassGen.cpp
@@ -13,6 +13,7 @@
 
 #include "mlir/TableGen/GenInfo.h"
 #include "mlir/TableGen/Pass.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -147,16 +148,30 @@ static void emitPassDecls(const Pass &pass, raw_ostream &os) {
   os << "#endif // " << enableVarName << "\n";
 }
 
+/// Returns true if all per-pass registration helpers and CLI/C-API exposure
+/// for `pass` should be skipped. This is the case for passes marked
+/// `isPrivate` when `--mlir-strip-private-pass-metadata` is set: such passes
+/// can only be created by C++ code, not invoked via a textual pass pipeline
+/// or via the C API.
+static bool shouldSkipRegistration(const Pass &pass) {
+  return pass.isPrivate() && stripPrivatePassMetadataEnabled();
+}
+
 /// Emit the code for registering each of the given passes with the global
 /// PassRegistry.
 static void emitRegistrations(llvm::ArrayRef<Pass> passes, raw_ostream &os) {
   os << "#ifdef GEN_PASS_REGISTRATION\n";
   os << "// Generate registrations for all passes.\n";
-  for (const Pass &pass : passes)
+  for (const Pass &pass : passes) {
+    if (shouldSkipRegistration(pass))
+      continue;
     os << "#define " << getPassRegistrationVarName(pass) << "\n";
+  }
   os << "#endif // GEN_PASS_REGISTRATION\n";
 
   for (const Pass &pass : passes) {
+    if (shouldSkipRegistration(pass))
+      continue;
     std::string passName = pass.getDef()->getName().str();
     std::string passEnableVarName = getPassRegistrationVarName(pass);
 
@@ -172,8 +187,11 @@ static void emitRegistrations(llvm::ArrayRef<Pass> passes, raw_ostream &os) {
   os << "#ifdef GEN_PASS_REGISTRATION\n";
   os << formatv(passGroupRegistrationCode, groupName);
 
-  for (const Pass &pass : passes)
+  for (const Pass &pass : passes) {
+    if (shouldSkipRegistration(pass))
+      continue;
     os << "  register" << pass.getDef()->getName() << "();\n";
+  }
 
   os << "}\n";
   os << "#undef GEN_PASS_REGISTRATION\n";
@@ -186,11 +204,14 @@ static void emitRegistrations(llvm::ArrayRef<Pass> passes, raw_ostream &os) {
 
 /// The code snippet used to generate the start of a pass base class.
 ///
-/// {0}: The def name of the pass record.
+/// {0}: The def name of the pass record (used as the C++ class identifier).
 /// {1}: The base class for the pass.
-/// {2): The command line argument for the pass.
-/// {3}: The summary for the pass.
+/// {2}: The command line argument for the pass (possibly obfuscated).
+/// {3}: The summary for the pass (possibly emptied for private passes).
 /// {4}: The dependent dialects registration.
+/// {5}: The display name returned by `getName()` / `getPassName()` (possibly
+///      obfuscated). Distinct from {0} so the C++ class identifier remains
+///      stable when the displayed name is obfuscated.
 const char *const baseClassBegin = R"(
 template <typename DerivedT>
 class {0}Base : public {1} {
@@ -214,9 +235,9 @@ class {0}Base : public {1} {
 
   /// Returns the derived pass name.
   static constexpr ::llvm::StringLiteral getPassName() {
-    return ::llvm::StringLiteral("{0}");
+    return ::llvm::StringLiteral("{5}");
   }
-  ::llvm::StringRef getName() const override { return "{0}"; }
+  ::llvm::StringRef getName() const override { return "{5}"; }
 
   /// Support isa/dyn_cast functionality for the derived pass class.
   static bool classof(const ::mlir::Pass *pass) {{
@@ -282,13 +303,16 @@ std::unique_ptr<::mlir::Pass> create{0}({0}Options options) {{
 
 /// Emit the declarations for each of the pass options.
 static void emitPassOptionDecls(const Pass &pass, raw_ostream &os) {
+  bool stripDescriptions =
+      pass.isPrivate() && stripPrivatePassMetadataEnabled();
   for (const PassOption &opt : pass.getOptions()) {
     os.indent(2) << "::mlir::Pass::"
                  << (opt.isListOption() ? "ListOption" : "Option");
 
+    StringRef desc = stripDescriptions ? StringRef("") : opt.getDescription();
     os << formatv(R"(<{0}> {1}{{*this, "{2}", ::llvm::cl::desc(R"PO({3})PO"))",
                   opt.getType(), opt.getCppVariableName(), opt.getArgument(),
-                  opt.getDescription().trim());
+                  desc.trim());
     if (std::optional<StringRef> defaultVal = opt.getDefaultValue())
       os << ", ::llvm::cl::init(" << defaultVal << ")";
     if (std::optional<StringRef> additionalFlags = opt.getAdditionalFlags())
@@ -299,11 +323,13 @@ static void emitPassOptionDecls(const Pass &pass, raw_ostream &os) {
 
 /// Emit the declarations for each of the pass statistics.
 static void emitPassStatisticDecls(const Pass &pass, raw_ostream &os) {
+  bool stripDescriptions =
+      pass.isPrivate() && stripPrivatePassMetadataEnabled();
   for (const PassStatistic &stat : pass.getStatistics()) {
+    StringRef desc = stripDescriptions ? StringRef("") : stat.getDescription();
     os << formatv(
         "  ::mlir::Pass::Statistic {0}{{this, \"{1}\", R\"PS({2})PS\"};\n",
-        stat.getCppVariableName(), stat.getName(),
-        stat.getDescription().trim());
+        stat.getCppVariableName(), stat.getName(), desc.trim());
   }
 }
 
@@ -334,10 +360,26 @@ static void emitPassDefs(const Pass &pass, raw_ostream &os) {
         "\n    ");
   }
 
+  // Privacy-aware substitutions for the base class template.
+  // - The display name returned by `getName()` / `getPassName()` is
+  //   obfuscated when `--mlir-obfuscate-private` is set and the pass is
+  //   marked `isPrivate`.
+  // - The CLI argument and summary are dropped when
+  //   `--mlir-strip-private-pass-metadata` is set and the pass is private,
+  //   since those passes are also omitted from CLI/C-API registration.
+  std::string displayName =
+      tblgen::maybeObfuscate(passName, pass.isPrivate()).str();
+  std::string argument =
+      tblgen::maybeObfuscate(pass.getArgument(), pass.isPrivate()).str();
+  std::string summary = pass.getSummary().trim().str();
+  if (pass.isPrivate() && stripPrivatePassMetadataEnabled()) {
+    argument.clear();
+    summary.clear();
+  }
+
   os << "namespace impl {\n";
-  os << formatv(baseClassBegin, passName, pass.getBaseClass(),
-                pass.getArgument(), pass.getSummary().trim(),
-                dependentDialectRegistrations);
+  os << formatv(baseClassBegin, passName, pass.getBaseClass(), argument,
+                summary, dependentDialectRegistrations, displayName);
 
   if (ArrayRef<PassOption> options = pass.getOptions(); !options.empty()) {
     os.indent(2) << formatv("{0}Base({0}Options options) : {0}Base() {{\n",
diff --git a/mlir/tools/mlir-tblgen/RewriterGen.cpp b/mlir/tools/mlir-tblgen/RewriterGen.cpp
index e3043708a46d1..6314046f4c6e8 100644
--- a/mlir/tools/mlir-tblgen/RewriterGen.cpp
+++ b/mlir/tools/mlir-tblgen/RewriterGen.cpp
@@ -19,6 +19,7 @@
 #include "mlir/TableGen/Operator.h"
 #include "mlir/TableGen/Pattern.h"
 #include "mlir/TableGen/Predicate.h"
+#include "mlir/TableGen/PrivateName.h"
 #include "mlir/TableGen/Property.h"
 #include "mlir/TableGen/Type.h"
 #include "llvm/ADT/FunctionExtras.h"
@@ -723,13 +724,15 @@ void PatternEmitter::emitOperandMatch(DagNode tree, StringRef opName,
       }
       auto self = formatv("(*{0}.begin()).getType()", operandName);
       StringRef verifier = staticMatcherHelper.getVerifierName(operandMatcher);
-      emitStaticVerifierCall(
-          verifier, opName, self.str(),
-          formatv(
-              "\"operand {0} of op '{1}' failed to satisfy constraint: '{2}'\"",
-              operandIndex, op.getOperationName(),
-              escapeString(constraint.getSummary()))
-              .str());
+    emitStaticVerifierCall(
+        verifier, opName, self.str(),
+        formatv(
+            "\"operand {0} of op '{1}' failed to satisfy constraint: '{2}'\"",
+            operandIndex,
+            tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                         op.isPrivate()),
+            escapeString(constraint.getSummary()))
+            .str());
     }
   }
 
@@ -911,8 +914,9 @@ void PatternEmitter::emitAttributeMatch(DagNode tree, StringRef castedName,
     emitMatchCheck(castedName, tgfmt("tblgen_attr", &fmtCtx),
                    formatv("\"expected op '{0}' to have attribute '{1}' "
                            "of type '{2}'\"",
-                           op.getOperationName(), namedAttr->name,
-                           attr.getStorageType()));
+                           tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                                        op.isPrivate()),
+                           namedAttr->name, attr.getStorageType()));
   }
 
   auto matcher = tree.getArgAsLeaf(argIndex);
@@ -942,7 +946,9 @@ void PatternEmitter::emitAttributeMatch(DagNode tree, StringRef castedName,
         verifier, castedName, "tblgen_attr",
         formatv("\"op '{0}' attribute '{1}' failed to satisfy constraint: "
                 "'{2}'\"",
-                op.getOperationName(), namedAttr->name,
+                tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                             op.isPrivate()),
+                namedAttr->name,
                 escapeString(matcher.getAsConstraint().getSummary()))
             .str());
   }
@@ -982,7 +988,9 @@ void PatternEmitter::emitPropertyMatch(DagNode tree, StringRef castedName,
         verifier, castedName, "tblgen_prop",
         formatv("\"op '{0}' property '{1}' failed to satisfy constraint: "
                 "'{2}'\"",
-                op.getOperationName(), namedProp->name,
+                tblgen::maybeObfuscateDotted(op.getOperationName(),
+                                             op.isPrivate()),
+                namedProp->name,
                 escapeString(matcher.getAsConstraint().getSummary()))
             .str());
   }



More information about the Mlir-commits mailing list