Skip to content

Commit

Permalink
[CIR][Codegen] Initial support for packed structures (#473)
Browse files Browse the repository at this point in the history
This PR adds a support for packed structures.

Basically, now both `pragma pack(...)` and
`__attribute__((aligned(...)))` should work.
The only problem is that `getAlignment` is not a total one - I fix only
a couple of issues I faced with - for struct types and arrays.
  • Loading branch information
gitoleg authored and lanza committed Apr 29, 2024
1 parent 00c0fc0 commit 82a4187
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 6 deletions.
11 changes: 10 additions & 1 deletion clang/lib/CIR/CodeGen/CIRDataLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@ class CIRDataLayout {

// `useABI` is `true` if not using prefered alignment.
unsigned getAlignment(mlir::Type ty, bool useABI) const {
if (llvm::isa<mlir::cir::StructType>(ty)) {
auto sTy = ty.cast<mlir::cir::StructType>();
if (sTy.getPacked() && useABI)
return 1;
} else if (llvm::isa<mlir::cir::ArrayType>(ty)) {
return getAlignment(ty.cast<mlir::cir::ArrayType>().getEltType(), useABI);
}

return useABI ? layout.getTypeABIAlignment(ty)
: layout.getTypePreferredAlignment(ty);
}

unsigned getABITypeAlign(mlir::Type ty) const {
return getAlignment(ty, true);
}
Expand Down Expand Up @@ -60,7 +69,7 @@ class CIRDataLayout {
/// returns 12 or 16 for x86_fp80, depending on alignment.
unsigned getTypeAllocSize(mlir::Type Ty) const {
// Round up to the next alignment boundary.
return llvm::alignTo(getTypeStoreSize(Ty), layout.getTypeABIAlignment(Ty));
return llvm::alignTo(getTypeStoreSize(Ty), getABITypeAlign(Ty));
}

unsigned getPointerTypeSizeInBits(mlir::Type Ty) const {
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
llvm::SmallVector<mlir::Type, 8> members;
auto structTy = type.dyn_cast<mlir::cir::StructType>();
assert(structTy && "expected cir.struct");
assert(!packed && "unpacked struct is NYI");

// Collect members and check if they are all zero.
bool isZero = true;
Expand All @@ -200,7 +199,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
mlir::cir::ConstStructAttr getAnonConstStruct(mlir::ArrayAttr arrayAttr,
bool packed = false,
mlir::Type ty = {}) {
assert(!packed && "NYI");
llvm::SmallVector<mlir::Type, 4> members;
for (auto &f : arrayAttr) {
auto ta = f.dyn_cast<mlir::TypedAttr>();
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprConst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ mlir::Attribute ConstantAggregateBuilder::buildFrom(
ArrayRef<mlir::Attribute> UnpackedElems = Elems;
llvm::SmallVector<mlir::Attribute, 32> UnpackedElemStorage;
if (DesiredSize < AlignedSize || DesiredSize.alignTo(Align) != DesiredSize) {
NaturalLayout = false;
Packed = true;
} else if (DesiredSize > AlignedSize) {
llvm_unreachable("NYI");
}

Expand Down
48 changes: 45 additions & 3 deletions clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ struct CIRRecordLowering final {
void lower(bool nonVirtualBaseType);
void lowerUnion();

/// Determines if we need a packed llvm struct.
void determinePacked(bool NVBaseType);

void computeVolatileBitfields();
void accumulateBases();
void accumulateVPtrs();
Expand Down Expand Up @@ -294,6 +297,11 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
// TODO: implemented packed structs
// TODO: implement padding
// TODO: support zeroInit

members.push_back(StorageInfo(Size, getUIntNType(8)));
determinePacked(nonVirtualBaseType);
members.pop_back();

fillOutputFields();
computeVolatileBitfields();
}
Expand Down Expand Up @@ -613,6 +621,41 @@ void CIRRecordLowering::accumulateFields() {
}
}

void CIRRecordLowering::determinePacked(bool NVBaseType) {
if (isPacked)
return;
CharUnits Alignment = CharUnits::One();
CharUnits NVAlignment = CharUnits::One();
CharUnits NVSize = !NVBaseType && cxxRecordDecl
? astRecordLayout.getNonVirtualSize()
: CharUnits::Zero();
for (std::vector<MemberInfo>::const_iterator Member = members.begin(),
MemberEnd = members.end();
Member != MemberEnd; ++Member) {
if (!Member->data)
continue;
// If any member falls at an offset that it not a multiple of its alignment,
// then the entire record must be packed.
if (Member->offset % getAlignment(Member->data))
isPacked = true;
if (Member->offset < NVSize)
NVAlignment = std::max(NVAlignment, getAlignment(Member->data));
Alignment = std::max(Alignment, getAlignment(Member->data));
}
// If the size of the record (the capstone's offset) is not a multiple of the
// record's alignment, it must be packed.
if (members.back().offset % Alignment)
isPacked = true;
// If the non-virtual sub-object is not a multiple of the non-virtual
// sub-object's alignment, it must be packed. We cannot have a packed
// non-virtual sub-object and an unpacked complete object or vise versa.
if (NVSize % NVAlignment)
isPacked = true;
// Update the alignment of the sentinel.
if (!isPacked)
members.back().data = getUIntNType(astContext.toBits(Alignment));
}

std::unique_ptr<CIRGenRecordLayout>
CIRGenTypes::computeRecordLayout(const RecordDecl *D,
mlir::cir::StructType *Ty) {
Expand Down Expand Up @@ -645,9 +688,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *D,
// Fill in the struct *after* computing the base type. Filling in the body
// signifies that the type is no longer opaque and record layout is complete,
// but we may need to recursively layout D while laying D out as a base type.
*Ty =
Builder.getCompleteStructTy(builder.fieldTypes, getRecordTypeName(D, ""),
/*packed=*/false, D);
*Ty = Builder.getCompleteStructTy(
builder.fieldTypes, getRecordTypeName(D, ""), builder.isPacked, D);

auto RL = std::make_unique<CIRGenRecordLayout>(
Ty ? *Ty : mlir::cir::StructType{},
Expand Down
37 changes: 37 additions & 0 deletions clang/test/CIR/CodeGen/packed-structs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

#pragma pack(1)

typedef struct {
int a0;
char a1;
} A;

typedef struct {
int b0;
char b1;
A a[6];
} B;

typedef struct {
int c0;
char c1;
} __attribute__((aligned(2))) C;


// CHECK: !ty_22A22 = !cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}>
// CHECK: !ty_22C22 = !cir.struct<struct "C" packed {!cir.int<s, 32>, !cir.int<s, 8>}>
// CHECK: !ty_22B22 = !cir.struct<struct "B" packed {!cir.int<s, 32>, !cir.int<s, 8>, !cir.array<!cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}> x 6>}>

// CHECK: cir.func {{.*@foo()}}
// CHECK: %0 = cir.alloca !ty_22A22, cir.ptr <!ty_22A22>, ["a"] {alignment = 1 : i64}
// CHECK: %1 = cir.alloca !ty_22B22, cir.ptr <!ty_22B22>, ["b"] {alignment = 1 : i64}
// CHECK: %2 = cir.alloca !ty_22C22, cir.ptr <!ty_22C22>, ["c"] {alignment = 2 : i64}
void foo() {
A a;
B b;
C c;
}


0 comments on commit 82a4187

Please sign in to comment.