https://github.com/erichkeane updated 
https://github.com/llvm/llvm-project/pull/184449

>From d41ae9cf53458b8ab9c4c1f516e8cbd2862474d0 Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Fri, 20 Feb 2026 08:32:50 -0800
Subject: [PATCH 1/2] [CIR] Implement 'typeid' operator lowering

This patch adds typeid lowering, which uses a lot of the infrastructure
from dynamic_cast.  However, this adds a `get_type_info` operation that
gets the type info out of a vtable pointer as well, which lets the
offset be handled by the ABI specific lowering code.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  42 ++++-
 clang/lib/CIR/CodeGen/CIRGenCXXABI.h          |   4 +
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          |   6 +-
 clang/lib/CIR/CodeGen/CIRGenException.cpp     |   2 +-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |   7 +-
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |   4 +-
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       |  60 ++++++
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  |  15 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |   2 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |   6 +-
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp |  61 ++++++-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  11 +-
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  14 +-
 .../CIR/Dialect/Transforms/CXXABILowering.cpp |  10 +
 .../Transforms/TargetLowering/CIRCXXABI.h     |   4 +
 .../TargetLowering/LowerItaniumCXXABI.cpp     |  17 ++
 clang/test/CIR/CodeGenCXX/Inputs/typeinfo     |  24 +++
 clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp    |  46 +++++
 .../CIR/CodeGenCXX/typeid-should-throw.cpp    | 172 ++++++++++++++++++
 clang/test/CIR/CodeGenCXX/typeid.cpp          | 104 +++++++++++
 20 files changed, 586 insertions(+), 25 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenCXX/Inputs/typeinfo
 create mode 100644 clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp
 create mode 100644 clang/test/CIR/CodeGenCXX/typeid-should-throw.cpp
 create mode 100644 clang/test/CIR/CodeGenCXX/typeid.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 3a17b0a381a76..ea175c35a4565 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2813,7 +2813,7 @@ def CIR_VTableGetVirtualFnAddrOp : 
CIR_Op<"vtable.get_virtual_fn_addr", [
     called.
 
     The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
-    have been returned by a previous call to `cir.vatble.get_vptr`. The
+    have been returned by a previous call to `cir.vtable.get_vptr`. The
     `index` operand is an index of the virtual function in the vtable.
 
     The return type is a pointer-to-pointer to the function type.
@@ -2845,6 +2845,46 @@ def CIR_VTableGetVirtualFnAddrOp : 
CIR_Op<"vtable.get_virtual_fn_addr", [
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// VTableGetTypeInfoOp
+//===----------------------------------------------------------------------===//
+
+def CIR_VTableGetTypeInfoOp : CIR_Op<"vtable.get_type_info", [
+  Pure
+]> {
+  let summary = "Get the address of the type_info from the vtable";
+  let description = [{
+    The `vtable.get_type_info` operation retreives the address of the dynamic
+    type_info/rtti object from an object's vtable. This is an ABI independent
+    abstraction of this operation.
+
+    The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
+    have been returned by a previous call to `cir.vtable.get_vptr`.
+
+    The return type is a loadable pointer to a `type_info` struct.
+
+    Example:
+    ```mlir
+    %5 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_A> -> !cir.ptr<!cir.vptr>
+    %6 = cir.load align(8) %5 : !cir.ptr<!cir.vptr>, !cir.vptr
+    %7 = cir.vtable.get_type_info %6 : !cir.vptr -> 
!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+    %8 = cir.load align(8) %7 : !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>, 
!cir.ptr<!rec_std3A3Atype_info>
+
+    ```
+  }];
+
+  let arguments = (ins Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr);
+  let results = (outs CIR_PointerType:$result);
+
+  let assemblyFormat = [{
+    $vptr attr-dict `:` qualified(type($vptr)) `->` qualified(type($result))
+  }];
+
+  let hasLLVMLowering = false;
+  let hasCXXABILowering = true;
+}
+
+
 
//===----------------------------------------------------------------------===//
 // VTTAddrPointOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h 
b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 04042597490a0..c59185f5a14b0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -169,6 +169,10 @@ class CIRGenCXXABI {
   getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
                                QualType catchHandlerType) = 0;
   virtual CatchTypeInfo getCatchAllTypeInfo();
+  virtual bool shouldTypeidBeNullChecked(QualType srcTy) = 0;
+  virtual mlir::Value emitTypeid(CIRGenFunction &cgf, QualType srcTy,
+                                 Address thisPtr, mlir::Type typeInfoPtrTy) = 
0;
+  virtual void emitBadTypeidCall(CIRGenFunction &cgf, mlir::Location loc) = 0;
 
   /// Get the implicit (second) parameter that comes after the "this" pointer,
   /// or nullptr if there is isn't one.
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index c92296352db4e..157dc3fdd56fb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1270,7 +1270,8 @@ void CallArg::copyInto(CIRGenFunction &cgf, Address addr,
 
 mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc,
                                             cir::FuncOp callee,
-                                            ArrayRef<mlir::Value> args) {
+                                            ArrayRef<mlir::Value> args,
+                                            mlir::NamedAttrList attrs) {
   // TODO(cir): set the calling convention to this runtime call.
   assert(!cir::MissingFeatures::opFuncCallingConv());
 
@@ -1278,6 +1279,9 @@ mlir::Value 
CIRGenFunction::emitRuntimeCall(mlir::Location loc,
   assert(call->getNumResults() <= 1 &&
          "runtime functions have at most 1 result");
 
+  if (!attrs.empty())
+    call->setAttrs(attrs);
+
   if (call->getNumResults() == 0)
     return nullptr;
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp 
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index be90e19d03c76..d5e176e18a11e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -193,7 +193,7 @@ static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
   auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
 
   cir::FuncOp personalityFn = cgm.createRuntimeFunction(
-      funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true);
+      funcTy, personality.personalityFn, {}, /*isLocal=*/true);
 
   return personalityFn.getSymName();
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index d0fa9234c73c3..51dc297e86d01 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1490,8 +1490,7 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
     LValue lv = emitLValue(e->getSubExpr());
     // Propagate the volatile qualifier to LValue, if exists in e.
     if (e->changesVolatileQualification())
-      cgm.errorNYI(e->getSourceRange(),
-                   "emitCastLValue: NoOp changes volatile qual");
+      lv.getQuals() = e->getType().getQualifiers();
     if (lv.isSimple()) {
       Address v = lv.getAddress();
       if (v.isValid()) {
@@ -2821,3 +2820,7 @@ bool 
CIRGenFunction::isLValueSuitableForInlineAtomic(LValue lv) {
   cgm.errorNYI("LValueSuitableForInlineAtomic LangOpts MSVolatile");
   return false;
 }
+
+LValue CIRGenFunction::emitCXXTypeidLValue(const CXXTypeidExpr *e) {
+  return makeNaturalAlignAddrLValue(emitCXXTypeidExpr(e), e->getType());
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index f5f3655802915..d4f354d5dd94d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -386,9 +386,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
     cgf.cgm.errorNYI(e->getSourceRange(),
                      "AggExprEmitter: VisitCXXScalarValueInitExpr");
   }
-  void VisitCXXTypeidExpr(CXXTypeidExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: 
VisitCXXTypeidExpr");
-  }
+  void VisitCXXTypeidExpr(CXXTypeidExpr *e) { emitAggLoadOfLValue(e); }
   void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e) {
     Visit(e->getSubExpr());
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 56a7539a841d1..0e94b50c39c40 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1334,3 +1334,63 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address 
thisAddr,
   return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
                                          destCirTy, isRefCast, thisAddr);
 }
+
+static mlir::Value emitCXXTypeidFromVTable(CIRGenFunction &cgf, const Expr *e,
+                                           mlir::Type typeInfoPtrTy,
+                                           bool hasNullCheck) {
+  Address thisPtr = cgf.emitLValue(e).getAddress();
+  QualType srcType = e->getType();
+
+  // C++ [class.cdtor]p4:
+  //   If the operand of typeid refers to the object under construction or
+  //   destruction and the static type of the operand is neither the 
constructor
+  //   or destructor’s class nor one of its bases, the behavior is undefined.
+  assert(!cir::MissingFeatures::sanitizers());
+
+  if (hasNullCheck && cgf.cgm.getCXXABI().shouldTypeidBeNullChecked(srcType)) {
+    mlir::Value isThisNull =
+        cgf.getBuilder().createPtrIsNull(thisPtr.getPointer());
+    // We don't really care about the value, we just want to make sure the
+    // 'true' side calls bad-type-id.
+    auto ifOp = cir::IfOp::create(
+        cgf.getBuilder(), cgf.getLoc(e->getSourceRange()), isThisNull,
+        /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) {
+          cgf.cgm.getCXXABI().emitBadTypeidCall(cgf, loc);
+        });
+    cgf.getBuilder().setInsertionPointAfter(ifOp);
+  }
+
+  return cgf.cgm.getCXXABI().emitTypeid(cgf, srcType, thisPtr, typeInfoPtrTy);
+}
+
+mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *e) {
+  mlir::Location loc = getLoc(e->getSourceRange());
+  mlir::Type resultType = cir::PointerType::get(convertType(e->getType()));
+  QualType ty = e->isTypeOperand() ? e->getTypeOperand(getContext())
+                                   : e->getExprOperand()->getType();
+  // C++ [expr.typeid]p2:
+  //   When typeid is applied to a glvalue expression whose type is a
+  //   polymorphic class type, the result refers to a std::type_info object
+  //   representing the type of the most derived object (that is, the dynamic
+  //   type) to which the glvalue refers.
+  // If the operand is already most derived object, no need to look up vtable.
+  if (!e->isTypeOperand() && e->isPotentiallyEvaluated() &&
+      !e->isMostDerived(getContext()))
+    return emitCXXTypeidFromVTable(*this, e->getExprOperand(), resultType,
+                                   e->hasNullCheck());
+
+  auto typeInfo =
+      cast<cir::GlobalViewAttr>(cgm.getAddrOfRTTIDescriptor(loc, ty));
+  // `getAddrOfRTTIDescriptor` lies to us and always gives us a uint8ptr as its
+  // type, however we need the value of the actual global to call the
+  // get-global-op, so look it up here.
+  auto typeInfoGlobal =
+      cast<cir::GlobalOp>(cgm.getGlobalValue(typeInfo.getSymbol().getValue()));
+  auto getTypeInfo = cir::GetGlobalOp::create(
+      builder, loc, builder.getPointerTo(typeInfoGlobal.getSymType()),
+      typeInfoGlobal.getSymName());
+  // The ABI is just generating these sometimes as ptr to u8, but they are
+  // simply a representation of the type_info. So we have to cast this, if
+  // necessary (createBitcast is a noop if the types match).
+  return builder.createBitcast(getTypeInfo, resultType);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index e3dca9bc0f3c7..2a7ee75b42dea 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1412,10 +1412,9 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
   }
 
   // Handle typeid(T).
-  if (base.dyn_cast<TypeInfoLValue>()) {
-    cgm.errorNYI("ConstantLValueEmitter: typeid");
-    return {};
-  }
+  if (TypeInfoLValue typeInfo = base.dyn_cast<TypeInfoLValue>())
+    return cast<cir::GlobalViewAttr>(cgm.getAddrOfRTTIDescriptor(
+        cgm.getBuilder().getUnknownLoc(), QualType(typeInfo.getType(), 0)));
 
   // Otherwise, it must be an expression.
   return Visit(base.get<const Expr *>());
@@ -1479,8 +1478,12 @@ ConstantLValue 
ConstantLValueEmitter::VisitBlockExpr(const BlockExpr *e) {
 
 ConstantLValue
 ConstantLValueEmitter::VisitCXXTypeidExpr(const CXXTypeidExpr *e) {
-  cgm.errorNYI(e->getSourceRange(), "ConstantLValueEmitter: cxx typeid expr");
-  return {};
+  if (e->isTypeOperand())
+    return cast<cir::GlobalViewAttr>(
+        cgm.getAddrOfRTTIDescriptor(cgm.getLoc(e->getSourceRange()),
+                                    e->getTypeOperand(cgm.getASTContext())));
+  return cast<cir::GlobalViewAttr>(cgm.getAddrOfRTTIDescriptor(
+      cgm.getLoc(e->getSourceRange()), e->getExprOperand()->getType()));
 }
 
 ConstantLValue ConstantLValueEmitter::VisitMaterializeTemporaryExpr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index e10c6d44cd35e..c64630db08965 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1092,6 +1092,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
     CXXDefaultArgExprScope scope(*this, dae);
     return emitLValue(dae->getExpr());
   }
+  case Expr::CXXTypeidExprClass:
+    return emitCXXTypeidLValue(cast<CXXTypeidExpr>(e));
   case Expr::ParenExprClass:
     return emitLValue(cast<ParenExpr>(e)->getSubExpr());
   case Expr::GenericSelectionExprClass:
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 2af3f35e355b1..a5003feb2a205 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1464,6 +1464,8 @@ class CIRGenFunction : public CIRGenTypeCache {
                                              cir::CaseOpKind kind,
                                              bool buildingTopLevelCase);
 
+  LValue emitCXXTypeidLValue(const CXXTypeidExpr *e);
+
   mlir::LogicalResult emitCaseStmt(const clang::CaseStmt &s,
                                    mlir::Type condType,
                                    bool buildingTopLevelCase);
@@ -1601,6 +1603,7 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
 
+  mlir::Value emitCXXTypeidExpr(const CXXTypeidExpr *e);
   mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce);
 
   /// Emit an expression as an initializer for an object (variable, field, 
etc.)
@@ -1638,7 +1641,8 @@ class CIRGenFunction : public CIRGenTypeCache {
   void emitReturnOfRValue(mlir::Location loc, RValue rv, QualType ty);
 
   mlir::Value emitRuntimeCall(mlir::Location loc, cir::FuncOp callee,
-                              llvm::ArrayRef<mlir::Value> args = {});
+                              llvm::ArrayRef<mlir::Value> args = {},
+                              mlir::NamedAttrList attrs = {});
 
   void emitInvariantStart(CharUnits size, mlir::Value addr, mlir::Location 
loc);
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index f195b9325a41c..49e9e40f03256 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -137,6 +137,11 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
                                       const CXXRecordDecl *unadjustedClass,
                                       const ReturnAdjustment &ra) override;
 
+  bool shouldTypeidBeNullChecked(QualType srcTy) override;
+  mlir::Value emitTypeid(CIRGenFunction &cgf, QualType SrcRecordTy,
+                         Address thisPtr, mlir::Type StdTypeInfoPtrTy) 
override;
+  void emitBadTypeidCall(CIRGenFunction &cgf, mlir::Location loc) override;
+
   mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
                                           QualType ty) override;
 
@@ -997,12 +1002,13 @@ const char *vTableClassNameForType(const CIRGenModule 
&cgm, const Type *ty) {
   case Type::ConstantArray:
   case Type::IncompleteArray:
   case Type::VariableArray:
-    cgm.errorNYI("VTableClassNameForType: __array_type_info");
-    break;
+    // abi::__array_type_info.
+    return "_ZTVN10__cxxabiv117__array_type_infoE";
 
   case Type::FunctionNoProto:
   case Type::FunctionProto:
-    cgm.errorNYI("VTableClassNameForType: __function_type_info");
+    // abi::__function_type_info.
+    return "_ZTVN10__cxxabiv120__function_type_infoE";
     break;
 
   case Type::Enum:
@@ -1033,11 +1039,13 @@ const char *vTableClassNameForType(const CIRGenModule 
&cgm, const Type *ty) {
 
   case Type::ObjCObjectPointer:
   case Type::Pointer:
-    cgm.errorNYI("VTableClassNameForType: __pointer_type_info");
+    // abi::__pointer_type_info.
+    return "_ZTVN10__cxxabiv119__pointer_type_infoE";
     break;
 
   case Type::MemberPointer:
-    cgm.errorNYI("VTableClassNameForType: __pointer_to_member_type_info");
+    // abi::__pointer_to_member_type_info.
+    return "_ZTVN10__cxxabiv129__pointer_to_member_type_infoE";
     break;
 
   case Type::HLSLAttributedResource:
@@ -1550,6 +1558,49 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
   return builder.getGlobalViewAttr(builder.getUInt8PtrTy(), gv);
 }
 
+bool CIRGenItaniumCXXABI::shouldTypeidBeNullChecked(QualType srcTy) {
+  return true;
+}
+
+void CIRGenItaniumCXXABI::emitBadTypeidCall(CIRGenFunction &cgf,
+                                            mlir::Location loc) {
+  // void __cxa_bad_typeid();
+  cir::FuncType fnTy =
+      cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
+  mlir::NamedAttrList attrs;
+  attrs.set(cir::CIRDialect::getNoReturnAttrName(),
+            mlir::UnitAttr::get(&cgf.cgm.getMLIRContext()));
+
+  cgf.emitRuntimeCall(
+      loc, cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_typeid", attrs), {},
+      attrs);
+  cir::UnreachableOp::create(cgf.getBuilder(), loc);
+  cgf.getBuilder().clearInsertionPoint();
+}
+
+mlir::Value CIRGenItaniumCXXABI::emitTypeid(CIRGenFunction &cgf, QualType 
srcTy,
+                                            Address thisPtr,
+                                            mlir::Type typeInfoPtrTy) {
+  auto *classDecl = srcTy->castAsCXXRecordDecl();
+  mlir::Location loc = cgm.getLoc(classDecl->getSourceRange());
+  mlir::Value vptr = cgf.getVTablePtr(loc, thisPtr, classDecl);
+  mlir::Value vtbl;
+
+  // TODO(cir): In classic codegen relative layouts cause us to do a
+  // 'load_relative' of -4 here. We probably don't want to reprensent this in
+  // CIR at all, but we should have the NYI here since this could be
+  // meaningful/notable for implementation of relative layout in the future.
+  if (cgm.getItaniumVTableContext().isRelativeLayout())
+    cgm.errorNYI("buildVTablePointer: isRelativeLayout");
+  else
+    vtbl = cir::VTableGetTypeInfoOp::create(
+        cgf.getBuilder(), loc, cgf.getBuilder().getPointerTo(typeInfoPtrTy),
+        vptr);
+
+  return cgf.getBuilder().createAlignedLoad(loc, typeInfoPtrTy, vtbl,
+                                            cgf.getPointerAlign());
+}
+
 mlir::Attribute CIRGenItaniumCXXABI::getAddrOfRTTIDescriptor(mlir::Location 
loc,
                                                              QualType ty) {
   return CIRGenItaniumRTTIBuilder(*this, cgm).buildTypeInfo(loc, ty);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 3ef487465ab80..2d2831b809884 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2379,7 +2379,7 @@ void CIRGenModule::setCIRFunctionAttributesForDefinition(
 cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
     StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable,
     bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,
-    mlir::ArrayAttr extraAttrs) {
+    mlir::NamedAttrList extraAttrs) {
   const Decl *d = gd.getDecl();
 
   if (const auto *fd = cast_or_null<FunctionDecl>(d)) {
@@ -2472,6 +2472,10 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
 
   if (d)
     setFunctionAttributes(gd, funcOp, /*isIncompleteFunction=*/false, isThunk);
+  if (!extraAttrs.empty()) {
+    extraAttrs.append(funcOp->getAttrs());
+    funcOp->setAttrs(extraAttrs);
+  }
 
   // 'dontDefer' actually means don't move this to the deferredDeclsToEmit 
list.
   if (dontDefer) {
@@ -2656,14 +2660,15 @@ static void setWindowsItaniumDLLImport(CIRGenModule 
&cgm, bool isLocal,
 }
 
 cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
-                                                StringRef name, 
mlir::ArrayAttr,
+                                                StringRef name,
+                                                mlir::NamedAttrList extraAttrs,
                                                 bool isLocal,
                                                 bool assumeConvergent) {
   if (assumeConvergent)
     errorNYI("createRuntimeFunction: assumeConvergent");
 
   cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(),
-                                             /*forVtable=*/false);
+                                             /*forVtable=*/false, extraAttrs);
 
   if (entry) {
     // TODO(cir): set the attributes of the function.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 0f456f1f39ceb..c422eb9cd272f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -621,7 +621,16 @@ class CIRGenModule : public CIRGenTypeCache {
                          clang::GlobalDecl gd, bool forVTable,
                          bool dontDefer = false, bool isThunk = false,
                          ForDefinition_t isForDefinition = NotForDefinition,
-                         mlir::ArrayAttr extraAttrs = {});
+                         mlir::NamedAttrList extraAttrs = {});
+
+  cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName,
+                                     mlir::Type funcType, clang::GlobalDecl gd,
+                                     bool forVTable,
+                                     mlir::NamedAttrList extraAttrs) {
+    return getOrCreateCIRFunction(mangledName, funcType, gd, forVTable,
+                                  /*dontDefer=*/false, /*isThunk=*/false,
+                                  NotForDefinition, extraAttrs);
+  }
 
   cir::FuncOp createCIRFunction(mlir::Location loc, llvm::StringRef name,
                                 cir::FuncType funcType,
@@ -637,7 +646,8 @@ class CIRGenModule : public CIRGenTypeCache {
                                const clang::FunctionDecl *funcDecl);
 
   cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
-                                    mlir::ArrayAttr = {}, bool isLocal = false,
+                                    mlir::NamedAttrList extraAttrs = {},
+                                    bool isLocal = false,
                                     bool assumeConvergent = false);
 
   static constexpr const char *builtinCoroId = "__builtin_coro_id";
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index cc6a6544a529a..b76dee98713cc 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -368,6 +368,15 @@ mlir::LogicalResult 
CIRGetRuntimeMemberOpABILowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRVTableGetTypeInfoOpABILowering::matchAndRewrite(
+    cir::VTableGetTypeInfoOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  mlir::Value loweredResult =
+      lowerModule->getCXXABI().lowerVTableGetTypeInfo(op, rewriter);
+  rewriter.replaceOp(op, loweredResult);
+  return mlir::success();
+}
+
 // Prepare the type converter for the CXXABI lowering pass.
 // Even though this is a CIR-to-CIR pass, we are eliminating some CIR types.
 static void prepareCXXABITypeConverter(mlir::TypeConverter &converter,
@@ -446,6 +455,7 @@ populateCXXABIConversionTarget(mlir::ConversionTarget 
&target,
         return typeConverter.isLegal(op.getSymType());
       });
   target.addIllegalOp<cir::DynamicCastOp>();
+  target.addIllegalOp<cir::VTableGetTypeInfoOp>();
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
index af5034db0a577..88c5265e57491 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
@@ -122,6 +122,10 @@ class CIRCXXABI {
 
   virtual mlir::Value lowerDynamicCast(cir::DynamicCastOp op,
                                        mlir::OpBuilder &builder) const = 0;
+
+  virtual mlir::Value
+  lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
+                         mlir::OpBuilder &builder) const = 0;
 };
 
 /// Creates an Itanium-family ABI.
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index e8853a27b2675..e972def4c416a 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -106,6 +106,8 @@ class LowerItaniumCXXABI : public CIRCXXABI {
 
   mlir::Value lowerDynamicCast(cir::DynamicCastOp op,
                                mlir::OpBuilder &builder) const override;
+  mlir::Value lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
+                                     mlir::OpBuilder &builder) const override;
 };
 
 } // namespace
@@ -758,5 +760,20 @@ LowerItaniumCXXABI::lowerDynamicCast(cir::DynamicCastOp op,
              })
       .getResult();
 }
+mlir::Value
+LowerItaniumCXXABI::lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
+                                           mlir::OpBuilder &builder) const {
+  mlir::Location loc = op->getLoc();
+  auto offset = cir::ConstantOp::create(
+      builder, op->getLoc(), cir::IntAttr::get(getPtrDiffCIRTy(lm), -1));
+
+  // Cast the vptr to type_info-ptr, so that we can go backwards 1 pointer.
+  auto vptrCast = cir::CastOp::create(builder, loc, op.getType(),
+                                      cir::CastKind::bitcast, op.getVptr());
+
+  return cir::PtrStrideOp::create(builder, loc, vptrCast.getType(), vptrCast,
+                                  offset)
+      .getResult();
+}
 
 } // namespace cir
diff --git a/clang/test/CIR/CodeGenCXX/Inputs/typeinfo 
b/clang/test/CIR/CodeGenCXX/Inputs/typeinfo
new file mode 100644
index 0000000000000..a68b10302c6fe
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/Inputs/typeinfo
@@ -0,0 +1,24 @@
+namespace std {
+  class type_info {
+  public:
+    virtual ~type_info();
+    const char* name() const { return __name; }
+    bool operator==(const type_info& __arg) const {
+     return __name == __arg.__name;
+    }
+
+    bool operator!=(const type_info& __arg) const {
+      return !operator==(__arg);
+    }
+
+    bool before(const type_info& __arg) const {
+      return __name < __arg.__name;
+    }
+
+    unsigned long hash_code() const {
+      return reinterpret_cast<unsigned long long>(__name);
+    }
+  protected:
+    const char *__name;
+  };
+}
diff --git a/clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp 
b/clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp
new file mode 100644
index 0000000000000..1e29256f8cd2b
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare -o %t.cir 2> 
%t-before.cir
+// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR
+// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -fclangir 
-emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=LLVM
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=LLVM
+
+#include <typeinfo>
+
+namespace Test1 {
+
+struct Item {
+  const std::type_info &ti;
+  const char *name;
+  void *(*make)();
+};
+
+template<typename T> void *make_impl() { return new T; }
+template<typename T> constexpr Item item(const char *name) {
+  return { typeid(T), name, make_impl<T> };
+}
+
+struct A { virtual ~A(); };
+struct B : virtual A {};
+struct C { int n; };
+
+// CIR: cir.global constant external @_ZN5Test15itemsE = #cir.const_array<[
+// CIR-SAME: #cir.const_record<{#cir.global_view<@_ZTIN5Test11AE> : 
!cir.ptr<!rec_std3A3Atype_info>, #cir.global_view<@".str"> : !cir.ptr<!s8i>, 
#cir.global_view<@_ZN5Test19make_implINS_1AEEEPvv> : !cir.ptr<!cir.func<() -> 
!cir.ptr<!void>>>}> : !rec_Test13A3AItem
+// CIR-SAME: #cir.const_record<{#cir.global_view<@_ZTIN5Test11BE> : 
!cir.ptr<!rec_std3A3Atype_info>, #cir.global_view<@".str.1"> : !cir.ptr<!s8i>, 
#cir.global_view<@_ZN5Test19make_implINS_1BEEEPvv> : !cir.ptr<!cir.func<() -> 
!cir.ptr<!void>>>}> : !rec_Test13A3AItem
+// CIR-SAME: #cir.const_record<{#cir.global_view<@_ZTIN5Test11CE> : 
!cir.ptr<!rec_std3A3Atype_info>, #cir.global_view<@".str.2"> : !cir.ptr<!s8i>, 
#cir.global_view<@_ZN5Test19make_implINS_1CEEEPvv> : !cir.ptr<!cir.func<() -> 
!cir.ptr<!void>>>}> : !rec_Test13A3AItem
+// CIR-SAME: #cir.const_record<{#cir.global_view<@_ZTIi> : 
!cir.ptr<!rec_std3A3Atype_info>, #cir.global_view<@".str.3"> : !cir.ptr<!s8i>, 
#cir.global_view<@_ZN5Test19make_implIiEEPvv> : !cir.ptr<!cir.func<() -> 
!cir.ptr<!void>>>}> : !rec_Test13A3AItem
+// CIR-SAME: ]> : !cir.array<!rec_Test13A3AItem x 4>
+//
+// LLVM: @_ZN5Test15itemsE ={{.*}} constant [4 x {{.*}}] [{{.*}} 
@_ZTIN5Test11AE, {{.*}} @_ZN5Test19make_implINS_1AEEEPvv {{.*}} 
@_ZTIN5Test11BE, {{.*}} @_ZN5Test19make_implINS_1BEEEPvv {{.*}} 
@_ZTIN5Test11CE, {{.*}} @_ZN5Test19make_implINS_1CEEEPvv {{.*}} @_ZTIi, {{.*}} 
@_ZN5Test19make_implIiEEPvv }]
+extern constexpr Item items[] = {
+  item<A>("A"), item<B>("B"), item<C>("C"), item<int>("int")
+};
+
+// CIR: cir.global constant external @_ZN5Test11xE = 
#cir.global_view<@_ZTIN5Test11AE>
+// LLVM: @_ZN5Test11xE ={{.*}} constant ptr @_ZTIN5Test11AE, align 8
+constexpr auto &x = items[0].ti;
+
+// CIR: cir.global constant external @_ZN5Test11yE = 
#cir.global_view<@_ZTIN5Test11BE>
+// LLVM: @_ZN5Test11yE ={{.*}} constant ptr @_ZTIN5Test11BE, align 8
+constexpr auto &y = typeid(B{});
+
+}
diff --git a/clang/test/CIR/CodeGenCXX/typeid-should-throw.cpp 
b/clang/test/CIR/CodeGenCXX/typeid-should-throw.cpp
new file mode 100644
index 0000000000000..9038d0010b753
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/typeid-should-throw.cpp
@@ -0,0 +1,172 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir 
--mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
+// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR
+// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR
+// RUN: %clang_cc1 %s -triple %itanium_abi_triple -Wno-unused-value -std=c++11 
-fclangir -emit-llvm -o - -std=c++11 | FileCheck %s --check-prefixes=LLVM
+// RUN: %clang_cc1 %s -triple %itanium_abi_triple -Wno-unused-value -std=c++11 
-emit-llvm -o - -std=c++11 | FileCheck %s --check-prefixes=LLVM
+namespace std {
+struct type_info;
+}
+
+struct A {
+  virtual ~A();
+  operator bool();
+};
+struct B : A {};
+
+void f1(A *x) { typeid(false, *x); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f1P1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f1P1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f2(bool b, A *x, A *y) { typeid(b ? *x : *y); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f2bP1AS0_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f2bP1AS0_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f3(bool b, A *x, A &y) { typeid(b ? *x : y); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f3bP1ARS_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f3bP1ARS_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f4(bool b, A &x, A *y) { typeid(b ? x : *y); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f4bR1APS_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f4bR1APS_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f5(volatile A *x) { typeid(*x); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f5PV1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f5PV1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f6(A *x) { typeid((B &)*(B *)x); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f6P1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f6P1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f7(A *x) { typeid((*x)); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f7P1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f7P1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f8(A *x) { typeid(x[0]); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f8P1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f8P1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f9(A *x) { typeid(0[x]); }
+// CIR-LABEL: cir.func{{.*}}@_Z2f9P1A
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z2f9P1A
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f10(A *x, A *y) { typeid(*y ?: *x); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f10P1AS0_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z3f10P1AS0_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f11(A *x, A &y) { typeid(*x ?: y); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f11P1ARS_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z3f11P1ARS_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f12(A &x, A *y) { typeid(x ?: *y); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f12R1APS_
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %{{.*}}, %[[NULL]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z3f12R1APS_
+// LLVM:       icmp eq {{.*}}, null
+// LLVM-NEXT:  br i1
+
+void f13(A &x, A &y) { typeid(x ?: y); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f13R1AS0_
+// CIR-NOT: @__cxa_bad_typeid()
+// LLVM-LABEL: define {{.*}}void @_Z3f13R1AS0_
+// LLVM-NOT:   icmp eq {{.*}}, null
+
+void f14(A *x) { typeid((const A &)(A)*x); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f14P1A
+// CIR-NOT: @__cxa_bad_typeid()
+// LLVM-LABEL: define {{.*}}void @_Z3f14P1A
+// LLVM-NOT:   icmp eq {{.*}}, null
+
+void f15(A *x) { typeid((A &&)*(A *)nullptr); }
+// CIR-LABEL: cir.func{{.*}}@_Z3f15P1A
+// In this example, it only passes classic codegen because the icmp doesn't
+// happen, it just does a branch on 'true' thanks to constant folding.  So 
we're
+// consistant with classic codegen.
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[NULL2:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %[[NULL]], %[[NULL2]])
+// CIR-NEXT: cir.if %[[CMP]]
+// CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+// CIR-NEXT: cir.unreachable
+// LLVM-LABEL: define {{.*}}void @_Z3f15P1A
+// LLVM-NOT:   icmp eq {{.*}}, null
diff --git a/clang/test/CIR/CodeGenCXX/typeid.cpp 
b/clang/test/CIR/CodeGenCXX/typeid.cpp
new file mode 100644
index 0000000000000..2a4d31ca9ef32
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/typeid.cpp
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -fclangir 
-emit-cir -fcxx-exceptions -fexceptions -mmlir 
--mlir-print-ir-before=cir-cxxabi-lowering -o %t.cir 2> %t-before.cir
+// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR,CIR-BEFORE
+// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR,CIR-AFTER
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -fclangir 
-emit-llvm -fcxx-exceptions -fexceptions -o - | FileCheck %s 
--check-prefixes=LLVM
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -emit-llvm 
-fcxx-exceptions -fexceptions -o - | FileCheck %s --check-prefixes=LLVM
+#include <typeinfo>
+
+namespace Test1 {
+
+// PR7400
+struct A { virtual void f(); };
+
+// CIR: cir.global constant external @_ZN5Test16int_tiE = 
#cir.global_view<@_ZTIi> : !cir.ptr<!rec_std3A3Atype_info>
+// LLVM: @_ZN5Test16int_tiE ={{.*}} constant ptr @_ZTIi, align 8
+const std::type_info &int_ti = typeid(int);
+
+// CIR: cir.global constant external @_ZN5Test14A_tiE = 
#cir.global_view<@_ZTIN5Test11AE> : !cir.ptr<!rec_std3A3Atype_info>
+// LLVM: @_ZN5Test14A_tiE ={{.*}} constant ptr @_ZTIN5Test11AE, align 8
+const std::type_info &A_ti = typeid(const volatile A &);
+
+volatile char c;
+
+// CIR: cir.global constant external @_ZN5Test14c_tiE = 
#cir.global_view<@_ZTIc> : !cir.ptr<!rec_std3A3Atype_info>
+// LLVM: @_ZN5Test14c_tiE ={{.*}} constant ptr @_ZTIc, align 8
+const std::type_info &c_ti = typeid(c);
+
+extern const double &d;
+
+// CIR: cir.global constant external @_ZN5Test14d_tiE = 
#cir.global_view<@_ZTId> : !cir.ptr<!rec_std3A3Atype_info>
+// LLVM: @_ZN5Test14d_tiE ={{.*}} constant ptr @_ZTId, align 8
+const std::type_info &d_ti = typeid(d);
+
+extern A &a;
+
+// CIR-AFTER: cir.global external @_ZN5Test14a_tiE = #cir.ptr<null> : 
!cir.ptr<!rec_std3A3Atype_info>
+
+// CIR-BEFORE: cir.global external @_ZN5Test14a_tiE = ctor : 
!cir.ptr<!rec_std3A3Atype_info> {
+// CIR-AFTER: cir.func{{.*}}@__cxx_global_var_init() {
+//
+// CIR-NEXT: %[[GET_GLOB_ATI:.*]] = cir.get_global @_ZN5Test14a_tiE : 
!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+// CIR-NEXT: %[[GET_GLOB_A:.*]] = cir.get_global @_ZN5Test11aE : 
!cir.ptr<!cir.ptr<!rec_Test13A3AA>>
+// CIR-NEXT: %[[LOAD_GLOB_A:.*]] = cir.load %[[GET_GLOB_A]] : 
!cir.ptr<!cir.ptr<!rec_Test13A3AA>>, !cir.ptr<!rec_Test13A3AA>
+// CIR-NEXT: %[[GET_VPTR:.*]] = cir.vtable.get_vptr %[[LOAD_GLOB_A]] : 
!cir.ptr<!rec_Test13A3AA> -> !cir.ptr<!cir.vptr>
+// CIR-NEXT: %[[LOAD_VPTR:.*]] = cir.load align(8) %[[GET_VPTR]] : 
!cir.ptr<!cir.vptr>, !cir.vptr
+// CIR-BEFORE: %[[GET_TYPEINFO:.*]] = cir.vtable.get_type_info %[[LOAD_VPTR]] 
: !cir.vptr -> !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+//
+// CIR-AFTER: %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s64i
+// CIR-AFTER: %[[VPTR_CAST:.*]] = cir.cast bitcast %[[LOAD_VPTR]] : !cir.vptr 
-> !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+// CIR-AFTER: %[[GET_TYPEINFO:.*]] = cir.ptr_stride %[[VPTR_CAST]], 
%[[NEG_ONE]] : (!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>, !s64i) -> 
!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+//
+// CIR-NEXT: %[[LOAD_TYPEINFO:.*]] = cir.load align(8) %[[GET_TYPEINFO]] : 
!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>, !cir.ptr<!rec_std3A3Atype_info>
+// CIR-NEXT: cir.store align(8) %[[LOAD_TYPEINFO]], %[[GET_GLOB_ATI]] : 
!cir.ptr<!rec_std3A3Atype_info>, !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+// CIR-AFTER: cir.return
+// CIR-NEXT:}
+// CIR: cir.global "private" constant external @_ZN5Test11aE : 
!cir.ptr<!rec_Test13A3AA>
+// LLVM: @_ZN5Test14a_tiE ={{.*}} global
+const std::type_info &a_ti = typeid(a);
+
+// CIR: cir.global constant external @_ZN5Test18A10_c_tiE = 
#cir.global_view<@_ZTIA10_c> : !cir.ptr<!rec_std3A3Atype_info>
+// LLVM: @_ZN5Test18A10_c_tiE ={{.*}} constant ptr @_ZTIA10_c, align 8
+const std::type_info &A10_c_ti = typeid(char const[10]);
+
+// CIR: cir.func private dso_local @__cxa_bad_typeid() attributes {noreturn}
+
+// CIR-LABEL: cir.func{{.*}} @_ZN5Test11fEPv
+// CIR-SAME:  personality(@__gxx_personality_v0)
+// LLVM-LABEL: define{{.*}} ptr @_ZN5Test11fEPv
+// LLVM-SAME:  personality ptr @__gxx_personality_v0
+const char *f(void *arg) {
+  // CIR: %[[ARG:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, 
["arg", init]
+  try {
+    // CIR: %[[ARG_VALUE:.*]] = cir.load{{.*}}%[[ARG]] : 
!cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+    // CIR-NEXT: %[[ARG_CAST:.*]] = cir.cast bitcast %[[ARG_VALUE]] : 
!cir.ptr<!void> -> !cir.ptr<!rec_Test13A3AA>
+    // CIR-NEXT: %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_Test13A3AA>
+    // CIR-NEXT: %[[CMP:.*]] = cir.cmp(eq, %[[ARG_CAST]], %[[NULL]])
+    // CIR-NEXT: cir.if %[[CMP]] {
+    // CIR-NEXT: cir.call @__cxa_bad_typeid() {noreturn} : () -> ()
+    // CIR-NEXT: cir.unreachable
+    // CIR-NEXT: }
+    //
+    // CIR: %[[GETVPTR:.*]] = cir.vtable.get_vptr %[[ARG_CAST]]
+    // CIR-NEXT: %[[LOAD_VPTR:.*]] = cir.load{{.*}} %[[GETVPTR]] : 
!cir.ptr<!cir.vptr>, !cir.vptr
+    //
+    // CIR-BEFORE: %[[GET_TYPEINFO:.*]] = cir.vtable.get_type_info 
%[[LOAD_VPTR]] : !cir.vptr -> !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+    //
+    // CIR-AFTER: %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s64i
+    // CIR-AFTER: %[[VPTR_CAST:.*]] = cir.cast bitcast %[[LOAD_VPTR]] : 
!cir.vptr -> !cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+    // CIR-AFTER: %[[GET_TYPEINFO:.*]] = cir.ptr_stride %[[VPTR_CAST]], 
%[[NEG_ONE]] : (!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>, !s64i) -> 
!cir.ptr<!cir.ptr<!rec_std3A3Atype_info>>
+    //
+    // CIR-NEXT: %[[LOAD_TYPEINFO:.*]] = cir.load{{.*}}%[[GET_TYPEINFO]]
+    // CIR-NEXT: cir.call @_ZNKSt9type_info4nameEv(%[[LOAD_TYPEINFO]])
+
+    // LLVM: br i1
+    // LLVM: invoke void @__cxa_bad_typeid()
+    return typeid(*static_cast<A *>(arg)).name();
+  } catch (...) {
+    // LLVM:      landingpad { ptr, i32 }
+    // LLVM-NEXT:   catch ptr null
+  }
+
+  return 0;
+}
+
+}

>From f6e1bfa5ce2796f941eb397b4bbfa77d848c8d06 Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Wed, 4 Mar 2026 11:50:44 -0800
Subject: [PATCH 2/2] correct cirops doc, remove improper setInsertionPoint,
 add MissingFeatures on AS

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 4 ++--
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       | 8 ++++++--
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 4 ----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ea175c35a4565..fffff1a9dc3e2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2858,8 +2858,8 @@ def CIR_VTableGetTypeInfoOp : 
CIR_Op<"vtable.get_type_info", [
     type_info/rtti object from an object's vtable. This is an ABI independent
     abstraction of this operation.
 
-    The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
-    have been returned by a previous call to `cir.vtable.get_vptr`.
+    The `vptr` operand must be a `!cir.vptr` value, which would have been
+    returned by a previous call to `cir.vtable.get_vptr`.
 
     The return type is a loadable pointer to a `type_info` struct.
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 0e94b50c39c40..97f496c89ab0f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1352,12 +1352,11 @@ static mlir::Value 
emitCXXTypeidFromVTable(CIRGenFunction &cgf, const Expr *e,
         cgf.getBuilder().createPtrIsNull(thisPtr.getPointer());
     // We don't really care about the value, we just want to make sure the
     // 'true' side calls bad-type-id.
-    auto ifOp = cir::IfOp::create(
+    cir::IfOp::create(
         cgf.getBuilder(), cgf.getLoc(e->getSourceRange()), isThisNull,
         /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) {
           cgf.cgm.getCXXABI().emitBadTypeidCall(cgf, loc);
         });
-    cgf.getBuilder().setInsertionPointAfter(ifOp);
   }
 
   return cgf.cgm.getCXXABI().emitTypeid(cgf, srcType, thisPtr, typeInfoPtrTy);
@@ -1368,6 +1367,11 @@ mlir::Value CIRGenFunction::emitCXXTypeidExpr(const 
CXXTypeidExpr *e) {
   mlir::Type resultType = cir::PointerType::get(convertType(e->getType()));
   QualType ty = e->isTypeOperand() ? e->getTypeOperand(getContext())
                                    : e->getExprOperand()->getType();
+
+  // If the non-default global var address space is not default, we need to do
+  // an address-space cast here.
+  assert(!cir::MissingFeatures::addressSpace());
+
   // C++ [expr.typeid]p2:
   //   When typeid is applied to a glvalue expression whose type is a
   //   polymorphic class type, the result refers to a std::type_info object
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 49e9e40f03256..fb9054c9f3bde 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1009,7 +1009,6 @@ const char *vTableClassNameForType(const CIRGenModule 
&cgm, const Type *ty) {
   case Type::FunctionProto:
     // abi::__function_type_info.
     return "_ZTVN10__cxxabiv120__function_type_infoE";
-    break;
 
   case Type::Enum:
     return "_ZTVN10__cxxabiv116__enum_type_infoE";
@@ -1041,12 +1040,10 @@ const char *vTableClassNameForType(const CIRGenModule 
&cgm, const Type *ty) {
   case Type::Pointer:
     // abi::__pointer_type_info.
     return "_ZTVN10__cxxabiv119__pointer_type_infoE";
-    break;
 
   case Type::MemberPointer:
     // abi::__pointer_to_member_type_info.
     return "_ZTVN10__cxxabiv129__pointer_to_member_type_infoE";
-    break;
 
   case Type::HLSLAttributedResource:
   case Type::HLSLInlineSpirv:
@@ -1575,7 +1572,6 @@ void 
CIRGenItaniumCXXABI::emitBadTypeidCall(CIRGenFunction &cgf,
       loc, cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_typeid", attrs), {},
       attrs);
   cir::UnreachableOp::create(cgf.getBuilder(), loc);
-  cgf.getBuilder().clearInsertionPoint();
 }
 
 mlir::Value CIRGenItaniumCXXABI::emitTypeid(CIRGenFunction &cgf, QualType 
srcTy,

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

Reply via email to