From 6edf90c7beca34320ae53e3984f68e001aee6498 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Tue, 13 May 2025 09:51:04 -0700 Subject: [PATCH 1/3] [CIR] Add PointerLikeType interface support for cir::PointerType This adds code to attach the OpenACC PointerLikeType interface to cir::PointerType, along with a unit test for the interface. --- .../OpenACC/CIROpenACCTypeInterfaces.h | 36 ++++ .../OpenACC/RegisterOpenACCExtensions.h | 22 +++ clang/lib/CIR/CodeGen/CIRGenerator.cpp | 7 + clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/Dialect/CMakeLists.txt | 1 + .../OpenACC/CIROpenACCTypeInterfaces.cpp | 41 +++++ clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt | 10 ++ .../OpenACC/RegisterOpenACCExtensions.cpp | 27 +++ clang/unittests/CIR/CMakeLists.txt | 11 ++ clang/unittests/CIR/PointerLikeTest.cpp | 160 ++++++++++++++++++ clang/unittests/CMakeLists.txt | 11 +- 11 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h create mode 100644 clang/include/clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h create mode 100644 clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp create mode 100644 clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt create mode 100644 clang/lib/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.cpp create mode 100644 clang/unittests/CIR/CMakeLists.txt create mode 100644 clang/unittests/CIR/PointerLikeTest.cpp diff --git a/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h b/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h new file mode 100644 index 0000000000000..eccb6838a491f --- /dev/null +++ b/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains external dialect interfaces for CIR. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CIR_DIALECT_OPENACC_CIROPENACCTYPEINTERFACES_H +#define CLANG_CIR_DIALECT_OPENACC_CIROPENACCTYPEINTERFACES_H + +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" + +namespace cir::acc { + +template +struct OpenACCPointerLikeModel + : public mlir::acc::PointerLikeType::ExternalModel< + OpenACCPointerLikeModel, T> { + mlir::Type getElementType(mlir::Type pointer) const { + return mlir::cast(pointer).getPointee(); + } + mlir::acc::VariableTypeCategory + getPointeeTypeCategory(mlir::Type pointer, + mlir::TypedValue varPtr, + mlir::Type varType) const; +}; + +} // namespace cir::acc + +#endif // CLANG_CIR_DIALECT_OPENACC_CIROPENACCTYPEINTERFACES_H diff --git a/clang/include/clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h b/clang/include/clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h new file mode 100644 index 0000000000000..13780a01ea1bb --- /dev/null +++ b/clang/include/clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CIR_DIALECT_OPENACC_REGISTEROPENACCEXTENSIONS_H +#define CLANG_CIR_DIALECT_OPENACC_REGISTEROPENACCEXTENSIONS_H + +namespace mlir { +class DialectRegistry; +} // namespace mlir + +namespace cir::acc { + +void registerOpenACCExtensions(mlir::DialectRegistry ®istry); + +} // namespace cir::acc + +#endif // CLANG_CIR_DIALECT_OPENACC_REGISTEROPENACCEXTENSIONS_H diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index aa3864deb733c..40252ffecfba1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -18,6 +18,7 @@ #include "clang/AST/DeclGroup.h" #include "clang/CIR/CIRGenerator.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h" using namespace cir; using namespace clang; @@ -38,6 +39,12 @@ void CIRGenerator::Initialize(ASTContext &astContext) { mlirContext = std::make_unique(); mlirContext->loadDialect(); mlirContext->getOrLoadDialect(); + + // Register extensions to integrate CIR types with OpenACC. + mlir::DialectRegistry registry; + cir::acc::registerOpenACCExtensions(registry); + mlirContext->appendDialectRegistry(registry); + cgm = std::make_unique( *mlirContext.get(), astContext, codeGenOpts, diags); } diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 7a701c3c0b82b..8f5796e59d3bb 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -35,6 +35,7 @@ add_clang_library(clangCIR clangBasic clangLex ${dialect_libs} + CIROpenACCSupport MLIRCIR MLIRCIRInterfaces ) diff --git a/clang/lib/CIR/Dialect/CMakeLists.txt b/clang/lib/CIR/Dialect/CMakeLists.txt index 9f57627c321fb..c825a61b2779b 100644 --- a/clang/lib/CIR/Dialect/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(IR) +add_subdirectory(OpenACC) add_subdirectory(Transforms) diff --git a/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp new file mode 100644 index 0000000000000..ea563ecdfb3bb --- /dev/null +++ b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of external dialect interfaces for CIR. +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h" + +namespace cir::acc { + +template <> +mlir::acc::VariableTypeCategory +OpenACCPointerLikeModel::getPointeeTypeCategory( + mlir::Type pointer, mlir::TypedValue varPtr, + mlir::Type varType) const { + mlir::Type eleTy = mlir::cast(pointer).getPointee(); + + if (auto mappableTy = mlir::dyn_cast(eleTy)) + return mappableTy.getTypeCategory(varPtr); + + if (isAnyIntegerOrFloatingPointType(eleTy) || + mlir::isa(eleTy) || mlir::isa(eleTy)) + return mlir::acc::VariableTypeCategory::scalar; + if (mlir::isa(eleTy)) + return mlir::acc::VariableTypeCategory::array; + if (mlir::isa(eleTy)) + return mlir::acc::VariableTypeCategory::composite; + if (mlir::isa(eleTy) || mlir::isa(eleTy)) + return mlir::acc::VariableTypeCategory::nonscalar; + + // Without further checking, this type cannot be categorized. + return mlir::acc::VariableTypeCategory::uncategorized; +} + +} // namespace cir::acc diff --git a/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt b/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt new file mode 100644 index 0000000000000..5b0a1620a1bff --- /dev/null +++ b/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt @@ -0,0 +1,10 @@ +add_clang_library(CIROpenACCSupport + CIROpenACCTypeInterfaces.cpp + RegisterOpenACCExtensions.cpp + + DEPENDS + MLIRCIRTypeConstraintsIncGen + + LINK_LIBS PUBLIC + MLIRIR + ) diff --git a/clang/lib/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.cpp b/clang/lib/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.cpp new file mode 100644 index 0000000000000..3dfe7aeb6b1d6 --- /dev/null +++ b/clang/lib/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Registration for OpenACC extensions as applied to CIR dialect. +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h" + +namespace cir::acc { + +void registerOpenACCExtensions(mlir::DialectRegistry ®istry) { + registry.addExtension(+[](mlir::MLIRContext *ctx, cir::CIRDialect *dialect) { + cir::PointerType::attachInterface< + OpenACCPointerLikeModel>(*ctx); + }); +} + +} // namespace cir::acc diff --git a/clang/unittests/CIR/CMakeLists.txt b/clang/unittests/CIR/CMakeLists.txt new file mode 100644 index 0000000000000..171201b7beb23 --- /dev/null +++ b/clang/unittests/CIR/CMakeLists.txt @@ -0,0 +1,11 @@ +add_clang_unittest(CIRUnitTests + PointerLikeTest.cpp + LLVM_COMPONENTS + Core + + LINK_LIBS + MLIRCIR + CIROpenACCSupport + MLIRIR + MLIROpenACCDialect + ) diff --git a/clang/unittests/CIR/PointerLikeTest.cpp b/clang/unittests/CIR/PointerLikeTest.cpp new file mode 100644 index 0000000000000..af8f32d237e71 --- /dev/null +++ b/clang/unittests/CIR/PointerLikeTest.cpp @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Unit tests for CIR implementation of OpenACC's PointertLikeType interface +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h" +#include "clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h" +#include "gtest/gtest.h" + +using namespace mlir; +using namespace cir; + +//===----------------------------------------------------------------------===// +// Test Fixture +//===----------------------------------------------------------------------===// + +class CIROpenACCPointerLikeTest : public ::testing::Test { +protected: + CIROpenACCPointerLikeTest() : b(&context), loc(UnknownLoc::get(&context)) { + context.loadDialect(); + context.loadDialect(); + + // Register extension to integrate CIR types with OpenACC. + mlir::DialectRegistry registry; + cir::acc::registerOpenACCExtensions(registry); + context.appendDialectRegistry(registry); + } + + MLIRContext context; + OpBuilder b; + Location loc; + + mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx, + clang::CharUnits size) { + // Note that mlir::IntegerType is used instead of cir::IntType here + // because we don't need sign information for this to be useful, so keep + // it simple. + return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64), + size.getQuantity()); + } + + // General handler for types without a specific test + void testElementType(mlir::Type ty) { + mlir::Type ptrTy = cir::PointerType::get(ty); + + // cir::PointerType should be castable to acc::PointerLikeType + auto pltTy = dyn_cast_if_present(ptrTy); + ASSERT_NE(pltTy, nullptr); + + EXPECT_EQ(pltTy.getElementType(), ty); + + OwningOpRef varPtrOp = b.create( + loc, ptrTy, ty, "", + getSizeFromCharUnits(&context, clang::CharUnits::One())); + + mlir::Value val = varPtrOp.get(); + mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory( + cast>(val), + mlir::acc::getVarType(varPtrOp.get())); + + if (isAnyIntegerOrFloatingPointType(ty) || + mlir::isa(ty) || mlir::isa(ty)) { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::scalar); + } else if (mlir::isa(ty)) { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::array); + } else if (mlir::isa(ty)) { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::composite); + } else if (mlir::isa(ty)) { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::nonscalar); + } else if (mlir::isa(ty)) { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::uncategorized); + } else { + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::uncategorized); + // If we hit this, we need to add support for a new type. + ASSERT_TRUE(false); + } + } +}; + +TEST_F(CIROpenACCPointerLikeTest, testPointerToInt) { + // Test various scalar types. + testElementType(cir::IntType::get(&context, 8, true)); + testElementType(cir::IntType::get(&context, 8, false)); + testElementType(cir::IntType::get(&context, 16, true)); + testElementType(cir::IntType::get(&context, 16, false)); + testElementType(cir::IntType::get(&context, 32, true)); + testElementType(cir::IntType::get(&context, 32, false)); + testElementType(cir::IntType::get(&context, 64, true)); + testElementType(cir::IntType::get(&context, 64, false)); + testElementType(cir::IntType::get(&context, 128, true)); + testElementType(cir::IntType::get(&context, 128, false)); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToBool) { + testElementType(cir::BoolType::get(&context)); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToFloat) { + testElementType(cir::SingleType::get(&context)); + testElementType(cir::DoubleType::get(&context)); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToPointer) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + mlir::Type ptrTy = cir::PointerType::get(i32Ty); + testElementType(ptrTy); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToArray) { + // Test an array type. + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + testElementType(cir::ArrayType::get(i32Ty, 10)); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToStruct) { + // Test a struct type. + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + llvm::ArrayRef fields = {i32Ty, i32Ty}; + cir::RecordType structTy = cir::RecordType::get( + &context, b.getStringAttr("S"), cir::RecordType::RecordKind::Struct); + structTy.complete(fields, false, false); + testElementType(structTy); + + // Test a union type. + cir::RecordType unionTy = cir::RecordType::get( + &context, b.getStringAttr("U"), cir::RecordType::RecordKind::Union); + unionTy.complete(fields, false, false); + testElementType(unionTy); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToFunction) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + cir::FuncType::get(SmallVector{i32Ty, i32Ty}, i32Ty); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToVector) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + mlir::Type vecTy = cir::VectorType::get(i32Ty, 4); + testElementType(vecTy); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToVoid) { + mlir::Type voidTy = cir::VoidType::get(&context); + testElementType(voidTy); +} diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index b4114d419b75c..2a84607aa0937 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -105,7 +105,9 @@ add_subdirectory(Index) add_subdirectory(InstallAPI) add_subdirectory(Serialization) add_subdirectory(Support) - +if (CLANG_ENABLE_CIR) + add_subdirectory(CIR) +endif() # If we're doing a single merged clang unit test binary, add that target after # all the previous subdirectories have been processed. @@ -127,3 +129,10 @@ add_distinct_clang_unittest(AllClangUnitTests # the merged clang unit test binary, we can update the include paths and make # this the default. include_directories(Tooling) + +if (CLANG_ENABLE_CIR) + set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include ) + set(MLIR_TABLEGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/tools/mlir/include) + include_directories(SYSTEM ${MLIR_INCLUDE_DIR}) + include_directories(${MLIR_TABLEGEN_OUTPUT_DIR}) +endif() From bfa6ca82ffa05b43e8a2c69673c867646208ae84 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 14 May 2025 16:15:14 -0700 Subject: [PATCH 2/3] Fix interior pointer handling --- .../OpenACC/CIROpenACCTypeInterfaces.cpp | 25 +- clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt | 2 + clang/unittests/CIR/CMakeLists.txt | 7 +- clang/unittests/CIR/PointerLikeTest.cpp | 302 +++++++++++++++--- 4 files changed, 285 insertions(+), 51 deletions(-) diff --git a/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp index ea563ecdfb3bb..3750dead60785 100644 --- a/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp +++ b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp @@ -11,15 +11,38 @@ //===----------------------------------------------------------------------===// #include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" namespace cir::acc { +mlir::Type getBaseType(mlir::Value varPtr) { + mlir::Operation *op = varPtr.getDefiningOp(); + assert(op && "Expected a defining operation"); + + // This is the variable definition we're looking for. + if (auto allocaOp = mlir::dyn_cast(*op)) + return allocaOp.getAllocaType(); + + // Look through casts to the source pointer. + if (auto castOp = mlir::dyn_cast(*op)) + return getBaseType(castOp.getSrc()); + + // Follow the source of ptr strides. + if (auto ptrStrideOp = mlir::dyn_cast(*op)) + return getBaseType(ptrStrideOp.getBase()); + + if (auto getMemberOp = mlir::dyn_cast(*op)) + return getBaseType(getMemberOp.getAddr()); + + return mlir::cast(varPtr.getType()).getPointee(); +} + template <> mlir::acc::VariableTypeCategory OpenACCPointerLikeModel::getPointeeTypeCategory( mlir::Type pointer, mlir::TypedValue varPtr, mlir::Type varType) const { - mlir::Type eleTy = mlir::cast(pointer).getPointee(); + mlir::Type eleTy = getBaseType(varPtr); if (auto mappableTy = mlir::dyn_cast(eleTy)) return mappableTy.getTypeCategory(varPtr); diff --git a/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt b/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt index 5b0a1620a1bff..de27f4cb27c59 100644 --- a/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/OpenACC/CMakeLists.txt @@ -7,4 +7,6 @@ add_clang_library(CIROpenACCSupport LINK_LIBS PUBLIC MLIRIR + MLIRCIR + MLIROpenACCDialect ) diff --git a/clang/unittests/CIR/CMakeLists.txt b/clang/unittests/CIR/CMakeLists.txt index 171201b7beb23..650fde38c48a9 100644 --- a/clang/unittests/CIR/CMakeLists.txt +++ b/clang/unittests/CIR/CMakeLists.txt @@ -1,4 +1,9 @@ -add_clang_unittest(CIRUnitTests +set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include ) +set(MLIR_TABLEGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/tools/mlir/include) +include_directories(SYSTEM ${MLIR_INCLUDE_DIR}) +include_directories(${MLIR_TABLEGEN_OUTPUT_DIR}) + +add_distinct_clang_unittest(CIRUnitTests PointerLikeTest.cpp LLVM_COMPONENTS Core diff --git a/clang/unittests/CIR/PointerLikeTest.cpp b/clang/unittests/CIR/PointerLikeTest.cpp index af8f32d237e71..c0da271d56d4c 100644 --- a/clang/unittests/CIR/PointerLikeTest.cpp +++ b/clang/unittests/CIR/PointerLikeTest.cpp @@ -44,18 +44,31 @@ class CIROpenACCPointerLikeTest : public ::testing::Test { MLIRContext context; OpBuilder b; Location loc; + llvm::StringMap recordNames; - mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx, - clang::CharUnits size) { + mlir::IntegerAttr getAlignOne(mlir::MLIRContext *ctx) { // Note that mlir::IntegerType is used instead of cir::IntType here // because we don't need sign information for this to be useful, so keep // it simple. + clang::CharUnits align = clang::CharUnits::One(); return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64), - size.getQuantity()); + align.getQuantity()); + } + + mlir::StringAttr getUniqueRecordName(const std::string &baseName) { + auto it = recordNames.find(baseName); + if (it == recordNames.end()) { + recordNames[baseName] = 0; + return b.getStringAttr(baseName); + } + + return b.getStringAttr(baseName + "." + + std::to_string(recordNames[baseName]++)); } // General handler for types without a specific test - void testElementType(mlir::Type ty) { + void testSingleType(mlir::Type ty, + mlir::acc::VariableTypeCategory expectedTypeCategory) { mlir::Type ptrTy = cir::PointerType::get(ty); // cir::PointerType should be castable to acc::PointerLikeType @@ -64,97 +77,288 @@ class CIROpenACCPointerLikeTest : public ::testing::Test { EXPECT_EQ(pltTy.getElementType(), ty); + OwningOpRef varPtrOp = + b.create(loc, ptrTy, ty, "", getAlignOne(&context)); + + mlir::Value val = varPtrOp.get(); + mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory( + cast>(val), + mlir::acc::getVarType(varPtrOp.get())); + + EXPECT_EQ(typeCategory, expectedTypeCategory); + } + + void testScalarType(mlir::Type ty) { + testSingleType(ty, mlir::acc::VariableTypeCategory::scalar); + } + + void testNonScalarType(mlir::Type ty) { + testSingleType(ty, mlir::acc::VariableTypeCategory::nonscalar); + } + + void testUncategorizedType(mlir::Type ty) { + testSingleType(ty, mlir::acc::VariableTypeCategory::uncategorized); + } + + void testArrayType(mlir::Type ty) { + // Build the array pointer type. + mlir::Type arrTy = cir::ArrayType::get(ty, 10); + mlir::Type ptrTy = cir::PointerType::get(arrTy); + + // Verify that the pointer points to the array type.. + auto pltTy = dyn_cast_if_present(ptrTy); + ASSERT_NE(pltTy, nullptr); + EXPECT_EQ(pltTy.getElementType(), arrTy); + + // Create an alloca for the array + OwningOpRef varPtrOp = + b.create(loc, ptrTy, arrTy, "", getAlignOne(&context)); + + // Verify that the type category is array. + mlir::Value val = varPtrOp.get(); + mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory( + cast>(val), + mlir::acc::getVarType(varPtrOp.get())); + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::array); + + // Create an array-to-pointer decay cast. + mlir::Type ptrToElemTy = cir::PointerType::get(ty); + OwningOpRef decayPtr = b.create( + loc, ptrToElemTy, cir::CastKind::array_to_ptrdecay, val); + mlir::Value decayVal = decayPtr.get(); + + // Verify that we still get the expected element type. + auto decayPltTy = + dyn_cast_if_present(decayVal.getType()); + ASSERT_NE(decayPltTy, nullptr); + EXPECT_EQ(decayPltTy.getElementType(), ty); + + // Verify that we still identify the type category as an array. + mlir::acc::VariableTypeCategory decayTypeCategory = + decayPltTy.getPointeeTypeCategory( + cast>(decayVal), + mlir::acc::getVarType(decayPtr.get())); + EXPECT_EQ(decayTypeCategory, mlir::acc::VariableTypeCategory::array); + + // Create an element access. + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + mlir::Value index = + b.create(loc, cir::IntAttr::get(i32Ty, 2)); + OwningOpRef accessPtr = + b.create(loc, ptrToElemTy, decayVal, index); + mlir::Value accessVal = accessPtr.get(); + + // Verify that we still get the expected element type. + auto accessPltTy = + dyn_cast_if_present(accessVal.getType()); + ASSERT_NE(accessPltTy, nullptr); + EXPECT_EQ(accessPltTy.getElementType(), ty); + + // Verify that we still identify the type category as an array. + mlir::acc::VariableTypeCategory accessTypeCategory = + accessPltTy.getPointeeTypeCategory( + cast>(accessVal), + mlir::acc::getVarType(accessPtr.get())); + EXPECT_EQ(accessTypeCategory, mlir::acc::VariableTypeCategory::array); + } + + // Structures and unions are accessed in the same way, so use a common test. + void testRecordType(mlir::Type ty1, mlir::Type ty2, + cir::RecordType::RecordKind kind) { + // Build the structure pointer type. + cir::RecordType structTy = + cir::RecordType::get(&context, getUniqueRecordName("S"), kind); + structTy.complete({ty1, ty2}, false, false); + mlir::Type ptrTy = cir::PointerType::get(structTy); + + // Verify that the pointer points to the structure type. + auto pltTy = dyn_cast_if_present(ptrTy); + ASSERT_NE(pltTy, nullptr); + EXPECT_EQ(pltTy.getElementType(), structTy); + + // Create an alloca for the array OwningOpRef varPtrOp = b.create( - loc, ptrTy, ty, "", - getSizeFromCharUnits(&context, clang::CharUnits::One())); + loc, ptrTy, structTy, "", getAlignOne(&context)); + // Verify that the type category is composite. mlir::Value val = varPtrOp.get(); mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory( cast>(val), mlir::acc::getVarType(varPtrOp.get())); + EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::composite); - if (isAnyIntegerOrFloatingPointType(ty) || - mlir::isa(ty) || mlir::isa(ty)) { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::scalar); - } else if (mlir::isa(ty)) { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::array); - } else if (mlir::isa(ty)) { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::composite); - } else if (mlir::isa(ty)) { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::nonscalar); - } else if (mlir::isa(ty)) { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::uncategorized); - } else { - EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::uncategorized); - // If we hit this, we need to add support for a new type. - ASSERT_TRUE(false); - } + // Access the first element of the structure. + OwningOpRef access1 = b.create( + loc, cir::PointerType::get(ty1), val, b.getStringAttr("f1"), 0); + mlir::Value accessVal1 = access1.get(); + + // Verify that we get the expected element type. + auto access1PltTy = + dyn_cast_if_present(accessVal1.getType()); + ASSERT_NE(access1PltTy, nullptr); + EXPECT_EQ(access1PltTy.getElementType(), ty1); + + // Verify that the type category is still composite. + mlir::acc::VariableTypeCategory access1TypeCategory = + access1PltTy.getPointeeTypeCategory( + cast>(accessVal1), + mlir::acc::getVarType(access1.get())); + EXPECT_EQ(access1TypeCategory, mlir::acc::VariableTypeCategory::composite); + + // Access the second element of the structure. + OwningOpRef access2 = b.create( + loc, cir::PointerType::get(ty2), val, b.getStringAttr("f2"), 1); + mlir::Value accessVal2 = access2.get(); + + // Verify that we get the expected element type. + auto access2PltTy = + dyn_cast_if_present(accessVal2.getType()); + ASSERT_NE(access2PltTy, nullptr); + EXPECT_EQ(access2PltTy.getElementType(), ty2); + + // Verify that the type category is still composite. + mlir::acc::VariableTypeCategory access2TypeCategory = + access2PltTy.getPointeeTypeCategory( + cast>(accessVal2), + mlir::acc::getVarType(access2.get())); + EXPECT_EQ(access2TypeCategory, mlir::acc::VariableTypeCategory::composite); + } + + void testStructType(mlir::Type ty1, mlir::Type ty2) { + testRecordType(ty1, ty2, cir::RecordType::RecordKind::Struct); + } + + void testUnionType(mlir::Type ty1, mlir::Type ty2) { + testRecordType(ty1, ty2, cir::RecordType::RecordKind::Union); + } + + // This is testing a case like this: + // + // struct S { + // int *f1; + // int *f2; + // } *p; + // int *pMember = p->f2; + // + // That is, we are not testing a pointer to a member, we're testing a pointer + // that is loaded as a member value. + void testPointerToMemberType( + mlir::Type ty, mlir::acc::VariableTypeCategory expectedTypeCategory) { + // Construct a struct type with two members that are pointers to the input + // type. + mlir::Type ptrTy = cir::PointerType::get(ty); + cir::RecordType structTy = + cir::RecordType::get(&context, getUniqueRecordName("S"), + cir::RecordType::RecordKind::Struct); + structTy.complete({ptrTy, ptrTy}, false, false); + mlir::Type structPptrTy = cir::PointerType::get(structTy); + + // Create an alloca for the struct. + OwningOpRef varPtrOp = b.create( + loc, structPptrTy, structTy, "S", getAlignOne(&context)); + mlir::Value val = varPtrOp.get(); + + // Get a pointer to the second member. + OwningOpRef access = b.create( + loc, cir::PointerType::get(ptrTy), val, b.getStringAttr("f2"), 1); + mlir::Value accessVal = access.get(); + + // Load the value of the second member. This is the pointer we want to test. + OwningOpRef loadOp = b.create(loc, accessVal); + mlir::Value loadVal = loadOp.get(); + + // Verify that the type category is the expected type category. + auto pltTy = dyn_cast_if_present(ptrTy); + mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory( + cast>(loadVal), + mlir::acc::getVarType(loadOp.get())); + + EXPECT_EQ(typeCategory, expectedTypeCategory); } }; TEST_F(CIROpenACCPointerLikeTest, testPointerToInt) { // Test various scalar types. - testElementType(cir::IntType::get(&context, 8, true)); - testElementType(cir::IntType::get(&context, 8, false)); - testElementType(cir::IntType::get(&context, 16, true)); - testElementType(cir::IntType::get(&context, 16, false)); - testElementType(cir::IntType::get(&context, 32, true)); - testElementType(cir::IntType::get(&context, 32, false)); - testElementType(cir::IntType::get(&context, 64, true)); - testElementType(cir::IntType::get(&context, 64, false)); - testElementType(cir::IntType::get(&context, 128, true)); - testElementType(cir::IntType::get(&context, 128, false)); + testScalarType(cir::IntType::get(&context, 8, true)); + testScalarType(cir::IntType::get(&context, 8, false)); + testScalarType(cir::IntType::get(&context, 16, true)); + testScalarType(cir::IntType::get(&context, 16, false)); + testScalarType(cir::IntType::get(&context, 32, true)); + testScalarType(cir::IntType::get(&context, 32, false)); + testScalarType(cir::IntType::get(&context, 64, true)); + testScalarType(cir::IntType::get(&context, 64, false)); + testScalarType(cir::IntType::get(&context, 128, true)); + testScalarType(cir::IntType::get(&context, 128, false)); } TEST_F(CIROpenACCPointerLikeTest, testPointerToBool) { - testElementType(cir::BoolType::get(&context)); + testScalarType(cir::BoolType::get(&context)); } TEST_F(CIROpenACCPointerLikeTest, testPointerToFloat) { - testElementType(cir::SingleType::get(&context)); - testElementType(cir::DoubleType::get(&context)); + testScalarType(cir::SingleType::get(&context)); + testScalarType(cir::DoubleType::get(&context)); } TEST_F(CIROpenACCPointerLikeTest, testPointerToPointer) { mlir::Type i32Ty = cir::IntType::get(&context, 32, true); mlir::Type ptrTy = cir::PointerType::get(i32Ty); - testElementType(ptrTy); + testScalarType(ptrTy); } TEST_F(CIROpenACCPointerLikeTest, testPointerToArray) { // Test an array type. mlir::Type i32Ty = cir::IntType::get(&context, 32, true); - testElementType(cir::ArrayType::get(i32Ty, 10)); + testArrayType(i32Ty); } TEST_F(CIROpenACCPointerLikeTest, testPointerToStruct) { // Test a struct type. + mlir::Type i16Ty = cir::IntType::get(&context, 16, true); mlir::Type i32Ty = cir::IntType::get(&context, 32, true); - llvm::ArrayRef fields = {i32Ty, i32Ty}; - cir::RecordType structTy = cir::RecordType::get( - &context, b.getStringAttr("S"), cir::RecordType::RecordKind::Struct); - structTy.complete(fields, false, false); - testElementType(structTy); + testStructType(i16Ty, i32Ty); +} +TEST_F(CIROpenACCPointerLikeTest, testPointerToUnion) { // Test a union type. - cir::RecordType unionTy = cir::RecordType::get( - &context, b.getStringAttr("U"), cir::RecordType::RecordKind::Union); - unionTy.complete(fields, false, false); - testElementType(unionTy); + mlir::Type i16Ty = cir::IntType::get(&context, 16, true); + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + testUnionType(i16Ty, i32Ty); } TEST_F(CIROpenACCPointerLikeTest, testPointerToFunction) { mlir::Type i32Ty = cir::IntType::get(&context, 32, true); - cir::FuncType::get(SmallVector{i32Ty, i32Ty}, i32Ty); + mlir::Type funcTy = + cir::FuncType::get(SmallVector{i32Ty, i32Ty}, i32Ty); + testNonScalarType(funcTy); } TEST_F(CIROpenACCPointerLikeTest, testPointerToVector) { mlir::Type i32Ty = cir::IntType::get(&context, 32, true); mlir::Type vecTy = cir::VectorType::get(i32Ty, 4); - testElementType(vecTy); + testNonScalarType(vecTy); } TEST_F(CIROpenACCPointerLikeTest, testPointerToVoid) { mlir::Type voidTy = cir::VoidType::get(&context); - testElementType(voidTy); + testUncategorizedType(voidTy); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToIntMember) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + testPointerToMemberType(i32Ty, mlir::acc::VariableTypeCategory::scalar); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToArrayMember) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + mlir::Type arrTy = cir::ArrayType::get(i32Ty, 10); + testPointerToMemberType(arrTy, mlir::acc::VariableTypeCategory::array); +} + +TEST_F(CIROpenACCPointerLikeTest, testPointerToStructMember) { + mlir::Type i32Ty = cir::IntType::get(&context, 32, true); + cir::RecordType structTy = cir::RecordType::get( + &context, getUniqueRecordName("S"), cir::RecordType::RecordKind::Struct); + structTy.complete({i32Ty, i32Ty}, false, false); + testPointerToMemberType(structTy, mlir::acc::VariableTypeCategory::composite); } From e191e9301a1a33f64f0153f2f5081ea04ad88f47 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 14 May 2025 16:29:24 -0700 Subject: [PATCH 3/3] Clean up includes and cmake settings --- .../clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h | 1 - clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp | 1 + clang/unittests/CMakeLists.txt | 7 ------- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h b/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h index eccb6838a491f..3011245cd8a03 100644 --- a/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h +++ b/clang/include/clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h @@ -14,7 +14,6 @@ #define CLANG_CIR_DIALECT_OPENACC_CIROPENACCTYPEINTERFACES_H #include "mlir/Dialect/OpenACC/OpenACC.h" -#include "clang/CIR/Dialect/IR/CIRTypes.h" namespace cir::acc { diff --git a/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp index 3750dead60785..de8dd9c55ee32 100644 --- a/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp +++ b/clang/lib/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.cpp @@ -12,6 +12,7 @@ #include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" namespace cir::acc { diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index 2a84607aa0937..aef28f914b640 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -129,10 +129,3 @@ add_distinct_clang_unittest(AllClangUnitTests # the merged clang unit test binary, we can update the include paths and make # this the default. include_directories(Tooling) - -if (CLANG_ENABLE_CIR) - set(MLIR_INCLUDE_DIR ${LLVM_MAIN_SRC_DIR}/../mlir/include ) - set(MLIR_TABLEGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/tools/mlir/include) - include_directories(SYSTEM ${MLIR_INCLUDE_DIR}) - include_directories(${MLIR_TABLEGEN_OUTPUT_DIR}) -endif()