Skip to content

[CIR] Add vptr type and generate vptr field when needed #151377

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 1, 2025
Merged
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
31 changes: 30 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,35 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr", [
}];
}

//===----------------------------------------------------------------------===//
// CIR_VPtrType
//===----------------------------------------------------------------------===//

def CIR_VPtrType : CIR_Type<"VPtr", "vptr", [
DeclareTypeInterfaceMethods<DataLayoutTypeInterface>
]> {
let summary = "CIR type that is used for the vptr member of C++ objects";
let description = [{
`cir.vptr` is a special type used as the type for the vptr member of a C++
object. This avoids using arbitrary pointer types to declare vptr values
and allows stronger type-based checking for operations that use or provide
access to the vptr.

This type will be the element type of the 'vptr' member of structures that
require a vtable pointer. A pointer to this type is returned by the
`cir.vtable.address_point` and `cir.vtable.get_vptr` operations, and this
pointer may be passed to the `cir.vtable.get_virtual_fn_addr` operation to
get the address of a virtual function pointer.

The pointer may also be cast to other pointer types in order to perform
pointer arithmetic based on information encoded in the AST layout to get
the offset from a pointer to a dynamic object to the base object pointer,
the base object offset value from the vtable, or the type information
entry for an object.
TODO: We should have special operations to do that too.
}];
}

//===----------------------------------------------------------------------===//
// BoolType
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -635,7 +664,7 @@ def CIRRecordType : Type<
def CIR_AnyType : AnyTypeOf<[
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_VectorType, CIR_IntType,
CIR_AnyFloatType, CIR_PointerType, CIR_FuncType, CIR_RecordType,
CIR_ComplexType
CIR_ComplexType, CIR_VPtrType
]>;

#endif // CLANG_CIR_DIALECT_IR_CIRTYPES_TD
13 changes: 10 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct CIRRecordLowering final {
// member type that ensures correct rounding.
struct MemberInfo final {
CharUnits offset;
enum class InfoKind { Field, Base } kind;
enum class InfoKind { VFPtr, Field, Base } kind;
mlir::Type data;
union {
const FieldDecl *fieldDecl;
Expand Down Expand Up @@ -87,6 +87,8 @@ struct CIRRecordLowering final {
accumulateBitFields(RecordDecl::field_iterator field,
RecordDecl::field_iterator fieldEnd);

mlir::Type getVFPtrType();

bool isAAPCS() const {
return astContext.getTargetInfo().getABI().starts_with("aapcs");
}
Expand Down Expand Up @@ -802,9 +804,14 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {

void CIRRecordLowering::accumulateVPtrs() {
if (astRecordLayout.hasOwnVFPtr())
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateVPtrs: hasOwnVFPtr");
members.push_back(MemberInfo(CharUnits::Zero(), MemberInfo::InfoKind::VFPtr,
getVFPtrType()));

if (astRecordLayout.hasOwnVBPtr())
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateVPtrs: hasOwnVBPtr");
}

mlir::Type CIRRecordLowering::getVFPtrType() {
return cir::VPtrType::get(builder.getContext());
}
17 changes: 17 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,23 @@ BoolType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
return 1;
}

//===----------------------------------------------------------------------===//
// VPtrType Definitions
//===----------------------------------------------------------------------===//

llvm::TypeSize
VPtrType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const {
// FIXME: consider size differences under different ABIs
return llvm::TypeSize::getFixed(64);
}

uint64_t VPtrType::getABIAlignment(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const {
// FIXME: consider alignment differences under different ABIs
return 8;
}

//===----------------------------------------------------------------------===//
// ArrayType Definitions
//===----------------------------------------------------------------------===//
Expand Down
13 changes: 13 additions & 0 deletions clang/test/CIR/CodeGen/virtual-function-calls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s

struct A {
virtual void f(char);
};

// This is just here to force the class definition to be emitted without
// requiring any other support. It will be removed when more complete
// vtable support is implemented.
A *a;

// CIR: !rec_A = !cir.record<struct "A" {!cir.vptr}>