[clang] 708053c - [CIR] Upstream initial support for union type (#137501)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 30 16:56:41 PDT 2025
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
More information about the cfe-commits
mailing list