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 = ) + : !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]> { + 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 i32>>`. + + Example: + ```mlir + cir.global linkonce_odr @_ZTV1B = ... + ... + %3 = cir.vtable.address_point(@_ZTV1B, address_point = ) : !cir.ptr i32>> + ``` + }]; + + let arguments = (ins OptionalAttr:$name, + Optional:$sym_addr, + AddressPointAttr:$address_point); + let results = (outs Res:$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( + symbolTable.lookupNearestSymbolFrom(*this, getNameAttr())); + if (!op) + return emitOpError("'") + << name << "' does not reference a valid cir.global"; + std::optional 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 +!u32i = !cir.int +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 = ) : !cir.ptr !u32i>>> + cir.return +} + +// ----- + +!s8i = !cir.int +!u8i = !cir.int +!u64i = !cir.int +!rec_anon_struct = !cir.record 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.int>>>', but provided result type is '!cir.ptr !cir.int>>>'}} + %0 = cir.vtable.address_point(@_ZTV1S, address_point = ) : !cir.ptr !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 +!u8i = !cir.int +!rec_anon_struct = !cir.record x 4>}> +!rec_S = !cir.record !u32i>>>}> + +module { + cir.global "private" external @_ZTV1S : !rec_anon_struct {alignment = 8 : i64} + cir.func @_ZN1SC2Ev(%arg0: !cir.ptr) { + %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} + cir.store %arg0, %0 : !cir.ptr, !cir.ptr> + %1 = cir.load %0 : !cir.ptr>, !cir.ptr + %2 = cir.vtable.address_point(@_ZTV1S, address_point = ) : !cir.ptr !u32i>>> + %3 = cir.cast(bitcast, %1 : !cir.ptr), !cir.ptr !u32i>>>> + cir.store align(8) %2, %3 : !cir.ptr !u32i>>>, !cir.ptr !u32i>>>> + cir.return + } +} + +// CHECK: cir.vtable.address_point(@_ZTV1S, address_point = ) : !cir.ptr !u32i>>>