llvm-project/clang/unittests/CIR/PointerLikeTest.cpp
Henrich Lauko c842705c61
[CIR] Streamline creation of mlir::IntegerAttrs using mlir::Builder (#141830)
- Uses getI<bitwidth>IntegerAttr builder method instead of explicit attribute and its type creation.
- Adds few helper functions `getAlignmentAttr` to build alignment representing mlir::IntegerAttr.
- Removes duplicit type parameters, that are inferred from mlir::IntegerAttr.

This mirrors incubator changes from https://github.com/llvm/clangir/pull/1645#event-17840237927
2025-05-29 12:37:38 +02:00

363 lines
14 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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<cir::CIRDialect>();
context.loadDialect<mlir::acc::OpenACCDialect>();
// Register extension to integrate CIR types with OpenACC.
mlir::DialectRegistry registry;
cir::acc::registerOpenACCExtensions(registry);
context.appendDialectRegistry(registry);
}
MLIRContext context;
OpBuilder b;
Location loc;
llvm::StringMap<unsigned> recordNames;
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 b.getI64IntegerAttr(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 testSingleType(mlir::Type ty,
mlir::acc::VariableTypeCategory expectedTypeCategory) {
mlir::Type ptrTy = cir::PointerType::get(ty);
// cir::PointerType should be castable to acc::PointerLikeType
auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
ASSERT_NE(pltTy, nullptr);
EXPECT_EQ(pltTy.getElementType(), ty);
OwningOpRef<cir::AllocaOp> varPtrOp =
b.create<cir::AllocaOp>(loc, ptrTy, ty, "", getAlignOne(&context));
mlir::Value val = varPtrOp.get();
mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
cast<TypedValue<mlir::acc::PointerLikeType>>(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<mlir::acc::PointerLikeType>(ptrTy);
ASSERT_NE(pltTy, nullptr);
EXPECT_EQ(pltTy.getElementType(), arrTy);
// Create an alloca for the array
OwningOpRef<cir::AllocaOp> varPtrOp =
b.create<cir::AllocaOp>(loc, ptrTy, arrTy, "", getAlignOne(&context));
// Verify that the type category is array.
mlir::Value val = varPtrOp.get();
mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
cast<TypedValue<mlir::acc::PointerLikeType>>(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<cir::CastOp> decayPtr = b.create<cir::CastOp>(
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<mlir::acc::PointerLikeType>(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<TypedValue<mlir::acc::PointerLikeType>>(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<cir::ConstantOp>(loc, cir::IntAttr::get(i32Ty, 2));
OwningOpRef<cir::PtrStrideOp> accessPtr =
b.create<cir::PtrStrideOp>(loc, ptrToElemTy, decayVal, index);
mlir::Value accessVal = accessPtr.get();
// Verify that we still get the expected element type.
auto accessPltTy =
dyn_cast_if_present<mlir::acc::PointerLikeType>(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<TypedValue<mlir::acc::PointerLikeType>>(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<mlir::acc::PointerLikeType>(ptrTy);
ASSERT_NE(pltTy, nullptr);
EXPECT_EQ(pltTy.getElementType(), structTy);
// Create an alloca for the array
OwningOpRef<cir::AllocaOp> varPtrOp = b.create<cir::AllocaOp>(
loc, ptrTy, structTy, "", getAlignOne(&context));
// Verify that the type category is composite.
mlir::Value val = varPtrOp.get();
mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
cast<TypedValue<mlir::acc::PointerLikeType>>(val),
mlir::acc::getVarType(varPtrOp.get()));
EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::composite);
// Access the first element of the structure.
OwningOpRef<cir::GetMemberOp> access1 = b.create<cir::GetMemberOp>(
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<mlir::acc::PointerLikeType>(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<TypedValue<mlir::acc::PointerLikeType>>(accessVal1),
mlir::acc::getVarType(access1.get()));
EXPECT_EQ(access1TypeCategory, mlir::acc::VariableTypeCategory::composite);
// Access the second element of the structure.
OwningOpRef<cir::GetMemberOp> access2 = b.create<cir::GetMemberOp>(
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<mlir::acc::PointerLikeType>(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<TypedValue<mlir::acc::PointerLikeType>>(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<cir::AllocaOp> varPtrOp = b.create<cir::AllocaOp>(
loc, structPptrTy, structTy, "S", getAlignOne(&context));
mlir::Value val = varPtrOp.get();
// Get a pointer to the second member.
OwningOpRef<cir::GetMemberOp> access = b.create<cir::GetMemberOp>(
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<cir::LoadOp> loadOp = b.create<cir::LoadOp>(loc, accessVal);
mlir::Value loadVal = loadOp.get();
// Verify that the type category is the expected type category.
auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
cast<TypedValue<mlir::acc::PointerLikeType>>(loadVal),
mlir::acc::getVarType(loadOp.get()));
EXPECT_EQ(typeCategory, expectedTypeCategory);
}
};
TEST_F(CIROpenACCPointerLikeTest, testPointerToInt) {
// Test various scalar types.
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) {
testScalarType(cir::BoolType::get(&context));
}
TEST_F(CIROpenACCPointerLikeTest, testPointerToFloat) {
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);
testScalarType(ptrTy);
}
TEST_F(CIROpenACCPointerLikeTest, testPointerToArray) {
// Test an array type.
mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
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);
testStructType(i16Ty, i32Ty);
}
TEST_F(CIROpenACCPointerLikeTest, testPointerToUnion) {
// Test a union type.
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);
mlir::Type funcTy =
cir::FuncType::get(SmallVector<mlir::Type, 2>{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);
testNonScalarType(vecTy);
}
TEST_F(CIROpenACCPointerLikeTest, testPointerToVoid) {
mlir::Type voidTy = cir::VoidType::get(&context);
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);
}