-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[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
base: main
Are you sure you want to change the base?
[CIR] Add VTableAddrPointOp #148730
Conversation
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.
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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. Full diff: https://github.com/llvm/llvm-project/pull/148730.diff 6 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 785478abb0778..16693129bf379 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -515,5 +515,35 @@ def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
];
}
+//===----------------------------------------------------------------------===//
+// AddressPointAttr
+//===----------------------------------------------------------------------===//
+
+def 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.ptr<!cir.ptr<() -> i32>>
+ ```
+ }];
+
+ let parameters = (ins "int32_t":$index,
+ "int32_t":$offset);
+
+ let assemblyFormat = [{
+ `<` struct($index, $offset) `>`
+ }];
+}
#endif // LLVM_CLANG_CIR_DIALECT_IR_CIRATTRS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d5cdb5aa91251..5813940b7e27c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1669,6 +1669,51 @@ def GetGlobalOp : CIR_Op<"get_global",
}];
}
+//===----------------------------------------------------------------------===//
+// VTableAddrPointOp
+//===----------------------------------------------------------------------===//
+
+def 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 a `!cir.ptr<!cir.ptr<() -> i32>>`.
+
+ Example:
+ ```mlir
+ cir.global linkonce_odr @_ZTV1B = ...
+ ...
+ %3 = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0,
+ offset = 2>) : !cir.ptr<!cir.ptr<() -> i32>>
+ ```
+ }];
+
+ let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name,
+ Optional<CIR_AnyType>:$sym_addr,
+ AddressPointAttr:$address_point);
+ let results = (outs Res<CIR_PointerType, "", []>:$addr);
+
+ let assemblyFormat = [{
+ `(`
+ ($name^)?
+ ($sym_addr^ `:` type($sym_addr))?
+ `,`
+ `address_point` `=` $address_point
+ `)`
+ `:` qualified(type($addr)) attr-dict
+ }];
+
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index c76737549a0fc..1881029f9899c 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -248,6 +248,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; }
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9e7615bdd7a7f..362f83c9b97e1 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1338,6 +1338,52 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return success();
}
+//===----------------------------------------------------------------------===//
+// VTableAddrPointOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ // vtable ptr is not coming from a symbol.
+ if (!getName())
+ return success();
+ 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 = dyn_cast_or_null<GlobalOp>(
+ symbolTable.lookupNearestSymbolFrom(*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();
+}
+
+LogicalResult cir::VTableAddrPointOp::verify() {
+ // The operation uses either a symbol or a value to operate, but not both
+ if (getName() && getSymAddr())
+ return emitOpError("should use either a symbol or value, but not both");
+
+ // If not a symbol, stick with the concrete type used for getSymAddr.
+ if (getSymAddr())
+ return success();
+
+ mlir::Type resultType = getAddr().getType();
+ auto intTy = cir::IntType::get(getContext(), 32, /*isSigned=*/false);
+ auto fnTy = cir::FuncType::get({}, intTy);
+
+ auto resTy = cir::PointerType::get(cir::PointerType::get(fnTy));
+
+ if (resultType != resTy)
+ return emitOpError("result type must be ")
+ << resTy << ", but provided result type is " << resultType << "";
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir
new file mode 100644
index 0000000000000..de3e6600427bf
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-vtable.cir
@@ -0,0 +1,22 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!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.ptr<!cir.ptr<!cir.func<() -> !u32i>>>
+ cir.return
+}
+
+// -----
+
+!s8i = !cir.int<s, 8>
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
+cir.global "private" external @_ZTV1S : !rec_anon_struct {alignment = 8 : i64}
+cir.func @incorrect_type() {
+ // 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>>>>'}}
+ %0 = cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u64i>>>
+ cir.return
+}
diff --git a/clang/test/CIR/IR/vtable-addrpt.cir b/clang/test/CIR/IR/vtable-addrpt.cir
new file mode 100644
index 0000000000000..1aa7f3aa99364
--- /dev/null
+++ b/clang/test/CIR/IR/vtable-addrpt.cir
@@ -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.ptr<!cir.ptr<!cir.func<() -> !u32i>>>}>
+
+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.ptr<!cir.ptr<!cir.func<() -> !u32i>>>
+ %3 = cir.cast(bitcast, %1 : !cir.ptr<!rec_S>), !cir.ptr<!cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>>
+ cir.store align(8) %2, %3 : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>, !cir.ptr<!cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>>
+ cir.return
+ }
+}
+
+// CHECK: cir.vtable.address_point(@_ZTV1S, address_point = <index = 0, offset = 2>) : !cir.ptr<!cir.ptr<!cir.func<() -> !u32i>>>
|
auto intTy = cir::IntType::get(getContext(), 32, /*isSigned=*/false); | ||
auto fnTy = cir::FuncType::get({}, intTy); | ||
|
||
auto resTy = cir::PointerType::get(cir::PointerType::get(fnTy)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, this is completely arbitrary. In the incubator, CIRGenBuildTy::getVirtualFnPtrType
returns this type, but it has a comment suggesting that we should create a special vtable pointer type that more explicitly identifies what the pointer is. I'm working on an incubator patch to do just that.
// AddressPointAttr | ||
//===----------------------------------------------------------------------===// | ||
|
||
def AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> { | |
def CIR_AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> { |
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.ptr<!cir.ptr<() -> i32>> | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.ptr<!cir.ptr<() -> i32>> | |
``` | |
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.ptr<!cir.ptr<() -> i32>> | |
``` |
// AddressPointAttr | ||
//===----------------------------------------------------------------------===// | ||
|
||
def AddressPointAttr : CIR_Attr<"AddressPoint", "address_point"> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason to have this as a special attribute and not just two I32Attr
in VTableAddrPointOp
?
I scanned through incubator codebase and all uses I found it is always accessed directly to index or offset, e.g. op.getAddressPointAttr().getIndex()
, op.getAddressPointAttr().getOffset()
.
Therefore, I believe we can remove the attribute entirely and use just op.getAddressPointIndex()
and op.getAddressPointOffset()
, or even simplify it to op.getIndex()
and op.getOffset()
, since it's clear from AddrPointOp
that it deals with AddressPoint
.
This can clean up address point op assembly as well to something like:
cir.vtable_address_point @_ZTV1B [index = 0, offset = 2] : !cir.ptr<!cir.ptr<() -> i32>>
^ This just my suggestion removing also some parenthesis and adding _
after vtable
in op name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are other places that use address point in the incubator but are not using the attribute just yet (e.g. emitVTTDefinition). The idea was to unify those references so they are easy to understand (versus reading random integers in tables). It's possible we need more work to make them adequate to use though, so it's possible we don't need this until we can also cover relative layouts and understand all we really need.
let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name, | ||
Optional<CIR_AnyType>:$sym_addr, | ||
AddressPointAttr:$address_point); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name, | |
Optional<CIR_AnyType>:$sym_addr, | |
AddressPointAttr:$address_point); | |
let arguments = (ins | |
OptionalAttr<FlatSymbolRefAttr>:$name, | |
Optional<CIR_AnyType>:$sym_addr, | |
AddressPointAttr:$address_point | |
); |
}]; | ||
|
||
let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name, | ||
Optional<CIR_AnyType>:$sym_addr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this should not be CIR_AnyType; should it be CIR_PtrTo<CIR_AnyFuncType> instead? However, neither the description nor the example clarifies what is expected for sym_addr. It would be helpful to include that in the documentation as well.
def VTableAddrPointOp : CIR_Op<"vtable.address_point", | ||
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def VTableAddrPointOp : CIR_Op<"vtable.address_point", | |
[Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> { | |
def CIR_VTableAddrPointOp : CIR_Op<"vtable.address_point", [ | |
Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface> | |
]> { |
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.