Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2764,6 +2764,8 @@ LValue CIRGenFunction::emitLValue(const Expr *E) {
case Expr::CXXAddrspaceCastExprClass:
case Expr::ObjCBridgedCastExprClass:
return emitCastLValue(cast<CastExpr>(E));
case Expr::CXXTypeidExprClass:
return emitCXXTypeidLValue(cast<CXXTypeidExpr>(E));
case Expr::OpaqueValueExprClass:
return emitOpaqueValueLValue(cast<OpaqueValueExpr>(E));

Expand Down
57 changes: 57 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1809,3 +1809,60 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address ThisAddr,
return CGM.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
destCirTy, isRefCast, ThisAddr);
}

mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *E) {
auto loc = getLoc(E->getSourceRange());

// Helper to create a GetGlobalOp from an RTTI descriptor attribute.
auto createGetGlobalForRTTI = [&](mlir::Attribute typeInfo) -> mlir::Value {
auto globalView = mlir::cast<cir::GlobalViewAttr>(typeInfo);
auto *globalOp = mlir::SymbolTable::lookupSymbolIn(CGM.getModule(),
globalView.getSymbol());
assert(globalOp && "RTTI global not found");
auto global = mlir::cast<cir::GlobalOp>(globalOp);

// The result type of GetGlobalOp is a pointer to the global's symbol type.
auto ptrTy = builder.getPointerTo(global.getSymType());
return cir::GetGlobalOp::create(builder, loc, ptrTy,
globalView.getSymbol());
};

// If this is a type operand, just get the address of the RTTI descriptor.
if (E->isTypeOperand()) {
QualType operandTy = E->getTypeOperand(getContext());
mlir::Attribute typeInfo = CGM.getAddrOfRTTIDescriptor(loc, operandTy);
return createGetGlobalForRTTI(typeInfo);
}

// 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 the most derived object, no need to look up
// vtable.
if (E->isPotentiallyEvaluated() && !E->isMostDerived(getContext())) {
// This requires emitting code similar to dynamic_cast that looks up the
// type_info pointer from the vtable. Note that this path also needs to
// handle null checking when E->hasNullCheck() is true.
llvm_unreachable("NYI: typeid with polymorphic types (vtable lookup)");
}

// For non-polymorphic types, just return the static RTTI descriptor.
QualType operandTy = E->getExprOperand()->getType();
mlir::Attribute typeInfo = CGM.getAddrOfRTTIDescriptor(loc, operandTy);
return createGetGlobalForRTTI(typeInfo);
}

LValue CIRGenFunction::emitCXXTypeidLValue(const CXXTypeidExpr *E) {
mlir::Value V = emitCXXTypeidExpr(E);

LValueBaseInfo baseInfo;
TBAAAccessInfo tbaaInfo;
CharUnits alignment = CGM.getNaturalTypeAlignment(E->getType(), &baseInfo, &tbaaInfo);

// Use the Address constructor that extracts the element type from the pointer
Address addr(V, alignment);
return LValue::makeAddr(addr, E->getType(), getContext(), baseInfo, tbaaInfo);
}
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,12 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::Value emitDynamicCast(Address ThisAddr, const CXXDynamicCastExpr *DCE);

/// Emit the operand of a typeid expression as an mlir::Value.
mlir::Value emitCXXTypeidExpr(const CXXTypeidExpr *E);

/// Emit a typeid expression as an l-value.
LValue emitCXXTypeidLValue(const CXXTypeidExpr *E);

/// Emits try/catch information for the current EH stack.
void emitEHResumeBlock(bool isCleanup, mlir::Block *ehResumeBlock,
mlir::Location loc);
Expand Down
119 changes: 119 additions & 0 deletions clang/test/CIR/CodeGen/typeid-lvalue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG

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;
};
}

// Test 1: Non-polymorphic type - simple struct
struct Simple {
int x;
};

void test_simple_type(const std::type_info*& out) {
// CIR-LABEL: cir.func {{.*}}@_Z16test_simple_type
out = &typeid(Simple);
// CIR: cir.get_global @_ZTI6Simple

// LLVM-LABEL: define {{.*}}@_Z16test_simple_type
// LLVM: store ptr @_ZTI6Simple

// OGCG-LABEL: define {{.*}}@_Z16test_simple_type
// OGCG: store ptr @_ZTI6Simple
}

// Test 2: Non-polymorphic type - expression operand
void test_expression_operand(const std::type_info*& out) {
// CIR-LABEL: cir.func {{.*}}@_Z23test_expression_operand
Simple s;
out = &typeid(s);
// CIR: cir.get_global @_ZTI6Simple

// LLVM-LABEL: define {{.*}}@_Z23test_expression_operand
// LLVM: store ptr @_ZTI6Simple

// OGCG-LABEL: define {{.*}}@_Z23test_expression_operand
// OGCG: store ptr @_ZTI6Simple
}

// Test 3: Polymorphic base class
struct Base {
virtual ~Base() = default;
};

struct Derived : Base {
int y;
};

// Test with non-polymorphic lookup (type operand)
void test_polymorphic_type(const std::type_info*& out) {
// CIR-LABEL: cir.func {{.*}}@_Z21test_polymorphic_type
out = &typeid(Base);
// CIR: cir.get_global @_ZTI4Base

// LLVM-LABEL: define {{.*}}@_Z21test_polymorphic_type
// LLVM: store ptr @_ZTI4Base

// OGCG-LABEL: define {{.*}}@_Z21test_polymorphic_type
// OGCG: store ptr @_ZTI4Base
}

// Test 4: Built-in type
void test_builtin_type(const std::type_info*& out) {
// CIR-LABEL: cir.func {{.*}}@_Z17test_builtin_type
out = &typeid(int);
// CIR: cir.get_global @_ZTIi

// LLVM-LABEL: define {{.*}}@_Z17test_builtin_type
// LLVM: store ptr @_ZTIi

// OGCG-LABEL: define {{.*}}@_Z17test_builtin_type
// OGCG: store ptr @_ZTIi
}

// Test 5: Passing typeid as function argument
void consume_type_info(const std::type_info& ti) {
// CIR-LABEL: cir.func {{.*}}@_Z17consume_type_info

// LLVM-LABEL: define {{.*}}@_Z17consume_type_info

// OGCG-LABEL: define {{.*}}@_Z17consume_type_info
}

void test_function_argument() {
// CIR-LABEL: cir.func {{.*}}@_Z22test_function_argumentv
consume_type_info(typeid(int));
// CIR: cir.get_global @_ZTIi
// CIR: cir.call @_Z17consume_type_info

// LLVM-LABEL: define {{.*}}@_Z22test_function_argumentv
// LLVM: call {{.*}}@_Z17consume_type_infoRKSt9type_info(ptr @_ZTIi)

// OGCG-LABEL: define {{.*}}@_Z22test_function_argumentv
// OGCG: call {{.*}}@_Z17consume_type_infoRKSt9type_info(ptr {{.*}}@_ZTIi)
}
Loading