1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Unit tests for CIR implementation of OpenACC's PointertLikeType interface
10//
11//===----------------------------------------------------------------------===//
12
13#include "mlir/Dialect/OpenACC/OpenACC.h"
14#include "mlir/IR/BuiltinTypes.h"
15#include "mlir/IR/Diagnostics.h"
16#include "mlir/IR/MLIRContext.h"
17#include "mlir/IR/Value.h"
18#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
19#include "clang/CIR/Dialect/IR/CIRDialect.h"
20#include "clang/CIR/Dialect/IR/CIRTypes.h"
21#include "clang/CIR/Dialect/OpenACC/CIROpenACCTypeInterfaces.h"
22#include "clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h"
23#include "gtest/gtest.h"
24
25using namespace mlir;
26using namespace cir;
27
28//===----------------------------------------------------------------------===//
29// Test Fixture
30//===----------------------------------------------------------------------===//
31
32class CIROpenACCPointerLikeTest : public ::testing::Test {
33protected:
34 CIROpenACCPointerLikeTest() : b(&context), loc(UnknownLoc::get(&context)) {
35 context.loadDialect<cir::CIRDialect>();
36 context.loadDialect<mlir::acc::OpenACCDialect>();
37
38 // Register extension to integrate CIR types with OpenACC.
39 mlir::DialectRegistry registry;
40 cir::acc::registerOpenACCExtensions(registry&: registry);
41 context.appendDialectRegistry(registry);
42 }
43
44 MLIRContext context;
45 OpBuilder b;
46 Location loc;
47 llvm::StringMap<unsigned> recordNames;
48
49 mlir::IntegerAttr getAlignOne(mlir::MLIRContext *ctx) {
50 // Note that mlir::IntegerType is used instead of cir::IntType here because
51 // we don't need sign information for this to be useful, so keep it simple.
52 clang::CharUnits align = clang::CharUnits::One();
53 return b.getI64IntegerAttr(align.getQuantity());
54 }
55
56 mlir::StringAttr getUniqueRecordName(const std::string &baseName) {
57 auto it = recordNames.find(Key: baseName);
58 if (it == recordNames.end()) {
59 recordNames[baseName] = 0;
60 return b.getStringAttr(baseName);
61 }
62
63 return b.getStringAttr(baseName + "." +
64 std::to_string(recordNames[baseName]++));
65 }
66
67 // General handler for types without a specific test
68 void testSingleType(mlir::Type ty,
69 mlir::acc::VariableTypeCategory expectedTypeCategory) {
70 mlir::Type ptrTy = cir::PointerType::get(ty);
71
72 // cir::PointerType should be castable to acc::PointerLikeType
73 auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
74 ASSERT_NE(pltTy, nullptr);
75
76 EXPECT_EQ(pltTy.getElementType(), ty);
77
78 OwningOpRef<cir::AllocaOp> varPtrOp =
79 b.create<cir::AllocaOp>(loc, ptrTy, ty, "", getAlignOne(&context));
80
81 mlir::Value val = varPtrOp.get();
82 mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
83 cast<TypedValue<mlir::acc::PointerLikeType>>(val),
84 mlir::acc::getVarType(varPtrOp.get()));
85
86 EXPECT_EQ(typeCategory, expectedTypeCategory);
87 }
88
89 void testScalarType(mlir::Type ty) {
90 testSingleType(ty, mlir::acc::VariableTypeCategory::scalar);
91 }
92
93 void testNonScalarType(mlir::Type ty) {
94 testSingleType(ty, mlir::acc::VariableTypeCategory::nonscalar);
95 }
96
97 void testUncategorizedType(mlir::Type ty) {
98 testSingleType(ty, mlir::acc::VariableTypeCategory::uncategorized);
99 }
100
101 void testArrayType(mlir::Type ty) {
102 // Build the array pointer type.
103 mlir::Type arrTy = cir::ArrayType::get(ty, 10);
104 mlir::Type ptrTy = cir::PointerType::get(arrTy);
105
106 // Verify that the pointer points to the array type..
107 auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
108 ASSERT_NE(pltTy, nullptr);
109 EXPECT_EQ(pltTy.getElementType(), arrTy);
110
111 // Create an alloca for the array
112 OwningOpRef<cir::AllocaOp> varPtrOp =
113 b.create<cir::AllocaOp>(loc, ptrTy, arrTy, "", getAlignOne(&context));
114
115 // Verify that the type category is array.
116 mlir::Value val = varPtrOp.get();
117 mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
118 cast<TypedValue<mlir::acc::PointerLikeType>>(val),
119 mlir::acc::getVarType(varPtrOp.get()));
120 EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::array);
121
122 // Create an array-to-pointer decay cast.
123 mlir::Type ptrToElemTy = cir::PointerType::get(ty);
124 OwningOpRef<cir::CastOp> decayPtr = b.create<cir::CastOp>(
125 loc, ptrToElemTy, cir::CastKind::array_to_ptrdecay, val);
126 mlir::Value decayVal = decayPtr.get();
127
128 // Verify that we still get the expected element type.
129 auto decayPltTy =
130 dyn_cast_if_present<mlir::acc::PointerLikeType>(decayVal.getType());
131 ASSERT_NE(decayPltTy, nullptr);
132 EXPECT_EQ(decayPltTy.getElementType(), ty);
133
134 // Verify that we still identify the type category as an array.
135 mlir::acc::VariableTypeCategory decayTypeCategory =
136 decayPltTy.getPointeeTypeCategory(
137 cast<TypedValue<mlir::acc::PointerLikeType>>(decayVal),
138 mlir::acc::getVarType(decayPtr.get()));
139 EXPECT_EQ(decayTypeCategory, mlir::acc::VariableTypeCategory::array);
140
141 // Create an element access.
142 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
143 mlir::Value index =
144 b.create<cir::ConstantOp>(loc, cir::IntAttr::get(i32Ty, 2));
145 OwningOpRef<cir::PtrStrideOp> accessPtr =
146 b.create<cir::PtrStrideOp>(loc, ptrToElemTy, decayVal, index);
147 mlir::Value accessVal = accessPtr.get();
148
149 // Verify that we still get the expected element type.
150 auto accessPltTy =
151 dyn_cast_if_present<mlir::acc::PointerLikeType>(accessVal.getType());
152 ASSERT_NE(accessPltTy, nullptr);
153 EXPECT_EQ(accessPltTy.getElementType(), ty);
154
155 // Verify that we still identify the type category as an array.
156 mlir::acc::VariableTypeCategory accessTypeCategory =
157 accessPltTy.getPointeeTypeCategory(
158 cast<TypedValue<mlir::acc::PointerLikeType>>(accessVal),
159 mlir::acc::getVarType(accessPtr.get()));
160 EXPECT_EQ(accessTypeCategory, mlir::acc::VariableTypeCategory::array);
161 }
162
163 // Structures and unions are accessed in the same way, so use a common test.
164 void testRecordType(mlir::Type ty1, mlir::Type ty2,
165 cir::RecordType::RecordKind kind) {
166 // Build the structure pointer type.
167 cir::RecordType structTy =
168 cir::RecordType::get(&context, getUniqueRecordName("S"), kind);
169 structTy.complete({ty1, ty2}, false, false);
170 mlir::Type ptrTy = cir::PointerType::get(structTy);
171
172 // Verify that the pointer points to the structure type.
173 auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
174 ASSERT_NE(pltTy, nullptr);
175 EXPECT_EQ(pltTy.getElementType(), structTy);
176
177 // Create an alloca for the array
178 OwningOpRef<cir::AllocaOp> varPtrOp = b.create<cir::AllocaOp>(
179 loc, ptrTy, structTy, "", getAlignOne(&context));
180
181 // Verify that the type category is composite.
182 mlir::Value val = varPtrOp.get();
183 mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
184 cast<TypedValue<mlir::acc::PointerLikeType>>(val),
185 mlir::acc::getVarType(varPtrOp.get()));
186 EXPECT_EQ(typeCategory, mlir::acc::VariableTypeCategory::composite);
187
188 // Access the first element of the structure.
189 OwningOpRef<cir::GetMemberOp> access1 = b.create<cir::GetMemberOp>(
190 loc, cir::PointerType::get(ty1), val, b.getStringAttr("f1"), 0);
191 mlir::Value accessVal1 = access1.get();
192
193 // Verify that we get the expected element type.
194 auto access1PltTy =
195 dyn_cast_if_present<mlir::acc::PointerLikeType>(accessVal1.getType());
196 ASSERT_NE(access1PltTy, nullptr);
197 EXPECT_EQ(access1PltTy.getElementType(), ty1);
198
199 // Verify that the type category is still composite.
200 mlir::acc::VariableTypeCategory access1TypeCategory =
201 access1PltTy.getPointeeTypeCategory(
202 cast<TypedValue<mlir::acc::PointerLikeType>>(accessVal1),
203 mlir::acc::getVarType(access1.get()));
204 EXPECT_EQ(access1TypeCategory, mlir::acc::VariableTypeCategory::composite);
205
206 // Access the second element of the structure.
207 OwningOpRef<cir::GetMemberOp> access2 = b.create<cir::GetMemberOp>(
208 loc, cir::PointerType::get(ty2), val, b.getStringAttr("f2"), 1);
209 mlir::Value accessVal2 = access2.get();
210
211 // Verify that we get the expected element type.
212 auto access2PltTy =
213 dyn_cast_if_present<mlir::acc::PointerLikeType>(accessVal2.getType());
214 ASSERT_NE(access2PltTy, nullptr);
215 EXPECT_EQ(access2PltTy.getElementType(), ty2);
216
217 // Verify that the type category is still composite.
218 mlir::acc::VariableTypeCategory access2TypeCategory =
219 access2PltTy.getPointeeTypeCategory(
220 cast<TypedValue<mlir::acc::PointerLikeType>>(accessVal2),
221 mlir::acc::getVarType(access2.get()));
222 EXPECT_EQ(access2TypeCategory, mlir::acc::VariableTypeCategory::composite);
223 }
224
225 void testStructType(mlir::Type ty1, mlir::Type ty2) {
226 testRecordType(ty1, ty2, cir::RecordType::RecordKind::Struct);
227 }
228
229 void testUnionType(mlir::Type ty1, mlir::Type ty2) {
230 testRecordType(ty1, ty2, cir::RecordType::RecordKind::Union);
231 }
232
233 // This is testing a case like this:
234 //
235 // struct S {
236 // int *f1;
237 // int *f2;
238 // } *p;
239 // int *pMember = p->f2;
240 //
241 // That is, we are not testing a pointer to a member, we're testing a pointer
242 // that is loaded as a member value.
243 void testPointerToMemberType(
244 mlir::Type ty, mlir::acc::VariableTypeCategory expectedTypeCategory) {
245 // Construct a struct type with two members that are pointers to the input
246 // type.
247 mlir::Type ptrTy = cir::PointerType::get(ty);
248 cir::RecordType structTy =
249 cir::RecordType::get(&context, getUniqueRecordName("S"),
250 cir::RecordType::RecordKind::Struct);
251 structTy.complete({ptrTy, ptrTy}, false, false);
252 mlir::Type structPptrTy = cir::PointerType::get(structTy);
253
254 // Create an alloca for the struct.
255 OwningOpRef<cir::AllocaOp> varPtrOp = b.create<cir::AllocaOp>(
256 loc, structPptrTy, structTy, "S", getAlignOne(&context));
257 mlir::Value val = varPtrOp.get();
258
259 // Get a pointer to the second member.
260 OwningOpRef<cir::GetMemberOp> access = b.create<cir::GetMemberOp>(
261 loc, cir::PointerType::get(ptrTy), val, b.getStringAttr("f2"), 1);
262 mlir::Value accessVal = access.get();
263
264 // Load the value of the second member. This is the pointer we want to test.
265 OwningOpRef<cir::LoadOp> loadOp = b.create<cir::LoadOp>(loc, accessVal);
266 mlir::Value loadVal = loadOp.get();
267
268 // Verify that the type category is the expected type category.
269 auto pltTy = dyn_cast_if_present<mlir::acc::PointerLikeType>(ptrTy);
270 mlir::acc::VariableTypeCategory typeCategory = pltTy.getPointeeTypeCategory(
271 cast<TypedValue<mlir::acc::PointerLikeType>>(loadVal),
272 mlir::acc::getVarType(loadOp.get()));
273
274 EXPECT_EQ(typeCategory, expectedTypeCategory);
275 }
276};
277
278TEST_F(CIROpenACCPointerLikeTest, testPointerToInt) {
279 // Test various scalar types.
280 testScalarType(cir::IntType::get(&context, 8, true));
281 testScalarType(cir::IntType::get(&context, 8, false));
282 testScalarType(cir::IntType::get(&context, 16, true));
283 testScalarType(cir::IntType::get(&context, 16, false));
284 testScalarType(cir::IntType::get(&context, 32, true));
285 testScalarType(cir::IntType::get(&context, 32, false));
286 testScalarType(cir::IntType::get(&context, 64, true));
287 testScalarType(cir::IntType::get(&context, 64, false));
288 testScalarType(cir::IntType::get(&context, 128, true));
289 testScalarType(cir::IntType::get(&context, 128, false));
290}
291
292TEST_F(CIROpenACCPointerLikeTest, testPointerToBool) {
293 testScalarType(cir::BoolType::get(&context));
294}
295
296TEST_F(CIROpenACCPointerLikeTest, testPointerToFloat) {
297 testScalarType(cir::SingleType::get(&context));
298 testScalarType(cir::DoubleType::get(&context));
299}
300
301TEST_F(CIROpenACCPointerLikeTest, testPointerToPointer) {
302 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
303 mlir::Type ptrTy = cir::PointerType::get(i32Ty);
304 testScalarType(ptrTy);
305}
306
307TEST_F(CIROpenACCPointerLikeTest, testPointerToArray) {
308 // Test an array type.
309 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
310 testArrayType(i32Ty);
311}
312
313TEST_F(CIROpenACCPointerLikeTest, testPointerToStruct) {
314 // Test a struct type.
315 mlir::Type i16Ty = cir::IntType::get(&context, 16, true);
316 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
317 testStructType(i16Ty, i32Ty);
318}
319
320TEST_F(CIROpenACCPointerLikeTest, testPointerToUnion) {
321 // Test a union type.
322 mlir::Type i16Ty = cir::IntType::get(&context, 16, true);
323 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
324 testUnionType(i16Ty, i32Ty);
325}
326
327TEST_F(CIROpenACCPointerLikeTest, testPointerToFunction) {
328 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
329 mlir::Type funcTy =
330 cir::FuncType::get(SmallVector<mlir::Type, 2>{i32Ty, i32Ty}, i32Ty);
331 testNonScalarType(funcTy);
332}
333
334TEST_F(CIROpenACCPointerLikeTest, testPointerToVector) {
335 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
336 mlir::Type vecTy = cir::VectorType::get(i32Ty, 4);
337 testNonScalarType(vecTy);
338}
339
340TEST_F(CIROpenACCPointerLikeTest, testPointerToVoid) {
341 mlir::Type voidTy = cir::VoidType::get(&context);
342 testUncategorizedType(voidTy);
343}
344
345TEST_F(CIROpenACCPointerLikeTest, testPointerToIntMember) {
346 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
347 testPointerToMemberType(i32Ty, mlir::acc::VariableTypeCategory::scalar);
348}
349
350TEST_F(CIROpenACCPointerLikeTest, testPointerToArrayMember) {
351 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
352 mlir::Type arrTy = cir::ArrayType::get(i32Ty, 10);
353 testPointerToMemberType(arrTy, mlir::acc::VariableTypeCategory::array);
354}
355
356TEST_F(CIROpenACCPointerLikeTest, testPointerToStructMember) {
357 mlir::Type i32Ty = cir::IntType::get(&context, 32, true);
358 cir::RecordType structTy = cir::RecordType::get(
359 &context, getUniqueRecordName("S"), cir::RecordType::RecordKind::Struct);
360 structTy.complete({i32Ty, i32Ty}, false, false);
361 testPointerToMemberType(structTy, mlir::acc::VariableTypeCategory::composite);
362}
363

source code of clang/unittests/CIR/PointerLikeTest.cpp