Skip to content

[CIR] Add VTableAddrPointOp #148730

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 1 commit into from
Aug 7, 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
32 changes: 32 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -516,4 +516,36 @@ def CIR_BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
];
}

//===----------------------------------------------------------------------===//
// AddressPointAttr
//===----------------------------------------------------------------------===//

def CIR_AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> {
let summary = "Address point attribute";

let description = [{
Attribute specifying the address point within a C++ virtual table (vtable).

The `index` (vtable index) parameter identifies which vtable to use within a
vtable group, while the `offset` (address point index) specifies the offset
within that vtable where the address begins.

Example:
```mlir
cir.global linkonce_odr @_ZTV1B = ...
...
%3 = cir.vtable.address_point(@_ZTV1B,
address_point = <index = 0, offset = 2>)
: !cir.vptr
```
}];

let parameters = (ins "int32_t":$index,
"int32_t":$offset);

let assemblyFormat = [{
`<` struct($index, $offset) `>`
}];
}

#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
43 changes: 43 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,49 @@ def CIR_GetGlobalOp : CIR_Op<"get_global", [
}];
}

//===----------------------------------------------------------------------===//
// VTableAddrPointOp
//===----------------------------------------------------------------------===//

def CIR_VTableAddrPointOp : CIR_Op<"vtable.address_point", [
Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>
]> {
let summary = "Get the vtable (global variable) address point";
let description = [{
The `vtable.address_point` operation retrieves the "effective" address
(address point) of a C++ virtual table. An object internal `__vptr`
gets initializated on top of the value returned by this operation.

`address_point.index` (vtable index) provides the appropriate vtable within
the vtable group (as specified by Itanium ABI), and `address_point.offset`
(address point index) the actual address point within that vtable.

The return type is always `!cir.vptr`.

Example:
```mlir
cir.global linkonce_odr @_ZTV1B = ...
...
%3 = cir.vtable.address_point(@_ZTV1B,
address_point = <index = 0, offset = 2>) : !cir.vptr
```
}];

let arguments = (ins
FlatSymbolRefAttr:$name,
CIR_AddressPointAttr:$address_point
);

let results = (outs Res<CIR_VPtrType, "", []>:$addr);

let assemblyFormat = [{
`(`
$name `,` `address_point` `=` $address_point
`)`
`:` qualified(type($addr)) attr-dict
}];
}

//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ struct MissingFeatures {
static bool thunks() { return false; }
static bool tryEmitAsConstant() { return false; }
static bool typeChecks() { return false; }
static bool vtableInitializer() { return false; }
static bool weakRefReference() { return false; }
static bool writebacks() { return false; }
static bool appleKext() { return false; }
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,27 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return success();
}

//===----------------------------------------------------------------------===//
// VTableAddrPointOp
//===----------------------------------------------------------------------===//

LogicalResult
cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
StringRef name = getName();

// Verify that the result type underlying pointer type matches the type of
// the referenced cir.global or cir.func op.
auto op = symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());
if (!op)
return emitOpError("'")
<< name << "' does not reference a valid cir.global";
std::optional<mlir::Attribute> init = op.getInitialValue();
if (!init)
return success();
assert(!cir::MissingFeatures::vtableInitializer());
return success();
}

//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
Expand Down
9 changes: 9 additions & 0 deletions clang/test/CIR/IR/invalid-vtable.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: cir-opt %s -verify-diagnostics

!s8i = !cir.int<s, 8>
!u32i = !cir.int<u, 32>
cir.func @reference_unknown_vtable() {
// expected-error @below {{'cir.vtable.address_point' op 'some_vtable' does not reference a valid cir.global}}
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr
cir.return
}
23 changes: 23 additions & 0 deletions clang/test/CIR/IR/vtable-addrpt.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: cir-opt %s | FileCheck %s

// Test the parsing and printing of a constructor that uses a vtable addess_point op.

!u32i = !cir.int<u, 32>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
!rec_S = !cir.record<struct "S" {!cir.vptr}>

module {
cir.global "private" external @_ZTV1S : !rec_anon_struct {alignment = 8 : i64}
cir.func @_ZN1SC2Ev(%arg0: !cir.ptr<!rec_S>) {
%0 = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["this", init] {alignment = 8 : i64}
cir.store %arg0, %0 : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
%1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
%2 = cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.vptr
%3 = cir.cast(bitcast, %1 : !cir.ptr<!rec_S>), !cir.ptr<!cir.vptr>
cir.store align(8) %2, %3 : !cir.vptr, !cir.ptr<!cir.vptr>
cir.return
}
}

// CHECK: cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.vptr