[clang] c186db8 - [CIR] Implement 'typeid' operator lowering (#184449)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 5 06:07:18 PST 2026
Author: Erich Keane
Date: 2026-03-05T06:07:13-08:00
New Revision: c186db874b9037a77829bce4ebf89bed46efdbb8
URL: https://github.com/llvm/llvm-project/commit/c186db874b9037a77829bce4ebf89bed46efdbb8
DIFF: https://github.com/llvm/llvm-project/commit/c186db874b9037a77829bce4ebf89bed46efdbb8.diff
LOG: [CIR] Implement 'typeid' operator lowering (#184449)
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.
Added:
clang/test/CIR/CodeGenCXX/Inputs/typeinfo
clang/test/CIR/CodeGenCXX/typeid-cxx11.cpp
clang/test/CIR/CodeGenCXX/typeid-should-throw.cpp
clang/test/CIR/CodeGenCXX/typeid.cpp
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/CodeGen/CIRGenCXXABI.h
clang/lib/CIR/CodeGen/CIRGenCall.cpp
clang/lib/CIR/CodeGen/CIRGenException.cpp
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenModule.h
clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e6c9aae03705f..5db68b44c2804 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2830,7 +2830,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.
@@ -2862,6 +2862,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.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..97f496c89ab0f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1334,3 +1334,67 @@ 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.
+ cir::IfOp::create(
+ cgf.getBuilder(), cgf.getLoc(e->getSourceRange()), isThisNull,
+ /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) {
+ cgf.cgm.getCXXABI().emitBadTypeidCall(cgf, loc);
+ });
+ }
+
+ 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();
+
+ // 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
+ // 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 56bdcfd7f8906..539d7839d1dfe 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..fb9054c9f3bde 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,13 +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");
- break;
+ // abi::__function_type_info.
+ return "_ZTVN10__cxxabiv120__function_type_infoE";
case Type::Enum:
return "_ZTVN10__cxxabiv116__enum_type_infoE";
@@ -1033,12 +1038,12 @@ const char *vTableClassNameForType(const CIRGenModule &cgm, const Type *ty) {
case Type::ObjCObjectPointer:
case Type::Pointer:
- cgm.errorNYI("VTableClassNameForType: __pointer_type_info");
- break;
+ // abi::__pointer_type_info.
+ return "_ZTVN10__cxxabiv119__pointer_type_infoE";
case Type::MemberPointer:
- cgm.errorNYI("VTableClassNameForType: __pointer_to_member_type_info");
- break;
+ // abi::__pointer_to_member_type_info.
+ return "_ZTVN10__cxxabiv129__pointer_to_member_type_infoE";
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
@@ -1550,6 +1555,48 @@ 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);
+}
+
+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 c9df1499ea453..b5905e2db6de1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2425,7 +2425,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)) {
@@ -2518,6 +2518,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) {
@@ -2702,14 +2706,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 48bb1f2ae20d9..ac1699b3b8bfe 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -625,7 +625,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,
@@ -641,7 +650,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;
+}
+
+}
More information about the cfe-commits
mailing list