Author: Bruno Cardoso Lopes
Date: 2026-10-10T01:57:57Z
New Revision: fa70420c78df1aff63387108f8c357cb0cb8b99c

URL: 
https://github.com/llvm/llvm-project/commit/fa70420c78df1aff63387108f8c357cb0cb8b99c
DIFF: 
https://github.com/llvm/llvm-project/commit/fa70420c78df1aff63387108f8c357cb0cb8b99c.diff

LOG: [CIR] Add bytecode encodings for attributes (#229586)

Without a BytecodeDialectInterface, MLIR encodes a dialect's attributes
through their assembly format, embedding the printed text in the
bytecode. This adds native encodings for 27 CIR attributes (constants,
constant initializers, global views, method/data-member pointers, C++
special-member attributes, and small metadata attributes), modelled on
the LLVM dialect's bytecode support.

On attribute-dense modules this shrinks the bytecode ~30-40% (8.6KB to
5.4KB across the new tests). The room ahead is huge, emitting bytecode
for CIRGenModule.cpp (441MB of CIR text) still exceeds 22GB RSS and 30
minutes with these encodings (64GB and 45 minutes without), since types
keep using the assembly fallback. This is paving towards selfhosting
with bytecode.

Coverage is partial by design: anything not listed keeps using the
assembly-format fallback, exactly as before. Types keep the fallback too
until the type encodings land separately.

Tests in the new clang/test/CIR/Bytecode directory round-trip each
encoding through -verify-roundtrip and check the emitted bytecode (via
llvm-strings) contains none of the attributes' printed forms.

Added: 
    clang/include/clang/CIR/Dialect/IR/CIRDialectBytecode.td
    clang/lib/CIR/Dialect/IR/CIRDialectBytecode.cpp
    clang/lib/CIR/Dialect/IR/CIRDialectBytecode.h
    clang/test/CIR/Bytecode/attributes.cir
    clang/test/CIR/Bytecode/constants.cir
    clang/test/CIR/Bytecode/global-view.cir
    clang/test/CIR/Bytecode/special-member.cir
    clang/test/CIR/Bytecode/vtable-blockaddr.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp
    clang/lib/CIR/Dialect/IR/CMakeLists.txt
    clang/test/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index eb4cf65b919c4..1ef72ace09c2b 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -631,6 +631,12 @@ def CIR_DataMemberAttr : CIR_ValueLikeAttr<"DataMember", 
"data_member"> {
       return $_get(type.getContext(), type,
                    mlir::DenseI32ArrayAttr::get(type.getContext(), path));
     }]>,
+    // Pointer-to-data-member reusing an existing path attribute.
+    AttrBuilderWithInferredContext<(ins "cir::DataMemberType":$type,
+                                       "mlir::DenseI32ArrayAttr":$member_path),
+                                  [{
+      return $_get(type.getContext(), type, member_path);
+    }]>,
   ];
 
   // This attribute gets lowered during CXXABILowering.

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRDialectBytecode.td 
b/clang/include/clang/CIR/Dialect/IR/CIRDialectBytecode.td
new file mode 100644
index 0000000000000..cf82c54c3114d
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialectBytecode.td
@@ -0,0 +1,298 @@
+//===-- CIRDialectBytecode.td - CIR bytecode defs ----------*- tablegen 
-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the CIR bytecode reader/writer definition file.
+//
+// Modelled on mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td.
+// With the default cBuilder, parameters must match the builder's argument
+// order. Parameter names determine the generated getter names.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CIR_DIALECT_BYTECODE
+#define CIR_DIALECT_BYTECODE
+
+include "mlir/IR/BytecodeBase.td"
+
+//===----------------------------------------------------------------------===//
+// Bytecode classes for attributes and types.
+//===----------------------------------------------------------------------===//
+
+class Attr<string type> : WithType<type, Attribute>;
+
+// The reader's templated readType dyn_casts and fails cleanly on mismatch.
+class Ty<string type> : WithType<type, Type>;
+
+class OptionalAttribute<string type> :
+  WithParser <"succeeded($_reader.readOptionalAttribute($_var))",
+  WithPrinter<"$_writer.writeOptionalAttribute($_getter)",
+  WithType<type, Attribute>>>;
+
+// For a parameter declared std::optional<...> rather than nullable: absent
+// needs a flag of its own.
+class OptionalInt<string type> :
+  WithParser <"succeeded(readOptionalInt($_reader, $_var))",
+  WithBuilder<"$_args",
+  WithPrinter<"writeOptionalInt($_writer, $_getter)",
+  WithType   <"std::optional<" # type # ">">>>>;
+
+// OptionalAttribute for a parameter declared std::optional<...>. Same wire
+// format, 
diff erent C++ spelling.
+class StdOptionalAttribute<string type> :
+  WithParser <"succeeded(readStdOptionalAttribute($_reader, $_var))",
+  WithBuilder<"$_args",
+  WithPrinter<"writeStdOptionalAttribute($_writer, $_getter)",
+  WithType   <"std::optional<" # type # ">">>>>;
+
+// A scoped enum as a validated varint; see readEnum.
+class EnumParam<string name> :
+  WithParser<"succeeded(readEnum($_reader, $_var, cir::symbolize" # name # ", 
\"" # name # "\"))",
+  WithPrinter<"$_writer.writeVarInt(static_cast<uint64_t>($_getter))",
+  WithType<"cir::" # name>>>;
+
+def CtorKindParam : EnumParam<"CtorKind">;
+def AssignKindParam : EnumParam<"AssignKind">;
+
+def CheckedInt :
+  WithParser<"succeeded(readCheckedInt($_reader, $_var))",
+  WithPrinter<"$_writer.writeSignedVarInt($_getter)",
+  WithType<"int">>>;
+
+def CheckedInt32 :
+  WithParser<"succeeded(readCheckedInt($_reader, $_var))",
+  WithPrinter<"$_writer.writeSignedVarInt($_getter)",
+  WithType<"int32_t">>>;
+
+def CheckedUnsigned :
+  WithParser<"succeeded(readCheckedInt($_reader, $_var))",
+  WithPrinter<"$_writer.writeVarInt($_getter)",
+  WithType<"unsigned">>>;
+
+def OptInfoAttr : DialectAttribute<(attr
+  CheckedUnsigned:$level,
+  CheckedUnsigned:$size
+)> {
+  let cBuilder = "$_resultType::getChecked([&]() { return reader.emitError(); 
}, context, $_args)";
+}
+
+// Negative offsets are legitimate.
+def AddressPointAttr : DialectAttribute<(attr
+  CheckedInt32:$index,
+  CheckedInt32:$offset
+)>;
+
+def OpenCLVersionAttr : DialectAttribute<(attr
+  CheckedInt32:$major,
+  CheckedInt32:$minor
+)>;
+
+def BlockAddrInfoAttr : DialectAttribute<(attr
+  Ty<"cir::PointerType">:$type,
+  Attr<"mlir::FlatSymbolRefAttr">:$func,
+  Attr<"mlir::StringAttr">:$label
+)>;
+
+def BitfieldInfoAttr : DialectAttribute<(attr
+  Attr<"mlir::StringAttr">:$name,
+  Type:$storage_type,
+  VarInt:$size,
+  VarInt:$offset,
+  Bool:$is_signed
+)>;
+
+//===----------------------------------------------------------------------===//
+// Constants
+//===----------------------------------------------------------------------===//
+//
+// Every literal in a function body is one of these, so each fallback
+// occurrence costs its full printed form.
+
+// The payload width is implied by the type and not stored twice, mirroring
+// builtin's IntegerAttr / FloatAttr.
+def IntAttr : DialectAttribute<(attr
+  Type:$type,
+  KnownWidthAPInt<"type">:$value
+)> {
+  let cBuilder = "get<$_resultType>(context, 
mlir::cast<cir::IntTypeInterface>(type), *value)";
+}
+
+def FPAttr : DialectAttribute<(attr
+  Type:$type,
+  KnownSemanticsAPFloat<"type">:$value
+)> {
+  let cBuilder = "get<$_resultType>(context, 
mlir::cast<cir::FPTypeInterface>(type), *value)";
+}
+
+// BoolType is a singleton, so the type is rebuilt on read rather than stored.
+def BoolAttr : DialectAttribute<(attr
+  Bool:$value
+)> {
+  let cBuilder = "get<$_resultType>(context, cir::BoolType::get(context), 
value)";
+}
+
+def ConstPtrAttr : DialectAttribute<(attr
+  Ty<"cir::PointerType">:$type,
+  Attr<"mlir::IntegerAttr">:$value
+)>;
+
+//===----------------------------------------------------------------------===//
+// Parameterless marker attributes
+//===----------------------------------------------------------------------===//
+//
+// No payload: each costs a kind index instead of its mnemonic.
+
+def CatchAllAttr : DialectAttribute<(attr)>;
+def UnwindAttr : DialectAttribute<(attr)>;
+
+//===----------------------------------------------------------------------===//
+// Constant initializers
+//===----------------------------------------------------------------------===//
+//
+// Each occurrence embeds its self type's full printed form; encoded, it
+// writes a type-table reference instead.
+
+def ZeroAttr : DialectAttribute<(attr
+  Type:$type
+)>;
+
+def UndefAttr : DialectAttribute<(attr
+  Type:$type
+)>;
+
+def PoisonAttr : DialectAttribute<(attr
+  Type:$type
+)>;
+
+def ConstArrayAttr : DialectAttribute<(attr
+  Type:$type,
+  Attribute:$elts,
+  CheckedInt:$trailingZerosNum
+)> {
+  let cBuilder = "$_resultType::getChecked([&]() { return reader.emitError(); 
}, context, $_args)";
+}
+
+def ConstRecordAttr : DialectAttribute<(attr
+  Type:$type,
+  Attr<"mlir::ArrayAttr">:$members
+)> {
+  let cBuilder = "$_resultType::getChecked([&]() { return reader.emitError(); 
}, context, $_args)";
+}
+
+// Each carries the aggregate type, which the fallback prints fully expanded.
+def CXXCtorAttr : DialectAttribute<(attr
+  Type:$type,
+  CtorKindParam:$ctorKind,
+  Bool:$isTrivial
+)>;
+
+def CXXDtorAttr : DialectAttribute<(attr
+  Type:$type,
+  Bool:$isTrivial
+)>;
+
+def CXXAssignAttr : DialectAttribute<(attr
+  Type:$type,
+  AssignKindParam:$assignKind,
+  Bool:$isTrivial
+)>;
+
+def VTableAttr : DialectAttribute<(attr
+  Type:$type,
+  Attr<"mlir::ArrayAttr">:$data
+)>;
+
+def BlockAddrDiffAttr : DialectAttribute<(attr
+  Ty<"cir::IntType">:$type,
+  Attr<"mlir::FlatSymbolRefAttr">:$func,
+  Attr<"mlir::StringAttr">:$lhs_label,
+  Attr<"mlir::StringAttr">:$rhs_label
+)>;
+
+// Containers only pay off once their nested attributes are encoded too.
+def GlobalViewAttr : DialectAttribute<(attr
+  Type:$type,
+  Attr<"mlir::FlatSymbolRefAttr">:$symbol,
+  OptionalAttribute<"mlir::ArrayAttr">:$indices,
+  Bool:$address_point
+)>;
+
+def TypeInfoAttr : DialectAttribute<(attr
+  Type:$type,
+  Attr<"mlir::ArrayAttr">:$data
+)>;
+
+def DynamicCastInfoAttr : DialectAttribute<(attr
+  Attr<"cir::GlobalViewAttr">:$src_rtti,
+  Attr<"cir::GlobalViewAttr">:$dest_rtti,
+  Attr<"mlir::FlatSymbolRefAttr">:$runtime_func,
+  Attr<"mlir::FlatSymbolRefAttr">:$bad_cast_func,
+  Attr<"cir::IntAttr">:$offset_hint
+)>;
+
+def ConstVectorAttr : DialectAttribute<(attr
+  Type:$type,
+  Attr<"mlir::ArrayAttr">:$elts
+)>;
+
+def ConstComplexAttr : DialectAttribute<(attr
+  Ty<"cir::ComplexType">:$type,
+  Attr<"mlir::TypedAttr">:$real,
+  Attr<"mlir::TypedAttr">:$imag
+)>;
+
+def DataMemberAttr : DialectAttribute<(attr
+  Ty<"cir::DataMemberType">:$type,
+  OptionalAttribute<"mlir::DenseI32ArrayAttr">:$member_path
+)>;
+
+def MethodAttr : DialectAttribute<(attr
+  Ty<"cir::MethodType">:$type,
+  StdOptionalAttribute<"mlir::FlatSymbolRefAttr">:$symbol,
+  OptionalInt<"uint64_t">:$vtable_offset
+)> {
+  let cBuilder = "$_resultType::getChecked([&]() { return reader.emitError(); 
}, context, $_args)";
+}
+
+def CIRDialectAttributes : DialectAttributes<"CIR"> {
+  let elems = [
+    OptInfoAttr,
+    AddressPointAttr,
+    OpenCLVersionAttr,
+    BlockAddrInfoAttr,
+    BitfieldInfoAttr,
+    IntAttr,
+    FPAttr,
+    BoolAttr,
+    ConstPtrAttr,
+    CatchAllAttr,
+    UnwindAttr,
+    ZeroAttr,
+    UndefAttr,
+    PoisonAttr,
+    ConstArrayAttr,
+    ConstRecordAttr,
+    VTableAttr,
+    BlockAddrDiffAttr,
+    CXXCtorAttr,
+    CXXDtorAttr,
+    CXXAssignAttr,
+    GlobalViewAttr,
+    TypeInfoAttr,
+    DynamicCastInfoAttr,
+    ConstVectorAttr,
+    ConstComplexAttr,
+    DataMemberAttr,
+    MethodAttr,
+    // APPEND ONLY. An entry's position in this list *is* its kind
+    // discriminator on disk, so inserting one renumbers every entry after
+    // it. No dialect version is recorded yet, so this is a same-revision
+    // scheme, not a compatibility promise.
+  ];
+}
+
+#endif // CIR_DIALECT_BYTECODE

diff  --git a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt 
b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
index 8add2f04afaf7..66fa7a40d28f7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt
@@ -23,6 +23,11 @@ mlir_tablegen(CIROpsAttributes.h.inc -gen-attrdef-decls)
 mlir_tablegen(CIROpsAttributes.cpp.inc -gen-attrdef-defs)
 add_public_tablegen_target(MLIRCIREnumsGen)
 
+# CIR dialect bytecode reader/writer tables.
+set(LLVM_TARGET_DEFINITIONS CIRDialectBytecode.td)
+mlir_tablegen(CIRDialectBytecode.cpp.inc -gen-bytecode -bytecode-dialect="CIR")
+add_public_tablegen_target(MLIRCIRDialectBytecodeIncGen)
+
 clang_tablegen(CIRLowering.inc -gen-cir-lowering
                SOURCE CIROps.td
                TARGET CIRLowering)

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index f3dc4dec729fc..80696e2f7e990 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -35,6 +35,8 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/LogicalResult.h"
 
+#include "CIRDialectBytecode.h"
+
 using namespace mlir;
 using namespace cir;
 
@@ -102,6 +104,7 @@ void cir::CIRDialect::initialize() {
 #include "clang/CIR/Dialect/IR/CIROps.cpp.inc"
       >();
   addInterfaces<CIROpAsmDialectInterface>();
+  detail::addBytecodeInterface(this);
 }
 
 Operation *cir::CIRDialect::materializeConstant(mlir::OpBuilder &builder,

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.cpp
new file mode 100644
index 0000000000000..877ab487bba44
--- /dev/null
+++ b/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.cpp
@@ -0,0 +1,208 @@
+//===- CIRDialectBytecode.cpp - CIR Bytecode Implementation 
---------------===//
+//
+// 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 "CIRDialectBytecode.h"
+#include "mlir/Bytecode/BytecodeImplementation.h"
+#include "mlir/IR/Diagnostics.h"
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+#include <limits>
+#include <type_traits>
+
+using namespace mlir;
+using namespace cir;
+
+namespace {
+
+// Both namespaces declare a BoolAttr, named unqualified below. This must live
+// in the inner namespace: at global scope it merges with the using-directives
+// and stays ambiguous.
+using cir::BoolAttr;
+
+//===--------------------------------------------------------------------===//
+// Helpers referenced by the generated readers
+//===--------------------------------------------------------------------===//
+
+// The payload width is implied by the type (mirroring BuiltinDialectBytecode)
+// and recovered here. A mismatched type fails rather than asserts: it came
+// from the same untrusted file, so it is a corrupt-input case.
+
+static LogicalResult readAPIntWithKnownWidth(DialectBytecodeReader &reader,
+                                             Type type, FailureOr<APInt> &val) 
{
+  auto intType = mlir::dyn_cast<cir::IntTypeInterface>(type);
+  if (!intType)
+    return reader.emitError()
+           << "expected a CIR integer type for cir.int, but got: " << type;
+  val = reader.readAPIntWithKnownWidth(intType.getWidth());
+  return success(succeeded(val));
+}
+
+static LogicalResult
+readAPFloatWithKnownSemantics(DialectBytecodeReader &reader, Type type,
+                              FailureOr<APFloat> &val) {
+  auto fpType = mlir::dyn_cast<cir::FPTypeInterface>(type);
+  if (!fpType)
+    return reader.emitError()
+           << "expected a CIR floating-point type for cir.fp, but got: "
+           << type;
+  val = reader.readAPFloatWithKnownSemantics(fpType.getFloatSemantics());
+  return success(succeeded(val));
+}
+
+// std::optional<T> parameters carry presence as a varint of its own ahead of
+// the value, so the full width survives.
+template <typename EntryTy>
+static LogicalResult readOptionalInt(DialectBytecodeReader &reader,
+                                     std::optional<EntryTy> &storage) {
+  static_assert(std::is_unsigned_v<EntryTy>,
+                "EntryTy must be unsigned: only unsigned varints are "
+                "supported here, so a negative value cannot be represented");
+  uint64_t present = 0;
+  if (failed(reader.readVarInt(present)))
+    return failure();
+  if (present == 0) {
+    storage = std::nullopt;
+    return success();
+  }
+  if (present != 1)
+    return reader.emitError() << "optional integer presence flag " << present
+                              << " is neither 0 nor 1";
+  uint64_t value = 0;
+  if (failed(reader.readVarInt(value)))
+    return failure();
+  // Out-of-range values fail rather than truncating into a plausible index.
+  if (value > static_cast<uint64_t>(std::numeric_limits<EntryTy>::max()))
+    return reader.emitError() << "optional integer value " << value
+                              << " does not fit in destination type";
+  storage = static_cast<EntryTy>(value);
+  return success();
+}
+
+template <typename EntryTy>
+static void writeOptionalInt(DialectBytecodeWriter &writer,
+                             std::optional<EntryTy> storage) {
+  static_assert(std::is_unsigned_v<EntryTy>,
+                "EntryTy must be unsigned: only unsigned varints are "
+                "supported here, so a negative value cannot be represented");
+  writer.writeVarInt(storage.has_value() ? 1 : 0);
+  if (storage)
+    writer.writeVarInt(*storage);
+}
+
+// std::optional<Attr> form (cir.method's symbol). A null attribute is not a
+// valid state and normalizes to std::nullopt on the wire.
+
+template <typename AttrTy>
+static LogicalResult readStdOptionalAttribute(DialectBytecodeReader &reader,
+                                              std::optional<AttrTy> &storage) {
+  AttrTy attr;
+  if (failed(reader.readOptionalAttribute(attr)))
+    return failure();
+  if (attr)
+    storage = attr;
+  else
+    storage = std::nullopt;
+  return success();
+}
+
+template <typename AttrTy>
+static void writeStdOptionalAttribute(DialectBytecodeWriter &writer,
+                                      std::optional<AttrTy> storage) {
+  assert(!storage || *storage);
+  writer.writeOptionalAttribute(storage.value_or(AttrTy()));
+}
+
+// A scoped enum validated through its symbolizer; out-of-range values fail
+// instead of casting undefined values into the enum.
+template <typename EnumTy>
+static LogicalResult readEnum(DialectBytecodeReader &reader, EnumTy &result,
+                              std::optional<EnumTy> (*symbolize)(uint32_t),
+                              llvm::StringRef enumName) {
+  uint64_t value = 0;
+  if (failed(reader.readVarInt(value)))
+    return failure();
+  if (value > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()))
+    return reader.emitError()
+           << enumName << " value " << value << " out of range";
+  std::optional<EnumTy> kind = symbolize(static_cast<uint32_t>(value));
+  if (!kind)
+    return reader.emitError() << "invalid " << enumName << " value " << value;
+  result = *kind;
+  return success();
+}
+
+// A varint narrowed to a fixed-width integer; out-of-range values fail
+// instead of truncating.
+template <typename IntTy>
+static LogicalResult readCheckedInt(DialectBytecodeReader &reader,
+                                    IntTy &result) {
+  static_assert(std::is_integral_v<IntTy> && !std::is_same_v<IntTy, bool>);
+  if constexpr (std::is_signed_v<IntTy>) {
+    int64_t value = 0;
+    if (failed(reader.readSignedVarInt(value)))
+      return failure();
+    if (value > static_cast<int64_t>(std::numeric_limits<IntTy>::max()) ||
+        value < static_cast<int64_t>(std::numeric_limits<IntTy>::min()))
+      return reader.emitError() << "integer value " << value
+                                << " does not fit in destination type";
+    result = static_cast<IntTy>(value);
+  } else {
+    uint64_t value = 0;
+    if (failed(reader.readVarInt(value)))
+      return failure();
+    if (value > static_cast<uint64_t>(std::numeric_limits<IntTy>::max()))
+      return reader.emitError() << "integer value " << value
+                                << " does not fit in destination type";
+    result = static_cast<IntTy>(value);
+  }
+  return success();
+}
+
+//===--------------------------------------------------------------------===//
+// Tablegen generated bytecode functions
+//===--------------------------------------------------------------------===//
+
+#include "clang/CIR/Dialect/IR/CIRDialectBytecode.cpp.inc"
+
+//===--------------------------------------------------------------------===//
+// CIRDialectBytecodeInterface
+//===--------------------------------------------------------------------===//
+
+/// Bytecode interface for the CIR dialect.
+struct CIRDialectBytecodeInterface : public BytecodeDialectInterface {
+  CIRDialectBytecodeInterface(Dialect *dialect)
+      : BytecodeDialectInterface(dialect) {}
+
+  Attribute readAttribute(DialectBytecodeReader &reader) const override {
+    return ::readAttribute(getContext(), reader);
+  }
+
+  LogicalResult writeAttribute(Attribute attr,
+                               DialectBytecodeWriter &writer) const override {
+    return ::writeAttribute(attr, writer);
+  }
+
+  Type readType(DialectBytecodeReader &reader) const override {
+    return ::readType(getContext(), reader);
+  }
+
+  LogicalResult writeType(Type type,
+                          DialectBytecodeWriter &writer) const override {
+    return ::writeType(type, writer);
+  }
+};
+} // namespace
+
+void cir::detail::addBytecodeInterface(CIRDialect *dialect) {
+  dialect->addInterfaces<CIRDialectBytecodeInterface>();
+}

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.h 
b/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.h
new file mode 100644
index 0000000000000..d16459200b9ed
--- /dev/null
+++ b/clang/lib/CIR/Dialect/IR/CIRDialectBytecode.h
@@ -0,0 +1,26 @@
+//===- CIRDialectBytecode.h - CIR Bytecode Implementation -------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines hooks into the CIR dialect bytecode implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIB_CLANG_CIR_DIALECT_IR_CIRDIALECTBYTECODE_H
+#define LIB_CLANG_CIR_DIALECT_IR_CIRDIALECTBYTECODE_H
+
+namespace cir {
+class CIRDialect;
+
+namespace detail {
+/// Add the interfaces necessary for encoding the CIR dialect components in
+/// bytecode.
+void addBytecodeInterface(CIRDialect *dialect);
+} // namespace detail
+} // namespace cir
+
+#endif // LIB_CLANG_CIR_DIALECT_IR_CIRDIALECTBYTECODE_H

diff  --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt 
b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index 0274005a28858..ac79ad52f95ec 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_clang_library(MLIRCIR
   CIRAttrs.cpp
   CIRDialect.cpp
+  CIRDialectBytecode.cpp
   CIRMemorySlot.cpp
   CIRTypes.cpp
   CIRDataLayout.cpp
@@ -8,6 +9,7 @@ add_clang_library(MLIRCIR
 
   DEPENDS
   MLIRCIROpsIncGen
+  MLIRCIRDialectBytecodeIncGen
   MLIRCIRTypeConstraintsIncGen
   MLIRCIREnumsGen
   MLIRCIROpInterfacesIncGen

diff  --git a/clang/test/CIR/Bytecode/attributes.cir 
b/clang/test/CIR/Bytecode/attributes.cir
new file mode 100644
index 0000000000000..b8a840b8cfe69
--- /dev/null
+++ b/clang/test/CIR/Bytecode/attributes.cir
@@ -0,0 +1,58 @@
+// RUN: cir-opt %s -verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s --emit-bytecode | llvm-strings | FileCheck %s 
--check-prefix=NATIVE
+
+// Round-trip the natively encoded attributes. Values are asymmetric (size !=
+// offset, negative offset) because a transposed pair of same-typed parameters
+// is exactly what a symmetric test cannot see.
+//
+// Check the attributes are not string-encoded in the blob.
+// NATIVE-NOT: #cir.
+
+!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
+!u64i = !cir.int<u, 64>
+
+module attributes {
+  cir.opt_info = #cir.opt_info<level = 2, size = 1>,
+  test.bitfield = #cir.bitfield_info<name = "f", storage_type = !u8i, size = 3,
+                                     offset = 5, is_signed = true>,
+  test.blockaddr = #cir.block_addr_info<@some_func, "some_label"> : 
!cir.ptr<!cir.void>,
+  test.addrpoint = #cir.address_point<index = 1, offset = -2>,
+  test.oclversion = #cir.cl.version<3, 0>,
+  test.i8 = #cir.int<127> : !u8i,
+  test.s32 = #cir.int<-559038737> : !s32i,
+  test.u64 = #cir.int<1099511627776> : !u64i,
+  test.i128 = #cir.int<18446744073709551617> : !cir.int<s, 128>,
+  test.f32 = #cir.fp<1.500000e+00> : !cir.float,
+  test.f64 = #cir.fp<3.141592653589793e+00> : !cir.double,
+  test.f80 = #cir.fp<1.500000e+00> : !cir.long_double<!cir.f80>,
+  test.true = #cir.bool<true> : !cir.bool,
+  test.false = #cir.bool<false> : !cir.bool,
+  test.nullptr = #cir.ptr<null> : !cir.ptr<!u8i>,
+  test.ptr = #cir.ptr<32 : i64> : !cir.ptr<!u8i>,
+  test.catchall = #cir.all,
+  test.unwind = #cir.unwind
+} {
+}
+
+// CHECK-DAG: #bfi_f = #cir.bitfield_info<name = "f", storage_type = !u8i, 
size = 3, offset = 5, is_signed = true>
+// CHECK-DAG: test.bitfield = #bfi_f
+// CHECK-DAG: cir.opt_info = #cir.opt_info<level = 2, size = 1>
+// CHECK-DAG: test.addrpoint = #cir.address_point<index = 1, offset = -2>
+// CHECK-DAG: test.blockaddr = #cir.block_addr_info<@some_func, "some_label"> 
: !cir.ptr<!void>
+// CHECK-DAG: test.oclversion = #cir.cl.version<3, 0>
+// CHECK-DAG: test.i8 = #cir.int<127> : !u8i
+// CHECK-DAG: test.s32 = #cir.int<-559038737> : !s32i
+// CHECK-DAG: test.u64 = #cir.int<1099511627776> : !u64i
+// CHECK-DAG: test.i128 = #cir.int<18446744073709551617> : !s128i
+// CHECK-DAG: test.f80 = #cir.fp<1.500000e+00> : !cir.long_double<!cir.f80>
+// CHECK-DAG: test.f32 = #cir.fp<1.500000e+00> : !cir.float
+// CHECK-DAG: test.f64 = #cir.fp<3.1415926535897931> : !cir.double
+// CHECK-DAG: #true = #cir.bool<true> : !cir.bool
+// CHECK-DAG: #false = #cir.bool<false> : !cir.bool
+// CHECK-DAG: test.true = #true
+// CHECK-DAG: test.false = #false
+// CHECK-DAG: test.nullptr = #cir.ptr<null> : !cir.ptr<!u8i>
+// CHECK-DAG: test.ptr = #cir.ptr<32 : i64> : !cir.ptr<!u8i>
+// CHECK-DAG: test.catchall = #cir.all
+// CHECK-DAG: test.unwind = #cir.unwind

diff  --git a/clang/test/CIR/Bytecode/constants.cir 
b/clang/test/CIR/Bytecode/constants.cir
new file mode 100644
index 0000000000000..22e3594478104
--- /dev/null
+++ b/clang/test/CIR/Bytecode/constants.cir
@@ -0,0 +1,43 @@
+// RUN: cir-opt %s -verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s --emit-bytecode | llvm-strings | FileCheck %s 
--check-prefix=NATIVE
+
+// Round-trip the constant-initializer attributes.
+//
+// Check the attributes are not string-encoded in the blob.
+// NATIVE-NOT: #cir.const_array<
+// NATIVE-NOT: #cir.const_record<
+// NATIVE-NOT: #cir.zero
+// NATIVE-NOT: #cir.undef
+// NATIVE-NOT: #cir.poison
+
+!s32i = !cir.int<s, 32>
+!s8i = !cir.int<s, 8>
+!rec = !cir.struct<"R" {data !s32i, data !s32i}>
+
+module {
+  cir.global external @z = #cir.zero : !cir.array<!s32i x 2>
+  cir.global external @u = #cir.undef : !s32i
+  cir.global external @p = #cir.poison : !s32i
+  cir.global external @ca = #cir.const_array<[#cir.int<1> : !s32i,
+                                              #cir.int<2> : !s32i]> : 
!cir.array<!s32i x 2>
+
+  // trailing_zeros is separate from the element list: 4-long array, one
+  // element stored. Dropping the count would produce a 1-element array.
+  cir.global external @caz = #cir.const_array<[#cir.int<7> : !s32i],
+                                              trailing_zeros> : 
!cir.array<!s32i x 4>
+  cir.global external @cr = #cir.const_record<{#cir.int<3> : !s32i,
+                                               #cir.int<4> : !s32i}> : !rec
+
+  // The string branch of elts: a StringAttr payload rather than an array of
+  // per-element attributes.
+  cir.global external @cs = #cir.const_array<"ab" : !cir.array<!s8i x 2>,
+                                             trailing_zeros> : !cir.array<!s8i 
x 4>
+}
+
+// CHECK-DAG: @z = #cir.zero : !cir.array<!s32i x 2>
+// CHECK-DAG: @u = #cir.undef : !s32i
+// CHECK-DAG: @p = #cir.poison : !s32i
+// CHECK-DAG: @ca = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : 
!s32i]> : !cir.array<!s32i x 2>
+// CHECK-DAG: @caz = #cir.const_array<[#cir.int<7> : !s32i], trailing_zeros> : 
!cir.array<!s32i x 4>
+// CHECK-DAG: @cr = #cir.const_record<{#cir.int<3> : !s32i, #cir.int<4> : 
!s32i}> : !rec_R
+// CHECK-DAG: @cs = #cir.const_array<"ab" : !cir.array<!s8i x 2>, 
trailing_zeros> : !cir.array<!s8i x 4>

diff  --git a/clang/test/CIR/Bytecode/global-view.cir 
b/clang/test/CIR/Bytecode/global-view.cir
new file mode 100644
index 0000000000000..3cc4f6b93d671
--- /dev/null
+++ b/clang/test/CIR/Bytecode/global-view.cir
@@ -0,0 +1,73 @@
+// RUN: cir-opt %s -verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s --emit-bytecode | llvm-strings | FileCheck %s 
--check-prefix=NATIVE
+
+// The type-carrying attributes: a container nesting other attributes only pays
+// off once those are encoded too.
+//
+// Check the attributes are not string-encoded in the blob.
+// NATIVE-NOT: #cir.global_view<
+// NATIVE-NOT: #cir.method<
+// NATIVE-NOT: #cir.data_member<
+// NATIVE-NOT: #cir.dyn_cast_info<
+// NATIVE-NOT: #cir.typeinfo<
+// NATIVE-NOT: #cir.const_vector<
+// NATIVE-NOT: #cir.const_complex<
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!rec_Marker = !cir.struct<"Marker" {data !s32i}>
+!rec_TI = !cir.struct<"TI" {data !cir.ptr<!u8i>, data !s32i}>
+!rec_Other = !cir.struct<"Other" {data !s32i, data !s32i}>
+!method_Other = !cir.method<!cir.func<(!s32i)> in !rec_Other>
+!s64i = !cir.int<s, 64>
+!rec_Base = !cir.struct<"Base" {data !cir.vptr}>
+!rec_Derived = !cir.struct<"Derived" {data !rec_Base}>
+
+#info = #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : 
!cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, 
runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = 
#cir.int<0> : !s64i>
+
+module {
+  cir.global external @plain = #cir.global_view<@some_sym> : 
!cir.ptr<!rec_Marker>
+  cir.global external @indexed = #cir.global_view<@some_sym, [3 : i32, 0 : 
i32]> : !cir.ptr<!rec_Marker>
+  cir.global external @addr_point = #cir.global_view<@some_sym, [0 : i32, 3 : 
i32], address_point true> : !cir.ptr<!u8i>
+  cir.global external @g7 = #cir.global_view<@s7> : !cir.ptr<!rec_Marker>
+  cir.global external @info = #cir.typeinfo<{#cir.global_view<@_ZTS6Widget> : 
!cir.ptr<!u8i>, #cir.int<2> : !s32i}> : !rec_TI
+  cir.global external @vec = #cir.const_vector<[#cir.int<1> : !s32i, 
#cir.int<2> : !s32i]> : !cir.vector<2 x !s32i>
+  cir.global external @cpx = #cir.const_complex<#cir.fp<1.000000e+00> : 
!cir.double, #cir.fp<2.000000e+00> : !cir.double> : !cir.complex<!cir.double>
+  cir.global external @dm = #cir.data_member<[0]> : !cir.data_member<!s32i in 
!rec_Other>
+  cir.global external @dm_single = #cir.data_member<[1]> : 
!cir.data_member<!s32i in !rec_Other>
+  cir.global external @dm_null = #cir.data_member<null> : 
!cir.data_member<!s32i in !rec_Other>
+
+  // A symbol, no target at all, and a vtable slot. Collapsing null into
+  // vtable_offset = 0 would miscompile a null pointer-to-member into a call
+  // of the first virtual function, hence the explicit present-zero case.
+  cir.global external @m_sym = #cir.method<@f> : !method_Other
+  cir.global external @m_null = #cir.method<null> : !method_Other
+  cir.global external @m_virtual = #cir.method<vtable_offset = 8> : 
!method_Other
+  cir.global external @m_virtual0 = #cir.method<vtable_offset = 0> : 
!method_Other
+  cir.global external @m_bigo = #cir.method<vtable_offset = 
18446744073709551615> : !method_Other
+
+  cir.func private @__dynamic_cast(!cir.ptr<!u8i>, !cir.ptr<!u8i>, 
!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+  cir.func private @__cxa_bad_cast()
+
+  cir.func @cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+    %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> 
!cir.ptr<!rec_Derived> #info
+    cir.return %0 : !cir.ptr<!rec_Derived>
+  }
+}
+
+// CHECK: cir.global external @plain = #cir.global_view<@some_sym> : 
!cir.ptr<!rec_Marker>
+// CHECK: cir.global external @indexed = #cir.global_view<@some_sym, [3 : i32, 
0 : i32]> : !cir.ptr<!rec_Marker>
+// CHECK: cir.global external @addr_point = #cir.global_view<@some_sym, [0 : 
i32, 3 : i32], address_point true> : !cir.ptr<!u8i>
+// CHECK: cir.global external @g7 = #cir.global_view<@s7> : 
!cir.ptr<!rec_Marker>
+// CHECK: cir.global external @info = 
#cir.typeinfo<{#cir.global_view<@_ZTS6Widget> : !cir.ptr<!u8i>, #cir.int<2> : 
!s32i}> : !rec_TI
+// CHECK: cir.global external @vec = #cir.const_vector<[#cir.int<1> : !s32i, 
#cir.int<2> : !s32i]> : !cir.vector<2 x !s32i>
+// CHECK: cir.global external @cpx = #cir.const_complex<#cir.fp<1.000000e+00> 
: !cir.double, #cir.fp<2.000000e+00> : !cir.double> : !cir.complex<!cir.double>
+// CHECK: cir.global external @dm = #cir.data_member<[0]> : 
!cir.data_member<!s32i in !rec_Other>
+// CHECK: cir.global external @dm_single = #cir.data_member<[1]> : 
!cir.data_member<!s32i in !rec_Other>
+// CHECK: cir.global external @dm_null = #cir.data_member<null> : 
!cir.data_member<!s32i in !rec_Other>
+// CHECK: cir.global external @m_sym = #cir.method<@f> : 
!cir.method<!cir.func<(!s32i)> in !rec_Other>
+// CHECK: cir.global external @m_null = #cir.method<null> : 
!cir.method<!cir.func<(!s32i)> in !rec_Other>
+// CHECK: cir.global external @m_virtual = #cir.method<vtable_offset = 8> : 
!cir.method<!cir.func<(!s32i)> in !rec_Other>
+// CHECK: cir.global external @m_virtual0 = #cir.method<vtable_offset = 0> : 
!cir.method<!cir.func<(!s32i)> in !rec_Other>
+// CHECK: cir.global external @m_bigo = #cir.method<vtable_offset = 
18446744073709551615> : !cir.method<!cir.func<(!s32i)> in !rec_Other>
+// CHECK: %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> 
!cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived

diff  --git a/clang/test/CIR/Bytecode/special-member.cir 
b/clang/test/CIR/Bytecode/special-member.cir
new file mode 100644
index 0000000000000..c98322449683d
--- /dev/null
+++ b/clang/test/CIR/Bytecode/special-member.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s -verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s --emit-bytecode | llvm-strings | FileCheck %s 
--check-prefix=NATIVE
+
+// The special-member attributes carry an aggregate type, so the fallback
+// embeds its fully expanded printed form per attribute. Encoded, each
+// attribute refers to the type table by index instead.
+//
+// Check the attributes are not string-encoded in the blob.
+// NATIVE-NOT: #cir.cxx_ctor<
+// NATIVE-NOT: #cir.cxx_dtor<
+// NATIVE-NOT: #cir.cxx_assign<
+
+!s32i = !cir.int<s, 32>
+!rec_Marker = !cir.struct<"Marker" {data !s32i}>
+
+module {
+  cir.func private @ctor_default(!cir.ptr<!rec_Marker>) 
func_info<#cir.cxx_ctor<!rec_Marker, default>>
+  cir.func private @ctor_copy(!cir.ptr<!rec_Marker>, !cir.ptr<!rec_Marker>) 
func_info<#cir.cxx_ctor<!rec_Marker, copy>>
+  cir.func private @ctor_move_trivial(!cir.ptr<!rec_Marker>, 
!cir.ptr<!rec_Marker>) func_info<#cir.cxx_ctor<!rec_Marker, move, trivial true>>
+  cir.func private @dtor(!cir.ptr<!rec_Marker>) 
func_info<#cir.cxx_dtor<!rec_Marker>>
+  cir.func private @dtor_trivial(!cir.ptr<!rec_Marker>) 
func_info<#cir.cxx_dtor<!rec_Marker, trivial true>>
+  cir.func private @assign_copy(!cir.ptr<!rec_Marker>, !cir.ptr<!rec_Marker>) 
func_info<#cir.cxx_assign<!rec_Marker, copy>>
+  cir.func private @assign_move_trivial(!cir.ptr<!rec_Marker>, 
!cir.ptr<!rec_Marker>) func_info<#cir.cxx_assign<!rec_Marker, move, trivial 
true>>
+}
+
+// Each kind appears: the write side widens a scoped enum to a varint the
+// reader casts back, and getting that wrong silently retargets the kind.
+// CHECK: cir.func private @ctor_default({{.*}}) 
func_info<#cir.cxx_ctor<!rec_Marker, default>>
+// CHECK: cir.func private @ctor_copy({{.*}}) 
func_info<#cir.cxx_ctor<!rec_Marker, copy>>
+// CHECK: cir.func private @ctor_move_trivial({{.*}}) 
func_info<#cir.cxx_ctor<!rec_Marker, move, trivial true>>
+// CHECK: cir.func private @dtor({{.*}}) func_info<#cir.cxx_dtor<!rec_Marker>>
+// CHECK: cir.func private @dtor_trivial({{.*}}) 
func_info<#cir.cxx_dtor<!rec_Marker, trivial true>>
+// CHECK: cir.func private @assign_copy({{.*}}) 
func_info<#cir.cxx_assign<!rec_Marker, copy>>
+// CHECK: cir.func private @assign_move_trivial({{.*}}) 
func_info<#cir.cxx_assign<!rec_Marker, move, trivial true>>

diff  --git a/clang/test/CIR/Bytecode/vtable-blockaddr.cir 
b/clang/test/CIR/Bytecode/vtable-blockaddr.cir
new file mode 100644
index 0000000000000..2ecc209e755a0
--- /dev/null
+++ b/clang/test/CIR/Bytecode/vtable-blockaddr.cir
@@ -0,0 +1,32 @@
+// RUN: cir-opt %s -verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s --emit-bytecode | llvm-strings | FileCheck %s 
--check-prefix=NATIVE
+
+// A vtable nests arrays of global_views, so under the fallback its printed
+// form repeats the element types at every level. Encoded, each level is a
+// reference instead.
+//
+// Check the attributes are not string-encoded in the blob.
+// NATIVE-NOT: #cir.block_addr_
diff <
+// NATIVE-NOT: #cir.vtable<
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+
+module {
+  cir.global external @tgt = #cir.int<0> : !s32i
+
+  cir.global external @vt = #cir.vtable<{
+    #cir.const_array<[#cir.global_view<@tgt> : !cir.ptr<!u8i>]> : 
!cir.array<!cir.ptr<!u8i> x 1>,
+    #cir.const_array<[#cir.global_view<@vt, [0 : i32, 2 : i32]> : 
!cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 1>
+  }> : !cir.struct<"VT" {data !cir.array<!cir.ptr<!u8i> x 1>, data 
!cir.array<!cir.ptr<!u8i> x 1>}>
+
+  cir.global external @bad = #cir.block_addr_
diff <@fn, "lbl", "lbl2"> : !s32i
+
+  cir.func @fn() {
+    cir.return
+  }
+}
+
+// CHECK-DAG: #cir.global_view<@tgt> : !cir.ptr<!u8i>
+// CHECK-DAG: #cir.global_view<@vt, [0 : i32, 2 : i32]> : !cir.ptr<!u8i>
+// CHECK-DAG: #cir.block_addr_
diff <@fn, "lbl", "lbl2"> : !s32i

diff  --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index d1d79c30ca9cb..4063f58ad0e08 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -264,6 +264,7 @@ if(CLANG_ENABLE_CIR)
       llvm-dis
       llvm-objdump
       llvm-readelf
+      llvm-strings
       not
       opt
       split-file


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to