Skip to content

[CIR][ABI] Implement basic struct CC lowering for x86_64 #784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 88 additions & 35 deletions clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,52 @@
#ifndef LLVM_CLANG_CIR_DIALECT_IR_CIRDATALAYOUT_H
#define LLVM_CLANG_CIR_DIALECT_IR_CIRDATALAYOUT_H

#include "mlir/Dialect/DLTI/DLTI.h"
#include "mlir/IR/BuiltinOps.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/TypeSize.h"

namespace cir {

class StructLayout;

// FIXME(cir): This might be replaced by a CIRDataLayout interface which can
// provide the same functionalities.
class CIRDataLayout {
bool bigEndian = false;

/// Primitive type alignment data. This is sorted by type and bit
/// width during construction.
llvm::LayoutAlignElem StructAlignment;

// The StructType -> StructLayout map.
mutable void *LayoutMap = nullptr;

public:
mlir::DataLayout layout;

/// Constructs a DataLayout from a specification string. See reset().
explicit CIRDataLayout(llvm::StringRef dataLayout, mlir::ModuleOp module)
: layout(module) {
reset(dataLayout);
}
/// Constructs a DataLayout the module's data layout attribute.
CIRDataLayout(mlir::ModuleOp modOp);

/// Parse a data layout string (with fallback to default values).
void reset(llvm::StringRef dataLayout);
void reset();

// Free all internal data structures.
void clear();

CIRDataLayout(mlir::ModuleOp modOp);

bool isBigEndian() const { return bigEndian; }

// `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 = mlir::cast<mlir::cir::StructType>(ty);
if (sTy.getPacked() && useABI)
return 1;
} else if (llvm::isa<mlir::cir::ArrayType>(ty)) {
return getAlignment(mlir::cast<mlir::cir::ArrayType>(ty).getEltType(),
useABI);
}

return useABI ? layout.getTypeABIAlignment(ty)
: layout.getTypePreferredAlignment(ty);
}
/// Returns a StructLayout object, indicating the alignment of the
/// struct, its size, and the offsets of its fields.
///
/// Note that this information is lazily cached.
const StructLayout *getStructLayout(mlir::cir::StructType Ty) const;

/// Internal helper method that returns requested alignment for type.
llvm::Align getAlignment(mlir::Type Ty, bool abi_or_pref) const;

unsigned getABITypeAlign(mlir::Type ty) const {
llvm::Align getABITypeAlign(mlir::Type ty) const {
return getAlignment(ty, true);
}

Expand All @@ -67,10 +68,10 @@ class CIRDataLayout {
/// the runtime size will be a positive integer multiple of the base size.
///
/// For example, returns 5 for i36 and 10 for x86_fp80.
unsigned getTypeStoreSize(mlir::Type Ty) const {
// FIXME: this is a bit inaccurate, see DataLayout::getTypeStoreSize for
// more information.
return llvm::divideCeil(layout.getTypeSizeInBits(Ty), 8);
llvm::TypeSize getTypeStoreSize(mlir::Type Ty) const {
llvm::TypeSize BaseSize = getTypeSizeInBits(Ty);
return {llvm::divideCeil(BaseSize.getKnownMinValue(), 8),
BaseSize.isScalable()};
}

/// Returns the offset in bytes between successive objects of the
Expand All @@ -81,20 +82,20 @@ class CIRDataLayout {
///
/// This is the amount that alloca reserves for this type. For example,
/// returns 12 or 16 for x86_fp80, depending on alignment.
unsigned getTypeAllocSize(mlir::Type Ty) const {
llvm::TypeSize getTypeAllocSize(mlir::Type Ty) const {
// Round up to the next alignment boundary.
return llvm::alignTo(getTypeStoreSize(Ty), getABITypeAlign(Ty));
return llvm::alignTo(getTypeStoreSize(Ty), getABITypeAlign(Ty).value());
}

unsigned getPointerTypeSizeInBits(mlir::Type Ty) const {
llvm::TypeSize getPointerTypeSizeInBits(mlir::Type Ty) const {
assert(mlir::isa<mlir::cir::PointerType>(Ty) &&
"This should only be called with a pointer type");
return layout.getTypeSizeInBits(Ty);
}

unsigned getTypeSizeInBits(mlir::Type Ty) const {
return layout.getTypeSizeInBits(Ty);
}
// The implementation of this method is provided inline as it is particularly
// well suited to constant folding when called on a specific Type subclass.
llvm::TypeSize getTypeSizeInBits(mlir::Type Ty) const;

mlir::Type getIntPtrType(mlir::Type Ty) const {
assert(mlir::isa<mlir::cir::PointerType>(Ty) && "Expected pointer type");
Expand All @@ -104,6 +105,58 @@ class CIRDataLayout {
}
};

/// Used to lazily calculate structure layout information for a target machine,
/// based on the DataLayout structure.
class StructLayout final
: public llvm::TrailingObjects<StructLayout, llvm::TypeSize> {
llvm::TypeSize StructSize;
llvm::Align StructAlignment;
unsigned IsPadded : 1;
unsigned NumElements : 31;

public:
llvm::TypeSize getSizeInBytes() const { return StructSize; }

llvm::TypeSize getSizeInBits() const { return 8 * StructSize; }

llvm::Align getAlignment() const { return StructAlignment; }

/// Returns whether the struct has padding or not between its fields.
/// NB: Padding in nested element is not taken into account.
bool hasPadding() const { return IsPadded; }

/// Given a valid byte offset into the structure, returns the structure
/// index that contains it.
unsigned getElementContainingOffset(uint64_t FixedOffset) const;

llvm::MutableArrayRef<llvm::TypeSize> getMemberOffsets() {
return llvm::MutableArrayRef(getTrailingObjects<llvm::TypeSize>(),
NumElements);
}

llvm::ArrayRef<llvm::TypeSize> getMemberOffsets() const {
return llvm::ArrayRef(getTrailingObjects<llvm::TypeSize>(), NumElements);
}

llvm::TypeSize getElementOffset(unsigned Idx) const {
assert(Idx < NumElements && "Invalid element idx!");
return getMemberOffsets()[Idx];
}

llvm::TypeSize getElementOffsetInBits(unsigned Idx) const {
return getElementOffset(Idx) * 8;
}

private:
friend class CIRDataLayout; // Only DataLayout can create this class

StructLayout(mlir::cir::StructType ST, const CIRDataLayout &DL);

size_t numTrailingObjects(OverloadToken<llvm::TypeSize>) const {
return NumElements;
}
};

} // namespace cir

#endif
6 changes: 6 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,12 @@ def StoreOp : CIR_Op<"store", [
```
}];

let builders = [
OpBuilder<(ins "Value":$value, "Value":$addr), [{
$_state.addOperands({value, addr});
}]>
];

let arguments = (ins CIR_AnyType:$value,
Arg<CIR_PointerType, "the address to store the value",
[MemWrite]>:$addr,
Expand Down
45 changes: 42 additions & 3 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct MissingFeatures {
// ObjC
static bool setObjCGCLValueClass() { return false; }
static bool objCLifetime() { return false; }
static bool objCIvarDecls() { return false; }

// Debug info
static bool generateDebugInfo() { return false; }
Expand Down Expand Up @@ -205,17 +206,42 @@ struct MissingFeatures {

//-- Missing AST queries

static bool recordDeclCanPassInRegisters() { return false; }
static bool CXXRecordDeclIsEmptyCXX11() { return false; }
static bool CXXRecordDeclIsPOD() { return false; }
static bool CXXRecordIsDynamicClass() { return false; }
static bool astContextGetExternalSource() { return false; }
static bool declGetMaxAlignment() { return false; }
static bool declHasAlignMac68kAttr() { return false; }
static bool declHasAlignNaturalAttr() { return false; }
static bool declHasMaxFieldAlignmentAttr() { return false; }
static bool fieldDeclIsBitfield() { return false; }
static bool fieldDeclIsPotentiallyOverlapping() { return false; }
static bool fieldDeclGetMaxFieldAlignment() { return false; }
static bool fieldDeclisUnnamedBitField() { return false; }
static bool funcDeclIsCXXConstructorDecl() { return false; }
static bool funcDeclIsCXXDestructorDecl() { return false; }
static bool funcDeclIsCXXMethodDecl() { return false; }
static bool funcDeclIsInlineBuiltinDeclaration() { return false; }
static bool funcDeclIsReplaceableGlobalAllocationFunction() { return false; }
static bool isCXXRecordDecl() { return false; }
static bool qualTypeIsReferenceType() { return false; }
static bool typeGetAsEnumType() { return false; }
static bool recordDeclCanPassInRegisters() { return false; }
static bool recordDeclHasAlignmentAttr() { return false; }
static bool recordDeclHasFlexibleArrayMember() { return false; }
static bool recordDeclIsCXXDecl() { return false; }
static bool recordDeclIsMSStruct() { return false; }
static bool recordDeclIsPacked() { return false; }
static bool recordDeclMayInsertExtraPadding() { return false; }
static bool typeGetAsBuiltinType() { return false; }
static bool typeGetAsEnumType() { return false; }
static bool typeIsCXXRecordDecl() { return false; }
static bool typeIsScalableType() { return false; }
static bool typeIsSized() { return false; }
static bool varDeclIsKNRPromoted() { return false; }

// We need to track parent (base) classes to determine the layout of a class.
static bool getCXXRecordBases() { return false; }

//-- Missing types

static bool fixedWidthIntegers() { return false; }
Expand All @@ -232,6 +258,18 @@ struct MissingFeatures {

//-- Other missing features

// We need to track the parent record types that represent a field
// declaration. This is necessary to determine the layout of a class.
static bool fieldDeclAbstraction() { return false; }

// There are some padding diagnostic features for Itanium ABI that we might
// wanna add later.
static bool bitFieldPaddingDiagnostics() { return false; }

// Clang considers both enums and records as tag types. We don't have a way to
// transparently handle both these types yet. Might need an interface here.
static bool tagTypeClassAbstraction() { return false; }

// Empty values might be passed as arguments to serve as padding, ensuring
// alignment and compliance (e.g. MIPS). We do not yet support this.
static bool argumentPadding() { return false; }
Expand Down Expand Up @@ -268,7 +306,7 @@ struct MissingFeatures {
// evaluating ABI-specific lowering.
static bool qualifiedTypes() { return false; }

// We're ignoring several details regarding ABI-halding for Swift.
// We're ignoring several details regarding ABI-handling for Swift.
static bool swift() { return false; }

// The AppleARM64 is using ItaniumCXXABI, which is not quite right.
Expand All @@ -281,6 +319,7 @@ struct MissingFeatures {
// If a store op is guaranteed to execute before the retun value load op, we
// can optimize away the store and load ops. Seems like an early optimization.
static bool returnValueDominatingStoreOptmiization() { return false; }

// Globals (vars and functions) may have attributes that are target depedent.
static bool setTargetAttributes() { return false; }

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
unsigned Pos = 0;
for (size_t I = 0; I < Elts.size(); ++I) {
auto EltSize = Layout.getTypeAllocSize(Elts[I]);
unsigned AlignMask = Layout.getABITypeAlign(Elts[I]) - 1;
unsigned AlignMask = Layout.getABITypeAlign(Elts[I]).value() - 1;
Pos = (Pos + AlignMask) & ~AlignMask;
if (Offset < Pos + EltSize) {
Indices.push_back(I);
Expand Down
Loading