https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/137501
>From b8011997fdc2548be80dd9305d758466a66cee4c Mon Sep 17 00:00:00 2001 From: Iris Shi <0...@owo.li> Date: Sun, 27 Apr 2025 15:16:19 +0800 Subject: [PATCH 1/2] [CIR] Upstream initial support for union type --- .../include/clang/CIR/Dialect/IR/CIRTypes.td | 1 + clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 25 ++-- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 2 +- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 94 +++++++++++- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 40 ++++-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 15 +- clang/test/CIR/CodeGen/union.c | 135 +++++++++++++++++- 7 files changed, 276 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index 0a821e152d353..afb7da1fa011d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -494,6 +494,7 @@ def CIR_RecordType : CIR_Type<"Record", "record", bool isComplete() const { return !isIncomplete(); }; bool isIncomplete() const; + mlir::Type getLargestMember(const mlir::DataLayout &dataLayout) const; size_t getNumElements() const { return getMembers().size(); }; std::string getKindAsStr() { switch (getKind()) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 0a518c0fd935d..9069d1a2cf6ac 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -317,20 +317,25 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { } unsigned recordCVR = base.getVRQualifiers(); - if (rec->isUnion()) { - cgm.errorNYI(field->getSourceRange(), "emitLValueForField: union"); - return LValue(); - } - assert(!cir::MissingFeatures::preservedAccessIndexRegion()); llvm::StringRef fieldName = field->getName(); - const CIRGenRecordLayout &layout = - cgm.getTypes().getCIRGenRecordLayout(field->getParent()); - unsigned fieldIndex = layout.getCIRFieldNo(field); - assert(!cir::MissingFeatures::lambdaFieldToName()); + if (rec->isUnion()) { + unsigned fieldIndex = field->getFieldIndex(); + assert(!cir::MissingFeatures::lambdaFieldToName()); + addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); + + } else { + assert(!cir::MissingFeatures::preservedAccessIndexRegion()); + + const CIRGenRecordLayout &layout = + cgm.getTypes().getCIRGenRecordLayout(field->getParent()); + unsigned fieldIndex = layout.getCIRFieldNo(field); - addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); + assert(!cir::MissingFeatures::lambdaFieldToName()); + + addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); + } // If this is a reference field, load the reference right now. if (fieldType->isReferenceType()) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 368a6cb27c0fd..e006a77c6e7d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -1,4 +1,4 @@ -//===--- CIRGenExprAgg.cpp - Emit CIR Code from Aggregate Expressions -----===// +//===- CIRGenExprAggregrate.cpp - Emit CIR Code from Aggregate Expressions ===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 83aba256cd48e..7b9631daacfdd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -56,7 +56,7 @@ struct CIRRecordLowering final { }; // The constructor. CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl, - bool isPacked); + bool packed); /// Constructs a MemberInfo instance from an offset and mlir::Type. MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) { @@ -64,6 +64,7 @@ struct CIRRecordLowering final { } void lower(); + void lowerUnion(); /// Determines if we need a packed llvm struct. void determinePacked(); @@ -83,6 +84,10 @@ struct CIRRecordLowering final { return CharUnits::fromQuantity(dataLayout.layout.getTypeABIAlignment(Ty)); } + bool isZeroInitializable(const FieldDecl *fd) { + return cirGenTypes.isZeroInitializable(fd->getType()); + } + /// Wraps cir::IntType with some implicit arguments. mlir::Type getUIntNType(uint64_t numBits) { unsigned alignedBits = llvm::PowerOf2Ceil(numBits); @@ -121,6 +126,13 @@ struct CIRRecordLowering final { /// Fills out the structures that are ultimately consumed. void fillOutputFields(); + void appendPaddingBytes(CharUnits size) { + if (!size.isZero()) { + fieldTypes.push_back(getByteArrayType(size)); + padded = true; + } + } + CIRGenTypes &cirGenTypes; CIRGenBuilderTy &builder; const ASTContext &astContext; @@ -136,6 +148,8 @@ struct CIRRecordLowering final { LLVM_PREFERRED_TYPE(bool) unsigned zeroInitializable : 1; LLVM_PREFERRED_TYPE(bool) + unsigned zeroInitializableAsBase : 1; + LLVM_PREFERRED_TYPE(bool) unsigned packed : 1; LLVM_PREFERRED_TYPE(bool) unsigned padded : 1; @@ -147,19 +161,19 @@ struct CIRRecordLowering final { } // namespace CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes, - const RecordDecl *recordDecl, - bool isPacked) + const RecordDecl *recordDecl, bool packed) : cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()), astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl), astRecordLayout( cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)), dataLayout(cirGenTypes.getCGModule().getModule()), - zeroInitializable(true), packed(isPacked), padded(false) {} + zeroInitializable(true), zeroInitializableAsBase(true), packed(packed), + padded(false) {} void CIRRecordLowering::lower() { if (recordDecl->isUnion()) { - cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), - "lower: union"); + lowerUnion(); + assert(!cir::MissingFeatures::bitfields()); return; } @@ -306,3 +320,71 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { // TODO: implement verification return rl; } + +void CIRRecordLowering::lowerUnion() { + CharUnits layoutSize = astRecordLayout.getSize(); + mlir::Type storageType = nullptr; + bool seenNamedMember = false; + + // Iterate through the fields setting bitFieldInfo and the Fields array. Also + // locate the "most appropriate" storage type. The heuristic for finding the + // storage type isn't necessary, the first (non-0-length-bitfield) field's + // type would work fine and be simpler but would be different than what we've + // been doing and cause lit tests to change. + for (const FieldDecl *field : recordDecl->fields()) { + mlir::Type fieldType; + if (field->isBitField()) + cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), + "bitfields in lowerUnion"); + else + fieldType = getStorageType(field); + + fields[field->getCanonicalDecl()] = 0; + + // Compute zero-initializable status. + // This union might not be zero initialized: it may contain a pointer to + // data member which might have some exotic initialization sequence. + // If this is the case, then we aught not to try and come up with a "better" + // type, it might not be very easy to come up with a Constant which + // correctly initializes it. + if (!seenNamedMember) { + seenNamedMember = field->getIdentifier(); + if (!seenNamedMember) + if (const RecordDecl *fieldRD = field->getType()->getAsRecordDecl()) + seenNamedMember = fieldRD->findFirstNamedDataMember(); + if (seenNamedMember && !isZeroInitializable(field)) { + zeroInitializable = zeroInitializableAsBase = false; + storageType = fieldType; + } + } + + // Because our union isn't zero initializable, we won't be getting a better + // storage type. + if (!zeroInitializable) + continue; + + // Conditionally update our storage type if we've got a new "better" one. + if (!storageType || getAlignment(fieldType) > getAlignment(storageType) || + (getAlignment(fieldType) == getAlignment(storageType) && + getSize(fieldType) > getSize(storageType))) + storageType = fieldType; + + // NOTE(cir): Track all union member's types, not just the largest one. It + // allows for proper type-checking and retain more info for analisys. + fieldTypes.push_back(fieldType); + } + + if (!storageType) + cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), + "No-storage Union NYI"); + + if (layoutSize < getSize(storageType)) + storageType = getByteArrayType(layoutSize); + + // NOTE(cir): Defer padding calculations to the lowering process. + appendPaddingBytes(layoutSize - getSize(storageType)); + + // Set packed if we need it. + if (layoutSize % getAlignment(storageType)) + packed = true; +} diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index c6133b9a20e4f..f61fc750d7c0e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -230,6 +230,32 @@ void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) { llvm_unreachable("failed to complete record"); } +/// Return the largest member of in the type. +/// +/// Recurses into union members never returning a union as the largest member. +Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { + assert(isUnion() && "Only call getLargestMember on unions"); + Type largestMember; + unsigned largestMemberSize = 0; + unsigned numElements = getNumElements(); + auto members = getMembers(); + if (getPadded()) + numElements -= 1; // The last element is padding. + for (unsigned i = 0; i < numElements; ++i) { + Type ty = members[i]; + if (!largestMember || + dataLayout.getTypeABIAlignment(ty) > + dataLayout.getTypeABIAlignment(largestMember) || + (dataLayout.getTypeABIAlignment(ty) == + dataLayout.getTypeABIAlignment(largestMember) && + dataLayout.getTypeSize(ty) > largestMemberSize)) { + largestMember = ty; + largestMemberSize = dataLayout.getTypeSize(largestMember); + } + } + return largestMember; +} + //===----------------------------------------------------------------------===// // Data Layout information for types //===----------------------------------------------------------------------===// @@ -237,10 +263,8 @@ void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) { llvm::TypeSize RecordType::getTypeSizeInBits(const mlir::DataLayout &dataLayout, mlir::DataLayoutEntryListRef params) const { - if (isUnion()) { - // TODO(CIR): Implement union layout. - return llvm::TypeSize::getFixed(8); - } + if (isUnion()) + return dataLayout.getTypeSize(getLargestMember(dataLayout)); unsigned recordSize = computeStructSize(dataLayout); return llvm::TypeSize::getFixed(recordSize * 8); @@ -249,10 +273,8 @@ RecordType::getTypeSizeInBits(const mlir::DataLayout &dataLayout, uint64_t RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { - if (isUnion()) { - // TODO(CIR): Implement union layout. - return 8; - } + if (isUnion()) + return dataLayout.getTypeABIAlignment(getLargestMember(dataLayout)); // Packed structures always have an ABI alignment of 1. if (getPacked()) @@ -268,8 +290,6 @@ RecordType::computeStructSize(const mlir::DataLayout &dataLayout) const { unsigned recordSize = 0; uint64_t recordAlignment = 1; - // We can't use a range-based for loop here because we might be ignoring the - // last element. for (mlir::Type ty : getMembers()) { // This assumes that we're calculating size based on the ABI alignment, not // the preferred alignment for each type. diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 8bb27942d9646..0dba45a5aba35 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1431,7 +1431,14 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, break; // Unions are lowered as only the largest member. case cir::RecordType::Union: - llvm_unreachable("Lowering of unions is NYI"); + if (auto largestMember = type.getLargestMember(dataLayout)) + llvmMembers.push_back( + convertTypeForMemory(converter, dataLayout, largestMember)); + if (type.getPadded()) { + auto last = *type.getMembers().rbegin(); + llvmMembers.push_back( + convertTypeForMemory(converter, dataLayout, last)); + } break; } @@ -1604,7 +1611,11 @@ mlir::LogicalResult CIRToLLVMGetMemberOpLowering::matchAndRewrite( return mlir::success(); } case cir::RecordType::Union: - return op.emitError() << "NYI: union get_member lowering"; + // Union members share the address space, so we just need a bitcast to + // conform to type-checking. + rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(op, llResTy, + adaptor.getAddr()); + return mlir::success(); } } diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c index c4db37f835add..71cb9c2b20ca1 100644 --- a/clang/test/CIR/CodeGen/union.c +++ b/clang/test/CIR/CodeGen/union.c @@ -5,25 +5,146 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll // RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s +union U1 { + int n; + char c; +}; + +// CIR: !rec_U1 = !cir.record<union "U1" {!s32i, !s8i}> +// LLVM: %union.U1 = type { i32 } +// OGCG: %union.U1 = type { i32 } + +union U2 { + char b; + short s; + int i; + float f; + double d; +}; + +// CIR: !rec_U2 = !cir.record<union "U2" {!s8i, !s16i, !s32i, !cir.float, !cir.double}> +// LLVM: %union.U2 = type { double } +// OGCG: %union.U2 = type { double } + union IncompleteU *p; -// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteU> +// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteU> // LLVM: @p = dso_local global ptr null // OGCG: @p = global ptr null, align 8 -void f(void) { +void f1(void) { union IncompleteU *p; } -// CIR: cir.func @f() -// CIR-NEXT: cir.alloca !cir.ptr<!rec_IncompleteU>, !cir.ptr<!cir.ptr<!rec_IncompleteU>>, ["p"] -// CIR-NEXT: cir.return +// CIR: cir.func @f1() +// CIR-NEXT: cir.alloca !cir.ptr<!rec_IncompleteU>, !cir.ptr<!cir.ptr<!rec_IncompleteU>>, ["p"] +// CIR-NEXT: cir.return -// LLVM: define void @f() +// LLVM: define void @f1() // LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 // LLVM-NEXT: ret void -// OGCG: define{{.*}} void @f() +// OGCG: define{{.*}} void @f1() // OGCG-NEXT: entry: // OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 // OGCG-NEXT: ret void + +int f2(void) { + union U1 u; + u.n = 42; + return u.n; +} + +// CIR: cir.func @f2() -> !s32i +// CIR-NEXT: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} +// CIR-NEXT: %1 = cir.alloca !rec_U1, !cir.ptr<!rec_U1>, ["u"] {alignment = 4 : i64} +// CIR-NEXT: %2 = cir.const #cir.int<42> : !s32i +// CIR-NEXT: %3 = cir.get_member %1[0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> +// CIR-NEXT: cir.store %2, %3 : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %4 = cir.get_member %1[0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> +// CIR-NEXT: %5 = cir.load %4 : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: cir.store %5, %0 : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %6 = cir.load %0 : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: cir.return %6 : !s32i + +// LLVM: define i32 @f2() +// LLVM-NEXT: %1 = alloca i32, i64 1, align 4 +// LLVM-NEXT: %2 = alloca %union.U1, i64 1, align 4 +// LLVM-NEXT: store i32 42, ptr %2, align 4 +// LLVM-NEXT: %3 = load i32, ptr %2, align 4 +// LLVM-NEXT: store i32 %3, ptr %1, align 4 +// LLVM-NEXT: %4 = load i32, ptr %1, align 4 +// LLVM-NEXT: ret i32 %4 + +// OGCG: define dso_local i32 @f2() +// OGCG-NEXT: entry: +// OGCG-NEXT: %u = alloca %union.U1, align 4 +// OGCG-NEXT: store i32 42, ptr %u, align 4 +// OGCG-NEXT: %0 = load i32, ptr %u, align 4 +// OGCG-NEXT: ret i32 %0 + + +void shouldGenerateUnionAccess(union U2 u) { + u.b = 0; + u.b; + u.i = 1; + u.i; + u.f = 0.1F; + u.f; + u.d = 0.1; + u.d; +} + +// CIR: cir.func @shouldGenerateUnionAccess(%arg0: !rec_U2 +// CIR-NEXT: %0 = cir.alloca !rec_U2, !cir.ptr<!rec_U2>, ["u", init] {alignment = 8 : i64} +// CIR-NEXT: cir.store %arg0, %0 : !rec_U2, !cir.ptr<!rec_U2> +// CIR-NEXT: %1 = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %2 = cir.cast(integral, %1 : !s32i), !s8i +// CIR-NEXT: %3 = cir.get_member %0[0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> +// CIR-NEXT: cir.store %2, %3 : !s8i, !cir.ptr<!s8i> +// CIR-NEXT: %4 = cir.get_member %0[0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> +// CIR-NEXT: %5 = cir.load %4 : !cir.ptr<!s8i>, !s8i +// CIR-NEXT: %6 = cir.const #cir.int<1> : !s32i +// CIR-NEXT: %7 = cir.get_member %0[2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> +// CIR-NEXT: cir.store %6, %7 : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %8 = cir.get_member %0[2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> +// CIR-NEXT: %9 = cir.load %8 : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %10 = cir.const #cir.fp<1.000000e-01> : !cir.float +// CIR-NEXT: %11 = cir.get_member %0[3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> +// CIR-NEXT: cir.store %10, %11 : !cir.float, !cir.ptr<!cir.float> +// CIR-NEXT: %12 = cir.get_member %0[3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> +// CIR-NEXT: %13 = cir.load %12 : !cir.ptr<!cir.float>, !cir.float +// CIR-NEXT: %14 = cir.const #cir.fp<1.000000e-01> : !cir.double +// CIR-NEXT: %15 = cir.get_member %0[4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> +// CIR-NEXT: cir.store %14, %15 : !cir.double, !cir.ptr<!cir.double> +// CIR-NEXT: %16 = cir.get_member %0[4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> +// CIR-NEXT: %17 = cir.load %16 : !cir.ptr<!cir.double>, !cir.double +// CIR-NEXT: cir.return + +// LLVM: define void @shouldGenerateUnionAccess(%union.U2 %0) { +// LLVM-NEXT: %2 = alloca %union.U2, i64 1, align 8 +// LLVM-NEXT: store %union.U2 %0, ptr %2, align 8 +// LLVM-NEXT: store i8 0, ptr %2, align 1 +// LLVM-NEXT: %3 = load i8, ptr %2, align 1 +// LLVM-NEXT: store i32 1, ptr %2, align 4 +// LLVM-NEXT: %4 = load i32, ptr %2, align 4 +// LLVM-NEXT: store float 0x3FB99999A0000000, ptr %2, align 4 +// LLVM-NEXT: %5 = load float, ptr %2, align 4 +// LLVM-NEXT: store double 1.000000e-01, ptr %2, align 8 +// LLVM-NEXT: %6 = load double, ptr %2, align 8 +// LLVM-NEXT: ret void + +// OGCG: define dso_local void @shouldGenerateUnionAccess(i64 %u.coerce) #0 { +// OGCG-NEXT: entry: +// OGCG-NEXT: %u = alloca %union.U2, align 8 +// OGCG-NEXT: %coerce.dive = getelementptr inbounds nuw %union.U2, ptr %u, i32 0, i32 0 +// OGCG-NEXT: store i64 %u.coerce, ptr %coerce.dive, align 8 +// OGCG-NEXT: store i8 0, ptr %u, align 8 +// OGCG-NEXT: %0 = load i8, ptr %u, align 8 +// OGCG-NEXT: store i32 1, ptr %u, align 8 +// OGCG-NEXT: %1 = load i32, ptr %u, align 8 +// OGCG-NEXT: store float 0x3FB99999A0000000, ptr %u, align 8 +// OGCG-NEXT: %2 = load float, ptr %u, align 8 +// OGCG-NEXT: store double 1.000000e-01, ptr %u, align 8 +// OGCG-NEXT: %3 = load double, ptr %u, align 8 +// OGCG-NEXT: ret void \ No newline at end of file >From b596096bc5d0c5b09df62c225577f1a8fd05bdd1 Mon Sep 17 00:00:00 2001 From: Iris Shi <0...@owo.li> Date: Wed, 30 Apr 2025 12:39:12 +0800 Subject: [PATCH 2/2] apply suggestions --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 22 +- clang/lib/CIR/CodeGen/CIRGenRecordLayout.h | 6 +- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 22 +- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 31 +-- clang/test/CIR/CodeGen/union.c | 221 ++++++++++++------ 5 files changed, 185 insertions(+), 117 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 9069d1a2cf6ac..1a9d84afd2617 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -319,24 +319,20 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { unsigned recordCVR = base.getVRQualifiers(); llvm::StringRef fieldName = field->getName(); + unsigned fieldIndex; + assert(!cir::MissingFeatures::lambdaFieldToName()); - if (rec->isUnion()) { - unsigned fieldIndex = field->getFieldIndex(); - assert(!cir::MissingFeatures::lambdaFieldToName()); - addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); - - } else { - assert(!cir::MissingFeatures::preservedAccessIndexRegion()); - + if (rec->isUnion()) + fieldIndex = field->getFieldIndex(); + else { const CIRGenRecordLayout &layout = cgm.getTypes().getCIRGenRecordLayout(field->getParent()); - unsigned fieldIndex = layout.getCIRFieldNo(field); - - assert(!cir::MissingFeatures::lambdaFieldToName()); - - addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); + fieldIndex = layout.getCIRFieldNo(field); } + addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex); + assert(!cir::MissingFeatures::preservedAccessIndexRegion()); + // If this is a reference field, load the reference right now. if (fieldType->isReferenceType()) { cgm.errorNYI(field->getSourceRange(), "emitLValueForField: reference type"); diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h index 39a9d16ffd766..11768b042e87e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h @@ -31,7 +31,7 @@ class CIRGenRecordLayout { /// Map from (non-bit-field) record field to the corresponding cir record type /// field no. This info is populated by the record builder. - llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldInfo; + llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldIdxMap; public: CIRGenRecordLayout(cir::RecordType completeObjectType) @@ -44,8 +44,8 @@ class CIRGenRecordLayout { /// Return cir::RecordType element number that corresponds to the field FD. unsigned getCIRFieldNo(const clang::FieldDecl *fd) const { fd = fd->getCanonicalDecl(); - assert(fieldInfo.count(fd) && "Invalid field for record!"); - return fieldInfo.lookup(fd); + assert(fieldIdxMap.count(fd) && "Invalid field for record!"); + return fieldIdxMap.lookup(fd); } }; diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 7b9631daacfdd..5bcd408b4072a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -142,7 +142,7 @@ struct CIRRecordLowering final { std::vector<MemberInfo> members; // Output fields, consumed by CIRGenTypes::computeRecordLayout llvm::SmallVector<mlir::Type, 16> fieldTypes; - llvm::DenseMap<const FieldDecl *, unsigned> fields; + llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap; cir::CIRDataLayout dataLayout; LLVM_PREFERRED_TYPE(bool) @@ -208,7 +208,8 @@ void CIRRecordLowering::fillOutputFields() { fieldTypes.push_back(member.data); if (member.kind == MemberInfo::InfoKind::Field) { if (member.fieldDecl) - fields[member.fieldDecl->getCanonicalDecl()] = fieldTypes.size() - 1; + fieldIdxMap[member.fieldDecl->getCanonicalDecl()] = + fieldTypes.size() - 1; // A field without storage must be a bitfield. assert(!cir::MissingFeatures::bitfields()); } @@ -310,7 +311,7 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { assert(!cir::MissingFeatures::bitfields()); // Add all the field numbers. - rl->fieldInfo.swap(lowering.fields); + rl->fieldIdxMap.swap(lowering.fieldIdxMap); // Dump the layout, if requested. if (getASTContext().getLangOpts().DumpRecordLayouts) { @@ -327,10 +328,7 @@ void CIRRecordLowering::lowerUnion() { bool seenNamedMember = false; // Iterate through the fields setting bitFieldInfo and the Fields array. Also - // locate the "most appropriate" storage type. The heuristic for finding the - // storage type isn't necessary, the first (non-0-length-bitfield) field's - // type would work fine and be simpler but would be different than what we've - // been doing and cause lit tests to change. + // locate the "most appropriate" storage type. for (const FieldDecl *field : recordDecl->fields()) { mlir::Type fieldType; if (field->isBitField()) @@ -339,12 +337,13 @@ void CIRRecordLowering::lowerUnion() { else fieldType = getStorageType(field); - fields[field->getCanonicalDecl()] = 0; + // This maps a field to its index. For unions, the index is always 0. + fieldIdxMap[field->getCanonicalDecl()] = 0; // Compute zero-initializable status. // This union might not be zero initialized: it may contain a pointer to // data member which might have some exotic initialization sequence. - // If this is the case, then we aught not to try and come up with a "better" + // If this is the case, then we ought not to try and come up with a "better" // type, it might not be very easy to come up with a Constant which // correctly initializes it. if (!seenNamedMember) { @@ -380,9 +379,8 @@ void CIRRecordLowering::lowerUnion() { if (layoutSize < getSize(storageType)) storageType = getByteArrayType(layoutSize); - - // NOTE(cir): Defer padding calculations to the lowering process. - appendPaddingBytes(layoutSize - getSize(storageType)); + else + appendPaddingBytes(layoutSize - getSize(storageType)); // Set packed if we need it. if (layoutSize % getAlignment(storageType)) diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index f61fc750d7c0e..d0c80911c3863 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -235,25 +235,18 @@ void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) { /// Recurses into union members never returning a union as the largest member. Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { assert(isUnion() && "Only call getLargestMember on unions"); - Type largestMember; - unsigned largestMemberSize = 0; - unsigned numElements = getNumElements(); - auto members = getMembers(); - if (getPadded()) - numElements -= 1; // The last element is padding. - for (unsigned i = 0; i < numElements; ++i) { - Type ty = members[i]; - if (!largestMember || - dataLayout.getTypeABIAlignment(ty) > - dataLayout.getTypeABIAlignment(largestMember) || - (dataLayout.getTypeABIAlignment(ty) == - dataLayout.getTypeABIAlignment(largestMember) && - dataLayout.getTypeSize(ty) > largestMemberSize)) { - largestMember = ty; - largestMemberSize = dataLayout.getTypeSize(largestMember); - } - } - return largestMember; + llvm::ArrayRef<Type> members = getMembers(); + // If the union is padded, we need to ignore the last member, + // which is the padding. + return *std::max_element( + members.begin(), getPadded() ? members.end() - 1 : members.end(), + [&](Type lhs, Type rhs) { + return dataLayout.getTypeABIAlignment(lhs) < + dataLayout.getTypeABIAlignment(rhs) || + (dataLayout.getTypeABIAlignment(lhs) == + dataLayout.getTypeABIAlignment(rhs) && + dataLayout.getTypeSize(lhs) < dataLayout.getTypeSize(rhs)); + }); } //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c index 71cb9c2b20ca1..790fbb7effdda 100644 --- a/clang/test/CIR/CodeGen/union.c +++ b/clang/test/CIR/CodeGen/union.c @@ -26,6 +26,24 @@ union U2 { // LLVM: %union.U2 = type { double } // OGCG: %union.U2 = type { double } +union U3 { + char c[5]; + int i; +} __attribute__((packed)); + +// CIR: !rec_U3 = !cir.record<union "U3" packed padded {!cir.array<!s8i x 5>, !s32i, !u8i}> +// LLVM: %union.U3 = type <{ i32, i8 }> +// OGCG: %union.U3 = type <{ i32, i8 }> + +union U4 { + char c[5]; + int i; +}; + +// CIR: !rec_U4 = !cir.record<union "U4" padded {!cir.array<!s8i x 5>, !s32i, !cir.array<!u8i x 4>}> +// LLVM: %union.U4 = type { i32, [4 x i8] } +// OGCG: %union.U4 = type { i32, [4 x i8] } + union IncompleteU *p; // CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteU> @@ -56,33 +74,32 @@ int f2(void) { } // CIR: cir.func @f2() -> !s32i -// CIR-NEXT: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} -// CIR-NEXT: %1 = cir.alloca !rec_U1, !cir.ptr<!rec_U1>, ["u"] {alignment = 4 : i64} -// CIR-NEXT: %2 = cir.const #cir.int<42> : !s32i -// CIR-NEXT: %3 = cir.get_member %1[0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> -// CIR-NEXT: cir.store %2, %3 : !s32i, !cir.ptr<!s32i> -// CIR-NEXT: %4 = cir.get_member %1[0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> -// CIR-NEXT: %5 = cir.load %4 : !cir.ptr<!s32i>, !s32i -// CIR-NEXT: cir.store %5, %0 : !s32i, !cir.ptr<!s32i> -// CIR-NEXT: %6 = cir.load %0 : !cir.ptr<!s32i>, !s32i -// CIR-NEXT: cir.return %6 : !s32i +// CIR-NEXT: %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} +// CIR-NEXT: %[[U:.*]] = cir.alloca !rec_U1, !cir.ptr<!rec_U1>, ["u"] {alignment = 4 : i64} +// CIR-NEXT: %[[I:.*]] = cir.const #cir.int<42> : !s32i +// CIR-NEXT: %[[N:.*]] = cir.get_member %[[U]][0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[I]], %[[N]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %[[N2:.*]] = cir.get_member %[[U]][0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i> +// CIR-NEXT: %[[VAL:.*]] = cir.load %[[N2]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: cir.store %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.load %[[RETVAL_ADDR]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: cir.return %[[RET]] : !s32i // LLVM: define i32 @f2() -// LLVM-NEXT: %1 = alloca i32, i64 1, align 4 -// LLVM-NEXT: %2 = alloca %union.U1, i64 1, align 4 -// LLVM-NEXT: store i32 42, ptr %2, align 4 -// LLVM-NEXT: %3 = load i32, ptr %2, align 4 -// LLVM-NEXT: store i32 %3, ptr %1, align 4 -// LLVM-NEXT: %4 = load i32, ptr %1, align 4 -// LLVM-NEXT: ret i32 %4 +// LLVM-NEXT: %[[RETVAL:.*]] = alloca i32, i64 1, align 4 +// LLVM-NEXT: %[[U:.*]] = alloca %union.U1, i64 1, align 4 +// LLVM-NEXT: store i32 42, ptr %[[U]], align 4 +// LLVM-NEXT: %[[N_VAL:.*]] = load i32, ptr %[[U]], align 4 +// LLVM-NEXT: store i32 %[[N_VAL]], ptr %[[RETVAL]], align 4 +// LLVM-NEXT: %[[RET:.*]] = load i32, ptr %[[RETVAL]], align 4 +// LLVM-NEXT: ret i32 %[[RET]] // OGCG: define dso_local i32 @f2() // OGCG-NEXT: entry: -// OGCG-NEXT: %u = alloca %union.U1, align 4 -// OGCG-NEXT: store i32 42, ptr %u, align 4 -// OGCG-NEXT: %0 = load i32, ptr %u, align 4 -// OGCG-NEXT: ret i32 %0 - +// OGCG-NEXT: %[[U:.*]] = alloca %union.U1, align 4 +// OGCG-NEXT: store i32 42, ptr %[[U]], align 4 +// OGCG-NEXT: %[[N_VAL:.*]] = load i32, ptr %[[U]], align 4 +// OGCG-NEXT: ret i32 %[[N_VAL]] void shouldGenerateUnionAccess(union U2 u) { u.b = 0; @@ -95,56 +112,120 @@ void shouldGenerateUnionAccess(union U2 u) { u.d; } -// CIR: cir.func @shouldGenerateUnionAccess(%arg0: !rec_U2 -// CIR-NEXT: %0 = cir.alloca !rec_U2, !cir.ptr<!rec_U2>, ["u", init] {alignment = 8 : i64} -// CIR-NEXT: cir.store %arg0, %0 : !rec_U2, !cir.ptr<!rec_U2> -// CIR-NEXT: %1 = cir.const #cir.int<0> : !s32i -// CIR-NEXT: %2 = cir.cast(integral, %1 : !s32i), !s8i -// CIR-NEXT: %3 = cir.get_member %0[0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> -// CIR-NEXT: cir.store %2, %3 : !s8i, !cir.ptr<!s8i> -// CIR-NEXT: %4 = cir.get_member %0[0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> -// CIR-NEXT: %5 = cir.load %4 : !cir.ptr<!s8i>, !s8i -// CIR-NEXT: %6 = cir.const #cir.int<1> : !s32i -// CIR-NEXT: %7 = cir.get_member %0[2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> -// CIR-NEXT: cir.store %6, %7 : !s32i, !cir.ptr<!s32i> -// CIR-NEXT: %8 = cir.get_member %0[2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> -// CIR-NEXT: %9 = cir.load %8 : !cir.ptr<!s32i>, !s32i -// CIR-NEXT: %10 = cir.const #cir.fp<1.000000e-01> : !cir.float -// CIR-NEXT: %11 = cir.get_member %0[3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> -// CIR-NEXT: cir.store %10, %11 : !cir.float, !cir.ptr<!cir.float> -// CIR-NEXT: %12 = cir.get_member %0[3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> -// CIR-NEXT: %13 = cir.load %12 : !cir.ptr<!cir.float>, !cir.float -// CIR-NEXT: %14 = cir.const #cir.fp<1.000000e-01> : !cir.double -// CIR-NEXT: %15 = cir.get_member %0[4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> -// CIR-NEXT: cir.store %14, %15 : !cir.double, !cir.ptr<!cir.double> -// CIR-NEXT: %16 = cir.get_member %0[4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> -// CIR-NEXT: %17 = cir.load %16 : !cir.ptr<!cir.double>, !cir.double +// CIR: cir.func @shouldGenerateUnionAccess(%[[ARG:.*]]: !rec_U2 +// CIR-NEXT: %[[U:.*]] = cir.alloca !rec_U2, !cir.ptr<!rec_U2>, ["u", init] {alignment = 8 : i64} +// CIR-NEXT: cir.store %[[ARG]], %[[U]] : !rec_U2, !cir.ptr<!rec_U2> +// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %[[ZERO_CHAR:.*]] = cir.cast(integral, %[[ZERO]] : !s32i), !s8i +// CIR-NEXT: %[[B_PTR:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> +// CIR-NEXT: cir.store %[[ZERO_CHAR]], %[[B_PTR]] : !s8i, !cir.ptr<!s8i> +// CIR-NEXT: %[[B_PTR2:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i> +// CIR-NEXT: %[[B_VAL:.*]] = cir.load %[[B_PTR2]] : !cir.ptr<!s8i>, !s8i +// CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR-NEXT: %[[I_PTR:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[ONE]], %[[I_PTR]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: %[[I_PTR2:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i> +// CIR-NEXT: %[[I_VAL:.*]] = cir.load %[[I_PTR2]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[FLOAT_VAL:.*]] = cir.const #cir.fp<1.000000e-01> : !cir.float +// CIR-NEXT: %[[F_PTR:.*]] = cir.get_member %[[U]][3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> +// CIR-NEXT: cir.store %[[FLOAT_VAL]], %[[F_PTR]] : !cir.float, !cir.ptr<!cir.float> +// CIR-NEXT: %[[F_PTR2:.*]] = cir.get_member %[[U]][3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float> +// CIR-NEXT: %[[F_VAL:.*]] = cir.load %[[F_PTR2]] : !cir.ptr<!cir.float>, !cir.float +// CIR-NEXT: %[[DOUBLE_VAL:.*]] = cir.const #cir.fp<1.000000e-01> : !cir.double +// CIR-NEXT: %[[D_PTR:.*]] = cir.get_member %[[U]][4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> +// CIR-NEXT: cir.store %[[DOUBLE_VAL]], %[[D_PTR]] : !cir.double, !cir.ptr<!cir.double> +// CIR-NEXT: %[[D_PTR2:.*]] = cir.get_member %[[U]][4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double> +// CIR-NEXT: %[[D_VAL:.*]] = cir.load %[[D_PTR2]] : !cir.ptr<!cir.double>, !cir.double // CIR-NEXT: cir.return -// LLVM: define void @shouldGenerateUnionAccess(%union.U2 %0) { -// LLVM-NEXT: %2 = alloca %union.U2, i64 1, align 8 -// LLVM-NEXT: store %union.U2 %0, ptr %2, align 8 -// LLVM-NEXT: store i8 0, ptr %2, align 1 -// LLVM-NEXT: %3 = load i8, ptr %2, align 1 -// LLVM-NEXT: store i32 1, ptr %2, align 4 -// LLVM-NEXT: %4 = load i32, ptr %2, align 4 -// LLVM-NEXT: store float 0x3FB99999A0000000, ptr %2, align 4 -// LLVM-NEXT: %5 = load float, ptr %2, align 4 -// LLVM-NEXT: store double 1.000000e-01, ptr %2, align 8 -// LLVM-NEXT: %6 = load double, ptr %2, align 8 +// LLVM: define void @shouldGenerateUnionAccess(%union.U2 %[[ARG:.*]]) +// LLVM-NEXT: %[[U:.*]] = alloca %union.U2, i64 1, align 8 +// LLVM-NEXT: store %union.U2 %[[ARG]], ptr %[[U]], align 8 +// LLVM-NEXT: store i8 0, ptr %[[U]], align 1 +// LLVM-NEXT: %[[B_VAL:.*]] = load i8, ptr %[[U]], align 1 +// LLVM-NEXT: store i32 1, ptr %[[U]], align 4 +// LLVM-NEXT: %[[I_VAL:.*]] = load i32, ptr %[[U]], align 4 +// LLVM-NEXT: store float 0x3FB99999A0000000, ptr %[[U]], align 4 +// LLVM-NEXT: %[[F_VAL:.*]] = load float, ptr %[[U]], align 4 +// LLVM-NEXT: store double 1.000000e-01, ptr %[[U]], align 8 +// LLVM-NEXT: %[[D_VAL:.*]] = load double, ptr %[[U]], align 8 // LLVM-NEXT: ret void -// OGCG: define dso_local void @shouldGenerateUnionAccess(i64 %u.coerce) #0 { +// OGCG: define dso_local void @shouldGenerateUnionAccess(i64 %[[ARG:.*]]) // OGCG-NEXT: entry: -// OGCG-NEXT: %u = alloca %union.U2, align 8 -// OGCG-NEXT: %coerce.dive = getelementptr inbounds nuw %union.U2, ptr %u, i32 0, i32 0 -// OGCG-NEXT: store i64 %u.coerce, ptr %coerce.dive, align 8 -// OGCG-NEXT: store i8 0, ptr %u, align 8 -// OGCG-NEXT: %0 = load i8, ptr %u, align 8 -// OGCG-NEXT: store i32 1, ptr %u, align 8 -// OGCG-NEXT: %1 = load i32, ptr %u, align 8 -// OGCG-NEXT: store float 0x3FB99999A0000000, ptr %u, align 8 -// OGCG-NEXT: %2 = load float, ptr %u, align 8 -// OGCG-NEXT: store double 1.000000e-01, ptr %u, align 8 -// OGCG-NEXT: %3 = load double, ptr %u, align 8 -// OGCG-NEXT: ret void \ No newline at end of file +// OGCG-NEXT: %[[U:.*]] = alloca %union.U2, align 8 +// OGCG-NEXT: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %union.U2, ptr %[[U]], i32 0, i32 0 +// OGCG-NEXT: store i64 %[[ARG]], ptr %[[COERCE_DIVE]], align 8 +// OGCG-NEXT: store i8 0, ptr %[[U]], align 8 +// OGCG-NEXT: %[[B_VAL:.*]] = load i8, ptr %[[U]], align 8 +// OGCG-NEXT: store i32 1, ptr %[[U]], align 8 +// OGCG-NEXT: %[[I_VAL:.*]] = load i32, ptr %[[U]], align 8 +// OGCG-NEXT: store float 0x3FB99999A0000000, ptr %[[U]], align 8 +// OGCG-NEXT: %[[F_VAL:.*]] = load float, ptr %[[U]], align 8 +// OGCG-NEXT: store double 1.000000e-01, ptr %[[U]], align 8 +// OGCG-NEXT: %[[D_VAL:.*]] = load double, ptr %[[U]], align 8 +// OGCG-NEXT: ret void + +void f3(union U3 u) { + u.c[2] = 0; +} + +// CIR: cir.func @f3(%[[ARG:.*]]: !rec_U3 +// CIR-NEXT: %[[U:.*]] = cir.alloca !rec_U3, !cir.ptr<!rec_U3>, ["u", init] {alignment = 1 : i64} +// CIR-NEXT: cir.store %[[ARG]], %[[U]] : !rec_U3, !cir.ptr<!rec_U3> +// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %[[ZERO_CHAR:.*]] = cir.cast(integral, %[[ZERO]] : !s32i), !s8i +// CIR-NEXT: %[[IDX:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr<!rec_U3> -> !cir.ptr<!cir.array<!s8i x 5>> +// CIR-NEXT: %[[C_DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[C_PTR]] : !cir.ptr<!cir.array<!s8i x 5>>), !cir.ptr<!s8i> +// CIR-NEXT: %[[ELEM_PTR:.*]] = cir.ptr_stride(%[[C_DECAY]] : !cir.ptr<!s8i>, %[[IDX]] : !s32i), !cir.ptr<!s8i> +// CIR-NEXT: cir.store %[[ZERO_CHAR]], %[[ELEM_PTR]] : !s8i, !cir.ptr<!s8i> +// CIR-NEXT: cir.return + +// LLVM: define void @f3(%union.U3 %[[ARG:.*]]) +// LLVM-NEXT: %[[U:.*]] = alloca %union.U3, i64 1, align 1 +// LLVM-NEXT: store %union.U3 %[[ARG]], ptr %[[U]], align 1 +// LLVM-NEXT: %[[C_PTR:.*]] = getelementptr i8, ptr %[[U]], i32 0 +// LLVM-NEXT: %[[ELEM_PTR:.*]] = getelementptr i8, ptr %[[C_PTR]], i64 2 +// LLVM-NEXT: store i8 0, ptr %[[ELEM_PTR]], align 1 +// LLVM-NEXT: ret void + +// OGCG: define dso_local void @f3(i40 %[[ARG:.*]]) +// OGCG-NEXT: entry: +// OGCG-NEXT: %[[U:.*]] = alloca %union.U3, align 1 +// OGCG-NEXT: store i40 %[[ARG]], ptr %[[U]], align 1 +// OGCG-NEXT: %[[ARRAYIDX:.*]] = getelementptr inbounds [5 x i8], ptr %[[U]], i64 0, i64 2 +// OGCG-NEXT: store i8 0, ptr %[[ARRAYIDX]], align 1 +// OGCG-NEXT: ret void + +void f5(union U4 u) { + u.c[4] = 65; +} + +// CIR: cir.func @f5(%[[ARG:.*]]: !rec_U4 +// CIR-NEXT: %[[U:.*]] = cir.alloca !rec_U4, !cir.ptr<!rec_U4>, ["u", init] {alignment = 4 : i64} +// CIR-NEXT: cir.store %[[ARG]], %[[U]] : !rec_U4, !cir.ptr<!rec_U4> +// CIR-NEXT: %[[CHAR_VAL:.*]] = cir.const #cir.int<65> : !s32i +// CIR-NEXT: %[[CHAR_CAST:.*]] = cir.cast(integral, %[[CHAR_VAL]] : !s32i), !s8i +// CIR-NEXT: %[[IDX:.*]] = cir.const #cir.int<4> : !s32i +// CIR-NEXT: %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr<!rec_U4> -> !cir.ptr<!cir.array<!s8i x 5>> +// CIR-NEXT: %[[C_DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[C_PTR]] : !cir.ptr<!cir.array<!s8i x 5>>), !cir.ptr<!s8i> +// CIR-NEXT: %[[ELEM_PTR:.*]] = cir.ptr_stride(%[[C_DECAY]] : !cir.ptr<!s8i>, %[[IDX]] : !s32i), !cir.ptr<!s8i> +// CIR-NEXT: cir.store %[[CHAR_CAST]], %[[ELEM_PTR]] : !s8i, !cir.ptr<!s8i> +// CIR-NEXT: cir.return + +// LLVM: define void @f5(%union.U4 %[[ARG:.*]]) +// LLVM-NEXT: %[[U:.*]] = alloca %union.U4, i64 1, align 4 +// LLVM-NEXT: store %union.U4 %[[ARG]], ptr %[[U]], align 4 +// LLVM-NEXT: %[[C_PTR:.*]] = getelementptr i8, ptr %[[U]], i32 0 +// LLVM-NEXT: %[[ELEM_PTR:.*]] = getelementptr i8, ptr %[[C_PTR]], i64 4 +// LLVM-NEXT: store i8 65, ptr %[[ELEM_PTR]], align 1 +// LLVM-NEXT: ret void + +// OGCG: define dso_local void @f5(i64 %[[ARG:.*]]) +// OGCG-NEXT: entry: +// OGCG-NEXT: %[[U:.*]] = alloca %union.U4, align 4 +// OGCG-NEXT: store i64 %[[ARG]], ptr %[[U]], align 4 +// OGCG-NEXT: %[[ARRAYIDX:.*]] = getelementptr inbounds [5 x i8], ptr %[[U]], i64 0, i64 4 +// OGCG-NEXT: store i8 65, ptr %[[ARRAYIDX]], align 4 +// OGCG-NEXT: ret void _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits