Author: Iris Shi Date: 2025-05-01T07:56:38+08:00 New Revision: 708053cd7ec576c67fd1c72cd6b836987fbee2ce
URL: https://github.com/llvm/llvm-project/commit/708053cd7ec576c67fd1c72cd6b836987fbee2ce DIFF: https://github.com/llvm/llvm-project/commit/708053cd7ec576c67fd1c72cd6b836987fbee2ce.diff LOG: [CIR] Upstream initial support for union type (#137501) Closes #136059 Added: Modified: clang/include/clang/CIR/Dialect/IR/CIRTypes.td clang/lib/CIR/CodeGen/CIRGenExpr.cpp clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp clang/lib/CIR/CodeGen/CIRGenRecordLayout.h clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp clang/lib/CIR/Dialect/IR/CIRTypes.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp clang/test/CIR/CodeGen/union.c Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index de754a982edcd..6ca6fbeeb6165 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -531,6 +531,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 a50e1fd18c807..da5a0b97a395e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -317,20 +317,21 @@ 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); - + unsigned fieldIndex; assert(!cir::MissingFeatures::lambdaFieldToName()); + if (rec->isUnion()) + fieldIndex = field->getFieldIndex(); + else { + const CIRGenRecordLayout &layout = + cgm.getTypes().getCIRGenRecordLayout(field->getParent()); + 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()) { 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/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 83aba256cd48e..5bcd408b4072a 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; @@ -130,12 +142,14 @@ 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) 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; } @@ -194,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()); } @@ -296,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) { @@ -306,3 +321,68 @@ 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. + for (const FieldDecl *field : recordDecl->fields()) { + mlir::Type fieldType; + if (field->isBitField()) + cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), + "bitfields in lowerUnion"); + else + fieldType = getStorageType(field); + + // 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 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) { + 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); + else + 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 b35442964c169..ce85e48c4555e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -230,6 +230,25 @@ 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"); + 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)); + }); +} + //===----------------------------------------------------------------------===// // Data Layout information for types //===----------------------------------------------------------------------===// @@ -237,10 +256,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 +266,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 +283,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 2c87255045df8..1afcee6857de1 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1436,7 +1436,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; } @@ -1609,7 +1616,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..790fbb7effdda 100644 --- a/clang/test/CIR/CodeGen/union.c +++ b/clang/test/CIR/CodeGen/union.c @@ -5,25 +5,227 @@ // 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 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> +// 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: %[[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: %[[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: %[[N_VAL:.*]] = load i32, ptr %[[U]], align 4 +// OGCG-NEXT: ret i32 %[[N_VAL]] + +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(%[[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 %[[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 %[[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 %[[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