diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTBAAAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRTBAAAttrs.td index 40e2d2d7f1d9..83d3d24c05c8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTBAAAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTBAAAttrs.td @@ -50,9 +50,82 @@ def CIR_TBAATagAttr : CIR_Attr<"TBAATag", "tbaa_tag", [], "TBAAAttr"> { let assemblyFormat = "`<` struct(params) `>`"; } +def CIR_TBAAMemberAttr : CIR_Attr<"TBAAMember", "tbaa_member", []> { + let summary = "Attribute representing a member of a TBAA structured type."; + let parameters = (ins "TBAAAttr":$type_desc, + "int64_t":$offset); + let description = [{ + Define a TBAA struct attribute. + + Example: + ```mlir + !ty_StructS = !cir.struct + #tbaa_scalar = #cir.tbaa_scalar + #tbaa_scalar1 = #cir.tbaa_scalar + #tbaa_struct = #cir.tbaa_struct, <#tbaa_scalar, 4>}> + ``` + + See the following link for more details: + https://llvm.org/docs/LangRef.html#tbaa-metadata + }]; + + let assemblyFormat = "`<` params `>`"; +} + +def CIR_TBAAMemberAttrArray : ArrayRefParameter<"TBAAMemberAttr"> { + let summary = "Array of TBAAMemberAttr attributes."; + let printer = [{ + $_printer << '{'; + llvm::interleaveComma($_self, $_printer, [&](TBAAMemberAttr attr) { + $_printer.printStrippedAttrOrType(attr); + }); + $_printer << '}'; + }]; + let parser = [{ + [&]() -> llvm::FailureOr> { + using Result = llvm::SmallVector; + if ($_parser.parseLBrace()) + return mlir::failure(); + llvm::FailureOr result = mlir::FieldParser::parse($_parser); + if (failed(result)) + return mlir::failure(); + if ($_parser.parseRBrace()) + return mlir::failure(); + return result; + }() + }]; +} + +def CIR_TBAAStructAttr : CIR_Attr<"TBAAStruct", + "tbaa_struct", [], "TBAAAttr"> { + let summary = "Describes a struct type in TBAA"; + + let parameters = (ins StringRefParameter<> : $id, + CIR_TBAAMemberAttrArray:$members); + + let description = [{ + Define a TBAA struct attribute. + + Example: + ```mlir + !ty_StructS = !cir.struct + #tbaa_scalar = #cir.tbaa_scalar + #tbaa_scalar1 = #cir.tbaa_scalar + // CIR_TBAAStructAttr + #tbaa_struct = #cir.tbaa_struct, <#tbaa_scalar, 4>}> + ``` + + See the following link for more details: + https://llvm.org/docs/LangRef.html#tbaa-metadata + }]; + + let assemblyFormat = "`<` struct(params) `>`"; +} + def CIR_AnyTBAAAttr : AnyAttrOf<[ CIR_TBAAAttr, CIR_TBAAOmnipotentChar, - CIR_TBAAScalarAttr, + CIR_TBAAScalarAttr, + CIR_TBAAStructAttr, CIR_TBAATagAttr ]>; diff --git a/clang/lib/CIR/CodeGen/CIRGenTBAA.cpp b/clang/lib/CIR/CodeGen/CIRGenTBAA.cpp index fa8325c654cb..f2d46a30cc53 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTBAA.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTBAA.cpp @@ -1,4 +1,5 @@ #include "CIRGenTBAA.h" +#include "CIRGenCXXABI.h" #include "CIRGenTypes.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/MLIRContext.h" @@ -204,7 +205,7 @@ cir::TBAAAttr CIRGenTBAA::getTypeInfo(clang::QualType qty) { // function. if (isValidBaseType(qty)) { assert(!cir::MissingFeatures::tbaaTagForStruct()); - return tbaa_NYI(mlirContext); + return getValidBaseTypeInfo(qty); } const clang::Type *ty = astContext.getCanonicalType(qty).getTypePtr(); @@ -248,9 +249,101 @@ mlir::ArrayAttr CIRGenTBAA::getTBAAStructInfo(clang::QualType qty) { } cir::TBAAAttr CIRGenTBAA::getBaseTypeInfo(clang::QualType qty) { - return tbaa_NYI(mlirContext); + return isValidBaseType(qty) ? getValidBaseTypeInfo(qty) : nullptr; +} + +cir::TBAAAttr CIRGenTBAA::getValidBaseTypeInfo(clang::QualType qty) { + assert(isValidBaseType(qty) && "Must be a valid base type"); + + const clang::Type *ty = astContext.getCanonicalType(qty).getTypePtr(); + + // nullptr is a valid value in the cache, so use find rather than [] + auto iter = baseTypeMetadataCache.find(ty); + if (iter != baseTypeMetadataCache.end()) + return iter->second; + + // First calculate the metadata, before recomputinyg the insertion point, as + // the helper can recursively call us. + auto typeNode = getBaseTypeInfoHelper(ty); + LLVM_ATTRIBUTE_UNUSED auto inserted = + baseTypeMetadataCache.insert({ty, typeNode}); + assert(inserted.second && "BaseType metadata was already inserted"); + + return typeNode; } +cir::TBAAAttr CIRGenTBAA::getBaseTypeInfoHelper(const clang::Type *ty) { + using namespace clang; + if (auto *tty = mlir::dyn_cast(ty)) { + const clang::RecordDecl *rd = tty->getDecl()->getDefinition(); + const ASTRecordLayout &layout = astContext.getASTRecordLayout(rd); + SmallVector fields; + if (const CXXRecordDecl *cxxrd = dyn_cast(rd)) { + // Handle C++ base classes. Non-virtual bases can treated a kind of + // field. Virtual bases are more complex and omitted, but avoid an + // incomplete view for NewStructPathTBAA. + if (codeGenOpts.NewStructPathTBAA && cxxrd->getNumVBases() != 0) + return nullptr; + for (const CXXBaseSpecifier &cxxBaseSpecifier : cxxrd->bases()) { + if (cxxBaseSpecifier.isVirtual()) + continue; + QualType baseQTy = cxxBaseSpecifier.getType(); + const CXXRecordDecl *baseRD = baseQTy->getAsCXXRecordDecl(); + if (baseRD->isEmpty()) + continue; + auto typeNode = isValidBaseType(baseQTy) ? getValidBaseTypeInfo(baseQTy) + : getTypeInfo(baseQTy); + if (!typeNode) + return nullptr; + uint64_t offset = layout.getBaseClassOffset(baseRD).getQuantity(); + [[maybe_unused]] uint64_t size = + astContext.getASTRecordLayout(baseRD).getDataSize().getQuantity(); + fields.push_back( + cir::TBAAMemberAttr::get(mlirContext, typeNode, offset)); + } + // The order in which base class subobjects are allocated is + // unspecified, so may differ from declaration order. In particular, + // Itanium ABI will allocate a primary base first. Since we exclude + // empty subobjects, the objects are not overlapping and their offsets + // are unique. + llvm::sort(fields, [](const cir::TBAAMemberAttr &lhs, + const cir::TBAAMemberAttr &rhs) { + return lhs.getOffset() < rhs.getOffset(); + }); + } + for (FieldDecl *field : rd->fields()) { + if (field->isZeroSize(astContext) || field->isUnnamedBitField()) + continue; + QualType fieldQTy = field->getType(); + auto typeNode = isValidBaseType(fieldQTy) ? getValidBaseTypeInfo(fieldQTy) + : getTypeInfo(fieldQTy); + if (!typeNode) + return nullptr; + + uint64_t bitOffset = layout.getFieldOffset(field->getFieldIndex()); + uint64_t offset = astContext.toCharUnitsFromBits(bitOffset).getQuantity(); + [[maybe_unused]] uint64_t size = + astContext.getTypeSizeInChars(fieldQTy).getQuantity(); + fields.push_back(cir::TBAAMemberAttr::get(mlirContext, typeNode, offset)); + } + + SmallString<256> outName; + if (features.CPlusPlus) { + // Don't use the mangler for C code. + llvm::raw_svector_ostream out(outName); + types.getCXXABI().getMangleContext().mangleCanonicalTypeName( + QualType(ty, 0), out); + } else { + outName = rd->getName(); + } + if (codeGenOpts.NewStructPathTBAA) { + assert(!cir::MissingFeatures::tbaaNewStructPath()); + return nullptr; + } + return cir::TBAAStructAttr::get(mlirContext, outName, fields); + } + return nullptr; +} cir::TBAAAttr CIRGenTBAA::getAccessTagInfo(TBAAAccessInfo tbaaInfo) { assert(!tbaaInfo.isIncomplete() && "Access to an object of an incomplete type!"); diff --git a/clang/lib/CIR/CodeGen/CIRGenTBAA.h b/clang/lib/CIR/CodeGen/CIRGenTBAA.h index 57a2c0fae226..301adf414139 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTBAA.h +++ b/clang/lib/CIR/CodeGen/CIRGenTBAA.h @@ -105,6 +105,7 @@ class CIRGenTBAA { [[maybe_unused]] const clang::LangOptions &features; llvm::DenseMap metadataCache; + llvm::DenseMap baseTypeMetadataCache; cir::TBAAAttr getChar(); @@ -113,6 +114,9 @@ class CIRGenTBAA { cir::TBAAAttr getTypeInfoHelper(clang::QualType qty); cir::TBAAAttr getScalarTypeInfo(clang::QualType qty); + cir::TBAAAttr getValidBaseTypeInfo(clang::QualType qty); + cir::TBAAAttr getBaseTypeInfoHelper(const clang::Type *ty); + public: CIRGenTBAA(mlir::MLIRContext *mlirContext, clang::ASTContext &astContext, CIRGenTypes &types, mlir::ModuleOp moduleOp, diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 07ec1721c124..a0a7f88a0f90 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -109,7 +109,7 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface { } return TypeSwitch(attr) .Case([&](auto attr) { + cir::TBAAStructAttr, cir::TBAATagAttr>([&](auto attr) { os << decltype(attr)::getMnemonic(); return AliasResult::OverridableAlias; }) diff --git a/clang/test/CIR/CodeGen/tbaa-struct.cpp b/clang/test/CIR/CodeGen/tbaa-struct.cpp new file mode 100644 index 000000000000..4b5916a9d3f8 --- /dev/null +++ b/clang/test/CIR/CodeGen/tbaa-struct.cpp @@ -0,0 +1,262 @@ +// This is inspired from clang/test/CodeGen/tbaa.cpp, with both CIR and LLVM checks. +// g13 is not supported due to DiscreteBitFieldABI is NYI. +// see clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp CIRRecordLowering::accumulateBitFields + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -O1 +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s + +// CIR: #tbaa[[NYI:.*]] = #cir.tbaa +// CIR: #tbaa[[CHAR:.*]] = #cir.tbaa_omnipotent_char +// CIR: #tbaa[[INT:.*]] = #cir.tbaa_scalar +// CIR: #tbaa[[SHORT:.*]] = #cir.tbaa_scalar +// CIR: #tbaa[[STRUCT_six:.*]] = #cir.tbaa_struct, <#tbaa[[CHAR]], 4>, <#tbaa[[CHAR]], 5>}> +// CIR: #tbaa[[STRUCT_StructA:.*]] = #cir.tbaa_struct, <#tbaa[[INT]], 4>, <#tbaa[[SHORT]], 8>, <#tbaa[[INT]], 12>}> +// CIR: #tbaa[[STRUCT_StructS:.*]] = #cir.tbaa_struct, <#tbaa[[INT]], 4>}> +// CIR: #tbaa[[STRUCT_StructS2:.*]] = #cir.tbaa_struct, <#tbaa[[INT]], 4>}> +// CIR: #tbaa[[TAG_six_b:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructA_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructA_f16:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructS_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructS_f16:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructS2_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructS2_f16:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[STRUCT_StructB:.*]] = #cir.tbaa_struct, <#tbaa[[STRUCT_StructA]], 4>, <#tbaa[[INT]], 20>}> +// CIR: #tbaa[[TAG_StructB_a_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructB_a_f16:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructB_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructB_a_f32_2:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[STRUCT_StructC:.*]] = #cir.tbaa_struct, <#tbaa[[STRUCT_StructB]], 4>, <#tbaa[[INT]], 28>}> +// CIR: #tbaa[[STRUCT_StructD:.*]] = #cir.tbaa_struct, <#tbaa[[STRUCT_StructB]], 4>, <#tbaa[[INT]], 28>, <#tbaa[[CHAR]], 32>}> +// CIR: #tbaa[[TAG_StructC_b_a_f32:.*]] = #cir.tbaa_tag +// CIR: #tbaa[[TAG_StructD_b_a_f32:.*]] = #cir.tbaa_tag + + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef struct +{ + uint16_t f16; + uint32_t f32; + uint16_t f16_2; + uint32_t f32_2; +} StructA; +typedef struct +{ + uint16_t f16; + StructA a; + uint32_t f32; +} StructB; +typedef struct +{ + uint16_t f16; + StructB b; + uint32_t f32; +} StructC; +typedef struct +{ + uint16_t f16; + StructB b; + uint32_t f32; + uint8_t f8; +} StructD; + +typedef struct +{ + uint16_t f16; + uint32_t f32; +} StructS; +typedef struct +{ + uint16_t f16; + uint32_t f32; +} StructS2; + +uint32_t g(uint32_t *s, StructA *A, uint64_t count) { + // CIR-LABEL: cir.func @_Z1g + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[INT]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + *s = 1; + A->f32 = 4; + return *s; +} + +uint32_t g2(uint32_t *s, StructA *A, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g2 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[INT]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u16i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u16i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f16]]) + *s = 1; + A->f16 = 4; + return *s; +} + +uint32_t g3(StructA *A, StructB *B, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g3 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructB_a_f32]]) + A->f32 = 1; + B->a.f32 = 4; + return A->f32; +} + +uint32_t g4(StructA *A, StructB *B, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g4 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u16i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u16i, !cir.ptr tbaa(#tbaa[[TAG_StructB_a_f16]]) + A->f32 = 1; + B->a.f16 = 4; + return A->f32; +} + +uint32_t g5(StructA *A, StructB *B, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g5 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructB_f32]]) + A->f32 = 1; + B->f32 = 4; + return A->f32; +} + +uint32_t g6(StructA *A, StructB *B, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g6 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructB_a_f32_2]]) + A->f32 = 1; + B->a.f32_2 = 4; + return A->f32; +} + +uint32_t g7(StructA *A, StructS *S, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g7 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f32]]) + A->f32 = 1; + S->f32 = 4; + return A->f32; +} + +uint32_t g8(StructA *A, StructS *S, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g8 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructA_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u16i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u16i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f16]]) + A->f32 = 1; + S->f16 = 4; + return A->f32; +} + +uint32_t g9(StructS *S, StructS2 *S2, uint64_t count) { + // CIR-LABEL: cir.func @_Z2g9 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS2_f32]]) + S->f32 = 1; + S2->f32 = 4; + return S->f32; +} + +uint32_t g10(StructS *S, StructS2 *S2, uint64_t count) { + // CIR-LABEL: cir.func @_Z3g10 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u16i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u16i, !cir.ptr tbaa(#tbaa[[TAG_StructS2_f16]]) + S->f32 = 1; + S2->f16 = 4; + return S->f32; +} + +uint32_t g11(StructC *C, StructD *D, uint64_t count) { + // CIR-LABEL: cir.func @_Z3g11 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructC_b_a_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructD_b_a_f32]]) + C->b.a.f32 = 1; + D->b.a.f32 = 4; + return C->b.a.f32; +} + +uint32_t g12(StructC *C, StructD *D, uint64_t count) { + // CIR-LABEL: cir.func @_Z3g12 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructB_a_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructB_a_f32]]) + StructB *b1 = &(C->b); + StructB *b2 = &(D->b); + // b1, b2 have different context. + b1->a.f32 = 1; + b2->a.f32 = 4; + return b1->a.f32; +} + +struct six { + char a; + int :0; + char b; + char c; +}; +char g14(struct six *a, struct six *b) { + // CIR-LABEL: cir.func @_Z3g14 + // CIR: %[[TMP1:.*]] = cir.load %{{.*}} : !cir.ptr>, !cir.ptr + // CIR: %[[TMP2:.*]] = cir.get_member %[[TMP1]][2] {name = "b"} : !cir.ptr -> !cir.ptr + // CIR: %[[TMP3:.*]] = cir.load %[[TMP2]] : !cir.ptr, !s8i tbaa(#tbaa[[TAG_six_b]]) + return a->b; +} + +// Types that differ only by name may alias. +typedef StructS StructS3; +uint32_t g15(StructS *S, StructS3 *S3, uint64_t count) { + // CIR-LABEL: cir.func @_Z3g15 + // CIR: %[[INT_1:.*]] = cir.const #cir.int<1> : !s32i + // CIR: %[[UINT_1:.*]] = cir.cast(integral, %[[INT_1]] : !s32i), !u32i + // CIR: cir.store %[[UINT_1]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f32]]) + // CIR: %[[INT_4:.*]] = cir.const #cir.int<4> : !s32i + // CIR: %[[UINT_4:.*]] = cir.cast(integral, %[[INT_4]] : !s32i), !u32i + // CIR: cir.store %[[UINT_4]], %{{.*}} : !u32i, !cir.ptr tbaa(#tbaa[[TAG_StructS_f32]]) + S->f32 = 1; + S3->f32 = 4; + return S->f32; +}