https://github.com/mzukovec updated 
https://github.com/llvm/llvm-project/pull/206317

>From 5000b9b7d1da7a11692ee12f2350bc16682e6cff Mon Sep 17 00:00:00 2001
From: mzukovec <[email protected]>
Date: Sun, 28 Jun 2026 09:51:02 +0200
Subject: [PATCH] [clang] Cap MS RTTI TypeDescriptor name strings like MSVC
 does

On the MSVC ABI, the RTTI TypeDescriptor embeds the type's decorated
name as a string. Unlike symbol names, this string was never run
through the MD5 capping that MSVC applies to over-long decorated
names, so deeply nested template types (e.g. containing local lambdas
whose mangled context re-expands the enclosing template arguments at
every level) produced exponentially growing writable .data COMDATs;
a 5-level nesting repro emitted an 82 MB object.

Wrap the TypeDescriptor name string in the same msvc_hashing_ostream
used for symbols. MSVC counts the leading '.' toward the 4096
character limit but excludes it from the hashed input, so the
threshold is one lower than for symbols. Both the hashed symbol and
the hashed name string match MSVC's output byte for byte (verified
against MSVC 19.51 at the threshold boundaries), and the repro drops
to a 3 KB object.

Name strings under the limit are unchanged. In particular, lambdas
keep their enclosing context, which is what keeps TypeDescriptors of
lambdas from different scopes distinct.
---
 clang/docs/ReleaseNotes.rst             |  6 +++++
 clang/lib/AST/MicrosoftMangle.cpp       | 16 +++++++----
 clang/test/CodeGenCXX/mangle-ms-md5.cpp | 36 +++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d6b978ec91659..c3133f3fe49f3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -115,6 +115,12 @@ ABI Changes in This Version
   break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
 - Fixed incorrect struct return when single large vector (256/512-bit) used on
   x86-64 targets. (#GH203760) The bug was introduced since Clang 21. 
(#GH120670)
+- Clang now applies MSVC's MD5 shortening to over-long Microsoft C++ RTTI type
+  descriptor name strings, matching the behavior already used for the RTTI
+  symbol names. Previously the full name string was always emitted, so deeply
+  nested template types (for example, ones containing local lambdas) could
+  produce very large writable ``.data`` sections. Emitted RTTI name strings
+  change only for types whose name exceeds the length limit. (#GH206313)
 
 AST Dumping Potentially Breaking Changes
 ----------------------------------------
diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index adfe260a0d091..59732ff0fed11 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -57,16 +57,17 @@ static GlobalDecl getGlobalDeclAsDeclContext(const 
DeclContext *DC) {
 
 struct msvc_hashing_ostream : public llvm::raw_svector_ostream {
   raw_ostream &OS;
+  size_t Threshold;
   llvm::SmallString<64> Buffer;
 
-  msvc_hashing_ostream(raw_ostream &OS)
-      : llvm::raw_svector_ostream(Buffer), OS(OS) {}
+  msvc_hashing_ostream(raw_ostream &OS, size_t Threshold = 4096)
+      : llvm::raw_svector_ostream(Buffer), OS(OS), Threshold(Threshold) {}
   ~msvc_hashing_ostream() override {
     StringRef MangledName = str();
     bool StartsWithEscape = MangledName.starts_with("\01");
     if (StartsWithEscape)
       MangledName = MangledName.drop_front(1);
-    if (MangledName.size() < 4096) {
+    if (MangledName.size() < Threshold) {
       OS << str();
       return;
     }
@@ -4193,8 +4194,13 @@ void MicrosoftMangleContextImpl::mangleCXXRTTI(QualType 
T, raw_ostream &Out) {
 
 void MicrosoftMangleContextImpl::mangleCXXRTTIName(
     QualType T, raw_ostream &Out, bool NormalizeIntegers = false) {
-  MicrosoftCXXNameMangler Mangler(*this, Out);
-  Mangler.getStream() << '.';
+  Out << '.';
+  // MSVC caps the length of the TypeDescriptor's name string the same way it
+  // caps decorated names, substituting "??@<md5>@" for over-long names. The
+  // leading '.' counts toward the 4096-character limit but is not part of
+  // the hashed input, so the threshold is one lower than for symbols.
+  msvc_hashing_ostream MHO(Out, /*Threshold=*/4095);
+  MicrosoftCXXNameMangler Mangler(*this, MHO);
   Mangler.mangleType(T, SourceRange(), MicrosoftCXXNameMangler::QMM_Result);
 }
 
diff --git a/clang/test/CodeGenCXX/mangle-ms-md5.cpp 
b/clang/test/CodeGenCXX/mangle-ms-md5.cpp
index 6cd145d9166b6..78f0a56a495d4 100644
--- a/clang/test/CodeGenCXX/mangle-ms-md5.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms-md5.cpp
@@ -34,6 +34,9 @@ struct Y4095 {
 Y4095::Y4095() {}
 // CHECK-DAG: @"??@a6a285da2eea70dba6b578022be61d81@??_R4@" = linkonce_odr 
constant %rtti.CompleteObjectLocator
 // CHECK-DAG: @"??@a6a285da2eea70dba6b578022be61d81@" = alias
+// The TypeDescriptor's name string is replaced by its md5 form as well, just
+// like MSVC does.
+// CHECK-DAG: @"??@c14087f0ec22b387aea7c59083f4f546@" = linkonce_odr global 
%rtti.TypeDescriptor37 { ptr @"??_7type_info@@6B@", ptr null, [38 x i8] 
c".??@5ca87b4c7c2322ca1e21f8b01a23e135@\00" }, comdat
 
 // RUN: %clang_cc1 -DTHROW -fcxx-exceptions -fms-compatibility-version=18.0 
-emit-llvm -o - -triple i686-pc-win32 %s | FileCheck --check-prefix=HAVECTOR %s
 // RUN: %clang_cc1 -DTHROW -fcxx-exceptions -fms-compatibility-version=19.0 
-emit-llvm -o - -triple i686-pc-win32 %s | FileCheck --check-prefix=OMITCTOR %s
@@ -71,3 +74,36 @@ int X4088(z) = 1515;
 // Use initialization to verify mangled name association in the il
 int X4089(z) = 1717;
 // CHECK-DAG: @"??@0269945400a3474730d6880df0967d8f@" = dso_local global i32 
1717, align 4
+
+// Verify the threshold where md5 mangling kicks in for the TypeDescriptor
+// name string. MSVC hashes the string once it reaches 4096 characters
+// counting the leading '.', which is one character sooner than for symbols
+// since the '.' is not part of the hashed input.
+
+// Name string is ".?AU" + 4089 + "@@" = 4095 characters: kept in full, even
+// though the "??_R0" symbol itself is over the limit and gets hashed.
+#define P4089 X4088(p)
+struct P4089 {
+  P4089();
+  virtual void f();
+};
+P4089::P4089() {}
+// CHECK-DAG: @"??@{{[0-9a-f]+}}@" = linkonce_odr global 
%rtti.TypeDescriptor4095 { ptr @"??_7type_info@@6B@", ptr null, [4096 x i8] 
c".?AU{{p+}}@@\00" }, comdat
+
+// Name string is 4096 characters: hashed. Hash value matches MSVC's output
+// for the same type byte for byte.
+#define R4090 X4089(r)
+struct R4090 {
+  R4090();
+  virtual void f();
+};
+R4090::R4090() {}
+// CHECK-DAG: @"??@{{[0-9a-f]+}}@" = linkonce_odr global 
%rtti.TypeDescriptor37 { ptr @"??_7type_info@@6B@", ptr null, [38 x i8] 
c".??@ad624783add47d25188bc51f70637e97@\00" }, comdat
+
+#define R4091 C2(X4089(s), s)
+struct R4091 {
+  R4091();
+  virtual void f();
+};
+R4091::R4091() {}
+// CHECK-DAG: @"??@{{[0-9a-f]+}}@" = linkonce_odr global 
%rtti.TypeDescriptor37 { ptr @"??_7type_info@@6B@", ptr null, [38 x i8] 
c".??@c9d63594821ede4ea693d109deaa7a17@\00" }, comdat

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

Reply via email to