[Mlir-commits] [clang] [llvm] [mlir] msvc abi/cir foundation mangling (PR #233258)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Oct 18 09:16:07 PDT 2026


https://github.com/R-Goc created https://github.com/llvm/llvm-project/pull/233258

- **[LLVMABI] Implement Windows x86_64 TargetInfo and SRetAfterThis support**
- **[MLIR][LLVM] Add Windows EH funclet ops to LLVM dialect**
- **[CIR] Add initial Microsoft C++ ABI support in ClangIR**


>From ab83ea96f7b1520f797292bffa47b9cdbe5d762c Mon Sep 17 00:00:00 2001
From: R-Goc <ryszardgoc at gmail.com>
Date: Fri, 16 Oct 2026 21:11:08 +0200
Subject: [PATCH 1/3] [LLVMABI] Implement Windows x86_64 TargetInfo and
 SRetAfterThis support

* Add SRetAfterThis classification to ArgInfo.
* Add IsInstanceMethod property to FunctionInfo.
* Implement WinX86_64TargetInfo for Windows x86_64 argument/return classification.
* Add unit tests in llvm/unittests/ABI/WinX86_64TargetInfoTest.cpp.

Assisted-by: Gemini:gemini-3.8-flash-high
---
 llvm/include/llvm/ABI/FunctionInfo.h          |  27 +-
 llvm/include/llvm/ABI/TargetInfo.h            |   4 +
 llvm/include/llvm/ABI/Types.h                 |  14 +
 llvm/lib/ABI/Targets/X86.cpp                  |  81 ++++++
 llvm/unittests/ABI/CMakeLists.txt             |   1 +
 llvm/unittests/ABI/FunctionInfoTest.cpp       |   5 +
 llvm/unittests/ABI/TypesTest.cpp              |  14 +
 .../unittests/ABI/WinX86_64TargetInfoTest.cpp | 250 ++++++++++++++++++
 8 files changed, 391 insertions(+), 5 deletions(-)
 create mode 100644 llvm/unittests/ABI/WinX86_64TargetInfoTest.cpp

diff --git a/llvm/include/llvm/ABI/FunctionInfo.h b/llvm/include/llvm/ABI/FunctionInfo.h
index 9edd588c480018..065d46ea638d62 100644
--- a/llvm/include/llvm/ABI/FunctionInfo.h
+++ b/llvm/include/llvm/ABI/FunctionInfo.h
@@ -83,11 +83,12 @@ class ArgInfo {
   bool CanBeFlattened : 1;
   unsigned NeededIntRegs : 3;
   unsigned NeededSseRegs : 3;
+  bool SRetAfterThis : 1;
 
   ArgInfo(Kind K = Direct)
       : TheKind(K), SignExt(false), ZeroExt(false), IndirectByVal(false),
         IndirectRealign(false), CanBeFlattened(false), NeededIntRegs(0),
-        NeededSseRegs(0) {}
+        NeededSseRegs(0), SRetAfterThis(false) {}
 
 public:
   /// \param T The type to coerce to. If null, the argument's original type is
@@ -111,7 +112,7 @@ class ArgInfo {
     return AI;
   }
 
-  static ArgInfo getExtend(const Type *T) {
+  static ArgInfo getExtend(const Type *T, bool Signed) {
     assert(T && "Type cannot be null");
     assert(T->isInteger() && "Unexpected type - only integers can be extended");
 
@@ -120,8 +121,7 @@ class ArgInfo {
     AI.Alignment = std::nullopt;
     AI.DirectAttr.Offset = 0;
 
-    const IntegerType *IntTy = cast<IntegerType>(T);
-    if (IntTy->isSigned())
+    if (Signed)
       AI.setSignExt();
     else
       AI.setZeroExt();
@@ -129,6 +129,13 @@ class ArgInfo {
     return AI;
   }
 
+  static ArgInfo getExtend(const Type *T) {
+    assert(T && "Type cannot be null");
+    assert(T->isInteger() && "Unexpected type - only integers can be extended");
+    const IntegerType *IntTy = cast<IntegerType>(T);
+    return getExtend(T, IntTy->isSigned());
+  }
+
   /// Realign: the caller couldn't guarantee sufficient alignment - the callee
   /// must copy the argument to a properly aligned temporary before use.
   static ArgInfo getIndirect(Align Align, bool ByVal, unsigned AddrSpace = 0,
@@ -268,6 +275,9 @@ class ArgInfo {
     return !SignExt && !ZeroExt;
   }
 
+  bool isSRetAfterThis() const { return SRetAfterThis; }
+  void setSRetAfterThis(bool Value) { SRetAfterThis = Value; }
+
   const Type *getCoerceToType() const {
     assert((isDirect() || isExtend() || isCoerceAndExpand()) &&
            "Invalid Kind!");
@@ -297,6 +307,8 @@ struct ArgEntry {
 
   ArgEntry(const Type *T) : ABIType(T), Info(ArgInfo::getDirect()) {}
   ArgEntry(const Type *T, ArgInfo A) : ABIType(T), Info(A) {}
+
+  const Type *getType() const { return ABIType; }
 };
 
 /// Whether a signature accepts arguments beyond its declared parameters, and
@@ -338,11 +350,13 @@ class FunctionInfo final : private TrailingObjects<FunctionInfo, ArgEntry> {
   unsigned NumArgs;
   CallingConv::ID CC = CallingConv::C;
   RequiredArgs Required;
+  bool IsInstanceMethod : 1;
 
   FunctionInfo(CallingConv::ID CC, const Type *RetTy, unsigned NumArguments,
                RequiredArgs Required)
       : ReturnType(RetTy), ReturnInfo(ArgInfo::getDirect()),
-        NumArgs(NumArguments), CC(CC), Required(Required) {}
+        NumArgs(NumArguments), CC(CC), Required(Required),
+        IsInstanceMethod(false) {}
 
   friend class TrailingObjects;
 
@@ -369,6 +383,9 @@ class FunctionInfo final : private TrailingObjects<FunctionInfo, ArgEntry> {
 
   CallingConv::ID getCallingConvention() const { return CC; }
 
+  bool isInstanceMethod() const { return IsInstanceMethod; }
+  void setIsInstanceMethod(bool Value) { IsInstanceMethod = Value; }
+
   bool isVariadic() const { return Required.allowsOptionalArgs(); }
 
   unsigned getNumRequiredArgs() const {
diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h
index 859eca06ba785a..4d85ed85ddbb5a 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -188,6 +188,10 @@ LLVM_ABI std::unique_ptr<TargetInfo>
 createX86_64TargetInfo(TypeBuilder &TB, X86AVXABILevel AVXLevel,
                        bool Has64BitPointers, const X86ABICompatInfo &Compat);
 
+LLVM_ABI std::unique_ptr<TargetInfo>
+createWinX86_64TargetInfo(TypeBuilder &TB, X86AVXABILevel AVXLevel,
+                          const ABICompatInfo &Compat);
+
 enum class AArch64ABIKind {
   AAPCS = 0,
   DarwinPCS,
diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index 746e107b3a32ca..3c95d1f6950e6c 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -17,13 +17,19 @@
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/bit.h"
 #include "llvm/Support/Alignment.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/TypeSize.h"
 
 namespace llvm {
+
+/// Returns the largest power of two less than or equal to the given value.
+inline uint64_t PowerOf2Floor(uint64_t A) { return llvm::bit_floor(A); }
+
 namespace abi {
 
 enum class TypeKind {
@@ -482,6 +488,14 @@ class TypeBuilder {
         IntegerType(BitWidth, Align, Signed, IsBitInt);
   }
 
+  const IntegerType *getIntegerType(TypeSize Size, bool Signed = false) {
+    uint64_t BitWidth = Size.getFixedValue();
+    return getIntegerType(
+        BitWidth,
+        Align(llvm::PowerOf2Floor(std::max<uint64_t>(1, BitWidth / 8))),
+        Signed);
+  }
+
   const FloatType *getFloatType(const fltSemantics &Semantics, Align Align) {
     return new (Allocator.Allocate<FloatType>()) FloatType(Semantics, Align);
   }
diff --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp
index d57c5a0cf516f4..4c8571ac37e6f9 100644
--- a/llvm/lib/ABI/Targets/X86.cpp
+++ b/llvm/lib/ABI/Targets/X86.cpp
@@ -1386,5 +1386,86 @@ createX86_64TargetInfo(TypeBuilder &TB, X86AVXABILevel AVXLevel,
                                             Compat);
 }
 
+namespace {
+
+class WinX86_64TargetInfo : public TargetInfo {
+  [[maybe_unused]] X86AVXABILevel AVXLevel;
+  ABICompatInfo CompatInfo;
+
+  ArgInfo classify(const Type *Ty, bool IsReturn, bool IsInstanceMethod) const {
+    if (Ty->isVoid())
+      return ArgInfo::getIgnore();
+
+    if (IsReturn && IsInstanceMethod && isAggregateTypeForABI(Ty)) {
+      ArgInfo AI =
+          getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+      AI.setSRetAfterThis(true);
+      return AI;
+    }
+
+    if (const auto *RT = dyn_cast<RecordType>(Ty)) {
+      if (!RT->canPassInRegisters() || RT->hasFlexibleArrayMember())
+        return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+
+      uint64_t Width = RT->getSizeInBits().getFixedValue();
+      if (Width > 64 || !llvm::isPowerOf2_64(Width))
+        return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+
+      const Type *CoerceTy =
+          TB.getIntegerType(TypeSize::getFixed(Width), /*IsSigned=*/false);
+      return ArgInfo::getDirect(CoerceTy);
+    }
+
+    if (const auto *IT = dyn_cast<IntegerType>(Ty)) {
+      // MSVC WinX86_64 ABI only extends 1-bit booleans; other small integers are passed directly.
+      if (IT->getSizeInBits().getFixedValue() == 1)
+        return ArgInfo::getExtend(Ty, IT->isSigned());
+      if (IT->getSizeInBits().getFixedValue() > 64)
+        return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+      return ArgInfo::getDirect();
+    }
+
+    if (Ty->isFloat() || Ty->isPointer() || Ty->isVector())
+      return ArgInfo::getDirect();
+
+    if (isAggregateTypeForABI(Ty)) {
+      uint64_t Width = Ty->getSizeInBits().getFixedValue();
+      if (Width > 64 || !llvm::isPowerOf2_64(Width))
+        return getNaturalAlignIndirect(Ty, getAllocaAddrSpace(), /*ByVal=*/false);
+      const Type *CoerceTy =
+          TB.getIntegerType(TypeSize::getFixed(Width), /*IsSigned=*/false);
+      return ArgInfo::getDirect(CoerceTy);
+    }
+
+    return ArgInfo::getDirect();
+  }
+
+public:
+  WinX86_64TargetInfo(TypeBuilder &TB, X86AVXABILevel AVXLevel,
+                      const ABICompatInfo &Compat)
+      : TargetInfo(TB), AVXLevel(AVXLevel), CompatInfo(Compat) {}
+
+  const ABICompatInfo &getABICompatInfo() const override {
+    return CompatInfo;
+  }
+
+  void computeInfo(FunctionInfo &FI) const override {
+    FI.getReturnInfo() = classify(FI.getReturnType(), /*IsReturn=*/true,
+                                  FI.isInstanceMethod());
+    for (auto &Arg : FI.arguments())
+      Arg.Info = classify(Arg.getType(), /*IsReturn=*/false,
+                          /*IsInstanceMethod=*/false);
+  }
+};
+
+
+} // namespace
+
+std::unique_ptr<TargetInfo>
+createWinX86_64TargetInfo(TypeBuilder &TB, X86AVXABILevel AVXLevel,
+                          const ABICompatInfo &Compat) {
+  return std::make_unique<WinX86_64TargetInfo>(TB, AVXLevel, Compat);
+}
+
 } // namespace abi
 } // namespace llvm
diff --git a/llvm/unittests/ABI/CMakeLists.txt b/llvm/unittests/ABI/CMakeLists.txt
index eb50b88b87d2df..efa5b339603fe6 100644
--- a/llvm/unittests/ABI/CMakeLists.txt
+++ b/llvm/unittests/ABI/CMakeLists.txt
@@ -11,5 +11,6 @@ add_llvm_unittest(ABITests
   TargetInfoTest.cpp
   AMDGPUTargetInfoTest.cpp
   X86TargetInfoTest.cpp
+  WinX86_64TargetInfoTest.cpp
   TypesTest.cpp
   )
diff --git a/llvm/unittests/ABI/FunctionInfoTest.cpp b/llvm/unittests/ABI/FunctionInfoTest.cpp
index 0a855edab9bb40..7db77800fa3739 100644
--- a/llvm/unittests/ABI/FunctionInfoTest.cpp
+++ b/llvm/unittests/ABI/FunctionInfoTest.cpp
@@ -132,6 +132,11 @@ TEST_F(FunctionInfoTest, IndirectAliasedSurvivesFunctionInfo) {
   EXPECT_EQ(Info.getIndirectAlign(), llvm::Align(8));
 }
 
+TEST_F(FunctionInfoTest, ArgEntryGetType) {
+  ArgEntry Entry(I32);
+  EXPECT_EQ(Entry.getType(), I32);
+}
+
 #if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
 TEST_F(FunctionInfoTest, CanBeFlattenedIsDirectOnly) {
   EXPECT_DEATH((void)ArgInfo::getIgnore().getCanBeFlattened(), "Invalid Kind");
diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp
index 588d1f914db12b..f37592f01a3a92 100644
--- a/llvm/unittests/ABI/TypesTest.cpp
+++ b/llvm/unittests/ABI/TypesTest.cpp
@@ -214,6 +214,20 @@ TEST_F(ABITypesTest, ScalarABISize) {
   EXPECT_EQ(TB.getComplexType(F32, Align(4))->getABISizeInBits(), 64u);
 }
 
+TEST_F(ABITypesTest, IntegerTypeFromTypeSize) {
+  // Power-of-2 byte sizes.
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(8))->getAlignment(), Align(1));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(16))->getAlignment(), Align(2));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(32))->getAlignment(), Align(4));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(64))->getAlignment(), Align(8));
+
+  // Non-byte and non-power-of-2 byte sizes should not assert on alignment.
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(1))->getAlignment(), Align(1));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(24))->getAlignment(), Align(2));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(40))->getAlignment(), Align(4));
+  EXPECT_EQ(TB.getIntegerType(TypeSize::getFixed(48))->getAlignment(), Align(4));
+}
+
 // A bool element takes one bit and a one-bit _BitInt element a whole byte.  The
 // total is rounded up to a power of two of at least 8 bits.
 TEST_F(ABITypesTest, VectorABISize) {
diff --git a/llvm/unittests/ABI/WinX86_64TargetInfoTest.cpp b/llvm/unittests/ABI/WinX86_64TargetInfoTest.cpp
new file mode 100644
index 00000000000000..84e416887452f4
--- /dev/null
+++ b/llvm/unittests/ABI/WinX86_64TargetInfoTest.cpp
@@ -0,0 +1,250 @@
+//===- WinX86_64TargetInfoTest.cpp - Windows x86-64 ABI unit tests --------===//
+//
+// 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 "llvm/ABI/FunctionInfo.h"
+#include "llvm/ABI/TargetInfo.h"
+#include "llvm/ABI/Types.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Allocator.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace llvm;
+
+using ABIType = llvm::abi::Type;
+using llvm::abi::ABICompatInfo;
+using llvm::abi::ArgInfo;
+using llvm::abi::createWinX86_64TargetInfo;
+using llvm::abi::FieldInfo;
+using llvm::abi::FunctionInfo;
+using llvm::abi::RecordFlags;
+using llvm::abi::StructPacking;
+using llvm::abi::TargetInfo;
+using llvm::abi::TypeBuilder;
+using llvm::abi::X86AVXABILevel;
+
+class WinX86_64TargetInfoTest : public ::testing::Test {
+protected:
+  llvm::BumpPtrAllocator Alloc;
+  TypeBuilder TB;
+  const ABIType *Void;
+  const ABIType *I1;
+  const ABIType *I8;
+  const ABIType *I16;
+  const ABIType *I32;
+  const ABIType *I64;
+  const ABIType *F32;
+  const ABIType *F64;
+
+  WinX86_64TargetInfoTest()
+      : TB(Alloc),
+        Void(TB.getVoidType()),
+        I1(TB.getIntegerType(1, llvm::Align(1), /*Signed=*/false)),
+        I8(TB.getIntegerType(8, llvm::Align(1), /*Signed=*/true)),
+        I16(TB.getIntegerType(16, llvm::Align(2), /*Signed=*/true)),
+        I32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/true)),
+        I64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/true)),
+        F32(TB.getFloatType(llvm::APFloat::IEEEsingle(), llvm::Align(4))),
+        F64(TB.getFloatType(llvm::APFloat::IEEEdouble(), llvm::Align(8))) {}
+
+  std::unique_ptr<TargetInfo> target() const {
+    return createWinX86_64TargetInfo(const_cast<TypeBuilder &>(TB),
+                                     X86AVXABILevel::None, ABICompatInfo());
+  }
+
+  const ABIType *structOfBytes(uint64_t NumBytes,
+                               RecordFlags Flags = RecordFlags::CanPassInRegisters) {
+    llvm::SmallVector<FieldInfo, 8> Fields;
+    for (uint64_t I = 0; I < NumBytes; ++I)
+      Fields.emplace_back(I8, I * 8);
+    return TB.getRecordType(Fields, llvm::TypeSize::getFixed(NumBytes * 8),
+                            llvm::Align(1), llvm::Align(1),
+                            StructPacking::Default, {}, {}, Flags);
+  }
+
+  const ArgInfo &classifyReturn(const ABIType *RetTy,
+                                std::unique_ptr<FunctionInfo> &FI,
+                                std::unique_ptr<TargetInfo> &TI,
+                                bool IsInstanceMethod = false) {
+    TI = target();
+    FI = FunctionInfo::create(llvm::CallingConv::C, RetTy, {});
+    FI->setIsInstanceMethod(IsInstanceMethod);
+    TI->computeInfo(*FI);
+    return FI->getReturnInfo();
+  }
+};
+
+static void expectDirectInteger(const ArgInfo &Info, unsigned Bits) {
+  ASSERT_TRUE(Info.isDirect());
+  const ABIType *Coerce = Info.getCoerceToType();
+  ASSERT_NE(Coerce, nullptr);
+  const auto *IT = llvm::dyn_cast<llvm::abi::IntegerType>(Coerce);
+  ASSERT_NE(IT, nullptr);
+  EXPECT_EQ(IT->getSizeInBits().getFixedValue(), Bits);
+}
+
+TEST_F(WinX86_64TargetInfoTest, ScalarReturns) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI;
+
+  // Void -> ignore.
+  EXPECT_TRUE(classifyReturn(Void, FI, TI).isIgnore());
+
+  // i1 -> extend (zero extend).
+  const ArgInfo &I1Info = classifyReturn(I1, FI, TI);
+  EXPECT_TRUE(I1Info.isExtend());
+  EXPECT_TRUE(I1Info.isZeroExt());
+
+  // i8 -> direct.
+  const ArgInfo &I8Info = classifyReturn(I8, FI, TI);
+  EXPECT_TRUE(I8Info.isDirect());
+  EXPECT_EQ(I8Info.getCoerceToType(), nullptr);
+
+  // i16 -> direct.
+  const ArgInfo &I16Info = classifyReturn(I16, FI, TI);
+  EXPECT_TRUE(I16Info.isDirect());
+  EXPECT_EQ(I16Info.getCoerceToType(), nullptr);
+
+  // i32 -> direct.
+  const ArgInfo &I32Info = classifyReturn(I32, FI, TI);
+  EXPECT_TRUE(I32Info.isDirect());
+  EXPECT_EQ(I32Info.getCoerceToType(), nullptr);
+
+  // i64 -> direct.
+  const ArgInfo &I64Info = classifyReturn(I64, FI, TI);
+  EXPECT_TRUE(I64Info.isDirect());
+  EXPECT_EQ(I64Info.getCoerceToType(), nullptr);
+
+  // Float -> direct.
+  const ArgInfo &F32Info = classifyReturn(F32, FI, TI);
+  EXPECT_TRUE(F32Info.isDirect());
+  EXPECT_EQ(F32Info.getCoerceToType(), nullptr);
+
+  // Double -> direct.
+  const ArgInfo &F64Info = classifyReturn(F64, FI, TI);
+  EXPECT_TRUE(F64Info.isDirect());
+  EXPECT_EQ(F64Info.getCoerceToType(), nullptr);
+}
+
+TEST_F(WinX86_64TargetInfoTest, DirectStructReturns) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI;
+
+  // 1-byte struct -> direct, coerced to i8.
+  const ABIType *S1 = structOfBytes(1);
+  expectDirectInteger(classifyReturn(S1, FI, TI), 8);
+
+  // 2-byte struct -> direct, coerced to i16.
+  const ABIType *S2 = structOfBytes(2);
+  expectDirectInteger(classifyReturn(S2, FI, TI), 16);
+
+  // 4-byte struct -> direct, coerced to i32.
+  const ABIType *S4 = structOfBytes(4);
+  expectDirectInteger(classifyReturn(S4, FI, TI), 32);
+
+  // 8-byte struct -> direct, coerced to i64.
+  const ABIType *S8 = structOfBytes(8);
+  expectDirectInteger(classifyReturn(S8, FI, TI), 64);
+}
+
+TEST_F(WinX86_64TargetInfoTest, IndirectStructReturns) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI;
+
+  for (uint64_t Bytes : {3u, 5u, 12u, 16u}) {
+    const ABIType *S = structOfBytes(Bytes);
+    const ArgInfo &Info = classifyReturn(S, FI, TI);
+    EXPECT_TRUE(Info.isIndirect());
+    EXPECT_FALSE(Info.getIndirectByVal());
+    EXPECT_FALSE(Info.isSRetAfterThis());
+  }
+}
+
+TEST_F(WinX86_64TargetInfoTest, NonTrivialStructReturn) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI;
+
+  // 4-byte struct with RecordFlags::None (cannot pass in registers).
+  const ABIType *S = structOfBytes(4, RecordFlags::None);
+  const ArgInfo &Info = classifyReturn(S, FI, TI);
+  EXPECT_TRUE(Info.isIndirect());
+  EXPECT_FALSE(Info.getIndirectByVal());
+}
+
+TEST_F(WinX86_64TargetInfoTest, InstanceMethodStructReturn) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI;
+
+  // Even a 4-byte struct returned from an instance method is indirect with SRetAfterThis = true.
+  const ABIType *S4 = structOfBytes(4);
+  const ArgInfo &Info =
+      classifyReturn(S4, FI, TI, /*IsInstanceMethod=*/true);
+  EXPECT_TRUE(Info.isIndirect());
+  EXPECT_FALSE(Info.getIndirectByVal());
+  EXPECT_TRUE(Info.isSRetAfterThis());
+}
+
+TEST_F(WinX86_64TargetInfoTest, ScalarAndStructArguments) {
+  std::unique_ptr<FunctionInfo> FI;
+  std::unique_ptr<TargetInfo> TI = target();
+
+  const ABIType *S4 = structOfBytes(4);
+  const ABIType *S16 = structOfBytes(16);
+
+  FI = FunctionInfo::create(llvm::CallingConv::C, Void,
+                            {I1, I8, I16, I32, I64, S4, S16});
+  TI->computeInfo(*FI);
+
+  // Arg 0: i1 -> extend (zero extend).
+  EXPECT_EQ(FI->getArgInfo(0).getType(), I1);
+  const ArgInfo &Arg0 = FI->getArgInfo(0).Info;
+  EXPECT_TRUE(Arg0.isExtend());
+  EXPECT_TRUE(Arg0.isZeroExt());
+
+  // Arg 1: i8 -> direct.
+  EXPECT_EQ(FI->getArgInfo(1).getType(), I8);
+  const ArgInfo &Arg1 = FI->getArgInfo(1).Info;
+  EXPECT_TRUE(Arg1.isDirect());
+  EXPECT_EQ(Arg1.getCoerceToType(), nullptr);
+
+  // Arg 2: i16 -> direct.
+  EXPECT_EQ(FI->getArgInfo(2).getType(), I16);
+  const ArgInfo &Arg2 = FI->getArgInfo(2).Info;
+  EXPECT_TRUE(Arg2.isDirect());
+  EXPECT_EQ(Arg2.getCoerceToType(), nullptr);
+
+  // Arg 3: i32 -> direct.
+  EXPECT_EQ(FI->getArgInfo(3).getType(), I32);
+  const ArgInfo &Arg3 = FI->getArgInfo(3).Info;
+  EXPECT_TRUE(Arg3.isDirect());
+  EXPECT_EQ(Arg3.getCoerceToType(), nullptr);
+
+  // Arg 4: i64 -> direct.
+  EXPECT_EQ(FI->getArgInfo(4).getType(), I64);
+  const ArgInfo &Arg4 = FI->getArgInfo(4).Info;
+  EXPECT_TRUE(Arg4.isDirect());
+  EXPECT_EQ(Arg4.getCoerceToType(), nullptr);
+
+  // Arg 5: 4-byte struct -> direct, coerced to i32.
+  EXPECT_EQ(FI->getArgInfo(5).getType(), S4);
+  const ArgInfo &Arg5 = FI->getArgInfo(5).Info;
+  expectDirectInteger(Arg5, 32);
+
+  // Arg 6: 16-byte struct -> indirect, ByVal = false.
+  EXPECT_EQ(FI->getArgInfo(6).getType(), S16);
+  const ArgInfo &Arg6 = FI->getArgInfo(6).Info;
+  EXPECT_TRUE(Arg6.isIndirect());
+  EXPECT_FALSE(Arg6.getIndirectByVal());
+}
+
+} // namespace

>From 80a02559367a8d3b349f0a073b032d028f8479a8 Mon Sep 17 00:00:00 2001
From: R-Goc <ryszardgoc at gmail.com>
Date: Fri, 16 Oct 2026 21:17:25 +0200
Subject: [PATCH 2/3] [MLIR][LLVM] Add Windows EH funclet ops to LLVM dialect

* Add cleanuppad, cleanupret, catchswitch, catchpad, catchret ops to LLVMIR dialect.
* Implement LLVM IR export translation for funclet operations.

Assisted-by: Gemini:gemini-3.8-flash-high
---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   | 136 +++++++++++++++++-
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    |  95 +++++++++++-
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        |  55 +++++++
 3 files changed, 279 insertions(+), 7 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 9f658bc7a4b817..f4fdd802aa171d 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -760,7 +760,8 @@ def LLVM_InvokeOp
                          DeclareOpInterfaceMethods<CallOpInterface>,
                          DeclareOpInterfaceMethods<SymbolUserOpInterface>,
                          DeclareOpInterfaceMethods<WeightedBranchOpInterface>,
-                         Terminator]> {
+                         Terminator,
+                         TokenConsumerTrait]> {
   let arguments = (ins
                    OptionalAttr<TypeAttrOf<LLVM_FunctionType>>:$var_callee_type,
                    OptionalAttr<FlatSymbolRefAttr>:$callee,
@@ -790,7 +791,19 @@ def LLVM_InvokeOp
       "Block*":$unwind, "ValueRange":$unwindOps)>,
     OpBuilder<(ins "LLVMFunctionType":$calleeType, "FlatSymbolRefAttr":$callee,
       "ValueRange":$ops, "Block*":$normal, "ValueRange":$normalOps,
-      "Block*":$unwind, "ValueRange":$unwindOps)>];
+      "Block*":$unwind, "ValueRange":$unwindOps)>,
+    OpBuilder<(ins "TypeRange":$resultTypes, "FlatSymbolRefAttr":$callee,
+                   "ValueRange":$args, "Block *":$normalDest, "Block *":$unwindDest,
+                   "ArrayRef<ValueRange>":$op_bundle_operands,
+                   "ArrayAttr":$op_bundle_tags), [{
+      build($_builder, $_state, resultTypes, /*var_callee_type=*/nullptr,
+            callee, args, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
+            /*normalDestOperands=*/ValueRange{},
+            /*unwindDestOperands=*/ValueRange{},
+            /*branch_weights=*/nullptr, /*CConv=*/nullptr,
+            /*default_func_attrs=*/nullptr, /*uniform_work_group_size=*/nullptr,
+            op_bundle_operands, op_bundle_tags, normalDest, unwindDest);
+    }]>];
   let hasCustomAssemblyFormat = 1;
   let hasVerifier = 1;
   let extraClassDeclaration = [{
@@ -812,6 +825,98 @@ def LLVM_LandingpadOp : LLVM_Op<"landingpad"> {
   let hasVerifier = 1;
 }
 
+def LLVM_CleanupPadOp : LLVM_Op<"cleanuppad", [TokenConsumerTrait]> {
+  let summary = "LLVM cleanuppad instruction";
+  let arguments = (ins Token:$parentPad, Variadic<LLVM_Type>:$args);
+  let results = (outs Token:$res);
+  let assemblyFormat = [{
+    `within` $parentPad (`[` $args^ `:` type($args) `]`)? attr-dict
+  }];
+}
+
+def LLVM_CleanupRetOp : LLVM_TerminatorOp<"cleanupret", [TokenConsumerTrait]> {
+  let summary = "LLVM cleanupret instruction";
+  let arguments = (ins Token:$cleanupPad);
+  let successors = (successor VariadicSuccessor<AnySuccessor>:$unwindDest);
+  let builders = [
+    OpBuilder<(ins "Value":$cleanupPad, CArg<"Block *", "nullptr">:$unwindDest), [{
+      build($_builder, $_state, cleanupPad,
+            unwindDest ? BlockRange(unwindDest) : BlockRange());
+    }]>
+  ];
+  let hasCustomAssemblyFormat = 1;
+  let extraClassDeclaration = [{
+    ::mlir::SuccessorRange getUnwindDest() const {
+      return {(*this)->successor_begin(), (*this)->successor_end()};
+    }
+    bool hasUnwindDest() const { return !getUnwindDest().empty(); }
+    Block *getUnwindDestBlock() const {
+      return hasUnwindDest() ? getUnwindDest().front() : nullptr;
+    }
+  }];
+}
+
+def LLVM_CatchSwitchOp : LLVM_TerminatorOp<"catchswitch", [
+    TokenConsumerTrait
+]> {
+  let summary = "LLVM catchswitch instruction";
+  let arguments = (ins Token:$parentPad);
+  let results = (outs Token:$res);
+  let successors = (successor VariadicSuccessor<AnySuccessor>:$successors);
+  let builders = [
+    OpBuilder<(ins "Value":$parentPad, "ArrayRef<Block *>":$handlers,
+                   CArg<"Block *", "nullptr">:$unwindDest), [{
+      SmallVector<Block *> succs(handlers.begin(), handlers.end());
+      if (unwindDest)
+        succs.push_back(unwindDest);
+      build($_builder, $_state, $_builder.getType<TokenType>(), parentPad, succs);
+      if (unwindDest)
+        $_state.addAttribute("has_unwind_dest", $_builder.getUnitAttr());
+    }]>
+  ];
+  let hasCustomAssemblyFormat = 1;
+  let extraClassDeclaration = [{
+    ::mlir::SuccessorRange getSuccessors() const {
+      return (*this)->getSuccessors();
+    }
+    bool hasUnwindDest() const { return (*this)->hasAttr("has_unwind_dest"); }
+    Block *getUnwindDest() const {
+      return hasUnwindDest() ? getSuccessors().back() : nullptr;
+    }
+    ::mlir::SuccessorRange getHandlers() const {
+      if (hasUnwindDest())
+        return getSuccessors().drop_back();
+      return getSuccessors();
+    }
+    unsigned getNumHandlers() const {
+      return getHandlers().size();
+    }
+  }];
+}
+
+def LLVM_CatchPadOp : LLVM_Op<"catchpad", [TokenConsumerTrait]> {
+  let summary = "LLVM catchpad instruction";
+  let arguments = (ins Token:$catchSwitch, Variadic<LLVM_Type>:$args);
+  let results = (outs Token:$res);
+  let assemblyFormat = [{
+    `within` $catchSwitch (`[` $args^ `:` type($args) `]`)? attr-dict
+  }];
+  let builders = [
+    OpBuilder<(ins "Value":$catchSwitch), [{
+      build($_builder, $_state, catchSwitch, ValueRange());
+    }]>
+  ];
+}
+
+def LLVM_CatchRetOp : LLVM_TerminatorOp<"catchret", [TokenConsumerTrait]> {
+  let summary = "LLVM catchret instruction";
+  let arguments = (ins Token:$catchPad);
+  let successors = (successor AnySuccessor:$dest);
+  let assemblyFormat = [{
+    `from` $catchPad `to` $dest attr-dict
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // VAArgOp
 //===----------------------------------------------------------------------===//
@@ -841,7 +946,8 @@ def LLVM_CallOp
           "call", [AttrSizedOperandSegments,
                    DeclareOpInterfaceMethods<FastmathFlagsInterface>,
                    DeclareOpInterfaceMethods<CallOpInterface>,
-                   DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+                   DeclareOpInterfaceMethods<SymbolUserOpInterface>,
+                   TokenConsumerTrait]> {
   let summary = "Call to an LLVM function.";
   let description = [{
     In LLVM IR, functions may return either 0 or 1 value. LLVM IR dialect
@@ -930,7 +1036,29 @@ def LLVM_CallOp
     OpBuilder<(ins "LLVMFunctionType":$calleeType, "FlatSymbolRefAttr":$callee,
                    CArg<"ValueRange", "{}">:$args)>,
     OpBuilder<(ins "LLVMFunctionType":$calleeType, "StringRef":$callee,
-                   CArg<"ValueRange", "{}">:$args)>
+                   CArg<"ValueRange", "{}">:$args)>,
+    OpBuilder<(ins "TypeRange":$resultTypes, "FlatSymbolRefAttr":$callee,
+                   "ValueRange":$args, "ArrayRef<ValueRange>":$op_bundle_operands,
+                   "ArrayAttr":$op_bundle_tags), [{
+      build($_builder, $_state, resultTypes, /*var_callee_type=*/nullptr,
+            callee, args, /*fastmathFlags=*/nullptr, /*CConv=*/nullptr,
+            /*TailCallKind=*/nullptr, /*memory_effects=*/nullptr,
+            /*convergent=*/nullptr, /*no_unwind=*/nullptr, /*will_return=*/nullptr,
+            /*noreturn=*/nullptr, /*returns_twice=*/nullptr, /*hot=*/nullptr,
+            /*cold=*/nullptr, /*noduplicate=*/nullptr,
+            /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr,
+            /*modular_format=*/nullptr, /*nobuiltins=*/nullptr,
+            /*allocsize=*/nullptr, /*optsize=*/nullptr, /*minsize=*/nullptr,
+            /*builtin=*/nullptr, /*nobuiltin=*/nullptr,
+            /*save_reg_params=*/nullptr, /*zero_call_used_regs=*/nullptr,
+            /*trap_func_name=*/nullptr, /*default_func_attrs=*/nullptr,
+            /*uniform_work_group_size=*/nullptr, op_bundle_operands,
+            op_bundle_tags, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
+            /*no_inline=*/nullptr, /*always_inline=*/nullptr,
+            /*inline_hint=*/nullptr, /*access_groups=*/nullptr,
+            /*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr,
+            /*tbaa=*/nullptr);
+    }]>
   ];
   let hasVerifier = 1;
   let hasCustomAssemblyFormat = 1;
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 1b0cc7bdb87990..7b9fed58836873 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -1713,10 +1713,11 @@ LogicalResult InvokeOp::verify() {
   if (unwindDest->empty())
     return emitError("must have at least one operation in unwind destination");
 
-  // In unwind destination, first operation must be LandingpadOp
-  if (!isa<LandingpadOp>(unwindDest->front()))
+  // In unwind destination, first operation must be LandingpadOp or funclet pad.
+  if (!isa<LandingpadOp, CleanupPadOp, CatchSwitchOp, NoneTokenOp>(
+          unwindDest->front()))
     return emitError("first operation in unwind destination should be a "
-                     "llvm.landingpad operation");
+                     "llvm.landingpad or funclet pad operation");
 
   if (failed(verifyOperandBundles(*this)))
     return failure();
@@ -1969,6 +1970,94 @@ ParseResult LandingpadOp::parse(OpAsmParser &parser, OperationState &result) {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// CleanupRetOp
+//===----------------------------------------------------------------------===//
+
+void CleanupRetOp::print(OpAsmPrinter &p) {
+  p << ' ' << getCleanupPad();
+  if (hasUnwindDest())
+    p << " unwind " << getUnwindDest().front();
+  p.printOptionalAttrDict((*this)->getAttrs());
+}
+
+ParseResult CleanupRetOp::parse(OpAsmParser &parser, OperationState &result) {
+  OpAsmParser::UnresolvedOperand cleanupPad;
+  if (parser.parseOperand(cleanupPad))
+    return failure();
+  Type tokenType = parser.getBuilder().getType<TokenType>();
+  if (parser.resolveOperand(cleanupPad, tokenType, result.operands))
+    return failure();
+  if (succeeded(parser.parseOptionalKeyword("unwind"))) {
+    Block *unwindDest = nullptr;
+    if (parser.parseSuccessor(unwindDest))
+      return failure();
+    result.addSuccessors(unwindDest);
+  }
+  if (parser.parseOptionalAttrDict(result.attributes))
+    return failure();
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// CatchSwitchOp
+//===----------------------------------------------------------------------===//
+
+void CatchSwitchOp::print(OpAsmPrinter &p) {
+  p << " within " << getParentPad() << " [";
+  llvm::interleaveComma(getHandlers(), p, [&](Block *b) { p << b; });
+  p << "] unwind ";
+  if (hasUnwindDest())
+    p << getUnwindDest();
+  else
+    p << "to caller";
+  p.printOptionalAttrDict((*this)->getAttrs(), {"has_unwind_dest"});
+}
+
+ParseResult CatchSwitchOp::parse(OpAsmParser &parser, OperationState &result) {
+  if (parser.parseKeyword("within"))
+    return failure();
+
+  OpAsmParser::UnresolvedOperand parentPad;
+  if (parser.parseOperand(parentPad))
+    return failure();
+  Type tokenType = parser.getBuilder().getType<TokenType>();
+  if (parser.resolveOperand(parentPad, tokenType, result.operands))
+    return failure();
+
+  SmallVector<Block *> handlers;
+  if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Square, [&]() {
+        Block *handler = nullptr;
+        if (parser.parseSuccessor(handler))
+          return failure();
+        handlers.push_back(handler);
+        return success();
+      }))
+    return failure();
+  for (Block *handler : handlers)
+    result.addSuccessors(handler);
+
+  if (parser.parseKeyword("unwind"))
+    return failure();
+
+  if (succeeded(parser.parseOptionalKeyword("to"))) {
+    if (parser.parseKeyword("caller"))
+      return failure();
+  } else {
+    Block *unwindDest = nullptr;
+    if (parser.parseSuccessor(unwindDest))
+      return failure();
+    result.addSuccessors(unwindDest);
+    result.addAttribute("has_unwind_dest", parser.getBuilder().getUnitAttr());
+  }
+
+  if (parser.parseOptionalAttrDict(result.attributes))
+    return failure();
+
+  result.addTypes(tokenType);
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // ExtractValueOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 7833d8857699ce..4883f363638ad1 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -763,6 +763,61 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     return success();
   }
 
+  if (auto cpOp = dyn_cast<LLVM::CleanupPadOp>(opInst)) {
+    llvm::Value *parentPad = moduleTranslation.lookupValue(cpOp.getParentPad());
+    SmallVector<llvm::Value *> args =
+        moduleTranslation.lookupValues(cpOp.getArgs());
+    llvm::CleanupPadInst *pad = builder.CreateCleanupPad(parentPad, args);
+    moduleTranslation.mapValue(cpOp.getResult(), pad);
+    return success();
+  }
+
+  if (auto crOp = dyn_cast<LLVM::CleanupRetOp>(opInst)) {
+    auto *cleanupPad = cast<llvm::CleanupPadInst>(
+        moduleTranslation.lookupValue(crOp.getCleanupPad()));
+    llvm::BasicBlock *unwindBB = nullptr;
+    if (!crOp.getUnwindDest().empty())
+      unwindBB = moduleTranslation.lookupBlock(crOp.getUnwindDest().front());
+    llvm::CleanupReturnInst *ret =
+        builder.CreateCleanupRet(cleanupPad, unwindBB);
+    if (unwindBB)
+      moduleTranslation.mapBranch(&opInst, ret);
+    return success();
+  }
+
+  if (auto csOp = dyn_cast<LLVM::CatchSwitchOp>(opInst)) {
+    llvm::Value *parentPad = moduleTranslation.lookupValue(csOp.getParentPad());
+    llvm::BasicBlock *unwindBB = nullptr;
+    if (csOp.hasUnwindDest())
+      unwindBB = moduleTranslation.lookupBlock(csOp.getUnwindDest());
+    llvm::CatchSwitchInst *cs = builder.CreateCatchSwitch(
+        parentPad, unwindBB, csOp.getNumHandlers());
+    for (Block *handler : csOp.getHandlers())
+      cs->addHandler(moduleTranslation.lookupBlock(handler));
+    moduleTranslation.mapValue(csOp.getResult(), cs);
+    moduleTranslation.mapBranch(&opInst, cs);
+    return success();
+  }
+
+  if (auto cpOp = dyn_cast<LLVM::CatchPadOp>(opInst)) {
+    llvm::Value *catchSwitch =
+        moduleTranslation.lookupValue(cpOp.getCatchSwitch());
+    SmallVector<llvm::Value *> args =
+        moduleTranslation.lookupValues(cpOp.getArgs());
+    llvm::CatchPadInst *pad = builder.CreateCatchPad(catchSwitch, args);
+    moduleTranslation.mapValue(cpOp.getResult(), pad);
+    return success();
+  }
+
+  if (auto crOp = dyn_cast<LLVM::CatchRetOp>(opInst)) {
+    auto *catchPad = cast<llvm::CatchPadInst>(
+        moduleTranslation.lookupValue(crOp.getCatchPad()));
+    llvm::BasicBlock *destBB = moduleTranslation.lookupBlock(crOp.getDest());
+    llvm::CatchReturnInst *ret = builder.CreateCatchRet(catchPad, destBB);
+    moduleTranslation.mapBranch(&opInst, ret);
+    return success();
+  }
+
   // Emit branches.  We need to look up the remapped blocks and ignore the
   // block arguments that were transformed into PHI nodes.
   if (auto brOp = dyn_cast<LLVM::BrOp>(opInst)) {

>From 046b3fa4a923df3ac5ee5a209b110081b965035e Mon Sep 17 00:00:00 2001
From: R-Goc <ryszardgoc at gmail.com>
Date: Fri, 16 Oct 2026 21:48:27 +0200
Subject: [PATCH 3/3] [CIR] Add initial Microsoft C++ ABI support in ClangIR

* Implement foundational CIRGenMicrosoftCXXABI class and driver wiring.
* Implement string literal mangling (??_C at _).
* Implement constructor this-return (hasThisReturn).
* Add initial lit test in clang/test/CIR/CodeGen/cxx-msvc-abi.cpp.

Assisted-by: Gemini:gemini-3.8-flash-high
---
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          |   14 +-
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         |    1 -
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       |    7 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |    4 +-
 .../lib/CIR/CodeGen/CIRGenMicrosoftCXXABI.cpp | 1143 ++++++++++++++---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |   43 +-
 clang/lib/CIR/CodeGen/CIRGenModule.h          |    7 +
 clang/lib/CIR/CodeGen/CIRGenVTables.h         |    8 +
 clang/lib/CIR/CodeGen/CIRGenerator.cpp        |   12 +-
 .../TargetLowering/LowerMicrosoftCXXABI.cpp   |   19 +-
 clang/test/CIR/CodeGen/cxx-msvc-abi.cpp       |  124 +-
 11 files changed, 1168 insertions(+), 214 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index c562e4308c1ab0..79a628e2f5b051 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -900,9 +900,6 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) {
                                ? astContext.VoidPtrTy
                                : astContext.VoidTy;
 
-  assert(!theCXXABI.hasThisReturn(gd) &&
-         "Please send PR with a test and remove this");
-
   assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
   assert(!cir::MissingFeatures::opCallFnInfoOpts());
 
@@ -1038,13 +1035,10 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXConstructorCall(
                               : RequiredArgs::All;
 
   GlobalDecl gd(d, ctorKind);
-  if (theCXXABI.hasThisReturn(gd))
-    cgm.errorNYI(d->getSourceRange(),
-                 "arrangeCXXConstructorCall: hasThisReturn");
-  if (theCXXABI.hasMostDerivedReturn(gd))
-    cgm.errorNYI(d->getSourceRange(),
-                 "arrangeCXXConstructorCall: hasMostDerivedReturn");
-  CanQualType resultType = astContext.VoidTy;
+  CanQualType resultType = theCXXABI.hasThisReturn(gd) ? argTypes.front()
+                           : theCXXABI.hasMostDerivedReturn(gd)
+                               ? astContext.VoidPtrTy
+                               : astContext.VoidTy;
 
   assert(!cir::MissingFeatures::opCallFnInfoOpts());
   assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9e6ff99792500a..2ea99be02dc3f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -1053,7 +1053,6 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
              "only the Microsoft ABI passes an implicit deleting dtor param");
       cgm.errorNYI(dd->getSourceRange(),
                    "deleting destructor with conditional delete: MSVC ABI");
-
     } else {
       assert(!dd->getOperatorDelete()->isDestroyingOperatorDelete() &&
              "destroying operator delete is handled by emitDestructorBody");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index decddca749d835..e80a2e6498fc33 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -342,8 +342,11 @@ static void emitNullBaseClassInitialization(CIRGenFunction &cgf,
   stores.emplace_back(CharUnits::Zero(), nvSize);
 
   // Each store is split by the existence of a vbptr.
-  // TODO(cir): This only needs handling for the MS CXXABI.
-  assert(!cir::MissingFeatures::msabi());
+  if (cgf.cgm.getTarget().getCXXABI().isMicrosoft() && base->getNumVBases()) {
+    cgf.cgm.errorNYI(base->getSourceRange(),
+                     "emitNullBaseClassInitialization: MSVC vbptr splitting");
+    return;
+  }
 
   // If the type contains a pointer to data member we can't memset it to zero.
   // Instead, create a null constant and copy it to the destination.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 1fa57c4087da2c..89030b720b3b43 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1082,9 +1082,9 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
   const auto *md = dyn_cast<CXXMethodDecl>(fd);
   if (md && md->isImplicitObjectMemberFunction()) {
     if (cgm.getCXXABI().hasThisReturn(gd))
-      cgm.errorNYI(fd->getSourceRange(), "this return");
+      retTy = md->getThisType();
     else if (cgm.getCXXABI().hasMostDerivedReturn(gd))
-      cgm.errorNYI(fd->getSourceRange(), "most derived return");
+      retTy = getContext().VoidPtrTy;
     cgm.getCXXABI().buildThisParam(*this, args);
   }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenMicrosoftCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenMicrosoftCXXABI.cpp
index 276e3e93cdda0f..924e63ee8b53bf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenMicrosoftCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenMicrosoftCXXABI.cpp
@@ -1,4 +1,4 @@
-//===--- CIRGenMicrosoftCXXABI.cpp - Emit CIR Code for MS C++ ABI --------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This provides C++ code generation targeting the Microsoft C++ ABI.
+// This provides C++ code generation targeting the Microsoft C++ ABI.  The class
+// in this file generates structures that follow the Microsoft C++ ABI, which is
+// documented at:
+//  https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
 //
 //===----------------------------------------------------------------------===//
 
@@ -14,95 +17,219 @@
 #include "CIRGenFunction.h"
 #include "CIRGenModule.h"
 
+#include "clang/AST/Attr.h"
+#include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Mangle.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/VTableBuilder.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/MissingFeatures.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
 
 namespace {
 
+static bool isDeletingDtor(GlobalDecl gd) {
+  return isa<CXXDestructorDecl>(gd.getDecl()) &&
+         (gd.getDtorType() == Dtor_Deleting ||
+          gd.getDtorType() == Dtor_VectorDeleting);
+}
+
+static QualType decomposeTypeForEH(ASTContext &context, QualType t,
+                                   bool &isConst, bool &isVolatile,
+                                   bool &isUnaligned) {
+  t = context.getExceptionObjectType(t);
+  isConst = false;
+  isVolatile = false;
+  isUnaligned = false;
+  QualType pointeeType = t->getPointeeType();
+  if (!pointeeType.isNull()) {
+    isConst = pointeeType.isConstQualified();
+    isVolatile = pointeeType.isVolatileQualified();
+    isUnaligned = pointeeType.getQualifiers().hasUnaligned();
+  }
+  if (const auto *mpty = t->getAs<MemberPointerType>())
+    t = context.getMemberPointerType(pointeeType.getUnqualifiedType(),
+                                     mpty->getQualifier(),
+                                     mpty->getMostRecentCXXRecordDecl());
+  if (t->isPointerType())
+    t = context.getPointerType(pointeeType.getUnqualifiedType());
+  return t;
+}
+
 class CIRGenMicrosoftCXXABI : public CIRGenCXXABI {
+  using VFTableIdTy = std::pair<const CXXRecordDecl *, CharUnits>;
+  llvm::DenseMap<VFTableIdTy, cir::GlobalOp> vftablesMap;
+
+  MicrosoftMangleContext &getMangleContext() {
+    return *cast<MicrosoftMangleContext>(mangleContext.get());
+  }
+
+  CharUnits getVirtualFunctionPrologueThisAdjustment(GlobalDecl gd);
+
+  mlir::Value getVBaseOffsetFromVBPtr(CIRGenFunction &cgf, mlir::Location loc,
+                                      Address thisAddr, int64_t vbPtrOffset,
+                                      unsigned vbTableIndex,
+                                      mlir::Value *vbPtrOut = nullptr);
+
 public:
   CIRGenMicrosoftCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) {}
 
+  bool hasThisReturn(clang::GlobalDecl gd) const override {
+    return isa<CXXConstructorDecl>(gd.getDecl());
+  }
+
+  bool hasMostDerivedReturn(clang::GlobalDecl gd) const override {
+    return false;
+  }
+
+  llvm::StringRef getPureVirtualCallName() override { return "_purecall"; }
+  llvm::StringRef getDeletedVirtualCallName() override { return "_purecall"; }
+
+  bool canSpeculativelyEmitVTable(const CXXRecordDecl *rd) const override {
+    return false;
+  }
+
+  bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
+    return !vtableClass->hasAttr<MSNoVTableAttr>();
+  }
+
+  bool exportThunk() override { return false; }
+
+  bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
+                              CXXDtorType dt) const override {
+    return dt != Dtor_Base;
+  }
+
+  bool isZeroInitializable(const MemberPointerType *mpt) override {
+    if (mpt->isMemberFunctionPointer())
+      return true;
+
+    const CXXRecordDecl *rd = mpt->getMostRecentCXXRecordDecl();
+    MSInheritanceModel inheritance = rd->getMSInheritanceModel();
+    return (!inheritanceModelHasVBTableOffsetField(inheritance) &&
+            rd->nullFieldOffsetIsZero());
+  }
+
+  bool requiresArrayCookie(const CXXNewExpr *e) override {
+    return e->getAllocatedType().isDestructedType();
+  }
+
+  CharUnits getArrayCookieSizeImpl(QualType elementType) override {
+    ASTContext &ctx = cgm.getASTContext();
+    return std::max(ctx.getTypeSizeInChars(ctx.getSizeType()),
+                    ctx.getTypeAlignInChars(elementType));
+  }
+
+  Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr,
+                                mlir::Value numElements, const CXXNewExpr *e,
+                                QualType elementType) override;
+
+  cir::GlobalLinkageKind
+  getCXXDestructorLinkage(GVALinkage linkage, const CXXDestructorDecl *dtor,
+                          CXXDtorType dt) const override {
+    if (linkage == GVA_Internal)
+      return cir::GlobalLinkageKind::InternalLinkage;
+
+    switch (dt) {
+    case Dtor_Base:
+      return cgm.getCIRLinkageForDeclarator(dtor, linkage);
+    case Dtor_Complete:
+      if (dtor->hasAttr<DLLExportAttr>())
+        return cir::GlobalLinkageKind::WeakODRLinkage;
+      return linkage == GVA_DiscardableODR
+                 ? cir::GlobalLinkageKind::LinkOnceODRLinkage
+                 : cir::GlobalLinkageKind::WeakODRLinkage;
+    case Dtor_Deleting:
+    case Dtor_VectorDeleting:
+      return cir::GlobalLinkageKind::LinkOnceODRLinkage;
+    case Dtor_Comdat:
+      llvm_unreachable("emitting dtor comdat as function?");
+    case Dtor_Unified:
+      llvm_unreachable("unexpected unified dtor type");
+    }
+    llvm_unreachable("invalid dtor type");
+  }
+
+  void setThunkLinkage(cir::FuncOp thunk, bool forVTable, GlobalDecl gd,
+                       bool returnAdjustment) override {
+    GVALinkage linkage = cgm.getASTContext().GetGVALinkageForFunction(
+        cast<FunctionDecl>(gd.getDecl()));
+
+    if (linkage == GVA_Internal)
+      thunk.setLinkage(cir::GlobalLinkageKind::InternalLinkage);
+    else if (returnAdjustment)
+      thunk.setLinkage(cir::GlobalLinkageKind::WeakODRLinkage);
+    else
+      thunk.setLinkage(cir::GlobalLinkageKind::LinkOnceODRLinkage);
+  }
+
+  CatchTypeInfo getCatchAllTypeInfo() override {
+    if (cgm.getASTContext().getLangOpts().EHAsynch)
+      return CatchTypeInfo{nullptr, 0};
+    else
+      return CatchTypeInfo{nullptr, 0x40};
+  }
+
+  void adjustCallArgsForDestructorThunk(CIRGenFunction &cgf, GlobalDecl gd,
+                                        CallArgList &callArgs) override {
+    assert((gd.getDtorType() == Dtor_VectorDeleting ||
+            gd.getDtorType() == Dtor_Deleting) &&
+           "Only vector deleting destructor thunks are available in this ABI");
+    callArgs.add(RValue::get(getStructorImplicitParamValue(cgf)),
+                 cgm.getASTContext().IntTy);
+  }
+
   AddedStructorArgCounts
   buildStructorSignature(GlobalDecl gd,
-                         SmallVectorImpl<CanQualType> &argTys) override {
-    cgm.errorNYI(gd.getDecl()->getSourceRange(),
-                 "buildStructorSignature: MSVC ABI");
-    return AddedStructorArgCounts{};
-  }
+                         llvm::SmallVectorImpl<CanQualType> &argTys) override;
 
   void addImplicitStructorParams(CIRGenFunction &cgf, QualType &resTy,
-                                 FunctionArgList &params) override {
-    cgf.cgm.errorNYI(cgf.curGD.getDecl()->getSourceRange(),
-                     "addImplicitStructorParams: MSVC ABI");
-  }
+                                 FunctionArgList &params) override;
 
   void emitInstanceFunctionProlog(SourceLocation loc,
-                                  CIRGenFunction &cgf) override {
-    cgf.cgm.errorNYI(loc, "emitInstanceFunctionProlog: MSVC ABI");
-  }
+                                  CIRGenFunction &cgf) override;
 
   AddedStructorArgs getImplicitConstructorArgs(CIRGenFunction &cgf,
                                                const CXXConstructorDecl *d,
                                                CXXCtorType type,
                                                bool forVirtualBase,
-                                               bool delegating) override {
-    cgf.cgm.errorNYI(d->getSourceRange(),
-                     "getImplicitConstructorArgs: MSVC ABI");
-    return AddedStructorArgs{};
-  }
+                                               bool delegating) override;
 
   mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf,
                                             const CXXDestructorDecl *dd,
                                             CXXDtorType type,
                                             bool forVirtualBase,
                                             bool delegating) override {
-    cgf.cgm.errorNYI(dd->getSourceRange(),
-                     "getCXXDestructorImplicitParam: MSVC ABI");
     return nullptr;
   }
 
-  void emitCXXConstructors(const CXXConstructorDecl *d) override {
-    cgm.errorNYI(d->getSourceRange(), "emitCXXConstructors: MSVC ABI");
-  }
-
-  void emitCXXDestructors(const CXXDestructorDecl *d) override {
-    cgm.errorNYI(d->getSourceRange(), "emitCXXDestructors: MSVC ABI");
-  }
-
-  void emitCXXStructor(GlobalDecl gd) override {
-    cgm.errorNYI(gd.getDecl()->getSourceRange(), "emitCXXStructor: MSVC ABI");
-  }
+  void emitCXXConstructors(const CXXConstructorDecl *d) override;
+  void emitCXXDestructors(const CXXDestructorDecl *d) override;
+  void emitCXXStructor(GlobalDecl gd) override;
 
   void emitDestructorCall(CIRGenFunction &cgf, const CXXDestructorDecl *dd,
                           CXXDtorType type, bool forVirtualBase,
                           bool delegating, Address thisAddr,
-                          QualType thisTy) override {
-    cgf.cgm.errorNYI(dd->getSourceRange(), "emitDestructorCall: MSVC ABI");
-  }
+                          QualType thisTy) override;
 
   mlir::Value emitVirtualDestructorCall(CIRGenFunction &cgf,
                                         const CXXDestructorDecl *dtor,
                                         CXXDtorType dtorType, Address thisAddr,
-                                        DeleteOrMemberCallExpr e) override {
-    cgf.cgm.errorNYI(dtor->getSourceRange(),
-                     "emitVirtualDestructorCall: MSVC ABI");
-    return nullptr;
-  }
+                                        DeleteOrMemberCallExpr e) override;
 
   void emitVirtualObjectDelete(CIRGenFunction &cgf, const CXXDeleteExpr *de,
                                Address ptr, QualType elementType,
-                               const CXXDestructorDecl *dtor) override {
-    cgf.cgm.errorNYI(de->getSourceRange(), "emitVirtualObjectDelete: MSVC ABI");
-  }
+                               const CXXDestructorDecl *dtor) override;
 
   size_t getSrcArgforCopyCtor(const CXXConstructorDecl *cd,
                               FunctionArgList &args) const override {
-    cgm.errorNYI(cd->getSourceRange(), "getSrcArgforCopyCtor: MSVC ABI");
     assert(args.size() >= 2 &&
            "expected the arglist to have at least two args!");
     // The 'most_derived' parameter goes second if the ctor is variadic and
@@ -114,210 +241,880 @@ class CIRGenMicrosoftCXXABI : public CIRGenCXXABI {
   }
 
   const CXXRecordDecl *
-  getThisArgumentTypeForMethod(const CXXMethodDecl *md) override {
-    cgm.errorNYI(md->getSourceRange(),
-                 "getThisArgumentTypeForMethod: MSVC ABI");
-    return md->getParent();
-  }
+  getThisArgumentTypeForMethod(const CXXMethodDecl *md) override;
 
   Address adjustThisArgumentForVirtualFunctionCall(CIRGenFunction &cgf,
                                                    GlobalDecl gd,
-                                                   Address thisAddr,
-                                                   bool virtualCall) override {
-    cgf.cgm.errorNYI(gd.getDecl()->getSourceRange(),
-                     "adjustThisArgumentForVirtualFunctionCall: MSVC ABI");
-    return thisAddr;
-  }
+                                                   Address thisPtr,
+                                                   bool virtualCall) override;
 
   bool isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
-                                           CIRGenFunction::VPtr vptr) override {
-    cgf.cgm.errorNYI("isVirtualOffsetNeededForVTableField: MSVC ABI");
-    return false;
+                                          CIRGenFunction::VPtr vptr) override {
+    return vptr.nearestVBase != nullptr;
   }
 
   cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd,
-                                CharUnits vptrOffset) override {
-    cgm.errorNYI(rd->getSourceRange(), "getAddrOfVTable: MSVC ABI");
-    return nullptr;
-  }
+                                CharUnits vptrOffset) override;
 
   mlir::Value getVTableAddressPoint(BaseSubobject base,
-                                    const CXXRecordDecl *vtableClass) override {
-    cgm.errorNYI(vtableClass->getSourceRange(),
-                 "getVTableAddressPoint: MSVC ABI");
-    return nullptr;
-  }
+                                    const CXXRecordDecl *vtableClass) override;
 
   mlir::Value getVTableAddressPointInStructor(
       CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
-      const CXXRecordDecl *nearestVBase) override {
-    cgf.cgm.errorNYI(vtableClass->getSourceRange(),
-                     "getVTableAddressPointInStructor: MSVC ABI");
-    return nullptr;
-  }
+      const CXXRecordDecl *nearestVBase) override;
 
   CIRGenCallee getVirtualFunctionPointer(CIRGenFunction &cgf, GlobalDecl gd,
                                          Address thisAddr, mlir::Type ty,
-                                         SourceLocation loc) override {
-    cgf.cgm.errorNYI(loc, "getVirtualFunctionPointer: MSVC ABI");
-    return CIRGenCallee();
-  }
+                                         SourceLocation loc) override;
 
   void emitVTableDefinitions(CIRGenVTables &cgvt,
-                             const CXXRecordDecl *rd) override {
-    cgm.errorNYI(rd->getSourceRange(), "emitVTableDefinitions: MSVC ABI");
-  }
+                             const CXXRecordDecl *rd) override;
 
-  void emitVirtualInheritanceTables(const CXXRecordDecl *rd) override {
-    cgm.errorNYI(rd->getSourceRange(),
-                 "emitVirtualInheritanceTables: MSVC ABI");
-  }
+  void emitVirtualInheritanceTables(const CXXRecordDecl *rd) override;
 
-  void
-  initializeHiddenVirtualInheritanceMembers(CIRGenFunction &cgf,
-                                            const CXXRecordDecl *rd) override {
-    cgf.cgm.errorNYI(rd->getSourceRange(),
-                     "initializeHiddenVirtualInheritanceMembers: MSVC ABI");
-  }
+  void initializeHiddenVirtualInheritanceMembers(
+      CIRGenFunction &cgf, const CXXRecordDecl *rd) override;
 
   mlir::Value
   getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &cgf,
                             Address thisAddr, const CXXRecordDecl *classDecl,
-                            const CXXRecordDecl *baseClassDecl) override {
-    cgf.cgm.errorNYI(loc, "getVirtualBaseClassOffset: MSVC ABI");
-    return nullptr;
-  }
+                            const CXXRecordDecl *baseClassDecl) override;
 
   cir::MethodAttr buildVirtualMethodAttr(cir::MethodType methodTy,
-                                         const CXXMethodDecl *md) override {
-    cgm.errorNYI(md->getSourceRange(), "buildVirtualMethodAttr: MSVC ABI");
-    return cir::MethodAttr();
-  }
+                                         const CXXMethodDecl *md) override;
 
   mlir::Value performThisAdjustment(CIRGenFunction &cgf, Address thisAddr,
                                     const CXXRecordDecl *unadjustedClass,
-                                    const ThunkInfo &ti) override {
-    cgf.cgm.errorNYI("performThisAdjustment: MSVC ABI");
-    return thisAddr.emitRawPointer();
-  }
+                                    const ThunkInfo &ti) override;
 
   mlir::Value performReturnAdjustment(CIRGenFunction &cgf, Address ret,
                                       const CXXRecordDecl *unadjustedClass,
-                                      const ReturnAdjustment &ra) override {
-    cgf.cgm.errorNYI("performReturnAdjustment: MSVC ABI");
-    return ret.emitRawPointer();
+                                      const ReturnAdjustment &ra) override;
+
+  bool shouldTypeidBeNullChecked(QualType srcTy) override;
+  mlir::Value emitTypeid(CIRGenFunction &cgf, QualType srcTy, Address thisPtr,
+                         mlir::Type typeInfoPtrTy) override;
+  void emitBadTypeidCall(CIRGenFunction &cgf, mlir::Location loc) override;
+  void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) override;
+
+  mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
+                              QualType srcRecordTy, QualType destRecordTy,
+                              cir::PointerType destCIRTy, bool isRefCast,
+                              Address src) override;
+
+  mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
+                                          QualType ty) override;
+  CatchTypeInfo
+  getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
+                               QualType catchHandlerType) override;
+
+  void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
+  void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
+  void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
+                          mlir::Value addr) override;
+};
+
+} // namespace
+
+CharUnits CIRGenMicrosoftCXXABI::getVirtualFunctionPrologueThisAdjustment(
+    GlobalDecl gd) {
+  const auto *md = cast<CXXMethodDecl>(gd.getDecl());
+
+  if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
+    if (gd.getDtorType() == Dtor_Complete)
+      return CharUnits();
+
+    gd = GlobalDecl(dd, cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors(
+                            cgm.getASTContext().getLangOpts())
+                            ? Dtor_VectorDeleting
+                            : Dtor_Deleting);
   }
 
-  bool canSpeculativelyEmitVTable(const CXXRecordDecl *rd) const override {
-    return false;
+  MethodVFTableLocation ml =
+      cgm.getMicrosoftVTableContext().getMethodVFTableLocation(gd);
+  CharUnits adjustment = ml.VFPtrOffset;
+
+  if (isa<CXXDestructorDecl>(md))
+    adjustment = CharUnits::Zero();
+
+  if (ml.VBase) {
+    const ASTRecordLayout &derivedLayout =
+        cgm.getASTContext().getASTRecordLayout(md->getParent());
+    adjustment += derivedLayout.getVBaseClassOffset(ml.VBase);
   }
 
-  bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
-    cgm.errorNYI(vtableClass->getSourceRange(),
-                 "doStructorsInitializeVPtrs: MSVC ABI");
-    return false;
+  return adjustment;
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::getVBaseOffsetFromVBPtr(
+    CIRGenFunction &cgf, mlir::Location loc, Address thisAddr,
+    int64_t vbPtrOffset, unsigned vbTableIndex, mlir::Value *vbPtrOut) {
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+  mlir::Value thisBytePtr =
+      builder.createBitcast(thisAddr.getPointer(), u8PtrTy);
+
+  mlir::Value vbPtrOffsetVal = builder.getSInt32(vbPtrOffset, loc);
+  mlir::Value vbPtr =
+      cir::PtrStrideOp::create(builder, loc, u8PtrTy, thisBytePtr, vbPtrOffsetVal);
+  if (vbPtrOut)
+    *vbPtrOut = vbPtr;
+
+  CharUnits vbPtrAlign = cgf.getPointerAlign();
+  Address vbPtrAddr(builder.createBitcast(vbPtr, builder.getPointerTo(u8PtrTy)),
+                    u8PtrTy, vbPtrAlign);
+  mlir::Value vbTable = builder.createLoad(loc, vbPtrAddr);
+
+  mlir::Type i32Ty = builder.getSInt32Ty();
+  cir::PointerType i32PtrTy = builder.getPointerTo(i32Ty);
+  mlir::Value vbTableI32Ptr = builder.createBitcast(vbTable, i32PtrTy);
+  mlir::Value vbTableIndexVal = builder.getSInt32(vbTableIndex, loc);
+  mlir::Value vbaseOffsPtr = cir::PtrStrideOp::create(
+      builder, loc, i32PtrTy, vbTableI32Ptr, vbTableIndexVal);
+  Address vbaseOffsAddr(vbaseOffsPtr, i32Ty, CharUnits::fromQuantity(4));
+  return builder.createLoad(loc, vbaseOffsAddr);
+}
+
+Address CIRGenMicrosoftCXXABI::initializeArrayCookie(CIRGenFunction &cgf,
+                                                     Address newPtr,
+                                                     mlir::Value numElements,
+                                                     const CXXNewExpr *e,
+                                                     QualType elementType) {
+  assert(requiresArrayCookie(e));
+  CharUnits cookieSize = getArrayCookieSizeImpl(elementType);
+  mlir::Location loc = cgf.getLoc(e->getSourceRange());
+
+  mlir::Type u8Ty = cgf.getBuilder().getUInt8Ty();
+  cir::PointerType u8PtrTy = cgf.getBuilder().getUInt8PtrTy();
+  mlir::Value baseBytePtr =
+      cgf.getBuilder().createBitcast(newPtr.getPointer(), u8PtrTy);
+
+  // In the MSVC ABI, the cookie is located at offset 0 of newPtr.
+  CharUnits baseAlignment = newPtr.getAlignment();
+  Address cookiePtr(baseBytePtr, u8Ty, baseAlignment);
+  Address numElementsPtr =
+      cookiePtr.withElementType(cgf.getBuilder(), cgf.sizeTy);
+  cgf.getBuilder().createStore(loc, numElements, numElementsPtr);
+
+  // Skip over the cookie to return the pointer to the element array.
+  mlir::Value dataOffset =
+      cgf.getBuilder().getSInt32(cookieSize.getQuantity(), loc);
+  mlir::Value dataPtr =
+      cgf.getBuilder().createPtrStride(loc, baseBytePtr, dataOffset);
+  mlir::Value finalPtr =
+      cgf.getBuilder().createPtrBitcast(dataPtr, newPtr.getElementType());
+  CharUnits finalAlignment = baseAlignment.alignmentAtOffset(cookieSize);
+  return Address(finalPtr, newPtr.getElementType(), finalAlignment);
+}
+
+CIRGenCXXABI::AddedStructorArgCounts
+CIRGenMicrosoftCXXABI::buildStructorSignature(
+    GlobalDecl gd, llvm::SmallVectorImpl<CanQualType> &argTys) {
+  AddedStructorArgCounts added;
+  if (isa<CXXDestructorDecl>(gd.getDecl()) &&
+      (gd.getDtorType() == Dtor_Deleting ||
+       gd.getDtorType() == Dtor_VectorDeleting)) {
+    argTys.push_back(cgm.getASTContext().IntTy);
+    ++added.suffix;
   }
+  auto *cd = dyn_cast<CXXConstructorDecl>(gd.getDecl());
+  if (!cd)
+    return added;
 
-  bool exportThunk() override { return false; }
+  const CXXRecordDecl *classDecl = cd->getParent();
+  const FunctionProtoType *fpt = cd->getType()->castAs<FunctionProtoType>();
+  if (classDecl->getNumVBases()) {
+    if (fpt->isVariadic()) {
+      argTys.insert(argTys.begin() + 1, cgm.getASTContext().IntTy);
+      ++added.prefix;
+    } else {
+      argTys.push_back(cgm.getASTContext().IntTy);
+      ++added.suffix;
+    }
+  }
 
-  bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
-                              CXXDtorType dt) const override {
-    cgm.errorNYI(dtor->getSourceRange(), "useThunkForDtorVariant: MSVC ABI");
-    return false;
+  return added;
+}
+
+void CIRGenMicrosoftCXXABI::addImplicitStructorParams(CIRGenFunction &cgf,
+                                                      QualType &resTy,
+                                                      FunctionArgList &params) {
+  ASTContext &context = cgm.getASTContext();
+  const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl());
+  assert(isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md));
+
+  if (isa<CXXConstructorDecl>(md) && md->getParent()->getNumVBases()) {
+    auto *isMostDerived = ImplicitParamDecl::Create(
+        context, /*DC=*/nullptr, cgf.curGD.getDecl()->getLocation(),
+        &context.Idents.get("is_most_derived"), context.IntTy,
+        ImplicitParamKind::Other);
+    const FunctionProtoType *fpt = md->getType()->castAs<FunctionProtoType>();
+    if (fpt->isVariadic())
+      params.insert(params.begin() + 1, isMostDerived);
+    else
+      params.push_back(isMostDerived);
+    getStructorImplicitParamDecl(cgf) = isMostDerived;
+  } else if (isDeletingDtor(cgf.curGD)) {
+    auto *shouldDelete = ImplicitParamDecl::Create(
+        context, /*DC=*/nullptr, cgf.curGD.getDecl()->getLocation(),
+        &context.Idents.get("should_call_delete"), context.IntTy,
+        ImplicitParamKind::Other);
+    params.push_back(shouldDelete);
+    getStructorImplicitParamDecl(cgf) = shouldDelete;
   }
+}
 
-  void setThunkLinkage(cir::FuncOp thunk, bool forVTable, GlobalDecl gd,
-                       bool returnAdjustment) override {
-    cgm.errorNYI(gd.getDecl()->getSourceRange(), "setThunkLinkage: MSVC ABI");
+void CIRGenMicrosoftCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
+                                                       CIRGenFunction &cgf) {
+  if (cgf.curFuncDecl && cgf.curFuncDecl->hasAttr<NakedAttr>())
+    return;
+
+  mlir::Value thisVal = loadIncomingCXXThis(cgf);
+  const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl());
+  if (!cgf.curFuncIsThunk && md->isVirtual()) {
+    CharUnits adjustment = getVirtualFunctionPrologueThisAdjustment(cgf.curGD);
+    if (!adjustment.isZero()) {
+      assert(adjustment.isPositive());
+      CIRGenBuilderTy &builder = cgf.getBuilder();
+      mlir::Location mlirLoc = cgf.getLoc(loc);
+      cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+      mlir::Value u8This = builder.createBitcast(thisVal, u8PtrTy);
+      mlir::Value negAdj =
+          builder.getSInt32(-adjustment.getQuantity(), mlirLoc);
+      mlir::Value adjusted =
+          cir::PtrStrideOp::create(builder, mlirLoc, u8PtrTy, u8This, negAdj);
+      thisVal = builder.createBitcast(adjusted, thisVal.getType());
+    }
   }
+  setCXXABIThisValue(cgf, thisVal);
 
-  StringRef getPureVirtualCallName() override { return "_purecall"; }
-  StringRef getDeletedVirtualCallName() override { return "_purecall"; }
+  if (hasThisReturn(cgf.curGD) || hasMostDerivedReturn(cgf.curGD)) {
+    if (cgf.returnValue.isValid())
+      cgf.getBuilder().createStore(cgf.getLoc(loc), thisVal, cgf.returnValue);
+  }
 
-  bool isZeroInitializable(const MemberPointerType *mpt) override {
-    cgm.errorNYI("isZeroInitializable: MSVC ABI");
-    return true;
+  if (isa<CXXConstructorDecl>(md) && md->getParent()->getNumVBases()) {
+    assert(getStructorImplicitParamDecl(cgf) &&
+           "no implicit parameter for a constructor with virtual bases?");
+    Address addr = cgf.getAddrOfLocalVar(getStructorImplicitParamDecl(cgf));
+    setStructorImplicitParamValue(
+        cgf, cgf.getBuilder().createLoad(cgf.getLoc(loc), addr));
   }
 
-  bool requiresArrayCookie(const CXXNewExpr *e) override {
-    cgm.errorNYI(e->getSourceRange(), "requiresArrayCookie: MSVC ABI");
-    return false;
+  if (isDeletingDtor(cgf.curGD)) {
+    assert(getStructorImplicitParamDecl(cgf) &&
+           "no implicit parameter for a deleting destructor?");
+    Address addr = cgf.getAddrOfLocalVar(getStructorImplicitParamDecl(cgf));
+    setStructorImplicitParamValue(
+        cgf, cgf.getBuilder().createLoad(cgf.getLoc(loc), addr));
   }
+}
 
-  CharUnits getArrayCookieSizeImpl(QualType elementType) override {
-    cgm.errorNYI("getArrayCookieSizeImpl: MSVC ABI");
-    return CharUnits::Zero();
+CIRGenCXXABI::AddedStructorArgs
+CIRGenMicrosoftCXXABI::getImplicitConstructorArgs(CIRGenFunction &cgf,
+                                                  const CXXConstructorDecl *d,
+                                                  CXXCtorType type,
+                                                  bool forVirtualBase,
+                                                  bool delegating) {
+  assert(type == Ctor_Complete || type == Ctor_Base);
+
+  if (!d->getParent()->getNumVBases())
+    return AddedStructorArgs{};
+
+  const FunctionProtoType *fpt = d->getType()->castAs<FunctionProtoType>();
+  mlir::Value mostDerivedArg;
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  mlir::Location loc = cgf.getLoc(d->getLocation());
+  if (delegating) {
+    mostDerivedArg = getStructorImplicitParamValue(cgf);
+  } else {
+    mostDerivedArg = builder.getSInt32(type == Ctor_Complete ? 1 : 0, loc);
+  }
+  if (fpt->isVariadic()) {
+    return AddedStructorArgs::withPrefix(
+        {{mostDerivedArg, cgm.getASTContext().IntTy}});
   }
+  return AddedStructorArgs::withSuffix(
+      {{mostDerivedArg, cgm.getASTContext().IntTy}});
+}
 
-  Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr,
-                                mlir::Value numElements, const CXXNewExpr *e,
-                                QualType elementType) override {
-    cgf.cgm.errorNYI(e->getSourceRange(), "initializeArrayCookie: MSVC ABI");
-    return newPtr;
+void CIRGenMicrosoftCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) {
+  cgm.emitGlobal(GlobalDecl(d, Ctor_Complete));
+}
+
+void CIRGenMicrosoftCXXABI::emitCXXDestructors(const CXXDestructorDecl *d) {
+  cgm.emitGlobal(GlobalDecl(d, Dtor_Base));
+
+  if (d->getParent()->getNumVBases() > 0 && d->hasAttr<DLLExportAttr>())
+    cgm.emitGlobal(GlobalDecl(d, Dtor_Complete));
+}
+
+void CIRGenMicrosoftCXXABI::emitCXXStructor(GlobalDecl gd) {
+  if (auto *ctor = dyn_cast<CXXConstructorDecl>(gd.getDecl())) {
+    auto fn = cgm.codegenCXXStructor(gd.getWithCtorType(Ctor_Complete));
+    cgm.maybeSetTrivialComdat(*ctor, fn);
+    return;
   }
 
-  bool shouldTypeidBeNullChecked(QualType srcTy) override {
-    cgm.errorNYI("shouldTypeidBeNullChecked: MSVC ABI");
-    return false;
+  auto *dtor = cast<CXXDestructorDecl>(gd.getDecl());
+  if (gd.getDtorType() == Dtor_Complete &&
+      dtor->getParent()->getNumVBases() == 0)
+    gd = gd.getWithDtorType(Dtor_Base);
+
+  if (gd.getDtorType() == Dtor_Deleting ||
+      gd.getDtorType() == Dtor_VectorDeleting) {
+    cgm.errorNYI(dtor->getSourceRange(),
+                 "emitCXXStructor: MSVC deleting destructor");
+    return;
   }
 
-  mlir::Value emitTypeid(CIRGenFunction &cgf, QualType srcTy, Address thisPtr,
-                         mlir::Type typeInfoPtrTy) override {
-    cgf.cgm.errorNYI(cgf.getLoc(srcTy->getAsCXXRecordDecl()->getLocation()),
-                     "emitTypeid: MSVC ABI");
-    return cgf.getBuilder().getNullPtr(
-        typeInfoPtrTy, cgf.getLoc(srcTy->getAsCXXRecordDecl()->getLocation()));
+  auto fn = cgm.codegenCXXStructor(gd);
+  cgm.maybeSetTrivialComdat(*dtor, fn);
+}
+
+void CIRGenMicrosoftCXXABI::emitDestructorCall(
+    CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
+    bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {
+  if (type == Dtor_Complete && dd->getParent()->getNumVBases() == 0)
+    type = Dtor_Base;
+
+  GlobalDecl gd(dd, type);
+  CIRGenCallee callee = CIRGenCallee::forDirect(
+      cgm.getAddrOfCXXStructor(gd).getOperation(), gd);
+
+  if (dd->isVirtual()) {
+    assert(type != CXXDtorType::Dtor_Deleting &&
+           "The deleting destructor should only be called via a virtual call");
+    thisAddr = adjustThisArgumentForVirtualFunctionCall(cgf, GlobalDecl(dd, type),
+                                                        thisAddr, false);
   }
 
-  void emitBadTypeidCall(CIRGenFunction &cgf, mlir::Location loc) override {
-    cgf.cgm.errorNYI(loc, "emitBadTypeidCall: MSVC ABI");
+  cgf.emitCXXDestructorCall(gd, callee, thisAddr.emitRawPointer(), thisTy,
+                            /*implicitParam=*/nullptr,
+                            /*implicitParamTy=*/QualType(), /*e=*/nullptr);
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::emitVirtualDestructorCall(
+    CIRGenFunction &cgf, const CXXDestructorDecl *dtor, CXXDtorType dtorType,
+    Address thisAddr, DeleteOrMemberCallExpr e) {
+  auto *ce = dyn_cast<const CXXMemberCallExpr *>(e);
+  auto *de = dyn_cast<const CXXDeleteExpr *>(e);
+  assert((ce != nullptr) ^ (de != nullptr));
+  assert(ce == nullptr || ce->arg_begin() == ce->arg_end());
+
+  ASTContext &context = cgm.getASTContext();
+  bool vectorDeletingDtorsEnabled =
+      context.getTargetInfo().emitVectorDeletingDtors(context.getLangOpts());
+  GlobalDecl gd(dtor, vectorDeletingDtorsEnabled ? Dtor_VectorDeleting
+                                                 : Dtor_Deleting);
+  const CIRGenFunctionInfo *fInfo =
+      &cgm.getTypes().arrangeCXXStructorDeclaration(gd);
+  cir::FuncType ty = cgm.getTypes().getFunctionType(*fInfo);
+  CIRGenCallee callee = CIRGenCallee::forVirtual(ce, gd, thisAddr, ty);
+
+  bool isDeleting = dtorType == Dtor_Deleting;
+  bool isArrayDelete = de && de->isArrayForm() && vectorDeletingDtorsEnabled;
+  bool isGlobalDelete = de && de->isGlobalDelete() &&
+                        context.getTargetInfo().callGlobalDeleteInDeletingDtor(
+                            context.getLangOpts());
+  int32_t flags = (isDeleting ? 1 : 0) | (isGlobalDelete ? 4 : 0) |
+                  (isArrayDelete ? 2 : 0);
+  mlir::Value implicitParam = cgf.getBuilder().getSInt32(
+      flags, cgf.getLoc(dtor->getLocation()));
+
+  QualType thisTy;
+  if (ce)
+    thisTy = ce->getObjectType();
+  else
+    thisTy = de->getDestroyedType();
+
+  while (const ArrayType *aTy = context.getAsArrayType(thisTy))
+    thisTy = aTy->getElementType();
+
+  thisAddr = adjustThisArgumentForVirtualFunctionCall(cgf, gd, thisAddr, true);
+  cgf.emitCXXDestructorCall(gd, callee, thisAddr.emitRawPointer(), thisTy,
+                            implicitParam, context.IntTy, ce);
+  return nullptr;
+}
+
+void CIRGenMicrosoftCXXABI::emitVirtualObjectDelete(
+    CIRGenFunction &cgf, const CXXDeleteExpr *de, Address ptr,
+    QualType elementType, const CXXDestructorDecl *dtor) {
+  if (!cgm.getASTContext().getTargetInfo().callGlobalDeleteInDeletingDtor(
+          cgm.getASTContext().getLangOpts())) {
+    cgf.cgm.errorNYI(
+        de->getSourceRange(),
+        "emitVirtualObjectDelete: legacy global delete without deleting dtor flag");
+    return;
   }
+  emitVirtualDestructorCall(cgf, dtor, Dtor_Deleting, ptr, de);
+}
 
-  void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) override {
-    cgm.errorNYI(loc, "emitBadCastCall: MSVC ABI");
+const CXXRecordDecl *
+CIRGenMicrosoftCXXABI::getThisArgumentTypeForMethod(const CXXMethodDecl *md) {
+  if (md->isVirtual()) {
+    GlobalDecl lookupGD;
+    if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
+      lookupGD = GlobalDecl(
+          dd, cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors(
+                  cgm.getASTContext().getLangOpts())
+                  ? Dtor_VectorDeleting
+                  : Dtor_Deleting);
+    } else {
+      lookupGD = GlobalDecl(md);
+    }
+    MethodVFTableLocation ml =
+        cgm.getMicrosoftVTableContext().getMethodVFTableLocation(lookupGD);
+    if (ml.VBase || !ml.VFPtrOffset.isZero())
+      return nullptr;
   }
+  return md->getParent();
+}
 
-  mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc,
-                              QualType srcRecordTy, QualType destRecordTy,
-                              cir::PointerType destCIRTy, bool isRefCast,
-                              Address src) override {
-    cgf.cgm.errorNYI(loc, "emitDynamicCast: MSVC ABI");
-    return cgf.getBuilder().getNullPtr(destCIRTy, loc);
+Address CIRGenMicrosoftCXXABI::adjustThisArgumentForVirtualFunctionCall(
+    CIRGenFunction &cgf, GlobalDecl gd, Address thisAddr, bool virtualCall) {
+  if (!virtualCall) {
+    CharUnits adjustment = getVirtualFunctionPrologueThisAdjustment(gd);
+    if (adjustment.isZero())
+      return thisAddr;
+
+    CIRGenBuilderTy &builder = cgf.getBuilder();
+    mlir::Location loc = cgf.getLoc(gd.getDecl()->getLocation());
+    cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+    mlir::Value u8This = builder.createBitcast(thisAddr.getPointer(), u8PtrTy);
+    assert(adjustment.isPositive());
+    mlir::Value adjConst =
+        builder.getSInt32(adjustment.getQuantity(), loc);
+    mlir::Value adjusted =
+        cir::PtrStrideOp::create(builder, loc, u8PtrTy, u8This, adjConst);
+    return Address(builder.createBitcast(adjusted, thisAddr.getElementType()),
+                   thisAddr.getElementType(), thisAddr.getAlignment());
   }
 
-  mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
-                                          QualType ty) override {
-    cgm.errorNYI(loc, "getAddrOfRTTIDescriptor: MSVC ABI");
+  const auto *md = cast<CXXMethodDecl>(gd.getDecl());
+  GlobalDecl lookupGD = gd;
+  if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
+    if (gd.getDtorType() == Dtor_Complete)
+      return thisAddr;
+
+    lookupGD = GlobalDecl(
+        dd, cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors(
+                cgm.getASTContext().getLangOpts())
+                ? Dtor_VectorDeleting
+                : Dtor_Deleting);
+  }
+  MethodVFTableLocation ml =
+      cgm.getMicrosoftVTableContext().getMethodVFTableLocation(lookupGD);
+
+  CharUnits staticOffset = ml.VFPtrOffset;
+  if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base)
+    staticOffset = CharUnits::Zero();
+
+  Address result = thisAddr;
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  mlir::Location loc = cgf.getLoc(gd.getDecl()->getLocation());
+  cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+
+  if (ml.VBase) {
+    const CXXRecordDecl *derived = md->getParent();
+    const CXXRecordDecl *vbase = ml.VBase;
+    mlir::Value vbaseOffset =
+        getVirtualBaseClassOffset(loc, cgf, result, derived, vbase);
+    mlir::Value rawPtr = builder.createBitcast(result.getPointer(), u8PtrTy);
+    mlir::Value vbasePtr =
+        cir::PtrStrideOp::create(builder, loc, u8PtrTy, rawPtr, vbaseOffset);
+    CharUnits vbaseAlign =
+        cgm.getVBaseAlignment(result.getAlignment(), derived, vbase);
+    result = Address(vbasePtr, builder.getUInt8Ty(), vbaseAlign);
+  }
+
+  if (!staticOffset.isZero()) {
+    assert(staticOffset.isPositive());
+    mlir::Value rawPtr = builder.createBitcast(result.getPointer(), u8PtrTy);
+    mlir::Value offsetVal =
+        builder.getSInt32(staticOffset.getQuantity(), loc);
+    mlir::Value adjustedPtr =
+        cir::PtrStrideOp::create(builder, loc, u8PtrTy, rawPtr, offsetVal);
+    result = Address(adjustedPtr, builder.getUInt8Ty(),
+                     result.getAlignment().alignmentAtOffset(staticOffset));
+  }
+
+  if (result.getElementType() != thisAddr.getElementType()) {
+    mlir::Value castPtr =
+        builder.createBitcast(result.getPointer(), thisAddr.getElementType());
+    result = Address(castPtr, thisAddr.getElementType(), result.getAlignment());
+  }
+
+  return result;
+}
+
+cir::GlobalOp
+CIRGenMicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *rd,
+                                       CharUnits vptrOffset) {
+  VFTableIdTy id(rd, vptrOffset);
+  auto it = vftablesMap.find(id);
+  if (it != vftablesMap.end())
+    return it->second;
+
+  MicrosoftVTableContext &vtContext = cgm.getMicrosoftVTableContext();
+  const VPtrInfoVector &vfptrs = vtContext.getVFPtrOffsets(rd);
+
+  const std::unique_ptr<VPtrInfo> *vfptrI =
+      llvm::find_if(vfptrs, [&](const std::unique_ptr<VPtrInfo> &vpi) {
+        return vpi->FullOffsetInMDC == vptrOffset;
+      });
+  if (vfptrI == vfptrs.end()) {
+    vftablesMap[id] = nullptr;
     return nullptr;
   }
+  const std::unique_ptr<VPtrInfo> &vfptr = *vfptrI;
 
-  CatchTypeInfo getCatchAllTypeInfo() override {
-    cgm.errorNYI("getCatchAllTypeInfo: MSVC ABI");
-    return CatchTypeInfo{nullptr, 0};
+  SmallString<256> vftableName;
+  {
+    llvm::raw_svector_ostream out(vftableName);
+    getMangleContext().mangleCXXVFTable(rd, vfptr->MangledPath, out);
   }
 
-  CatchTypeInfo
-  getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
-                               QualType catchHandlerType) override {
-    cgm.errorNYI(loc, "getAddrOfCXXCatchHandlerType: MSVC ABI");
-    return CatchTypeInfo{nullptr, 0};
+  if (auto gv = cgm.getGlobalValue(vftableName)) {
+    auto globalOp = dyn_cast<cir::GlobalOp>(gv);
+    vftablesMap[id] = globalOp;
+    return globalOp;
   }
 
-  void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override {
-    cgm.errorNYI("emitRethrow: MSVC ABI");
+  cir::GlobalLinkageKind linkage =
+      rd->hasAttr<DLLImportAttr>()
+          ? cir::GlobalLinkageKind::LinkOnceODRLinkage
+          : cgm.getVTableLinkage(rd);
+
+  cir::GlobalOp vtable = cgm.createGlobalOp(
+      cgm.getLoc(rd->getLocation()), vftableName,
+      cgm.getVTableComponentType(), /*isConstant=*/true);
+  vtable.setLinkage(linkage);
+
+  if (rd->hasAttr<DLLExportAttr>())
+    vtable.setVisibility(mlir::SymbolTable::Visibility::Public);
+
+  vftablesMap[id] = vtable;
+  return vtable;
+}
+
+mlir::Value
+CIRGenMicrosoftCXXABI::getVTableAddressPoint(BaseSubobject base,
+                                             const CXXRecordDecl *vtableClass) {
+  cir::GlobalOp vtable = getAddrOfVTable(vtableClass, base.getBaseOffset());
+  if (!vtable)
+    return nullptr;
+
+  mlir::OpBuilder &builder = cgm.getBuilder();
+  auto vtablePtrTy = cir::VPtrType::get(builder.getContext());
+
+  return cir::VTableAddrPointOp::create(
+      builder, cgm.getLoc(vtableClass->getSourceRange()), vtablePtrTy,
+      mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr()),
+      cir::AddressPointAttr::get(cgm.getBuilder().getContext(),
+                                 /*vtableIndex=*/0,
+                                 /*addressPointIndex=*/0));
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::getVTableAddressPointInStructor(
+    CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
+    const CXXRecordDecl *nearestVBase) {
+  return getVTableAddressPoint(base, vtableClass);
+}
+
+CIRGenCallee CIRGenMicrosoftCXXABI::getVirtualFunctionPointer(
+    CIRGenFunction &cgf, GlobalDecl gd, Address thisAddr, mlir::Type ty,
+    SourceLocation loc) {
+  CIRGenBuilderTy &builder = cgm.getBuilder();
+  mlir::Location mlirLoc = cgf.getLoc(loc);
+  cir::PointerType tyPtr = builder.getPointerTo(ty);
+  auto *methodDecl = cast<CXXMethodDecl>(gd.getDecl());
+
+  Address vptr =
+      adjustThisArgumentForVirtualFunctionCall(cgf, gd, thisAddr, /*virtualCall=*/true);
+  mlir::Value vtable = cgf.getVTablePtr(mlirLoc, vptr, methodDecl->getParent());
+
+  MicrosoftVTableContext &vftContext = cgm.getMicrosoftVTableContext();
+  MethodVFTableLocation ml = vftContext.getMethodVFTableLocation(gd);
+
+  mlir::Value vtableSlotPtr = cir::VTableGetVirtualFnAddrOp::create(
+      builder, mlirLoc, builder.getPointerTo(tyPtr), vtable, ml.Index);
+  mlir::Value vfunc = builder.createAlignedLoad(mlirLoc, tyPtr, vtableSlotPtr,
+                                                cgf.getPointerAlign());
+
+  return CIRGenCallee(gd, vfunc.getDefiningOp());
+}
+
+void CIRGenMicrosoftCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
+                                                  const CXXRecordDecl *rd) {
+  MicrosoftVTableContext &vftContext = cgm.getMicrosoftVTableContext();
+  const VPtrInfoVector &vfptrs = vftContext.getVFPtrOffsets(rd);
+
+  for (const auto &info : vfptrs) {
+    cir::GlobalOp vtable = getAddrOfVTable(rd, info->FullOffsetInMDC);
+    if (!vtable || vtable.hasInitializer())
+      continue;
+
+    const VTableLayout &vtLayout =
+        vftContext.getVFTableLayout(rd, info->FullOffsetInMDC);
+    cir::GlobalLinkageKind linkage =
+        rd->hasAttr<DLLImportAttr>()
+            ? cir::GlobalLinkageKind::LinkOnceODRLinkage
+            : cgm.getVTableLinkage(rd);
+
+    mlir::Attribute rtti = nullptr;
+    if (cgm.getASTContext().getLangOpts().RTTIData &&
+        llvm::any_of(vtLayout.vtable_components(),
+                     [](const VTableComponent &vtc) { return vtc.isRTTIKind(); })) {
+      rtti = getAddrOfRTTIDescriptor(cgm.getLoc(rd->getLocation()),
+                                     cgm.getASTContext().getCanonicalTagType(rd));
+    }
+
+    cgvt.createVTableInitializer(vtable, vtLayout, rtti,
+                                 cir::isLocalLinkage(linkage));
+    vtable.setLinkage(linkage);
+    if (cgm.supportsCOMDAT() && cir::isWeakForLinker(linkage))
+      vtable.setSelfComdat();
+    cgm.setGVProperties(vtable, rd);
   }
+}
+
+void CIRGenMicrosoftCXXABI::emitVirtualInheritanceTables(
+    const CXXRecordDecl *rd) {
+  if (!rd->getNumVBases())
+    return;
+  cgm.errorNYI(rd->getSourceRange(), "emitVirtualInheritanceTables: MSVC VBTables");
+}
+
+void CIRGenMicrosoftCXXABI::initializeHiddenVirtualInheritanceMembers(
+    CIRGenFunction &cgf, const CXXRecordDecl *rd) {
+  if (!rd->getNumVBases())
+    return;
+  cgf.cgm.errorNYI(rd->getSourceRange(),
+                   "initializeHiddenVirtualInheritanceMembers: vbptr stores");
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::getVirtualBaseClassOffset(
+    mlir::Location loc, CIRGenFunction &cgf, Address thisAddr,
+    const CXXRecordDecl *classDecl, const CXXRecordDecl *baseClassDecl) {
+  const ASTContext &context = cgm.getASTContext();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  int64_t vbPtrChars =
+      context.getASTRecordLayout(classDecl).getVBPtrOffset().getQuantity();
+  unsigned vbIndex =
+      cgm.getMicrosoftVTableContext().getVBTableIndex(classDecl, baseClassDecl);
+
+  mlir::Value vbaseOffset =
+      getVBaseOffsetFromVBPtr(cgf, loc, thisAddr, vbPtrChars, vbIndex);
+
+  mlir::Type ptrDiffTy = cgm.ptrDiffTy;
+  mlir::Value vbaseOffsetCast = builder.createIntCast(vbaseOffset, ptrDiffTy);
+  mlir::Value vbPtrOffsetCast =
+      builder.getConstantInt(loc, ptrDiffTy, vbPtrChars);
+  return builder.createAdd(loc, vbPtrOffsetCast, vbaseOffsetCast);
+}
 
-  void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override {
-    cgf.cgm.errorNYI(e->getSourceRange(), "emitThrow: MSVC ABI");
+cir::MethodAttr
+CIRGenMicrosoftCXXABI::buildVirtualMethodAttr(cir::MethodType methodTy,
+                                              const CXXMethodDecl *md) {
+  assert(md->isVirtual() && "only deal with virtual member functions");
+
+  MethodVFTableLocation ml =
+      cgm.getMicrosoftVTableContext().getMethodVFTableLocation(md);
+  const ASTContext &astContext = cgm.getASTContext();
+  CharUnits pointerWidth = astContext.toCharUnitsFromBits(
+      astContext.getTargetInfo().getPointerWidth(LangAS::Default));
+  uint64_t vtableOffset = ml.Index * pointerWidth.getQuantity();
+
+  return cir::MethodAttr::get(methodTy, vtableOffset);
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::performThisAdjustment(
+    CIRGenFunction &cgf, Address thisAddr, const CXXRecordDecl *unadjustedClass,
+    const ThunkInfo &ti) {
+  const ThisAdjustment &ta = ti.This;
+  if (ta.isEmpty())
+    return thisAddr.emitRawPointer();
+
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  mlir::Location loc = cgf.getLoc(
+      unadjustedClass ? unadjustedClass->getLocation() : SourceLocation());
+  cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+  mlir::Value v = builder.createBitcast(thisAddr.getPointer(), u8PtrTy);
+
+  if (!ta.Virtual.isEmpty()) {
+    assert(ta.Virtual.Microsoft.VtordispOffset < 0);
+    mlir::Value vtorDispOffset =
+        builder.getSInt32(ta.Virtual.Microsoft.VtordispOffset, loc);
+    mlir::Value vtorDispPtr =
+        cir::PtrStrideOp::create(builder, loc, u8PtrTy, v, vtorDispOffset);
+    mlir::Type i32Ty = builder.getSInt32Ty();
+    mlir::Value vtorDispI32Ptr =
+        builder.createBitcast(vtorDispPtr, builder.getPointerTo(i32Ty));
+    mlir::Value vtorDisp = builder.createLoad(
+        loc, Address(vtorDispI32Ptr, i32Ty, CharUnits::fromQuantity(4)));
+    mlir::Value negVtorDisp = builder.createNeg(loc, vtorDisp);
+    v = cir::PtrStrideOp::create(builder, loc, u8PtrTy, v, negVtorDisp);
+
+    if (ta.Virtual.Microsoft.VBPtrOffset) {
+      assert(ta.Virtual.Microsoft.VBPtrOffset > 0);
+      assert(ta.Virtual.Microsoft.VBOffsetOffset >= 0);
+      mlir::Value vbPtr;
+      unsigned vbTableIndex = ta.Virtual.Microsoft.VBOffsetOffset / 4;
+      mlir::Value vbaseOffset = getVBaseOffsetFromVBPtr(
+          cgf, loc, Address(v, builder.getUInt8Ty(), cgf.getPointerAlign()),
+          -ta.Virtual.Microsoft.VBPtrOffset, vbTableIndex, &vbPtr);
+      v = cir::PtrStrideOp::create(builder, loc, u8PtrTy, vbPtr, vbaseOffset);
+    }
   }
 
-  void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
-                          mlir::Value addr) override {
-    cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: MSVC ABI");
+  if (ta.NonVirtual) {
+    mlir::Value nonVirt = builder.getSInt32(ta.NonVirtual, loc);
+    v = cir::PtrStrideOp::create(builder, loc, u8PtrTy, v, nonVirt);
   }
-};
 
-} // namespace
+  return builder.createBitcast(v, thisAddr.getElementType());
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::performReturnAdjustment(
+    CIRGenFunction &cgf, Address ret, const CXXRecordDecl *unadjustedClass,
+    const ReturnAdjustment &ra) {
+  if (ra.isEmpty())
+    return ret.emitRawPointer();
+
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+  mlir::Location loc = cgf.getLoc(
+      unadjustedClass ? unadjustedClass->getLocation() : SourceLocation());
+  cir::PointerType u8PtrTy = builder.getUInt8PtrTy();
+  mlir::Value v = builder.createBitcast(ret.getPointer(), u8PtrTy);
+
+  if (ra.Virtual.Microsoft.VBIndex) {
+    assert(ra.Virtual.Microsoft.VBIndex > 0);
+    mlir::Value vbPtr;
+    mlir::Value vbaseOffset = getVBaseOffsetFromVBPtr(
+        cgf, loc, ret, ra.Virtual.Microsoft.VBPtrOffset,
+        ra.Virtual.Microsoft.VBIndex, &vbPtr);
+    v = cir::PtrStrideOp::create(builder, loc, u8PtrTy, vbPtr, vbaseOffset);
+  }
+
+  if (ra.NonVirtual) {
+    mlir::Value nonVirt = builder.getSInt32(ra.NonVirtual, loc);
+    v = cir::PtrStrideOp::create(builder, loc, u8PtrTy, v, nonVirt);
+  }
+
+  return builder.createBitcast(v, ret.getElementType());
+}
+
+bool CIRGenMicrosoftCXXABI::shouldTypeidBeNullChecked(QualType srcTy) {
+  const CXXRecordDecl *srcDecl = srcTy->getAsCXXRecordDecl();
+  return !cgm.getASTContext().getASTRecordLayout(srcDecl).hasExtendableVFPtr();
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::emitTypeid(CIRGenFunction &cgf,
+                                              QualType srcTy, Address thisPtr,
+                                              mlir::Type typeInfoPtrTy) {
+  cgf.cgm.errorNYI(cgf.getLoc(srcTy->getAsCXXRecordDecl()->getLocation()),
+                   "emitTypeid: MSVC ABI");
+  return cgf.getBuilder().getNullPtr(
+      typeInfoPtrTy,
+      cgf.getLoc(srcTy->getAsCXXRecordDecl()->getLocation()));
+}
+
+void CIRGenMicrosoftCXXABI::emitBadTypeidCall(CIRGenFunction &cgf,
+                                              mlir::Location loc) {
+  cgf.cgm.errorNYI(loc, "emitBadTypeidCall: MSVC ABI");
+  cir::UnreachableOp::create(cgf.getBuilder(), loc);
+}
+
+void CIRGenMicrosoftCXXABI::emitBadCastCall(CIRGenFunction &cgf,
+                                            mlir::Location loc) {
+  cgm.errorNYI(loc, "emitBadCastCall: MSVC ABI");
+}
+
+mlir::Value CIRGenMicrosoftCXXABI::emitDynamicCast(
+    CIRGenFunction &cgf, mlir::Location loc, QualType srcRecordTy,
+    QualType destRecordTy, cir::PointerType destCIRTy, bool isRefCast,
+    Address src) {
+  cgf.cgm.errorNYI(loc, "emitDynamicCast: MSVC ABI");
+  return cgf.getBuilder().getNullPtr(destCIRTy, loc);
+}
+
+mlir::Attribute
+CIRGenMicrosoftCXXABI::getAddrOfRTTIDescriptor(mlir::Location loc,
+                                               QualType ty) {
+  SmallString<256> mangledName;
+  {
+    llvm::raw_svector_ostream out(mangledName);
+    getMangleContext().mangleCXXRTTI(ty, out);
+  }
+
+  CIRGenBuilderTy &builder = cgm.getBuilder();
+  cir::PointerType voidPtrTy = builder.getVoidPtrTy();
+
+  if (auto gv = cgm.getGlobalValue(mangledName))
+    return builder.getGlobalViewAttr(voidPtrTy, cast<cir::GlobalOp>(gv));
+
+  cir::GlobalOp gv = cgm.createGlobalOp(
+      loc, mangledName, voidPtrTy, /*isConstant=*/true);
+  gv.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
+  return builder.getGlobalViewAttr(voidPtrTy, gv);
+}
+
+CatchTypeInfo
+CIRGenMicrosoftCXXABI::getAddrOfCXXCatchHandlerType(mlir::Location loc,
+                                                    QualType ty,
+                                                    QualType catchHandlerType) {
+  bool isConst, isVolatile, isUnaligned;
+  ty = decomposeTypeForEH(cgm.getASTContext(), ty, isConst, isVolatile,
+                          isUnaligned);
+
+  bool isReference = catchHandlerType->isReferenceType();
+
+  uint32_t flags = 0;
+  if (isConst)
+    flags |= 1;
+  if (isVolatile)
+    flags |= 2;
+  if (isUnaligned)
+    flags |= 4;
+  if (isReference)
+    flags |= 8;
+
+  auto rtti = dyn_cast<cir::GlobalViewAttr>(getAddrOfRTTIDescriptor(loc, ty));
+  assert(rtti && "expected GlobalViewAttr");
+  return CatchTypeInfo{rtti, flags};
+}
+
+void CIRGenMicrosoftCXXABI::emitRethrow(CIRGenFunction &cgf, bool isNoReturn) {
+  if (isNoReturn) {
+    CIRGenBuilderTy &builder = cgf.getBuilder();
+    assert(cgf.currSrcLoc && "expected source location");
+    mlir::Location loc = cgf.getLoc(*cgf.currSrcLoc);
+    cir::ThrowOp::create(builder, loc, mlir::Value(), mlir::FlatSymbolRefAttr(),
+                         mlir::FlatSymbolRefAttr());
+    cir::UnreachableOp::create(builder, loc);
+  } else {
+    cgm.errorNYI("emitRethrow with isNoReturn false: MSVC ABI");
+  }
+}
+
+void CIRGenMicrosoftCXXABI::emitThrow(CIRGenFunction &cgf,
+                                      const CXXThrowExpr *e) {
+  cgf.cgm.errorNYI(e->getSourceRange(), "emitThrow: MSVC ABI");
+}
+
+void CIRGenMicrosoftCXXABI::registerGlobalDtor(const VarDecl *vd,
+                                               cir::FuncOp dtor,
+                                               mlir::Value addr) {
+  if (vd->isNoDestroy(cgm.getASTContext()))
+    return;
+
+  if (cgm.getLangOpts().HLSL) {
+    cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: HLSL");
+    return;
+  }
+}
 
 CIRGenCXXABI *clang::CIRGen::CreateCIRGenMicrosoftCXXABI(CIRGenModule &cgm) {
   return new CIRGenMicrosoftCXXABI(cgm);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 614a731158e455..0ef107ecb9a596 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2178,7 +2178,7 @@ cir::GlobalLinkageKind CIRGenModule::getFunctionLinkage(GlobalDecl gd) {
 
 static cir::GlobalOp
 generateStringLiteral(mlir::Location loc, mlir::TypedAttr c,
-                      cir::GlobalLinkageKind lt, CIRGenModule &cgm,
+                      cir::GlobalLinkageKind linkageKind, CIRGenModule &cgm,
                       StringRef globalName, CharUnits alignment) {
   LangAS as = CodeGenUtils::getGlobalConstantAddressSpace(cgm.getLangOpts(),
                                                           cgm.getTarget());
@@ -2194,7 +2194,7 @@ generateStringLiteral(mlir::Location loc, mlir::TypedAttr c,
   // Set up extra information and add to the module
   gv.setAlignmentAttr(cgm.getSize(alignment));
   gv.setLinkageAttr(
-      cir::GlobalLinkageKindAttr::get(cgm.getBuilder().getContext(), lt));
+      cir::GlobalLinkageKindAttr::get(cgm.getBuilder().getContext(), linkageKind));
   assert(!cir::MissingFeatures::opGlobalThreadLocal());
   assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
   CIRGenModule::setInitializer(gv, c);
@@ -2246,27 +2246,38 @@ cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s,
         uint64_t(alignment.getQuantity()) > *gv.getAlignment())
       gv.setAlignmentAttr(getSize(alignment));
   } else {
+    std::string globalVariableName;
+    cir::GlobalLinkageKind linkageKind;
+
     // Mangle the string literal if that's how the ABI merges duplicate strings.
     // Don't do it if they are writable, since we don't want writes in one TU to
     // affect strings in another.
     if (getCXXABI().getMangleContext().shouldMangleStringLiteral(s) &&
         !getLangOpts().WritableStrings) {
-      errorNYI(s->getSourceRange(),
-               "getGlobalForStringLiteral: mangle string literals");
+      llvm::raw_string_ostream out(globalVariableName);
+      getCXXABI().getMangleContext().mangleStringLiteral(s, out);
+      linkageKind = cir::GlobalLinkageKind::LinkOnceODRLinkage;
+    } else {
+      linkageKind = cir::GlobalLinkageKind::PrivateLinkage;
+      // Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
+      // we need to do that explicitly.
+      globalVariableName = getUniqueGlobalName(name.str());
+    }
+
+    if (auto existingGV = getGlobalValue(globalVariableName)) {
+      gv = cast<cir::GlobalOp>(existingGV);
+      constantStringMap[c] = gv;
+      return gv;
     }
 
-    // Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
-    // we need to do that explicitly.
-    std::string uniqueName = getUniqueGlobalName(name.str());
     // Synthetic string literals (e.g., from SourceLocExpr) may not have valid
     // source locations. Use unknown location in those cases.
     mlir::Location loc = s->getBeginLoc().isValid()
                              ? getLoc(s->getSourceRange())
                              : builder.getUnknownLoc();
     auto typedC = llvm::cast<mlir::TypedAttr>(c);
-    gv = generateStringLiteral(loc, typedC,
-                               cir::GlobalLinkageKind::PrivateLinkage, *this,
-                               uniqueName, alignment);
+    gv = generateStringLiteral(loc, typedC, linkageKind, *this,
+                               globalVariableName, alignment);
     setDSOLocal(static_cast<mlir::Operation *>(gv));
     constantStringMap[c] = gv;
 
@@ -2907,9 +2918,10 @@ StringRef CIRGenModule::getMangledName(GlobalDecl gd) {
   // constructors get mangled the same.
   if (const auto *cd = dyn_cast<CXXConstructorDecl>(canonicalGd.getDecl())) {
     if (!getTarget().getCXXABI().hasConstructorVariants()) {
-      errorNYI(cd->getSourceRange(),
-               "getMangledName: C++ constructor without variants");
-      return cast<NamedDecl>(gd.getDecl())->getIdentifier()->getName();
+      CXXCtorType origCtorType = gd.getCtorType();
+      assert(origCtorType == Ctor_Base || origCtorType == Ctor_Complete);
+      if (origCtorType == Ctor_Base)
+        canonicalGd = GlobalDecl(cd, Ctor_Complete);
     }
   }
 
@@ -3745,12 +3757,13 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
   }
 
   // All MSVC dtors other than the base dtor are linkonce_odr and delegate to
-  // each other bottoming out wiht the base dtor. Therefore we emit non-base
+  // each other bottoming out with the base dtor. Therefore we emit non-base
   // dtors on usage, even if there is no dtor definition in the TU.
   if (isa_and_nonnull<CXXDestructorDecl>(d) &&
       getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(d),
                                          gd.getDtorType()))
-    errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor");
+    errorNYI(d->getSourceRange(),
+             "getOrCreateCIRFunction: MSVC non-base destructor thunk");
 
   // This is the first use or definition of a mangled name. If there is a
   // deferred decl with this name, remember that we need to emit it at the end
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 80bf5940e461ea..aa450e6ce45ee2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -591,6 +591,13 @@ class CIRGenModule : public CIRGenTypeCache {
     return vtables.getItaniumVTableContext();
   }
 
+  MicrosoftVTableContext &getMicrosoftVTableContext() {
+    return vtables.getMicrosoftVTableContext();
+  }
+  const MicrosoftVTableContext &getMicrosoftVTableContext() const {
+    return vtables.getMicrosoftVTableContext();
+  }
+
   /// This contains all the decls which have definitions but which are deferred
   /// for emission and therefore should only be output if they are actually
   /// used. If a decl is in this, then it is known to have not been referenced
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h b/clang/lib/CIR/CodeGen/CIRGenVTables.h
index 1e4da7d8f945f0..29fde7445c9465 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.h
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h
@@ -76,6 +76,14 @@ class CIRGenVTables {
     return *llvm::cast<clang::ItaniumVTableContext>(vtContext);
   }
 
+  clang::MicrosoftVTableContext &getMicrosoftVTableContext() {
+    return *cast<clang::MicrosoftVTableContext>(vtContext);
+  }
+
+  const clang::MicrosoftVTableContext &getMicrosoftVTableContext() const {
+    return *cast<clang::MicrosoftVTableContext>(vtContext);
+  }
+
   /// Generate a construction vtable for the given base subobject.
   cir::GlobalOp
   generateConstructionVTable(const CXXRecordDecl *rd, const BaseSubobject &base,
diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index bc0aed0b01ff03..e7b47ae0169779 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -160,8 +160,16 @@ void CIRGenerator::HandleTagDeclDefinition(TagDecl *d) {
 
   // For MSVC compatibility, treat declarations of static data members with
   // inline initializers as definitions.
-  if (astContext->getTargetInfo().getCXXABI().isMicrosoft())
-    cgm->errorNYI(d->getSourceRange(), "HandleTagDeclDefinition: MSABI");
+  if (astContext->getTargetInfo().getCXXABI().isMicrosoft()) {
+    for (Decl *member : d->decls()) {
+      if (auto *vd = dyn_cast<VarDecl>(member)) {
+        if (astContext->isMSStaticDataMemberInlineDefinition(vd) &&
+            astContext->DeclMustBeEmitted(vd)) {
+          cgm->emitGlobal(vd);
+        }
+      }
+    }
+  }
 
   // For OpenMP emit declare reduction functions or declare mapper, if
   // required.
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerMicrosoftCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerMicrosoftCXXABI.cpp
index 1cd7306d74dba9..e8d881493dccbb 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerMicrosoftCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerMicrosoftCXXABI.cpp
@@ -1,4 +1,4 @@
-//===---- LowerMicrosoftCXXABI.cpp - Emit CIR for Microsoft-specific code -===//
+//===---- LowerMicrosoftCXXABI.cpp - Emit CIR code Microsoft-specific code -===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -59,10 +59,10 @@ class LowerMicrosoftCXXABI : public CIRCXXABI {
     llvm_unreachable("Microsoft ABI member pointer lowering NYI");
   }
 
-  void
-  lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
-                 mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
-                 mlir::ConversionPatternRewriter &rewriter) const override {
+  void lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee,
+                      mlir::Value &thisArg, mlir::Value loweredMethod,
+                      mlir::Value loweredObjectPtr,
+                      mlir::ConversionPatternRewriter &rewriter) const override {
     llvm_unreachable("Microsoft ABI method pointer lowering NYI");
   }
 
@@ -136,7 +136,9 @@ class LowerMicrosoftCXXABI : public CIRCXXABI {
 
   clang::CharUnits
   getArrayCookieSizeImpl(clang::CharUnits elementAlign) const override {
-    llvm_unreachable("Microsoft ABI array cookie lowering NYI");
+    clang::CharUnits cookieSize =
+        clang::CharUnits::fromQuantity(getPtrSizeInBits() / 8);
+    return std::max(cookieSize, elementAlign);
   }
 
   mlir::Value readArrayCookieImpl(mlir::Location loc, mlir::Value allocPtr,
@@ -144,7 +146,10 @@ class LowerMicrosoftCXXABI : public CIRCXXABI {
                                   clang::CharUnits cookieAlignment,
                                   const mlir::DataLayout &dataLayout,
                                   CIRBaseBuilderTy &builder) const override {
-    llvm_unreachable("Microsoft ABI array cookie lowering NYI");
+    mlir::Type sizeTy = builder.getUIntNTy(getPtrSizeInBits());
+    mlir::Value cookiePtr =
+        builder.createBitcast(allocPtr, builder.getPointerTo(sizeTy));
+    return builder.createLoad(loc, cookiePtr);
   }
 };
 
diff --git a/clang/test/CIR/CodeGen/cxx-msvc-abi.cpp b/clang/test/CIR/CodeGen/cxx-msvc-abi.cpp
index e1a4d7519f5d46..4e3f3ab3800cda 100644
--- a/clang/test/CIR/CodeGen/cxx-msvc-abi.cpp
+++ b/clang/test/CIR/CodeGen/cxx-msvc-abi.cpp
@@ -1,6 +1,126 @@
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++17 -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
 
-void empty() {}
+// Adapted from Clang CodeGenCXX/microsoft-abi-methods.cpp,
+// microsoft-abi-structors.cpp, and microsoft-abi-vftables.cpp.
 
-// CIR: cir.func {{.*}} @"?empty@@YAXXZ"()
+//===----------------------------------------------------------------------===//
+// Section 1: Member Functions & Calling Conventions
+//===----------------------------------------------------------------------===//
+
+class MethodsTest {
+public:
+  void simple_method() {}
+  void vararg_method(const char *fmt, ...) {}
+  static void static_method() {}
+};
+
+void call_methods() {
+  MethodsTest obj;
+  obj.simple_method();
+  obj.vararg_method("test");
+  MethodsTest::static_method();
+}
+
+// CIR-DAG: cir.global {{.*}} linkonce_odr {{.*}} @"??_C at _
+// CIR-DAG: cir.func {{.*}} @"?simple_method at MethodsTest@@QEAAXXZ"(%{{.*}}: !cir.ptr<!rec_MethodsTest>
+// CIR-DAG: cir.func {{.*}} @"?vararg_method at MethodsTest@@QEAAXPEBDZZ"(%{{.*}}: !cir.ptr<!rec_MethodsTest>{{.*}}, %{{.*}}: !cir.ptr<!s8i>{{.*}}, ...)
+// CIR-DAG: cir.func {{.*}} @"?static_method at MethodsTest@@SAXXZ"()
+
+//===----------------------------------------------------------------------===//
+// Section 2: Structors and 'this' Return Calling Convention
+//===----------------------------------------------------------------------===//
+
+class BasicA {
+  int x;
+public:
+  BasicA() { x = 42; }
+  ~BasicA();
+};
+
+BasicA::~BasicA() {}
+
+// Destructors return void.
+// CIR-LABEL: cir.func {{.*}} @"??1BasicA@@QEAA at XZ"
+
+void test_basic() {
+  BasicA a;
+}
+
+// In the MSVC ABI, constructors return 'this' (hasThisReturn is true).
+// CIR-LABEL: cir.func {{.*}} @"??0BasicA@@QEAA at XZ"
+// CIR-SAME: (%[[THIS_ARG:.*]]: !cir.ptr<!rec_BasicA>{{.*}}) -> (!cir.ptr<!rec_BasicA>
+// CIR:   %[[RETVAL:.*]] = cir.alloca "__retval"
+// CIR:   %[[THIS_ALLOCA:.*]] = cir.alloca "this"
+// CIR:   cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
+// CIR:   %[[LOADED_THIS:.*]] = cir.load %[[THIS_ALLOCA]]
+// CIR:   cir.store {{.*}}%[[LOADED_THIS]], %[[RETVAL]]
+// CIR:   cir.const #cir.int<42>
+// CIR:   %[[LOADED_RET:.*]] = cir.load %[[RETVAL]]
+// CIR:   cir.return %[[LOADED_RET]] : !cir.ptr<!rec_BasicA>
+// CIR: }
+
+// Constructor & Destructor Inheritance chain:
+class Base {
+public:
+  Base();
+  ~Base();
+};
+
+class Derived : public Base {
+public:
+  Derived();
+  ~Derived();
+};
+
+Derived::Derived() {}
+Derived::~Derived() {}
+
+// Derived ctor calls Base ctor and returns this:
+// CIR-LABEL: cir.func {{.*}} @"??0Derived@@QEAA at XZ"
+// CIR-SAME: (%[[THIS:.*]]: !cir.ptr<!rec_Derived>{{.*}}) -> (!cir.ptr<!rec_Derived>
+// CIR:   cir.call @"??0Base@@QEAA at XZ"
+// CIR:   cir.return %{{.*}} : !cir.ptr<!rec_Derived>
+
+// Derived dtor calls Base dtor:
+// CIR-LABEL: cir.func {{.*}} @"??1Derived@@QEAA at XZ"
+// CIR-SAME: (%[[THIS:.*]]: !cir.ptr<!rec_Derived>
+// CIR:   cir.call @"??1Base@@QEAA at XZ"
+
+//===----------------------------------------------------------------------===//
+// Section 3: Virtual Functions and Vector/Scalar Deleting Destructors
+//===----------------------------------------------------------------------===//
+
+class Shape {
+public:
+  virtual ~Shape();
+  virtual void draw();
+};
+
+void call_virtual(Shape *s) {
+  s->draw();
+}
+
+// Virtual call uses vtable lookup:
+// CIR-LABEL: cir.func {{.*}} @"?call_virtual@@YAXPEAVShape@@@Z"
+// CIR-SAME: (%[[ARG:.*]]: !cir.ptr<!rec_Shape>{{.*}})
+// CIR:   %[[VPTR:.*]] = cir.vtable.get_vptr
+// CIR:   %[[VTABLE_PTR:.*]] = cir.load align(8) %[[VPTR]]
+// CIR:   %[[VFN_SLOT:.*]] = cir.vtable.get_virtual_fn_addr %[[VTABLE_PTR]][1]
+// CIR:   %[[VFN:.*]] = cir.load align(8) %[[VFN_SLOT]]
+// CIR:   cir.call %[[VFN]](%{{.*}})
+
+void delete_virtual(Shape *s) {
+  delete s;
+}
+
+// Virtual destructor call triggers vector/scalar deleting destructor (?_E or ?_G)
+// with implicit should_call_delete flag passed as an argument:
+// CIR-LABEL: cir.func {{.*}} @"?delete_virtual@@YAXPEAVShape@@@Z"
+// CIR-SAME: (%[[ARG:.*]]: !cir.ptr<!rec_Shape>{{.*}})
+// CIR:   %[[FLAG:.*]] = cir.const #cir.int<1> : !s32i
+// CIR:   %[[VPTR:.*]] = cir.vtable.get_vptr
+// CIR:   %[[VTABLE_PTR:.*]] = cir.load align(8) %[[VPTR]]
+// CIR:   %[[VFN_SLOT:.*]] = cir.vtable.get_virtual_fn_addr %[[VTABLE_PTR]][0]
+// CIR:   %[[VFN:.*]] = cir.load align(8) %[[VFN_SLOT]]
+// CIR:   cir.call %[[VFN]](%{{.*}}, %[[FLAG]])



More information about the Mlir-commits mailing list