Skip to content

Commit 8377a82

Browse files
committed
[CIR] Add VTableAddrPointOp
This change adds the definition of VTableAddrPointOp and the related AddressPointAttr to the CIR dialect, along with tests for the parsing and verification of these elements. Code to generate this operation will be added in a later change.
1 parent 3ab95e4 commit 8377a82

File tree

6 files changed

+167
-0
lines changed

6 files changed

+167
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,5 +515,35 @@ def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
515515
];
516516
}
517517

518+
//===----------------------------------------------------------------------===//
519+
// AddressPointAttr
520+
//===----------------------------------------------------------------------===//
521+
522+
def AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> {
523+
let summary = "Address point attribute";
524+
525+
let description = [{
526+
Attribute specifying the address point within a C++ virtual table (vtable).
527+
528+
The `index` (vtable index) parameter identifies which vtable to use within a
529+
vtable group, while the `offset` (address point index) specifies the offset
530+
within that vtable where the address begins.
531+
532+
Example:
533+
```mlir
534+
cir.global linkonce_odr @_ZTV1B = ...
535+
...
536+
%3 = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0, offset = 2>)
537+
: !cir.ptr<!cir.ptr<() -> i32>>
538+
```
539+
}];
540+
541+
let parameters = (ins "int32_t":$index,
542+
"int32_t":$offset);
543+
544+
let assemblyFormat = [{
545+
`<` struct($index, $offset) `>`
546+
}];
547+
}
518548

519549
#endif // LLVM_CLANG_CIR_DIALECT_IR_CIRATTRS_TD

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,51 @@ def GetGlobalOp : CIR_Op<"get_global",
16691669
}];
16701670
}
16711671

1672+
//===----------------------------------------------------------------------===//
1673+
// VTableAddrPointOp
1674+
//===----------------------------------------------------------------------===//
1675+
1676+
def VTableAddrPointOp : CIR_Op<"vtable.address_point",
1677+
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
1678+
let summary = "Get the vtable (global variable) address point";
1679+
let description = [{
1680+
The `vtable.address_point` operation retrieves the effective address
1681+
(address point) of a C++ virtual table. An object internal `__vptr`
1682+
gets initializated on top of the value returned by this operation.
1683+
1684+
`address_point.index` (vtable index) provides the appropriate vtable within
1685+
the vtable group (as specified by Itanium ABI), and `address_point.offset`
1686+
(address point index) the actual address point within that vtable.
1687+
1688+
The return type is always a `!cir.ptr<!cir.ptr<() -> i32>>`.
1689+
1690+
Example:
1691+
```mlir
1692+
cir.global linkonce_odr @_ZTV1B = ...
1693+
...
1694+
%3 = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0,
1695+
offset = 2>) : !cir.ptr<!cir.ptr<() -> i32>>
1696+
```
1697+
}];
1698+
1699+
let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name,
1700+
Optional<CIR_AnyType>:$sym_addr,
1701+
AddressPointAttr:$address_point);
1702+
let results = (outs Res<CIR_PointerType, "", []>:$addr);
1703+
1704+
let assemblyFormat = [{
1705+
`(`
1706+
($name^)?
1707+
($sym_addr^ `:` type($sym_addr))?
1708+
`,`
1709+
`address_point` `=` $address_point
1710+
`)`
1711+
`:` qualified(type($addr)) attr-dict
1712+
}];
1713+
1714+
let hasVerifier = 1;
1715+
}
1716+
16721717
//===----------------------------------------------------------------------===//
16731718
// SetBitfieldOp
16741719
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ struct MissingFeatures {
248248
static bool thunks() { return false; }
249249
static bool tryEmitAsConstant() { return false; }
250250
static bool typeChecks() { return false; }
251+
static bool vtableInitializer() { return false; }
251252
static bool weakRefReference() { return false; }
252253
static bool writebacks() { return false; }
253254

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,52 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
13381338
return success();
13391339
}
13401340

1341+
//===----------------------------------------------------------------------===//
1342+
// VTableAddrPointOp
1343+
//===----------------------------------------------------------------------===//
1344+
1345+
LogicalResult
1346+
cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1347+
// vtable ptr is not coming from a symbol.
1348+
if (!getName())
1349+
return success();
1350+
StringRef name = *getName();
1351+
1352+
// Verify that the result type underlying pointer type matches the type of
1353+
// the referenced cir.global or cir.func op.
1354+
auto op = dyn_cast_or_null<GlobalOp>(
1355+
symbolTable.lookupNearestSymbolFrom(*this, getNameAttr()));
1356+
if (!op)
1357+
return emitOpError("'")
1358+
<< name << "' does not reference a valid cir.global";
1359+
std::optional<mlir::Attribute> init = op.getInitialValue();
1360+
if (!init)
1361+
return success();
1362+
assert(!cir::MissingFeatures::vtableInitializer());
1363+
return success();
1364+
}
1365+
1366+
LogicalResult cir::VTableAddrPointOp::verify() {
1367+
// The operation uses either a symbol or a value to operate, but not both
1368+
if (getName() && getSymAddr())
1369+
return emitOpError("should use either a symbol or value, but not both");
1370+
1371+
// If not a symbol, stick with the concrete type used for getSymAddr.
1372+
if (getSymAddr())
1373+
return success();
1374+
1375+
mlir::Type resultType = getAddr().getType();
1376+
auto intTy = cir::IntType::get(getContext(), 32, /*isSigned=*/false);
1377+
auto fnTy = cir::FuncType::get({}, intTy);
1378+
1379+
auto resTy = cir::PointerType::get(cir::PointerType::get(fnTy));
1380+
1381+
if (resultType != resTy)
1382+
return emitOpError("result type must be ")
1383+
<< resTy << ", but provided result type is " << resultType << "";
1384+
return success();
1385+
}
1386+
13411387
//===----------------------------------------------------------------------===//
13421388
// FuncOp
13431389
//===----------------------------------------------------------------------===//

clang/test/CIR/IR/invalid-vtable.cir

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: cir-opt %s -verify-diagnostics -split-input-file
2+
3+
!s8i = !cir.int<s, 8>
4+
!u32i = !cir.int<u, 32>
5+
cir.func @reference_unknown_vtable() {
6+
// expected-error @below {{'cir.vtable.address_point' op 'some_vtable' does not reference a valid cir.global}}
7+
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>
8+
cir.return
9+
}
10+
11+
// -----
12+
13+
!s8i = !cir.int<s, 8>
14+
!u8i = !cir.int<u, 8>
15+
!u64i = !cir.int<u, 64>
16+
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
17+
cir.global "private" external @_ZTV1S : !rec_anon_struct {alignment = 8 : i64}
18+
cir.func @incorrect_type() {
19+
// expected-error @below {{'cir.vtable.address_point' op result type must be '!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>', but provided result type is '!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 64>>>>'}}
20+
%0 = cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u64i>>>
21+
cir.return
22+
}

clang/test/CIR/IR/vtable-addrpt.cir

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: cir-opt %s | FileCheck %s
2+
3+
// Test the parsing and printing of a constructor that uses a vtable addess_point op.
4+
5+
!u32i = !cir.int<u, 32>
6+
!u8i = !cir.int<u, 8>
7+
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
8+
!rec_S = !cir.record<struct "S" {!cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>}>
9+
10+
module {
11+
cir.global "private" external @_ZTV1S : !rec_anon_struct {alignment = 8 : i64}
12+
cir.func @_ZN1SC2Ev(%arg0: !cir.ptr<!rec_S>) {
13+
%0 = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["this", init] {alignment = 8 : i64}
14+
cir.store %arg0, %0 : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
15+
%1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
16+
%2 = cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>
17+
%3 = cir.cast(bitcast, %1 : !cir.ptr<!rec_S>), !cir.ptr<!cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>>
18+
cir.store align(8) %2, %3 : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>, !cir.ptr<!cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>>
19+
cir.return
20+
}
21+
}
22+
23+
// CHECK: cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>

0 commit comments

Comments
 (0)