@@ -65,7 +65,7 @@ static bool isValidBaseType(clang::QualType qty) {
65
65
return false ;
66
66
}
67
67
68
- cir::TBAAAttr CIRGenTBAA::getScalarTypeInfo (clang::QualType qty) {
68
+ cir::TBAAScalarAttr CIRGenTBAA::getScalarTypeInfo (clang::QualType qty) {
69
69
const clang::Type *ty = astContext.getCanonicalType (qty).getTypePtr ();
70
70
assert (mlir::isa<clang::BuiltinType>(ty));
71
71
const clang::BuiltinType *bty = mlir::dyn_cast<BuiltinType>(ty);
@@ -159,12 +159,78 @@ cir::TBAAAttr CIRGenTBAA::getTypeInfoHelper(clang::QualType qty) {
159
159
// they involve a significant representation difference. We don't
160
160
// currently do so, however.
161
161
if (ty->isPointerType () || ty->isReferenceType ()) {
162
- if (!codeGenOpts.PointerTBAA ) {
163
- return cir::TBAAScalarAttr::get (mlirContext, " any pointer" ,
164
- types.convertType (qty));
162
+ auto anyPtr = cir::TBAAScalarAttr::get (mlirContext, " any pointer" ,
163
+ types.convertType (qty));
164
+ if (!codeGenOpts.PointerTBAA )
165
+ return anyPtr;
166
+ // C++ [basic.lval]p11 permits objects to accessed through an l-value of
167
+ // similar type. Two types are similar under C++ [conv.qual]p2 if the
168
+ // decomposition of the types into pointers, member pointers, and arrays has
169
+ // the same structure when ignoring cv-qualifiers at each level of the
170
+ // decomposition. Meanwhile, C makes T(*)[] and T(*)[N] compatible, which
171
+ // would really complicate any attempt to distinguish pointers to arrays by
172
+ // their bounds. It's simpler, and much easier to explain to users, to
173
+ // simply treat all pointers to arrays as pointers to their element type for
174
+ // aliasing purposes. So when creating a TBAA tag for a pointer type, we
175
+ // recursively ignore both qualifiers and array types when decomposing the
176
+ // pointee type. The only meaningful remaining structure is the number of
177
+ // pointer types we encountered along the way, so we just produce the tag
178
+ // "p<depth> <base type tag>". If we do find a member pointer type, for now
179
+ // we just conservatively bail out with AnyPtr (below) rather than trying to
180
+ // create a tag that honors the similar-type rules while still
181
+ // distinguishing different kinds of member pointer.
182
+ unsigned ptrDepth = 0 ;
183
+ do {
184
+ ptrDepth++;
185
+ ty = ty->getPointeeType ()->getBaseElementTypeUnsafe ();
186
+ } while (ty->isPointerType ());
187
+ assert (!isa<VariableArrayType>(ty));
188
+ // When the underlying type is a builtin type, we compute the pointee type
189
+ // string recursively, which is implicitly more forgiving than the standards
190
+ // require. Effectively, we are turning the question "are these types
191
+ // compatible/similar" into "are accesses to these types allowed to alias".
192
+ // In both C and C++, the latter question has special carve-outs for
193
+ // signedness mismatches that only apply at the top level. As a result, we
194
+ // are allowing e.g. `int *` l-values to access `unsigned *` objects.
195
+ SmallString<256 > tyName;
196
+
197
+ if (isa<BuiltinType>(ty)) {
198
+ auto scalarAttr = getScalarTypeInfo (ty->getCanonicalTypeInternal ());
199
+ tyName = scalarAttr.getId ();
200
+ } else {
201
+ // Be conservative if the type isn't a RecordType. We are specifically
202
+ // required to do this for member pointers until we implement the
203
+ // similar-types rule.
204
+ const auto *rt = ty->getAs <RecordType>();
205
+ if (!rt)
206
+ return anyPtr;
207
+
208
+ // For unnamed structs or unions C's compatible types rule applies. Two
209
+ // compatible types in different compilation units can have different
210
+ // mangled names, meaning the metadata emitted below would incorrectly
211
+ // mark them as no-alias. Use AnyPtr for such types in both C and C++, as
212
+ // C and C++ types may be visible when doing LTO.
213
+ //
214
+ // Note that using AnyPtr is overly conservative. We could summarize the
215
+ // members of the type, as per the C compatibility rule in the future.
216
+ // This also covers anonymous structs and unions, which have a different
217
+ // compatibility rule, but it doesn't matter because you can never have a
218
+ // pointer to an anonymous struct or union.
219
+ if (!rt->getDecl ()->getDeclName ())
220
+ return anyPtr;
221
+
222
+ // For non-builtin types use the mangled name of the canonical type.
223
+ llvm::raw_svector_ostream tyOut (tyName);
224
+ types.getCXXABI ().getMangleContext ().mangleCanonicalTypeName (
225
+ QualType (ty, 0 ), tyOut);
165
226
}
166
- assert (!cir::MissingFeatures::tbaaPointer ());
167
- return tbaa_NYI (mlirContext);
227
+
228
+ SmallString<256 > outName (" p" );
229
+ outName += std::to_string (ptrDepth);
230
+ outName += " " ;
231
+ outName += tyName;
232
+ return cir::TBAAScalarAttr::get (mlirContext, outName,
233
+ types.convertType (qty), anyPtr);
168
234
}
169
235
// Accesses to arrays are accesses to objects of their element types.
170
236
if (codeGenOpts.NewStructPathTBAA && ty->isArrayType ()) {
0 commit comments