From ed031ed85ebee5fa07400c24ecea28af4bf2a333 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 06:19:25 -0300 Subject: [PATCH 01/16] [NFC] Add simple value-target StoreOp builder --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 797e19d55d83..bc97a065f74c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -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:$addr, From a1d3005e70ffa865b728b3e04a005f1c0b6f458c Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 06:27:07 -0300 Subject: [PATCH 02/16] [NFC] Add Record ABI argumnet type bits --- clang/include/clang/CIR/MissingFeatures.h | 2 ++ clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp | 1 + .../Transforms/TargetLowering/ABIInfoImpl.cpp | 8 ++++++++ .../Transforms/TargetLowering/ABIInfoImpl.h | 2 ++ .../Transforms/TargetLowering/CIRCXXABI.h | 20 +++++++++++++++++++ .../TargetLowering/ItaniumCXXABI.cpp | 8 ++++++++ 6 files changed, 41 insertions(+) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 89b4069f3686..a5e5b4a615fd 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -206,6 +206,7 @@ struct MissingFeatures { //-- Missing AST queries static bool recordDeclCanPassInRegisters() { return false; } + static bool recordDeclIsCXXDecl() { return false; } static bool funcDeclIsCXXConstructorDecl() { return false; } static bool funcDeclIsCXXDestructorDecl() { return false; } static bool funcDeclIsCXXMethodDecl() { return false; } @@ -214,6 +215,7 @@ struct MissingFeatures { static bool qualTypeIsReferenceType() { return false; } static bool typeGetAsEnumType() { return false; } static bool typeGetAsBuiltinType() { return false; } + static bool typeIsCXXRecordDecl() { return false; } static bool varDeclIsKNRPromoted() { return false; } //-- Missing types diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index 26d055b69351..c423db0c6b41 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -1,4 +1,5 @@ #include "clang/CIR/Dialect/IR/CIRDataLayout.h" +#include "mlir/Dialect/DLTI/DLTI.h" #include "llvm/ADT/StringRef.h" namespace cir { diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp index e5ddcff6b5e7..e4fd449cb38f 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp @@ -45,5 +45,13 @@ Type useFirstFieldIfTransparentUnion(Type Ty) { return Ty; } +CIRCXXABI::RecordArgABI getRecordArgABI(const StructType RT, + CIRCXXABI &CXXABI) { + if (::cir::MissingFeatures::typeIsCXXRecordDecl()) { + llvm_unreachable("NYI"); + } + return CXXABI.getRecordArgABI(RT); +} + } // namespace cir } // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h index 80f43d9a5e9f..9e45bc4e0ecc 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h @@ -30,6 +30,8 @@ bool isAggregateTypeForABI(Type T); /// should ensure that all elements of the union have the same "machine type". Type useFirstFieldIfTransparentUnion(Type Ty); +CIRCXXABI::RecordArgABI getRecordArgABI(const StructType RT, CIRCXXABI &CXXABI); + } // namespace cir } // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h index 3cc1bde1f763..42e666999005 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h @@ -40,6 +40,26 @@ class CIRCXXABI { /// If the C++ ABI requires the given type be returned in a particular way, /// this method sets RetAI and returns true. virtual bool classifyReturnType(LowerFunctionInfo &FI) const = 0; + + /// Specify how one should pass an argument of a record type. + enum RecordArgABI { + /// Pass it using the normal C aggregate rules for the ABI, potentially + /// introducing extra copies and passing some or all of it in registers. + RAA_Default = 0, + + /// Pass it on the stack using its defined layout. The argument must be + /// evaluated directly into the correct stack position in the arguments + /// area, + /// and the call machinery must not move it or introduce extra copies. + RAA_DirectInMemory, + + /// Pass it as a pointer to temporary memory. + RAA_Indirect + }; + + /// Returns how an argument of the given record type should be passed. + /// FIXME(cir): This expects a CXXRecordDecl! Not any record type. + virtual RecordArgABI getRecordArgABI(const StructType RD) const = 0; }; /// Creates an Itanium-family ABI. diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp index 1bbf8fe47fbe..9daba7d1a10c 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp @@ -43,6 +43,14 @@ class ItaniumCXXABI : public CIRCXXABI { UseARMGuardVarABI(UseARMGuardVarABI), Use32BitVTableOffsetABI(false) {} bool classifyReturnType(LowerFunctionInfo &FI) const override; + + // FIXME(cir): This expects a CXXRecordDecl! Not any record type. + RecordArgABI getRecordArgABI(const StructType RD) const override { + assert(!::cir::MissingFeatures::recordDeclIsCXXDecl()); + // If C++ prohibits us from making a copy, pass by address. + assert(!::cir::MissingFeatures::recordDeclCanPassInRegisters()); + return RAA_Default; + } }; } // namespace From f6778c9de9b4283dedb5ca68f245bdbbfb0883c0 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 06:30:21 -0300 Subject: [PATCH 03/16] [NFC] Allow bitcast of StructTypes for ABI type coercion --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index d316f2d15632..686e71f66de7 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -458,6 +458,10 @@ LogicalResult CastOp::verify() { return success(); } case cir::CastKind::bitcast: { + // Allow bitcast of structs for calling conventions. + if (isa(srcType) || isa(resType)) + return success(); + // This is the only cast kind where we don't want vector types to decay // into the element type. if ((!mlir::isa(getSrc().getType()) || From 898beeeb790e1e124ae4009f0a5898fc438de464 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 06:31:51 -0300 Subject: [PATCH 04/16] [NFC] Implement target getter in CIR's ABIInfo --- clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp | 2 ++ clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h | 2 ++ clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h | 1 + 3 files changed, 5 insertions(+) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp index 3ed29dd4d549..4e2a81de9fc1 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp @@ -27,6 +27,8 @@ CIRCXXABI &ABIInfo::getCXXABI() const { return LT.getCXXABI(); } CIRLowerContext &ABIInfo::getContext() const { return LT.getContext(); } +const clang::TargetInfo &ABIInfo::getTarget() const { return LT.getTarget(); } + const ::cir::CIRDataLayout &ABIInfo::getDataLayout() const { return LT.getDataLayout(); } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h index 67d628f4eb30..bbcd906e849a 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h @@ -41,6 +41,8 @@ class ABIInfo { CIRLowerContext &getContext() const; + const clang::TargetInfo &getTarget() const; + const ::cir::CIRDataLayout &getDataLayout() const; virtual void computeInfo(LowerFunctionInfo &FI) const = 0; diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h index 9e6149707c07..d6f20941544f 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h @@ -60,6 +60,7 @@ class LowerTypes { LowerModule &getLM() const { return LM; } CIRCXXABI &getCXXABI() const { return CXXABI; } CIRLowerContext &getContext() { return context; } + const clang::TargetInfo &getTarget() const { return Target; } MLIRContext *getMLIRContext() { return mlirContext; } /// Convert clang calling convention to LLVM callilng convention. From aa515c761358ca48cafbe4a7c60c8ea189343b45 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 06:33:46 -0300 Subject: [PATCH 05/16] [CIR][IR] Fix VectorType alignment calculation --- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 7bbd614645b9..cd826c43fc08 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -32,6 +32,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TypeSwitch.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" #include using cir::MissingFeatures; @@ -448,13 +449,13 @@ llvm::TypeSize mlir::cir::VectorType::getTypeSizeInBits( uint64_t mlir::cir::VectorType::getABIAlignment( const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { - return getSize() * dataLayout.getTypeABIAlignment(getEltType()); + return llvm::NextPowerOf2(dataLayout.getTypeSizeInBits(*this)); } uint64_t mlir::cir::VectorType::getPreferredAlignment( const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { - return getSize() * dataLayout.getTypePreferredAlignment(getEltType()); + return llvm::NextPowerOf2(dataLayout.getTypeSizeInBits(*this)); } llvm::TypeSize From 2be078576c067c8053f094eeedb8812eeb8fedb8 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 07:29:47 -0300 Subject: [PATCH 06/16] [NFC] Remove unused LLVM datalayout string from CIRDataLayout constructor --- clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h | 13 +++---------- clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp | 2 +- .../Transforms/TargetLowering/LowerTypes.cpp | 2 +- clang/test/CIR/Lowering/address-space.cir | 3 ++- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index e2fd966e3cb2..b98ed8c04c77 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -12,10 +12,8 @@ #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" namespace cir { @@ -25,20 +23,15 @@ class CIRDataLayout { 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. diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index c423db0c6b41..6c6cc6e61062 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -30,7 +30,7 @@ CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} { } } -void CIRDataLayout::reset(llvm::StringRef Desc) { clear(); } +void CIRDataLayout::reset() { clear(); } void CIRDataLayout::clear() {} diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp index 20e4dc643df0..e7eaa2bda2d0 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp @@ -37,7 +37,7 @@ LowerTypes::LowerTypes(LowerModule &LM, StringRef DLString) : LM(LM), context(LM.getContext()), Target(LM.getTarget()), CXXABI(LM.getCXXABI()), TheABIInfo(LM.getTargetLoweringInfo().getABIInfo()), - mlirContext(LM.getMLIRContext()), DL(DLString, LM.getModule()) {} + mlirContext(LM.getMLIRContext()), DL(LM.getModule()) {} /// Return the ABI-specific function type for a CIR function type. FuncType LowerTypes::getFunctionType(const LowerFunctionInfo &FI) { diff --git a/clang/test/CIR/Lowering/address-space.cir b/clang/test/CIR/Lowering/address-space.cir index 1b2d01e8b1db..c1b88d7a8fe0 100644 --- a/clang/test/CIR/Lowering/address-space.cir +++ b/clang/test/CIR/Lowering/address-space.cir @@ -5,7 +5,8 @@ module attributes { cir.triple = "spirv64-unknown-unknown", - llvm.data_layout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1" + llvm.data_layout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + dlti.dl_spec = #dlti.dl_spec<> // Avoid assert errors. } { // LLVM: define void @foo(ptr %0) cir.func @foo(%arg0: !cir.ptr) { From a0e31c815c2506d88c2dbd9608e0005450579d4a Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 20:15:11 -0300 Subject: [PATCH 07/16] [NFC] Use LLVM's TypeSize and Align abstractions in CIRDataLayout --- .../clang/CIR/Dialect/IR/CIRDataLayout.h | 25 +++++++++++-------- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index b98ed8c04c77..7c9ab5ff9f5a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -14,6 +14,8 @@ #include "mlir/IR/BuiltinOps.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/TypeSize.h" namespace cir { @@ -35,21 +37,22 @@ class CIRDataLayout { bool isBigEndian() const { return bigEndian; } // `useABI` is `true` if not using prefered alignment. - unsigned getAlignment(mlir::Type ty, bool useABI) const { + llvm::Align getAlignment(mlir::Type ty, bool useABI) const { if (llvm::isa(ty)) { auto sTy = mlir::cast(ty); if (sTy.getPacked() && useABI) - return 1; + return llvm::Align(1); } else if (llvm::isa(ty)) { return getAlignment(mlir::cast(ty).getEltType(), useABI); } - return useABI ? layout.getTypeABIAlignment(ty) - : layout.getTypePreferredAlignment(ty); + uint align = useABI ? layout.getTypeABIAlignment(ty) + : layout.getTypePreferredAlignment(ty); + return llvm::Align(align); } - unsigned getABITypeAlign(mlir::Type ty) const { + llvm::Align getABITypeAlign(mlir::Type ty) const { return getAlignment(ty, true); } @@ -60,10 +63,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 { + llvm::TypeSize getTypeStoreSize(mlir::Type Ty) const { // FIXME: this is a bit inaccurate, see DataLayout::getTypeStoreSize for // more information. - return llvm::divideCeil(layout.getTypeSizeInBits(Ty), 8); + return {llvm::divideCeil(layout.getTypeSizeInBits(Ty), 8), false}; } /// Returns the offset in bytes between successive objects of the @@ -74,18 +77,18 @@ 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(Ty) && "This should only be called with a pointer type"); return layout.getTypeSizeInBits(Ty); } - unsigned getTypeSizeInBits(mlir::Type Ty) const { + llvm::TypeSize getTypeSizeInBits(mlir::Type Ty) const { return layout.getTypeSizeInBits(Ty); } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 577c21d683e7..02177ccf170f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -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); From 8a6589bcb16f7d6f0b0cbace25c84de6dd61d7ce Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 21:14:34 -0300 Subject: [PATCH 08/16] [NFC] Add DataLayout getter in LowerModule --- .../lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h index 46ac0c105269..44cd5a0ae1cb 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h @@ -23,6 +23,7 @@ #include "mlir/Interfaces/DataLayoutInterfaces.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" #include @@ -55,6 +56,10 @@ class LowerModule { MLIRContext *getMLIRContext() { return module.getContext(); } ModuleOp &getModule() { return module; } + const ::cir::CIRDataLayout &getDataLayout() const { + return types.getDataLayout(); + } + const TargetLoweringInfo &getTargetLoweringInfo(); // FIXME(cir): This would be in ASTContext, not CodeGenModule. From 1ec7e7ba592da44cb18573607596c4dc959cd7ba Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sat, 10 Aug 2024 22:39:13 -0300 Subject: [PATCH 09/16] [NFC] Implement CIRRecordLayout class --- .../TargetLowering/CIRRecordLayout.cpp | 42 ++++++- .../TargetLowering/CIRRecordLayout.h | 107 +++++++++++++++++- 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.cpp index 370ada5411a0..68b777fa7755 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.cpp @@ -16,7 +16,47 @@ namespace mlir { namespace cir { -CIRRecordLayout::CIRRecordLayout() {} +// Constructor for C++ records. +CIRRecordLayout::CIRRecordLayout( + const CIRLowerContext &Ctx, clang::CharUnits size, + clang::CharUnits alignment, clang::CharUnits preferredAlignment, + clang::CharUnits unadjustedAlignment, clang::CharUnits requiredAlignment, + bool hasOwnVFPtr, bool hasExtendableVFPtr, clang::CharUnits vbptroffset, + clang::CharUnits datasize, ArrayRef fieldoffsets, + clang::CharUnits nonvirtualsize, clang::CharUnits nonvirtualalignment, + clang::CharUnits preferrednvalignment, + clang::CharUnits SizeOfLargestEmptySubobject, const Type PrimaryBase, + bool IsPrimaryBaseVirtual, const Type BaseSharingVBPtr, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase) + : Size(size), DataSize(datasize), Alignment(alignment), + PreferredAlignment(preferredAlignment), + UnadjustedAlignment(unadjustedAlignment), + RequiredAlignment(requiredAlignment), CXXInfo(new CXXRecordLayoutInfo) { + // NOTE(cir): Clang does a far more elaborate append here by leveraging the + // custom ASTVector class. For now, we'll do a simple append. + FieldOffsets.insert(FieldOffsets.end(), fieldoffsets.begin(), + fieldoffsets.end()); + + assert(!PrimaryBase && "Layout for class with inheritance is NYI"); + // CXXInfo->PrimaryBase.setPointer(PrimaryBase); + assert(!IsPrimaryBaseVirtual && "Layout for virtual base class is NYI"); + // CXXInfo->PrimaryBase.setInt(IsPrimaryBaseVirtual); + CXXInfo->NonVirtualSize = nonvirtualsize; + CXXInfo->NonVirtualAlignment = nonvirtualalignment; + CXXInfo->PreferredNVAlignment = preferrednvalignment; + CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject; + // FIXME(cir): I'm assuming that since we are not dealing with inherited + // classes yet, removing the following lines will be ok. + // CXXInfo->BaseOffsets = BaseOffsets; + // CXXInfo->VBaseOffsets = VBaseOffsets; + CXXInfo->HasOwnVFPtr = hasOwnVFPtr; + CXXInfo->VBPtrOffset = vbptroffset; + CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr; + // FIXME(cir): Probably not necessary for now. + // CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr; + CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject; + CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase; +} } // namespace cir } // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.h index 4ba672da9b43..b282f32f8a9d 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRRecordLayout.h @@ -14,17 +14,122 @@ #ifndef LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRRECORDLAYOUT_H #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRRECORDLAYOUT_H +#include "mlir/IR/Types.h" +#include "mlir/Support/LLVM.h" +#include "clang/AST/CharUnits.h" +#include +#include + namespace mlir { namespace cir { class CIRLowerContext; +// FIXME(cir): Perhaps this logic can be moved to the CIR dialect, specifically +// the data layout abstractions. + /// This class contains layout information for one RecordDecl, which is a /// struct/union/class. The decl represented must be a definition, not a /// forward declaration. This class is also used to contain layout information /// for one ObjCInterfaceDecl. class CIRRecordLayout { - CIRRecordLayout(); + +private: + friend class CIRLowerContext; + + /// Size of record in characters. + clang::CharUnits Size; + + /// Size of record in characters without tail padding. + clang::CharUnits DataSize; + + // Alignment of record in characters. + clang::CharUnits Alignment; + + // Preferred alignment of record in characters. This can be different than + // Alignment in cases where it is beneficial for performance or backwards + // compatibility preserving (e.g. AIX-ABI). + clang::CharUnits PreferredAlignment; + + // Maximum of the alignments of the record members in characters. + clang::CharUnits UnadjustedAlignment; + + /// The required alignment of the object. In the MS-ABI the + /// __declspec(align()) trumps #pramga pack and must always be obeyed. + clang::CharUnits RequiredAlignment; + + /// Array of field offsets in bits. + /// FIXME(cir): Create a custom CIRVector instead? + std::vector FieldOffsets; + + struct CXXRecordLayoutInfo { + /// The non-virtual size (in chars) of an object, which is the size of the + /// object without virtual bases. + clang::CharUnits NonVirtualSize; + + /// The non-virtual alignment (in chars) of an object, which is the + /// alignment of the object without virtual bases. + clang::CharUnits NonVirtualAlignment; + + /// The preferred non-virtual alignment (in chars) of an object, which is + /// the preferred alignment of the object without virtual bases. + clang::CharUnits PreferredNVAlignment; + + /// The size of the largest empty subobject (either a base or a member). + /// Will be zero if the class doesn't contain any empty subobjects. + clang::CharUnits SizeOfLargestEmptySubobject; + + /// Virtual base table offset (Microsoft-only). + clang::CharUnits VBPtrOffset; + + /// Does this class provide a virtual function table (vtable in Itanium, + /// vftbl in Microsoft) that is independent from its base classes? + bool HasOwnVFPtr : 1; + + /// Does this class have a vftable that could be extended by a derived + /// class. The class may have inherited this pointer from a primary base + /// class. + bool HasExtendableVFPtr : 1; + + /// True if this class contains a zero sized member or base or a base with a + /// zero sized member or base. Only used for MS-ABI. + bool EndsWithZeroSizedObject : 1; + + /// True if this class is zero sized or first base is zero sized or has this + /// property. Only used for MS-ABI. + bool LeadsWithZeroSizedBase : 1; + }; + + /// CXXInfo - If the record layout is for a C++ record, this will have + /// C++ specific information about the record. + CXXRecordLayoutInfo *CXXInfo = nullptr; + + // Constructor for C++ records. + CIRRecordLayout( + const CIRLowerContext &Ctx, clang::CharUnits size, + clang::CharUnits alignment, clang::CharUnits preferredAlignment, + clang::CharUnits unadjustedAlignment, clang::CharUnits requiredAlignment, + bool hasOwnVFPtr, bool hasExtendableVFPtr, clang::CharUnits vbptroffset, + clang::CharUnits datasize, ArrayRef fieldoffsets, + clang::CharUnits nonvirtualsize, clang::CharUnits nonvirtualalignment, + clang::CharUnits preferrednvalignment, + clang::CharUnits SizeOfLargestEmptySubobject, const Type PrimaryBase, + bool IsPrimaryBaseVirtual, const Type BaseSharingVBPtr, + bool EndsWithZeroSizedObject, bool LeadsWithZeroSizedBase); + + ~CIRRecordLayout() = default; + +public: + /// Get the record alignment in characters. + clang::CharUnits getAlignment() const { return Alignment; } + + /// Get the record size in characters. + clang::CharUnits getSize() const { return Size; } + + /// Get the offset of the given field index, in bits. + uint64_t getFieldOffset(unsigned FieldNo) const { + return FieldOffsets[FieldNo]; + } }; } // namespace cir From 81135dc847b5d72c2636644b263d902e0e29c38c Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 06:06:36 -0300 Subject: [PATCH 10/16] [FIX] Ensure CIRLowerContext owns LangOpts --- .../CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp | 2 +- .../CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp index f7020f37f513..d0ae22aecad6 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp @@ -23,7 +23,7 @@ namespace mlir { namespace cir { -CIRLowerContext::CIRLowerContext(ModuleOp module, clang::LangOptions &LOpts) +CIRLowerContext::CIRLowerContext(ModuleOp module, clang::LangOptions LOpts) : MLIRCtx(module.getContext()), LangOpts(LOpts) {} CIRLowerContext::~CIRLowerContext() {} diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h index a803fb992e74..51f0ffc9d56c 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h @@ -42,7 +42,7 @@ class CIRLowerContext : public llvm::RefCountedBase { /// The language options used to create the AST associated with /// this ASTContext object. - clang::LangOptions &LangOpts; + clang::LangOptions LangOpts; //===--------------------------------------------------------------------===// // Built-in Types @@ -51,7 +51,7 @@ class CIRLowerContext : public llvm::RefCountedBase { Type CharTy; public: - CIRLowerContext(ModuleOp module, clang::LangOptions &LOpts); + CIRLowerContext(ModuleOp module, clang::LangOptions LOpts); CIRLowerContext(const CIRLowerContext &) = delete; CIRLowerContext &operator=(const CIRLowerContext &) = delete; ~CIRLowerContext(); From 870cf35b118d2ea9d6c76cdb2169bb6ea625c376 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 07:34:36 -0300 Subject: [PATCH 11/16] [NFC] Implement missing bits in CIRLowerContext --- .../Dialect/Transforms/TargetLowering/CIRLowerContext.cpp | 5 +++++ .../Dialect/Transforms/TargetLowering/CIRLowerContext.h | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp index d0ae22aecad6..efaaff8892a4 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp @@ -136,6 +136,11 @@ clang::CharUnits CIRLowerContext::toCharUnitsFromBits(int64_t BitSize) const { return clang::CharUnits::fromQuantity(BitSize / getCharWidth()); } +/// Convert a size in characters to a size in characters. +int64_t CIRLowerContext::toBits(clang::CharUnits CharSize) const { + return CharSize.getQuantity() * getCharWidth(); +} + clang::TypeInfoChars CIRLowerContext::getTypeInfoInChars(Type T) const { if (auto arrTy = dyn_cast(T)) llvm_unreachable("NYI"); diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h index 51f0ffc9d56c..758caf2b0233 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRLowerContext_H #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRLowerContext_H +#include "CIRRecordLayout.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Types.h" #include "mlir/Interfaces/DataLayoutInterfaces.h" @@ -69,6 +70,10 @@ class CIRLowerContext : public llvm::RefCountedBase { Type initBuiltinType(clang::BuiltinType::Kind K); public: + const clang::TargetInfo &getTargetInfo() const { return *Target; } + + const clang::LangOptions &getLangOpts() const { return LangOpts; } + MLIRContext *getMLIRContext() const { return MLIRCtx; } //===--------------------------------------------------------------------===// @@ -89,6 +94,9 @@ class CIRLowerContext : public llvm::RefCountedBase { /// Convert a size in bits to a size in characters. clang::CharUnits toCharUnitsFromBits(int64_t BitSize) const; + /// Convert a size in characters to a size in bits. + int64_t toBits(clang::CharUnits CharSize) const; + clang::CharUnits getTypeSizeInChars(Type T) const { // FIXME(cir): We should query MLIR's Datalayout here instead. return getTypeInfoInChars(T).Width; From 34756c42f4c80d25b5cd6acfdc2720858d54a8f4 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 06:17:33 -0300 Subject: [PATCH 12/16] [NFC] Implement RecordLayoutBuilder --- clang/include/clang/CIR/MissingFeatures.h | 36 +- .../TargetLowering/CIRLowerContext.h | 5 + .../TargetLowering/RecordLayoutBuilder.cpp | 637 ++++++++++++++++++ 3 files changed, 675 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index a5e5b4a615fd..abd94a0d9570 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -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; } @@ -205,19 +206,39 @@ struct MissingFeatures { //-- Missing AST queries - static bool recordDeclCanPassInRegisters() { return false; } - static bool recordDeclIsCXXDecl() { 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 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 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; } @@ -234,6 +255,14 @@ 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; } + // 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; } @@ -283,6 +312,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; } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h index 758caf2b0233..5a87f71c2bdc 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.h @@ -110,6 +110,11 @@ class CIRLowerContext : public llvm::RefCountedBase { /// More type predicates useful for type checking/promotion bool isPromotableIntegerType(Type T) const; // C99 6.3.1.1p2 + + /// Get or compute information about the layout of the specified + /// record (struct/union/class) \p D, which indicates its size and field + /// position information. + const CIRRecordLayout &getCIRRecordLayout(const Type D) const; }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/RecordLayoutBuilder.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/RecordLayoutBuilder.cpp index 8f606940702f..2f947c5143ef 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/RecordLayoutBuilder.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/RecordLayoutBuilder.cpp @@ -10,3 +10,640 @@ // queries are adapted to operate on the CIR dialect, however. // //===----------------------------------------------------------------------===// + +#include "CIRLowerContext.h" +#include "CIRRecordLayout.h" +#include "mlir/IR/Types.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace mlir; +using namespace mlir::cir; + +namespace { + +//===-----------------------------------------------------------------------==// +// EmptySubobjectMap Implementation +//===----------------------------------------------------------------------===// + +/// Keeps track of which empty subobjects exist at different offsets while +/// laying out a C++ class. +class EmptySubobjectMap { + const CIRLowerContext &Context; + uint64_t CharWidth; + + /// The class whose empty entries we're keeping track of. + const StructType Class; + + /// The highest offset known to contain an empty base subobject. + clang::CharUnits MaxEmptyClassOffset; + + /// Compute the size of the largest base or member subobject that is empty. + void ComputeEmptySubobjectSizes(); + +public: + /// This holds the size of the largest empty subobject (either a base + /// or a member). Will be zero if the record being built doesn't contain + /// any empty classes. + clang::CharUnits SizeOfLargestEmptySubobject; + + EmptySubobjectMap(const CIRLowerContext &Context, const StructType Class) + : Context(Context), CharWidth(Context.getCharWidth()), Class(Class) { + ComputeEmptySubobjectSizes(); + } + + /// Return whether a field can be placed at the given offset. + bool canPlaceFieldAtOffset(const Type Ty, clang::CharUnits Offset); +}; + +void EmptySubobjectMap::ComputeEmptySubobjectSizes() { + // Check the bases. + assert(!::cir::MissingFeatures::getCXXRecordBases()); + + // Check the fields. + for (const auto FT : Class.getMembers()) { + assert(!::cir::MissingFeatures::qualifiedTypes()); + const auto RT = dyn_cast(FT); + + // We only care about record types. + if (!RT) + continue; + + // TODO(cir): Handle nested record types. + llvm_unreachable("NYI"); + } +} + +bool EmptySubobjectMap::canPlaceFieldAtOffset(const Type Ty, + clang::CharUnits Offset) { + llvm_unreachable("NYI"); +} + +//===-----------------------------------------------------------------------==// +// ItaniumRecordLayoutBuilder Implementation +//===----------------------------------------------------------------------===// + +class ItaniumRecordLayoutBuilder { +protected: + // FIXME(cir): Remove this and make the appropriate fields public. + friend class mlir::cir::CIRLowerContext; + + const CIRLowerContext &Context; + + EmptySubobjectMap *EmptySubobjects; + + /// Size - The current size of the record layout. + uint64_t Size; + + /// Alignment - The current alignment of the record layout. + clang::CharUnits Alignment; + + /// PreferredAlignment - The preferred alignment of the record layout. + clang::CharUnits PreferredAlignment; + + /// The alignment if attribute packed is not used. + clang::CharUnits UnpackedAlignment; + + /// \brief The maximum of the alignments of top-level members. + clang::CharUnits UnadjustedAlignment; + + SmallVector FieldOffsets; + + /// Whether the external AST source has provided a layout for this + /// record. + unsigned UseExternalLayout : 1; + + /// Whether we need to infer alignment, even when we have an + /// externally-provided layout. + unsigned InferAlignment : 1; + + /// Packed - Whether the record is packed or not. + unsigned Packed : 1; + + unsigned IsUnion : 1; + + unsigned IsMac68kAlign : 1; + + unsigned IsNaturalAlign : 1; + + unsigned IsMsStruct : 1; + + /// UnfilledBitsInLastUnit - If the last field laid out was a bitfield, + /// this contains the number of bits in the last unit that can be used for + /// an adjacent bitfield if necessary. The unit in question is usually + /// a byte, but larger units are used if IsMsStruct. + unsigned char UnfilledBitsInLastUnit; + + /// LastBitfieldStorageUnitSize - If IsMsStruct, represents the size of the + /// storage unit of the previous field if it was a bitfield. + unsigned char LastBitfieldStorageUnitSize; + + /// MaxFieldAlignment - The maximum allowed field alignment. This is set by + /// #pragma pack. + clang::CharUnits MaxFieldAlignment; + + /// DataSize - The data size of the record being laid out. + uint64_t DataSize; + + clang::CharUnits NonVirtualSize; + clang::CharUnits NonVirtualAlignment; + clang::CharUnits PreferredNVAlignment; + + /// If we've laid out a field but not included its tail padding in Size yet, + /// this is the size up to the end of that field. + clang::CharUnits PaddedFieldSize; + + /// The primary base class (if one exists) of the class we're laying out. + const StructType PrimaryBase; + + /// Whether the primary base of the class we're laying out is virtual. + bool PrimaryBaseIsVirtual; + + /// Whether the class provides its own vtable/vftbl pointer, as opposed to + /// inheriting one from a primary base class. + bool HasOwnVFPtr; + + /// the flag of field offset changing due to packed attribute. + bool HasPackedField; + + /// An auxiliary field used for AIX. When there are OverlappingEmptyFields + /// existing in the aggregate, the flag shows if the following first non-empty + /// or empty-but-non-overlapping field has been handled, if any. + bool HandledFirstNonOverlappingEmptyField; + +public: + ItaniumRecordLayoutBuilder(const CIRLowerContext &Context, + EmptySubobjectMap *EmptySubobjects) + : Context(Context), EmptySubobjects(EmptySubobjects), Size(0), + Alignment(clang::CharUnits::One()), + PreferredAlignment(clang::CharUnits::One()), + UnpackedAlignment(clang::CharUnits::One()), + UnadjustedAlignment(clang::CharUnits::One()), UseExternalLayout(false), + InferAlignment(false), Packed(false), IsUnion(false), + IsMac68kAlign(false), + IsNaturalAlign(!Context.getTargetInfo().getTriple().isOSAIX()), + IsMsStruct(false), UnfilledBitsInLastUnit(0), + LastBitfieldStorageUnitSize(0), + MaxFieldAlignment(clang::CharUnits::Zero()), DataSize(0), + NonVirtualSize(clang::CharUnits::Zero()), + NonVirtualAlignment(clang::CharUnits::One()), + PreferredNVAlignment(clang::CharUnits::One()), + PaddedFieldSize(clang::CharUnits::Zero()), PrimaryBaseIsVirtual(false), + HasOwnVFPtr(false), HasPackedField(false), + HandledFirstNonOverlappingEmptyField(false) {} + + void layout(const StructType D); + + void layoutFields(const StructType D); + void layoutField(const Type Ty, bool InsertExtraPadding); + + void UpdateAlignment(clang::CharUnits NewAlignment, + clang::CharUnits UnpackedNewAlignment, + clang::CharUnits PreferredAlignment); + + void checkFieldPadding(uint64_t Offset, uint64_t UnpaddedOffset, + uint64_t UnpackedOffset, unsigned UnpackedAlign, + bool isPacked, const Type Ty); + + clang::CharUnits getSize() const { + assert(Size % Context.getCharWidth() == 0); + return Context.toCharUnitsFromBits(Size); + } + uint64_t getSizeInBits() const { return Size; } + + void setSize(clang::CharUnits NewSize) { Size = Context.toBits(NewSize); } + void setSize(uint64_t NewSize) { Size = NewSize; } + + clang::CharUnits getDataSize() const { + assert(DataSize % Context.getCharWidth() == 0); + return Context.toCharUnitsFromBits(DataSize); + } + + /// Initialize record layout for the given record decl. + void initializeLayout(const Type Ty); + + uint64_t getDataSizeInBits() const { return DataSize; } + + void setDataSize(clang::CharUnits NewSize) { + DataSize = Context.toBits(NewSize); + } + void setDataSize(uint64_t NewSize) { DataSize = NewSize; } +}; + +void ItaniumRecordLayoutBuilder::layout(const StructType RT) { + initializeLayout(RT); + + // Lay out the vtable and the non-virtual bases. + assert(!::cir::MissingFeatures::isCXXRecordDecl() && + !::cir::MissingFeatures::CXXRecordIsDynamicClass()); + + layoutFields(RT); + + // NonVirtualSize = Context.toCharUnitsFromBits( + // llvm::alignTo(getSizeInBits(), + // Context.getTargetInfo().getCharAlign())); + // NonVirtualAlignment = Alignment; + // PreferredNVAlignment = PreferredAlignment; + + // // Lay out the virtual bases and add the primary virtual base offsets. + // LayoutVirtualBases(RD, RD); + + // // Finally, round the size of the total struct up to the alignment + // // of the struct itself. + // FinishLayout(RD); +} + +void ItaniumRecordLayoutBuilder::initializeLayout(const mlir::Type Ty) { + if (const auto RT = dyn_cast(Ty)) { + IsUnion = RT.isUnion(); + assert(!::cir::MissingFeatures::recordDeclIsMSStruct()); + } + + assert(!::cir::MissingFeatures::recordDeclIsPacked()); + + // Honor the default struct packing maximum alignment flag. + if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct) { + llvm_unreachable("NYI"); + } + + // mac68k alignment supersedes maximum field alignment and attribute aligned, + // and forces all structures to have 2-byte alignment. The IBM docs on it + // allude to additional (more complicated) semantics, especially with regard + // to bit-fields, but gcc appears not to follow that. + if (::cir::MissingFeatures::declHasAlignMac68kAttr()) { + llvm_unreachable("NYI"); + } else { + if (::cir::MissingFeatures::declHasAlignNaturalAttr()) + llvm_unreachable("NYI"); + + if (::cir::MissingFeatures::declHasMaxFieldAlignmentAttr()) + llvm_unreachable("NYI"); + + if (::cir::MissingFeatures::declGetMaxAlignment()) + llvm_unreachable("NYI"); + } + + HandledFirstNonOverlappingEmptyField = + !Context.getTargetInfo().defaultsToAIXPowerAlignment() || IsNaturalAlign; + + // If there is an external AST source, ask it for the various offsets. + if (const auto RT = dyn_cast(Ty)) { + if (::cir::MissingFeatures::astContextGetExternalSource()) { + llvm_unreachable("NYI"); + } + } +} + +void ItaniumRecordLayoutBuilder::layoutField(const Type D, + bool InsertExtraPadding) { + // auto FieldClass = D.dyn_cast(); + assert(!::cir::MissingFeatures::fieldDeclIsPotentiallyOverlapping() && + !::cir::MissingFeatures::CXXRecordDeclIsEmptyCXX11()); + bool IsOverlappingEmptyField = false; // FIXME(cir): Needs more features. + + clang::CharUnits FieldOffset = (IsUnion || IsOverlappingEmptyField) + ? clang::CharUnits::Zero() + : getDataSize(); + + const bool DefaultsToAIXPowerAlignment = + Context.getTargetInfo().defaultsToAIXPowerAlignment(); + bool FoundFirstNonOverlappingEmptyFieldForAIX = false; + if (DefaultsToAIXPowerAlignment && !HandledFirstNonOverlappingEmptyField) { + llvm_unreachable("NYI"); + } + + assert(!::cir::MissingFeatures::fieldDeclIsBitfield()); + + uint64_t UnpaddedFieldOffset = getDataSizeInBits() - UnfilledBitsInLastUnit; + // Reset the unfilled bits. + UnfilledBitsInLastUnit = 0; + LastBitfieldStorageUnitSize = 0; + + llvm::Triple Target = Context.getTargetInfo().getTriple(); + + clang::AlignRequirementKind AlignRequirement = + clang::AlignRequirementKind::None; + clang::CharUnits FieldSize; + clang::CharUnits FieldAlign; + // The amount of this class's dsize occupied by the field. + // This is equal to FieldSize unless we're permitted to pack + // into the field's tail padding. + clang::CharUnits EffectiveFieldSize; + + auto setDeclInfo = [&](bool IsIncompleteArrayType) { + auto TI = Context.getTypeInfoInChars(D); + FieldAlign = TI.Align; + // Flexible array members don't have any size, but they have to be + // aligned appropriately for their element type. + EffectiveFieldSize = FieldSize = + IsIncompleteArrayType ? clang::CharUnits::Zero() : TI.Width; + AlignRequirement = TI.AlignRequirement; + }; + + if (isa(D) && cast(D).getSize() == 0) { + llvm_unreachable("NYI"); + } else { + setDeclInfo(false /* IsIncompleteArrayType */); + + if (::cir::MissingFeatures::fieldDeclIsPotentiallyOverlapping()) + llvm_unreachable("NYI"); + + if (IsMsStruct) + llvm_unreachable("NYI"); + } + + assert(!::cir::MissingFeatures::recordDeclIsPacked() && + !::cir::MissingFeatures::CXXRecordDeclIsPOD()); + bool FieldPacked = false; // FIXME(cir): Needs more features. + + // When used as part of a typedef, or together with a 'packed' attribute, the + // 'aligned' attribute can be used to decrease alignment. In that case, it + // overrides any computed alignment we have, and there is no need to upgrade + // the alignment. + auto alignedAttrCanDecreaseAIXAlignment = [AlignRequirement, FieldPacked] { + // Enum alignment sources can be safely ignored here, because this only + // helps decide whether we need the AIX alignment upgrade, which only + // applies to floating-point types. + return AlignRequirement == clang::AlignRequirementKind::RequiredByTypedef || + (AlignRequirement == clang::AlignRequirementKind::RequiredByRecord && + FieldPacked); + }; + + // The AIX `power` alignment rules apply the natural alignment of the + // "first member" if it is of a floating-point data type (or is an aggregate + // whose recursively "first" member or element is such a type). The alignment + // associated with these types for subsequent members use an alignment value + // where the floating-point data type is considered to have 4-byte alignment. + // + // For the purposes of the foregoing: vtable pointers, non-empty base classes, + // and zero-width bit-fields count as prior members; members of empty class + // types marked `no_unique_address` are not considered to be prior members. + clang::CharUnits PreferredAlign = FieldAlign; + if (DefaultsToAIXPowerAlignment && !alignedAttrCanDecreaseAIXAlignment() && + (FoundFirstNonOverlappingEmptyFieldForAIX || IsNaturalAlign)) { + llvm_unreachable("NYI"); + } + + // The align if the field is not packed. This is to check if the attribute + // was unnecessary (-Wpacked). + clang::CharUnits UnpackedFieldAlign = FieldAlign; + clang::CharUnits PackedFieldAlign = clang::CharUnits::One(); + clang::CharUnits UnpackedFieldOffset = FieldOffset; + // clang::CharUnits OriginalFieldAlign = UnpackedFieldAlign; + + assert(!::cir::MissingFeatures::fieldDeclGetMaxFieldAlignment()); + clang::CharUnits MaxAlignmentInChars = clang::CharUnits::Zero(); + PackedFieldAlign = std::max(PackedFieldAlign, MaxAlignmentInChars); + PreferredAlign = std::max(PreferredAlign, MaxAlignmentInChars); + UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars); + + // The maximum field alignment overrides the aligned attribute. + if (!MaxFieldAlignment.isZero()) { + llvm_unreachable("NYI"); + } + + if (!FieldPacked) + FieldAlign = UnpackedFieldAlign; + if (DefaultsToAIXPowerAlignment) + llvm_unreachable("NYI"); + if (FieldPacked) { + llvm_unreachable("NYI"); + } + + clang::CharUnits AlignTo = + !DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign; + // Round up the current record size to the field's alignment boundary. + FieldOffset = FieldOffset.alignTo(AlignTo); + UnpackedFieldOffset = UnpackedFieldOffset.alignTo(UnpackedFieldAlign); + + if (UseExternalLayout) { + llvm_unreachable("NYI"); + } else { + if (!IsUnion && EmptySubobjects) { + // Check if we can place the field at this offset. + while (/*!EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset)*/ + false) { + llvm_unreachable("NYI"); + } + } + } + + // Place this field at the current location. + FieldOffsets.push_back(Context.toBits(FieldOffset)); + + if (!UseExternalLayout) + checkFieldPadding(Context.toBits(FieldOffset), UnpaddedFieldOffset, + Context.toBits(UnpackedFieldOffset), + Context.toBits(UnpackedFieldAlign), FieldPacked, D); + + if (InsertExtraPadding) { + llvm_unreachable("NYI"); + } + + // Reserve space for this field. + if (!IsOverlappingEmptyField) { + // uint64_t EffectiveFieldSizeInBits = Context.toBits(EffectiveFieldSize); + if (IsUnion) + llvm_unreachable("NYI"); + else + setDataSize(FieldOffset + EffectiveFieldSize); + + PaddedFieldSize = std::max(PaddedFieldSize, FieldOffset + FieldSize); + setSize(std::max(getSizeInBits(), getDataSizeInBits())); + } else { + llvm_unreachable("NYI"); + } + + // Remember max struct/class ABI-specified alignment. + UnadjustedAlignment = std::max(UnadjustedAlignment, FieldAlign); + UpdateAlignment(FieldAlign, UnpackedFieldAlign, PreferredAlign); + + // For checking the alignment of inner fields against + // the alignment of its parent record. + // FIXME(cir): We need to track the parent record of the current type being + // laid out. A regular mlir::Type has not way of doing this. In fact, we will + // likely need an external abstraction, as I don't think this is possible with + // just the field type. + assert(!::cir::MissingFeatures::fieldDeclAbstraction()); + + if (Packed && !FieldPacked && PackedFieldAlign < FieldAlign) + llvm_unreachable("NYI"); +} + +void ItaniumRecordLayoutBuilder::layoutFields(const StructType D) { + // Layout each field, for now, just sequentially, respecting alignment. In + // the future, this will need to be tweakable by targets. + assert(!::cir::MissingFeatures::recordDeclMayInsertExtraPadding() && + !Context.getLangOpts().SanitizeAddressFieldPadding); + bool InsertExtraPadding = false; + assert(!::cir::MissingFeatures::recordDeclHasFlexibleArrayMember()); + bool HasFlexibleArrayMember = false; + for (const auto FT : D.getMembers()) { + layoutField(FT, InsertExtraPadding && (FT != D.getMembers().back() || + !HasFlexibleArrayMember)); + } +} + +void ItaniumRecordLayoutBuilder::UpdateAlignment( + clang::CharUnits NewAlignment, clang::CharUnits UnpackedNewAlignment, + clang::CharUnits PreferredNewAlignment) { + // The alignment is not modified when using 'mac68k' alignment or when + // we have an externally-supplied layout that also provides overall alignment. + if (IsMac68kAlign || (UseExternalLayout && !InferAlignment)) + return; + + if (NewAlignment > Alignment) { + assert(llvm::isPowerOf2_64(NewAlignment.getQuantity()) && + "Alignment not a power of 2"); + Alignment = NewAlignment; + } + + if (UnpackedNewAlignment > UnpackedAlignment) { + assert(llvm::isPowerOf2_64(UnpackedNewAlignment.getQuantity()) && + "Alignment not a power of 2"); + UnpackedAlignment = UnpackedNewAlignment; + } + + if (PreferredNewAlignment > PreferredAlignment) { + assert(llvm::isPowerOf2_64(PreferredNewAlignment.getQuantity()) && + "Alignment not a power of 2"); + PreferredAlignment = PreferredNewAlignment; + } +} + +void ItaniumRecordLayoutBuilder::checkFieldPadding( + uint64_t Offset, uint64_t UnpaddedOffset, uint64_t UnpackedOffset, + unsigned UnpackedAlign, bool isPacked, const Type Ty) { + // We let objc ivars without warning, objc interfaces generally are not used + // for padding tricks. + if (::cir::MissingFeatures::objCIvarDecls()) + llvm_unreachable("NYI"); + + // FIXME(cir): Should the following be skiped in CIR? + // Don't warn about structs created without a SourceLocation. This can + // be done by clients of the AST, such as codegen. + + unsigned CharBitNum = Context.getTargetInfo().getCharWidth(); + + // Warn if padding was introduced to the struct/class. + if (!IsUnion && Offset > UnpaddedOffset) { + unsigned PadSize = Offset - UnpaddedOffset; + // bool InBits = true; + if (PadSize % CharBitNum == 0) { + PadSize = PadSize / CharBitNum; + // InBits = false; + } + assert(::cir::MissingFeatures::bitFieldPaddingDiagnostics()); + } + if (isPacked && Offset != UnpackedOffset) { + HasPackedField = true; + } +} + +//===-----------------------------------------------------------------------==// +// Misc. Helper Functions +//===----------------------------------------------------------------------===// + +bool isMsLayout(const CIRLowerContext &Context) { + return Context.getTargetInfo().getCXXABI().isMicrosoft(); +} + +/// Does the target C++ ABI require us to skip over the tail-padding +/// of the given class (considering it as a base class) when allocating +/// objects? +static bool mustSkipTailPadding(clang::TargetCXXABI ABI, const StructType RD) { + assert(!::cir::MissingFeatures::recordDeclIsCXXDecl()); + switch (ABI.getTailPaddingUseRules()) { + case clang::TargetCXXABI::AlwaysUseTailPadding: + return false; + + case clang::TargetCXXABI::UseTailPaddingUnlessPOD03: + // FIXME: To the extent that this is meant to cover the Itanium ABI + // rules, we should implement the restrictions about over-sized + // bitfields: + // + // http://itanium-cxx-abi.github.io/cxx-abi/abi.html#POD : + // In general, a type is considered a POD for the purposes of + // layout if it is a POD type (in the sense of ISO C++ + // [basic.types]). However, a POD-struct or POD-union (in the + // sense of ISO C++ [class]) with a bitfield member whose + // declared width is wider than the declared type of the + // bitfield is not a POD for the purpose of layout. Similarly, + // an array type is not a POD for the purpose of layout if the + // element type of the array is not a POD for the purpose of + // layout. + // + // Where references to the ISO C++ are made in this paragraph, + // the Technical Corrigendum 1 version of the standard is + // intended. + // FIXME(cir): This always returns true since we can't check if a CIR record + // is a POD type. + assert(!::cir::MissingFeatures::CXXRecordDeclIsPOD()); + return true; + + case clang::TargetCXXABI::UseTailPaddingUnlessPOD11: + // This is equivalent to RD->getTypeForDecl().isCXX11PODType(), + // but with a lot of abstraction penalty stripped off. This does + // assume that these properties are set correctly even in C++98 + // mode; fortunately, that is true because we want to assign + // consistently semantics to the type-traits intrinsics (or at + // least as many of them as possible). + llvm_unreachable("NYI"); + } + + llvm_unreachable("bad tail-padding use kind"); +} + +} // namespace + +/// Get or compute information about the layout of the specified record +/// (struct/union/class), which indicates its size and field position +/// information. +const CIRRecordLayout &CIRLowerContext::getCIRRecordLayout(const Type D) const { + assert(isa(D) && "Not a record type"); + auto RT = dyn_cast(D); + + assert(RT.isComplete() && "Cannot get layout of forward declarations!"); + + // FIXME(cir): Cache the layout. Also, use a more MLIR-based approach. + + const CIRRecordLayout *NewEntry = nullptr; + + if (isMsLayout(*this)) { + llvm_unreachable("NYI"); + } else { + // FIXME(cir): Add if-else separating C and C++ records. + assert(!::cir::MissingFeatures::isCXXRecordDecl()); + EmptySubobjectMap EmptySubobjects(*this, RT); + ItaniumRecordLayoutBuilder Builder(*this, &EmptySubobjects); + Builder.layout(RT); + + // In certain situations, we are allowed to lay out objects in the + // tail-padding of base classes. This is ABI-dependent. + // FIXME: this should be stored in the record layout. + bool skipTailPadding = mustSkipTailPadding(getTargetInfo().getCXXABI(), RT); + + // FIXME: This should be done in FinalizeLayout. + clang::CharUnits DataSize = + skipTailPadding ? Builder.getSize() : Builder.getDataSize(); + clang::CharUnits NonVirtualSize = + skipTailPadding ? DataSize : Builder.NonVirtualSize; + assert(!::cir::MissingFeatures::CXXRecordIsDynamicClass()); + // FIXME(cir): Whose responsible for freeing the allocation below? + NewEntry = new CIRRecordLayout( + *this, Builder.getSize(), Builder.Alignment, Builder.PreferredAlignment, + Builder.UnadjustedAlignment, + /*RequiredAlignment : used by MS-ABI)*/ + Builder.Alignment, Builder.HasOwnVFPtr, /*RD->isDynamicClass()=*/false, + clang::CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets, + NonVirtualSize, Builder.NonVirtualAlignment, + Builder.PreferredNVAlignment, + EmptySubobjects.SizeOfLargestEmptySubobject, Builder.PrimaryBase, + Builder.PrimaryBaseIsVirtual, nullptr, false, false); + } + + // TODO(cir): Cache the layout. + // TODO(cir): Add option to dump the layouts. + + return *NewEntry; +} From 0643695a45ef60c231b1ed2df809af58def746ac Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 07:56:08 -0300 Subject: [PATCH 13/16] [NFC] Refactor bit from CIRDataLayout --- .../clang/CIR/Dialect/IR/CIRDataLayout.h | 32 +++----- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp | 79 +++++++++++-------- 3 files changed, 59 insertions(+), 53 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index 7c9ab5ff9f5a..c580f44a3205 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -19,6 +19,8 @@ namespace cir { +// FIXME(cir): This might be replaced by a CIRDataLayout interface which can +// provide the same functionalities. class CIRDataLayout { bool bigEndian = false; @@ -36,21 +38,9 @@ class CIRDataLayout { bool isBigEndian() const { return bigEndian; } - // `useABI` is `true` if not using prefered alignment. - llvm::Align getAlignment(mlir::Type ty, bool useABI) const { - if (llvm::isa(ty)) { - auto sTy = mlir::cast(ty); - if (sTy.getPacked() && useABI) - return llvm::Align(1); - } else if (llvm::isa(ty)) { - return getAlignment(mlir::cast(ty).getEltType(), - useABI); - } - - uint align = useABI ? layout.getTypeABIAlignment(ty) - : layout.getTypePreferredAlignment(ty); - return llvm::Align(align); - } + + /// Internal helper method that returns requested alignment for type. + llvm::Align getAlignment(mlir::Type Ty, bool abi_or_pref) const; llvm::Align getABITypeAlign(mlir::Type ty) const { return getAlignment(ty, true); @@ -64,9 +54,9 @@ class CIRDataLayout { /// /// For example, returns 5 for i36 and 10 for x86_fp80. llvm::TypeSize getTypeStoreSize(mlir::Type Ty) const { - // FIXME: this is a bit inaccurate, see DataLayout::getTypeStoreSize for - // more information. - return {llvm::divideCeil(layout.getTypeSizeInBits(Ty), 8), false}; + llvm::TypeSize BaseSize = getTypeSizeInBits(Ty); + return {llvm::divideCeil(BaseSize.getKnownMinValue(), 8), + BaseSize.isScalable()}; } /// Returns the offset in bytes between successive objects of the @@ -88,9 +78,9 @@ class CIRDataLayout { return layout.getTypeSizeInBits(Ty); } - llvm::TypeSize 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(Ty) && "Expected pointer type"); diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index abd94a0d9570..74504752e062 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -234,6 +234,7 @@ struct MissingFeatures { static bool typeGetAsBuiltinType() { return false; } static bool typeGetAsEnumType() { return false; } static bool typeIsCXXRecordDecl() { 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. diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index 6c6cc6e61062..d62729d39435 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -1,37 +1,52 @@ #include "clang/CIR/Dialect/IR/CIRDataLayout.h" -#include "mlir/Dialect/DLTI/DLTI.h" -#include "llvm/ADT/StringRef.h" - -namespace cir { - -CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} { - auto dlSpec = mlir::dyn_cast( - modOp->getAttr(mlir::DLTIDialect::kDataLayoutAttrName)); - assert(dlSpec && "expected dl_spec in the module"); - auto entries = dlSpec.getEntries(); - - for (auto entry : entries) { - auto entryKey = entry.getKey(); - auto strKey = mlir::dyn_cast(entryKey); - if (!strKey) - continue; - auto entryName = strKey.strref(); - if (entryName == mlir::DLTIDialect::kDataLayoutEndiannessKey) { - auto value = mlir::dyn_cast(entry.getValue()); - assert(value && "expected string attribute"); - auto endian = value.getValue(); - if (endian == mlir::DLTIDialect::kDataLayoutEndiannessBig) - bigEndian = true; - else if (endian == mlir::DLTIDialect::kDataLayoutEndiannessLittle) - bigEndian = false; - else - llvm_unreachable("unknown endianess"); - } - } -} +#include "clang/CIR/MissingFeatures.h" + +//===----------------------------------------------------------------------===// +// DataLayout Class Implementation +//===----------------------------------------------------------------------===// + +using namespace cir; + +CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} { reset(); } -void CIRDataLayout::reset() { clear(); } +void CIRDataLayout::reset() { + clear(); + + // NOTE(cir): Alignment setter functions are skipped as these should already + // be set in MLIR's data layout. +} void CIRDataLayout::clear() {} -} // namespace cir +/*! + \param abi_or_pref Flag that determines which alignment is returned. true + returns the ABI alignment, false returns the preferred alignment. + \param Ty The underlying type for which alignment is determined. + + Get the ABI (\a abi_or_pref == true) or preferred alignment (\a abi_or_pref + == false) for the requested type \a Ty. + */ +llvm::Align CIRDataLayout::getAlignment(mlir::Type Ty, bool abi_or_pref) const { + + // FIXME(cir): This does not account for differnt address spaces, and relies + // on CIR's data layout to give the proper alignment. + assert(!::cir::MissingFeatures::addressSpace()); + + // Fetch type alignment from MLIR's data layout. + uint align = abi_or_pref ? layout.getTypeABIAlignment(Ty) + : layout.getTypePreferredAlignment(Ty); + return llvm::Align(align); +} + +// 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 CIRDataLayout::getTypeSizeInBits(mlir::Type Ty) const { + assert(!::cir::MissingFeatures::typeIsSized() && + "Cannot getTypeInfo() on a type that is unsized!"); + + // FIXME(cir): This does not account for different address spaces, and relies + // on CIR's data layout to give the proper ABI-specific type width. + assert(!::cir::MissingFeatures::addressSpace()); + + return llvm::TypeSize::getFixed(layout.getTypeSizeInBits(Ty)); +} From 0aeaf1770d2841a038e2bb3bb2df0f7866ce4f74 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 09:53:49 -0300 Subject: [PATCH 14/16] [NFC] Add support for CIR struct layout --- .../clang/CIR/Dialect/IR/CIRDataLayout.h | 67 +++++++ clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp | 170 +++++++++++++++++- 3 files changed, 236 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index c580f44a3205..841f6b8458f6 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -14,16 +14,26 @@ #include "mlir/IR/BuiltinOps.h" #include "clang/CIR/Dialect/IR/CIRTypes.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; @@ -38,6 +48,11 @@ class CIRDataLayout { bool isBigEndian() const { return bigEndian; } + /// 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; @@ -90,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 { + 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 getMemberOffsets() { + return llvm::MutableArrayRef(getTrailingObjects(), + NumElements); + } + + llvm::ArrayRef getMemberOffsets() const { + return llvm::ArrayRef(getTrailingObjects(), 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) const { + return NumElements; + } +}; + } // namespace cir #endif diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 74504752e062..bc55ec99345a 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -234,6 +234,7 @@ struct MissingFeatures { 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; } diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index d62729d39435..64de1df2049b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -1,22 +1,159 @@ #include "clang/CIR/Dialect/IR/CIRDataLayout.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" +using namespace cir; + +//===----------------------------------------------------------------------===// +// Support for StructLayout +//===----------------------------------------------------------------------===// + +StructLayout::StructLayout(mlir::cir::StructType ST, const CIRDataLayout &DL) + : StructSize(llvm::TypeSize::getFixed(0)) { + assert(!ST.isIncomplete() && "Cannot get layout of opaque structs"); + IsPadded = false; + NumElements = ST.getNumElements(); + + // Loop over each of the elements, placing them in memory. + for (unsigned i = 0, e = NumElements; i != e; ++i) { + mlir::Type Ty = ST.getMembers()[i]; + if (i == 0 && ::cir::MissingFeatures::typeIsScalableType()) + llvm_unreachable("Scalable types are not yet supported in CIR"); + + assert(!::cir::MissingFeatures::recordDeclIsPacked() && + "Cannot identify packed structs"); + const llvm::Align TyAlign = DL.getABITypeAlign(Ty); + + // Add padding if necessary to align the data element properly. + // Currently the only structure with scalable size will be the homogeneous + // scalable vector types. Homogeneous scalable vector types have members of + // the same data type so no alignment issue will happen. The condition here + // assumes so and needs to be adjusted if this assumption changes (e.g. we + // support structures with arbitrary scalable data type, or structure that + // contains both fixed size and scalable size data type members). + if (!StructSize.isScalable() && !isAligned(TyAlign, StructSize)) { + IsPadded = true; + StructSize = llvm::TypeSize::getFixed(alignTo(StructSize, TyAlign)); + } + + // Keep track of maximum alignment constraint. + StructAlignment = std::max(TyAlign, StructAlignment); + + getMemberOffsets()[i] = StructSize; + // Consume space for this data item + StructSize += DL.getTypeAllocSize(Ty); + } + + // Add padding to the end of the struct so that it could be put in an array + // and all array elements would be aligned correctly. + if (!StructSize.isScalable() && !isAligned(StructAlignment, StructSize)) { + IsPadded = true; + StructSize = llvm::TypeSize::getFixed(alignTo(StructSize, StructAlignment)); + } +} + +/// getElementContainingOffset - Given a valid offset into the structure, +/// return the structure index that contains it. +unsigned StructLayout::getElementContainingOffset(uint64_t FixedOffset) const { + assert(!StructSize.isScalable() && + "Cannot get element at offset for structure containing scalable " + "vector types"); + llvm::TypeSize Offset = llvm::TypeSize::getFixed(FixedOffset); + llvm::ArrayRef MemberOffsets = getMemberOffsets(); + + const auto *SI = + std::upper_bound(MemberOffsets.begin(), MemberOffsets.end(), Offset, + [](llvm::TypeSize LHS, llvm::TypeSize RHS) -> bool { + return llvm::TypeSize::isKnownLT(LHS, RHS); + }); + assert(SI != MemberOffsets.begin() && "Offset not in structure type!"); + --SI; + assert(llvm::TypeSize::isKnownLE(*SI, Offset) && "upper_bound didn't work"); + assert((SI == MemberOffsets.begin() || + llvm::TypeSize::isKnownLE(*(SI - 1), Offset)) && + (SI + 1 == MemberOffsets.end() || + llvm::TypeSize::isKnownGT(*(SI + 1), Offset)) && + "Upper bound didn't work!"); + + // Multiple fields can have the same offset if any of them are zero sized. + // For example, in { i32, [0 x i32], i32 }, searching for offset 4 will stop + // at the i32 element, because it is the last element at that offset. This is + // the right one to return, because anything after it will have a higher + // offset, implying that this element is non-empty. + return SI - MemberOffsets.begin(); +} + //===----------------------------------------------------------------------===// // DataLayout Class Implementation //===----------------------------------------------------------------------===// -using namespace cir; +namespace { + +class StructLayoutMap { + using LayoutInfoTy = llvm::DenseMap; + LayoutInfoTy LayoutInfo; + +public: + ~StructLayoutMap() { + // Remove any layouts. + for (const auto &I : LayoutInfo) { + StructLayout *Value = I.second; + Value->~StructLayout(); + free(Value); + } + } + + StructLayout *&operator[](mlir::cir::StructType STy) { + return LayoutInfo[STy]; + } +}; + +} // namespace CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} { reset(); } void CIRDataLayout::reset() { clear(); + LayoutMap = nullptr; + bigEndian = false; + // ManglingMode = MM_None; + // NonIntegralAddressSpaces.clear(); + StructAlignment = + llvm::LayoutAlignElem::get(llvm::Align(1), llvm::Align(8), 0); + // NOTE(cir): Alignment setter functions are skipped as these should already // be set in MLIR's data layout. } -void CIRDataLayout::clear() {} +void CIRDataLayout::clear() { + delete static_cast(LayoutMap); + LayoutMap = nullptr; +} + +const StructLayout * +CIRDataLayout::getStructLayout(mlir::cir::StructType Ty) const { + if (!LayoutMap) + LayoutMap = new StructLayoutMap(); + + StructLayoutMap *STM = static_cast(LayoutMap); + StructLayout *&SL = (*STM)[Ty]; + if (SL) + return SL; + + // Otherwise, create the struct layout. Because it is variable length, we + // malloc it, then use placement new. + StructLayout *L = (StructLayout *)llvm::safe_malloc( + StructLayout::totalSizeToAlloc(Ty.getNumElements())); + + // Set SL before calling StructLayout's ctor. The ctor could cause other + // entries to be added to TheMap, invalidating our reference. + SL = L; + + new (L) StructLayout(Ty, *this); + + return L; +} /*! \param abi_or_pref Flag that determines which alignment is returned. true @@ -28,6 +165,19 @@ void CIRDataLayout::clear() {} */ llvm::Align CIRDataLayout::getAlignment(mlir::Type Ty, bool abi_or_pref) const { + if (llvm::isa(Ty)) { + // Packed structure types always have an ABI alignment of one. + if (::cir::MissingFeatures::recordDeclIsPacked() && abi_or_pref) + llvm_unreachable("NYI"); + + // Get the layout annotation... which is lazily created on demand. + const StructLayout *Layout = + getStructLayout(llvm::cast(Ty)); + const llvm::Align Align = + abi_or_pref ? StructAlignment.ABIAlign : StructAlignment.PrefAlign; + return std::max(Align, Layout->getAlignment()); + } + // FIXME(cir): This does not account for differnt address spaces, and relies // on CIR's data layout to give the proper alignment. assert(!::cir::MissingFeatures::addressSpace()); @@ -44,6 +194,22 @@ llvm::TypeSize CIRDataLayout::getTypeSizeInBits(mlir::Type Ty) const { assert(!::cir::MissingFeatures::typeIsSized() && "Cannot getTypeInfo() on a type that is unsized!"); + if (auto structTy = llvm::dyn_cast(Ty)) { + + // FIXME(cir): CIR struct's data layout implementation doesn't do a good job + // of handling unions particularities. We should have a separate union type. + if (structTy.isUnion()) { + auto largestMember = structTy.getLargestMember(layout); + return llvm::TypeSize::getFixed(layout.getTypeSizeInBits(largestMember)); + } + + // FIXME(cir): We should be able to query the size of a struct directly to + // its data layout implementation instead of requiring a separate + // StructLayout object. + // Get the layout annotation... which is lazily created on demand. + return getStructLayout(structTy)->getSizeInBits(); + } + // FIXME(cir): This does not account for different address spaces, and relies // on CIR's data layout to give the proper ABI-specific type width. assert(!::cir::MissingFeatures::addressSpace()); From 3a78de3793f77aa608d3fcb055c1c4ae9c27b2cc Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Sun, 11 Aug 2024 10:20:18 -0300 Subject: [PATCH 15/16] [CIR][ABI] Implement basic struct CC lowering for x86_64 This patch adds the necessary bits for unraveling struct arguments and return values for the x86_64 calling convention. --- clang/include/clang/CIR/MissingFeatures.h | 7 +- .../Transforms/TargetLowering/ABIInfoImpl.cpp | 2 +- .../TargetLowering/CIRLowerContext.cpp | 22 ++ .../TargetLowering/LowerFunction.cpp | 367 +++++++++++++++++- .../Transforms/TargetLowering/LowerFunction.h | 9 + .../Transforms/TargetLowering/Targets/X86.cpp | 265 ++++++++++++- .../x86_64/x86_64-call-conv-lowering-pass.cpp | 29 ++ 7 files changed, 691 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index bc55ec99345a..3f56c8fd29e3 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -226,6 +226,7 @@ struct MissingFeatures { static bool isCXXRecordDecl() { return false; } static bool qualTypeIsReferenceType() { 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; } @@ -265,6 +266,10 @@ struct MissingFeatures { // 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; } @@ -301,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. diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp index e4fd449cb38f..041c801dbe2e 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp @@ -26,7 +26,7 @@ bool classifyReturnType(const CIRCXXABI &CXXABI, LowerFunctionInfo &FI, Type Ty = FI.getReturnType(); if (const auto RT = dyn_cast(Ty)) { - llvm_unreachable("NYI"); + assert(!::cir::MissingFeatures::isCXXRecordDecl()); } return CXXABI.classifyReturnType(FI); diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp index efaaff8892a4..57d29643ca3c 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp @@ -52,6 +52,8 @@ clang::TypeInfo CIRLowerContext::getTypeInfoImpl(const Type T) const { auto typeKind = clang::Type::Builtin; if (isa(T)) { typeKind = clang::Type::Builtin; + } else if (isa(T)) { + typeKind = clang::Type::Record; } else { llvm_unreachable("Unhandled type class"); } @@ -92,6 +94,26 @@ clang::TypeInfo CIRLowerContext::getTypeInfoImpl(const Type T) const { llvm_unreachable("Unknown builtin type!"); break; } + case clang::Type::Record: { + const auto RT = dyn_cast(T); + assert(!::cir::MissingFeatures::tagTypeClassAbstraction()); + + // Only handle TagTypes (names types) for now. + assert(RT.getName() && "Anonymous record is NYI"); + + // NOTE(cir): Clang does some hanlding of invalid tagged declarations here. + // Not sure if this is necessary in CIR. + + if (::cir::MissingFeatures::typeGetAsEnumType()) { + llvm_unreachable("NYI"); + } + + const CIRRecordLayout &Layout = getCIRRecordLayout(RT); + Width = toBits(Layout.getSize()); + Align = toBits(Layout.getAlignment()); + assert(!::cir::MissingFeatures::recordDeclHasAlignmentAttr()); + break; + } default: llvm_unreachable("Unhandled type class"); } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp index 136ea500d014..27b515cc9939 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp @@ -32,6 +32,210 @@ using ABIArgInfo = ::cir::ABIArgInfo; namespace mlir { namespace cir { +namespace { + +Value buildAddressAtOffset(LowerFunction &LF, Value addr, + const ABIArgInfo &info) { + if (unsigned offset = info.getDirectOffset()) { + llvm_unreachable("NYI"); + } + return addr; +} + +/// Given a struct pointer that we are accessing some number of bytes out of it, +/// try to gep into the struct to get at its inner goodness. Dive as deep as +/// possible without entering an element with an in-memory size smaller than +/// DstSize. +Value enterStructPointerForCoercedAccess(Value SrcPtr, StructType SrcSTy, + uint64_t DstSize, LowerFunction &CGF) { + // We can't dive into a zero-element struct. + if (SrcSTy.getNumElements() == 0) + llvm_unreachable("NYI"); + + Type FirstElt = SrcSTy.getMembers()[0]; + + // If the first elt is at least as large as what we're looking for, or if the + // first element is the same size as the whole struct, we can enter it. The + // comparison must be made on the store size and not the alloca size. Using + // the alloca size may overstate the size of the load. + uint64_t FirstEltSize = CGF.LM.getDataLayout().getTypeStoreSize(FirstElt); + if (FirstEltSize < DstSize && + FirstEltSize < CGF.LM.getDataLayout().getTypeStoreSize(SrcSTy)) + return SrcPtr; + + llvm_unreachable("NYI"); +} + +/// Create a store to \param Dst from \param Src where the source and +/// destination may have different types. +/// +/// This safely handles the case when the src type is larger than the +/// destination type; the upper bits of the src will be lost. +void createCoercedStore(Value Src, Value Dst, bool DstIsVolatile, + LowerFunction &CGF) { + Type SrcTy = Src.getType(); + Type DstTy = Dst.getType(); + if (SrcTy == DstTy) { + llvm_unreachable("NYI"); + } + + // FIXME(cir): We need a better way to handle datalayout queries. + assert(isa(SrcTy)); + llvm::TypeSize SrcSize = CGF.LM.getDataLayout().getTypeAllocSize(SrcTy); + + if (StructType DstSTy = dyn_cast(DstTy)) { + Dst = enterStructPointerForCoercedAccess(Dst, DstSTy, + SrcSize.getFixedValue(), CGF); + assert(isa(Dst.getType())); + DstTy = cast(Dst.getType()).getPointee(); + } + + PointerType SrcPtrTy = dyn_cast(SrcTy); + PointerType DstPtrTy = dyn_cast(DstTy); + // TODO(cir): Implement address space. + if (SrcPtrTy && DstPtrTy && !::cir::MissingFeatures::addressSpace()) { + llvm_unreachable("NYI"); + } + + // If the source and destination are integer or pointer types, just do an + // extension or truncation to the desired type. + if ((isa(SrcTy) || isa(SrcTy)) && + (isa(DstTy) || isa(DstTy))) { + llvm_unreachable("NYI"); + } + + llvm::TypeSize DstSize = CGF.LM.getDataLayout().getTypeAllocSize(DstTy); + + // If store is legal, just bitcast the src pointer. + assert(!::cir::MissingFeatures::vectorType()); + if (SrcSize.getFixedValue() <= DstSize.getFixedValue()) { + // Dst = Dst.withElementType(SrcTy); + CGF.buildAggregateStore(Src, Dst, DstIsVolatile); + } else { + llvm_unreachable("NYI"); + } +} + +// FIXME(cir): Create a custom rewriter class to abstract this away. +Value createBitcast(Value Src, Type Ty, LowerFunction &LF) { + return LF.getRewriter().create(Src.getLoc(), Ty, CastKind::bitcast, + Src); +} + +/// Coerces a \param Src value to a value of type \param Ty. +/// +/// This safely handles the case when the src type is smaller than the +/// destination type; in this situation the values of bits which not present in +/// the src are undefined. +/// +/// NOTE(cir): This method has partial parity with CGCall's CreateCoercedLoad. +/// Unlike the original codegen, this function does not emit a coerced load +/// since CIR's type checker wouldn't allow it. Instead, it casts the existing +/// ABI-agnostic value to it's ABI-aware counterpart. Nevertheless, we should +/// try to follow the same logic as the original codegen for correctness. +Value createCoercedValue(Value Src, Type Ty, LowerFunction &CGF) { + Type SrcTy = Src.getType(); + + // If SrcTy and Ty are the same, just reuse the exising load. + if (SrcTy == Ty) + return Src; + + // If it is the special boolean case, simply bitcast it. + if ((isa(SrcTy) && isa(Ty)) || + (isa(SrcTy) && isa(Ty))) + return createBitcast(Src, Ty, CGF); + + llvm::TypeSize DstSize = CGF.LM.getDataLayout().getTypeAllocSize(Ty); + + if (auto SrcSTy = dyn_cast(SrcTy)) { + Src = enterStructPointerForCoercedAccess(Src, SrcSTy, + DstSize.getFixedValue(), CGF); + SrcTy = Src.getType(); + } + + llvm::TypeSize SrcSize = CGF.LM.getDataLayout().getTypeAllocSize(SrcTy); + + // If the source and destination are integer or pointer types, just do an + // extension or truncation to the desired type. + if ((isa(Ty) || isa(Ty)) && + (isa(SrcTy) || isa(SrcTy))) { + llvm_unreachable("NYI"); + } + + // If load is legal, just bitcast the src pointer. + if (!SrcSize.isScalable() && !DstSize.isScalable() && + SrcSize.getFixedValue() >= DstSize.getFixedValue()) { + // Generally SrcSize is never greater than DstSize, since this means we are + // losing bits. However, this can happen in cases where the structure has + // additional padding, for example due to a user specified alignment. + // + // FIXME: Assert that we aren't truncating non-padding bits when have access + // to that information. + // Src = Src.withElementType(); + return CGF.buildAggregateBitcast(Src, Ty); + } + + llvm_unreachable("NYI"); +} + +Value emitAddressAtOffset(LowerFunction &LF, Value addr, + const ABIArgInfo &info) { + if (unsigned offset = info.getDirectOffset()) { + llvm_unreachable("NYI"); + } + return addr; +} + +/// After the calling convention is lowered, an ABI-agnostic type might have to +/// be loaded back to its ABI-aware couterpart so it may be returned. If they +/// differ, we have to do a coerced load. A coerced load, which means to load a +/// type to another despite that they represent the same value. The simplest +/// cases can be solved with a mere bitcast. +/// +/// This partially replaces CreateCoercedLoad from the original codegen. +/// However, instead of emitting the load, it emits a cast. +/// +/// FIXME(cir): Improve parity with the original codegen. +Value castReturnValue(Value Src, Type Ty, LowerFunction &LF) { + Type SrcTy = Src.getType(); + + // If SrcTy and Ty are the same, nothing to do. + if (SrcTy == Ty) + return Src; + + // If is the special boolean case, simply bitcast it. + if (isa(SrcTy) && isa(Ty)) + return createBitcast(Src, Ty, LF); + + llvm::TypeSize DstSize = LF.LM.getDataLayout().getTypeAllocSize(Ty); + + // FIXME(cir): Do we need the EnterStructPointerForCoercedAccess routine here? + + llvm::TypeSize SrcSize = LF.LM.getDataLayout().getTypeAllocSize(SrcTy); + + if ((isa(Ty) || isa(Ty)) && + (isa(SrcTy) || isa(SrcTy))) { + llvm_unreachable("NYI"); + } + + // If load is legal, just bitcast the src pointer. + if (!SrcSize.isScalable() && !DstSize.isScalable() && + SrcSize.getFixedValue() >= DstSize.getFixedValue()) { + // Generally SrcSize is never greater than DstSize, since this means we are + // losing bits. However, this can happen in cases where the structure has + // additional padding, for example due to a user specified alignment. + // + // FIXME: Assert that we aren't truncating non-padding bits when have access + // to that information. + return LF.getRewriter().create(Src.getLoc(), Ty, CastKind::bitcast, + Src); + } + + llvm_unreachable("NYI"); +} + +} // namespace + // FIXME(cir): Pass SrcFn and NewFn around instead of having then as attributes. LowerFunction::LowerFunction(LowerModule &LM, PatternRewriter &rewriter, FuncOp srcFn, FuncOp newFn) @@ -140,7 +344,57 @@ LowerFunction::buildFunctionProlog(const LowerFunctionInfo &FI, FuncOp Fn, break; } - llvm_unreachable("NYI"); + assert(!::cir::MissingFeatures::vectorType()); + + // Allocate original argument to be "uncoerced". + // FIXME(cir): We should have a alloca op builder that does not required + // the pointer type to be explicitly passed. + // FIXME(cir): Get the original name of the argument, as well as the + // proper alignment for the given type being allocated. + auto Alloca = rewriter.create( + Fn.getLoc(), rewriter.getType(Ty), Ty, + /*name=*/StringRef(""), + /*alignment=*/rewriter.getI64IntegerAttr(4)); + + Value Ptr = buildAddressAtOffset(*this, Alloca.getResult(), ArgI); + + // Fast-isel and the optimizer generally like scalar values better than + // FCAs, so we flatten them if this is safe to do for this argument. + StructType STy = dyn_cast(ArgI.getCoerceToType()); + if (ArgI.isDirect() && ArgI.getCanBeFlattened() && STy && + STy.getNumElements() > 1) { + llvm_unreachable("NYI"); + } else { + // Simple case, just do a coerced store of the argument into the alloca. + assert(NumIRArgs == 1); + Value AI = Fn.getArgument(FirstIRArg); + // TODO(cir): Set argument name in the new function. + createCoercedStore(AI, Ptr, /*DstIsVolatile=*/false, *this); + } + + // Match to what EmitParamDecl is expecting for this type. + if (::cir::MissingFeatures::evaluationKind()) { + llvm_unreachable("NYI"); + } else { + // FIXME(cir): Should we have an ParamValue abstraction like in the + // original codegen? + ArgVals.push_back(Alloca); + } + + // NOTE(cir): Once we have uncoerced the argument, we should be able to + // RAUW the original argument alloca with the new one. This assumes that + // the argument is used only to be stored in a alloca. + Value arg = SrcFn.getArgument(ArgNo); + assert(arg.hasOneUse()); + for (auto *firstStore : arg.getUsers()) { + assert(isa(firstStore)); + auto argAlloca = cast(firstStore).getAddr(); + rewriter.replaceAllUsesWith(argAlloca, Alloca); + rewriter.eraseOp(firstStore); + rewriter.eraseOp(argAlloca.getDefiningOp()); + } + + break; } default: llvm_unreachable("Unhandled ABIArgInfo::Kind"); @@ -162,6 +416,7 @@ LogicalResult LowerFunction::buildFunctionEpilog(const LowerFunctionInfo &FI) { // NOTE(cir): no-return, naked, and no result functions should be handled in // CIRGen. + Value RV = {}; Type RetTy = FI.getReturnType(); const ABIArgInfo &RetAI = FI.getReturnInfo(); @@ -193,7 +448,21 @@ LogicalResult LowerFunction::buildFunctionEpilog(const LowerFunctionInfo &FI) { return success(); } } else { - llvm_unreachable("NYI"); + // NOTE(cir): Unlike the original codegen, CIR may have multiple return + // statements in the function body. We have to handle this here. + mlir::PatternRewriter::InsertionGuard guard(rewriter); + NewFn->walk([&](ReturnOp returnOp) { + rewriter.setInsertionPoint(returnOp); + + // TODO(cir): I'm not sure if we need this offset here or in CIRGen. + // Perhaps both? For now I'm just ignoring it. + // Value V = emitAddressAtOffset(*this, getResultAlloca(returnOp), + // RetAI); + + RV = castReturnValue(returnOp->getOperand(0), RetAI.getCoerceToType(), + *this); + rewriter.replaceOpWithNewOp(returnOp, RV); + }); } // TODO(cir): Should AutoreleaseResult be handled here? @@ -246,6 +515,33 @@ LogicalResult LowerFunction::generateCode(FuncOp oldFn, FuncOp newFn, return success(); } +void LowerFunction::buildAggregateStore(Value Val, Value Dest, + bool DestIsVolatile) { + // In LLVM codegen: + // Function to store a first-class aggregate into memory. We prefer to + // store the elements rather than the aggregate to be more friendly to + // fast-isel. + assert(mlir::isa(Dest.getType()) && "Storing in a non-pointer!"); + (void)DestIsVolatile; + + // Circumvent CIR's type checking. + Type pointeeTy = mlir::cast(Dest.getType()).getPointee(); + if (Val.getType() != pointeeTy) { + // NOTE(cir): We only bitcast and store if the types have the same size. + assert((LM.getDataLayout().getTypeSizeInBits(Val.getType()) == + LM.getDataLayout().getTypeSizeInBits(pointeeTy)) && + "Incompatible types"); + auto loc = Val.getLoc(); + Val = rewriter.create(loc, pointeeTy, CastKind::bitcast, Val); + } + + rewriter.create(Val.getLoc(), Val, Dest); +} + +Value LowerFunction::buildAggregateBitcast(Value Val, Type DestTy) { + return rewriter.create(Val.getLoc(), DestTy, CastKind::bitcast, Val); +} + /// Rewrite a call operation to abide to the ABI calling convention. /// /// FIXME(cir): This method has partial parity to CodeGenFunction's @@ -436,7 +732,38 @@ Value LowerFunction::rewriteCallOp(const LowerFunctionInfo &CallInfo, break; } - llvm_unreachable("NYI"); + // FIXME: Avoid the conversion through memory if possible. + Value Src = {}; + if (!isa(I->getType())) { + llvm_unreachable("NYI"); + } else { + // NOTE(cir): I'm leaving L/RValue stuff for CIRGen to handle. + Src = *I; + } + + // If the value is offst in memory, apply the offset now. + // FIXME(cir): Is this offset already handled in CIRGen? + Src = emitAddressAtOffset(*this, Src, ArgInfo); + + // Fast-isel and the optimizer generally like scalar values better than + // FCAs, so we flatten them if this is safe to do for this argument. + StructType STy = dyn_cast(ArgInfo.getCoerceToType()); + if (STy && ArgInfo.isDirect() && ArgInfo.getCanBeFlattened()) { + llvm_unreachable("NYI"); + } else { + // In the simple case, just pass the coerced loaded value. + assert(NumIRArgs == 1); + Value Load = createCoercedValue(Src, ArgInfo.getCoerceToType(), *this); + + // FIXME(cir): We should probably handle CMSE non-secure calls here + + // since they are a ARM-specific feature. + if (::cir::MissingFeatures::undef()) + llvm_unreachable("NYI"); + IRCallArgs[FirstIRArg] = Load; + } + + break; } default: llvm::outs() << "Missing ABIArgInfo::Kind: " << ArgInfo.getKind() << "\n"; @@ -519,7 +846,39 @@ Value LowerFunction::rewriteCallOp(const LowerFunctionInfo &CallInfo, } } - llvm_unreachable("NYI"); + // If coercing a fixed vector from a scalable vector for ABI + // compatibility, and the types match, use the llvm.vector.extract + // intrinsic to perform the conversion. + if (::cir::MissingFeatures::vectorType()) { + llvm_unreachable("NYI"); + } + + // FIXME(cir): Use return value slot here. + Value RetVal = callOp.getResult(); + // TODO(cir): Check for volatile return values. + + // NOTE(cir): If the function returns, there should always be a valid + // return value present. Instead of setting the return value here, we + // should have the ReturnValueSlot object set it beforehand. + if (!RetVal) { + RetVal = callOp.getResult(); + // TODO(cir): Check for volatile return values. + } + + // An empty record can overlap other data (if declared with + // no_unique_address); omit the store for such types - as there is no + // actual data to store. + if (dyn_cast(RetTy) && + cast(RetTy).getNumElements() != 0) { + // NOTE(cir): I'm assuming we don't need to change any offsets here. + // Value StorePtr = emitAddressAtOffset(*this, RetVal, RetAI); + RetVal = + createCoercedValue(newCallOp.getResult(), RetVal.getType(), *this); + } + + // NOTE(cir): No need to convert from a temp to an RValue. This is + // done in CIRGen + return RetVal; } default: llvm::errs() << "Unhandled ABIArgInfo kind: " << RetAI.getKind() << "\n"; diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h index 6a892ef79d9f..bd46bcdd1d8b 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h @@ -71,6 +71,15 @@ class LowerFunction { LogicalResult generateCode(FuncOp oldFn, FuncOp newFn, const LowerFunctionInfo &FnInfo); + // Emit the most simple cir.store possible (e.g. a store for a whole + // struct), which can later be broken down in other CIR levels (or prior + // to dialect codegen). + void buildAggregateStore(Value Val, Value Dest, bool DestIsVolatile); + + // Emit a simple bitcast for a coerced aggregate type to convert it from an + // ABI-agnostic to an ABI-aware type. + Value buildAggregateBitcast(Value Val, Type DestTy); + /// Rewrite a call operation to abide to the ABI calling convention. LogicalResult rewriteCallOp(CallOp op, ReturnValueSlot retValSlot = ReturnValueSlot()); diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp index 477ccd312cc4..4a6124ad898a 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp @@ -20,6 +20,19 @@ namespace cir { namespace { +/// \p returns the size in bits of the largest (native) vector for \p AVXLevel. +unsigned getNativeVectorSizeForAVXABI(X86AVXABILevel AVXLevel) { + switch (AVXLevel) { + case X86AVXABILevel::AVX512: + return 512; + case X86AVXABILevel::AVX: + return 256; + case X86AVXABILevel::None: + return 128; + } + llvm_unreachable("Unknown AVXLevel"); +} + /// Return true if the specified [start,end) bit range is known to either be /// off the end of the specified type or being in alignment padding. The user /// type specified is known to be at most 128 bits in size, and have passed @@ -36,7 +49,44 @@ static bool BitsContainNoUserData(Type Ty, unsigned StartBit, unsigned EndBit, if (TySize <= StartBit) return true; - llvm_unreachable("NYI"); + if (auto arrTy = llvm::dyn_cast(Ty)) { + llvm_unreachable("NYI"); + } + + if (auto structTy = llvm::dyn_cast(Ty)) { + const CIRRecordLayout &Layout = Context.getCIRRecordLayout(Ty); + + // If this is a C++ record, check the bases first. + if (::cir::MissingFeatures::isCXXRecordDecl() || + ::cir::MissingFeatures::getCXXRecordBases()) { + llvm_unreachable("NYI"); + } + + // Verify that no field has data that overlaps the region of interest. Yes + // this could be sped up a lot by being smarter about queried fields, + // however we're only looking at structs up to 16 bytes, so we don't care + // much. + unsigned idx = 0; + for (auto type : structTy.getMembers()) { + unsigned FieldOffset = (unsigned)Layout.getFieldOffset(idx); + + // If we found a field after the region we care about, then we're done. + if (FieldOffset >= EndBit) + break; + + unsigned FieldStart = FieldOffset < StartBit ? StartBit - FieldOffset : 0; + if (!BitsContainNoUserData(type, FieldStart, EndBit - FieldOffset, + Context)) + return false; + + ++idx; + } + + // If nothing in this record overlapped the area of interest, we're good. + return true; + } + + return false; } /// Return a floating point type at the specified offset. @@ -53,6 +103,33 @@ Type getFPTypeAtOffset(Type IRType, unsigned IROffset, class X86_64ABIInfo : public ABIInfo { using Class = ::cir::X86ArgClass; + /// Implement the X86_64 ABI merging algorithm. + /// + /// Merge an accumulating classification \arg Accum with a field + /// classification \arg Field. + /// + /// \param Accum - The accumulating classification. This should + /// always be either NoClass or the result of a previous merge + /// call. In addition, this should never be Memory (the caller + /// should just return Memory for the aggregate). + static Class merge(Class Accum, Class Field); + + /// Implement the X86_64 ABI post merging algorithm. + /// + /// Post merger cleanup, reduces a malformed Hi and Lo pair to + /// final MEMORY or SSE classes when necessary. + /// + /// \param AggregateSize - The size of the current aggregate in + /// the classification process. + /// + /// \param Lo - The classification for the parts of the type + /// residing in the low word of the containing object. + /// + /// \param Hi - The classification for the parts of the type + /// residing in the higher words of the containing object. + /// + void postMerge(unsigned AggregateSize, Class &Lo, Class &Hi) const; + /// Determine the x86_64 register classes in which the given type T should be /// passed. /// @@ -88,8 +165,20 @@ class X86_64ABIInfo : public ABIInfo { Type GetINTEGERTypeAtOffset(Type DestTy, unsigned IROffset, Type SourceTy, unsigned SourceOffset) const; + /// The 0.98 ABI revision clarified a lot of ambiguities, + /// unfortunately in ways that were not always consistent with + /// certain previous compilers. In particular, platforms which + /// required strict binary compatibility with older versions of GCC + /// may need to exempt themselves. + bool honorsRevision0_98() const { + return !getTarget().getTriple().isOSDarwin(); + } + + X86AVXABILevel AVXLevel; + public: - X86_64ABIInfo(LowerTypes &CGT, X86AVXABILevel AVXLevel) : ABIInfo(CGT) {} + X86_64ABIInfo(LowerTypes &CGT, X86AVXABILevel AVXLevel) + : ABIInfo(CGT), AVXLevel(AVXLevel) {} ::cir::ABIArgInfo classifyReturnType(Type RetTy) const; @@ -160,6 +249,92 @@ void X86_64ABIInfo::classify(Type Ty, uint64_t OffsetBase, Class &Lo, Class &Hi, } else if (isa(Ty)) { Current = Class::Integer; + } else if (const auto RT = dyn_cast(Ty)) { + uint64_t Size = getContext().getTypeSize(Ty); + + // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger + // than eight eightbytes, ..., it has class MEMORY. + if (Size > 512) + llvm_unreachable("NYI"); + + // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial + // copy constructor or a non-trivial destructor, it is passed by invisible + // reference. + if (getRecordArgABI(RT, getCXXABI())) + llvm_unreachable("NYI"); + + // Assume variable sized types are passed in memory. + if (::cir::MissingFeatures::recordDeclHasFlexibleArrayMember()) + llvm_unreachable("NYI"); + + const auto &Layout = getContext().getCIRRecordLayout(Ty); + + // Reset Lo class, this will be recomputed. + Current = Class::NoClass; + + // If this is a C++ record, classify the bases first. + assert(!::cir::MissingFeatures::isCXXRecordDecl() && + !::cir::MissingFeatures::getCXXRecordBases()); + + // Classify the fields one at a time, merging the results. + bool UseClang11Compat = getContext().getLangOpts().getClangABICompat() <= + clang::LangOptions::ClangABI::Ver11 || + getContext().getTargetInfo().getTriple().isPS(); + bool IsUnion = RT.isUnion() && !UseClang11Compat; + + // FIXME(cir): An interface to handle field declaration might be needed. + assert(!::cir::MissingFeatures::fieldDeclAbstraction()); + for (auto [idx, FT] : llvm::enumerate(RT.getMembers())) { + uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx); + assert(!::cir::MissingFeatures::fieldDeclIsBitfield()); + bool BitField = false; + + // Ignore padding bit-fields. + if (BitField && !::cir::MissingFeatures::fieldDeclisUnnamedBitField()) + llvm_unreachable("NYI"); + + // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger than + // eight eightbytes, or it contains unaligned fields, it has class + // MEMORY. + // + // The only case a 256-bit or a 512-bit wide vector could be used is + // when the struct contains a single 256-bit or 512-bit element. Early + // check and fallback to memory. + // + // FIXME: Extended the Lo and Hi logic properly to work for size wider + // than 128. + if (Size > 128 && ((!IsUnion && Size != getContext().getTypeSize(FT)) || + Size > getNativeVectorSizeForAVXABI(AVXLevel))) { + llvm_unreachable("NYI"); + } + // Note, skip this test for bit-fields, see below. + if (!BitField && Offset % getContext().getTypeAlign(RT)) { + llvm_unreachable("NYI"); + } + + // Classify this field. + // + // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate + // exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class + // NO_CLASS. + Class FieldLo, FieldHi; + + // Bit-fields require special handling, they do not force the + // structure to be passed in memory even if unaligned, and + // therefore they can straddle an eightbyte. + if (BitField) { + llvm_unreachable("NYI"); + } else { + classify(FT, Offset, FieldLo, FieldHi, isNamedArg); + } + Lo = merge(Lo, FieldLo); + Hi = merge(Hi, FieldHi); + if (Lo == Class::Memory || Hi == Class::Memory) + break; + } + + postMerge(Size, Lo, Hi); } else { llvm::outs() << "Missing X86 classification for type " << Ty << "\n"; llvm_unreachable("NYI"); @@ -245,7 +420,15 @@ Type X86_64ABIInfo::GetINTEGERTypeAtOffset(Type DestTy, unsigned IROffset, } if (auto RT = dyn_cast(DestTy)) { - llvm_unreachable("NYI"); + // If this is a struct, recurse into the field at the specified offset. + const ::cir::StructLayout *SL = getDataLayout().getStructLayout(RT); + if (IROffset < SL->getSizeInBytes()) { + unsigned FieldIdx = SL->getElementContainingOffset(IROffset); + IROffset -= SL->getElementOffset(FieldIdx); + + return GetINTEGERTypeAtOffset(RT.getMembers()[FieldIdx], IROffset, + SourceTy, SourceOffset); + } } // Okay, we don't have any better idea of what to pass, so we pass this in @@ -328,7 +511,7 @@ ::cir::ABIArgInfo X86_64ABIInfo::classifyReturnType(Type RetTy) const { if (HighPart) llvm_unreachable("NYI"); - return ABIArgInfo::getDirect(RetTy); + return ABIArgInfo::getDirect(resType); } ABIArgInfo X86_64ABIInfo::classifyArgumentType(Type Ty, unsigned freeIntRegs, @@ -463,6 +646,80 @@ void X86_64ABIInfo::computeInfo(LowerFunctionInfo &FI) const { } } +X86_64ABIInfo::Class X86_64ABIInfo::merge(Class Accum, Class Field) { + // AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is + // classified recursively so that always two fields are + // considered. The resulting class is calculated according to + // the classes of the fields in the eightbyte: + // + // (a) If both classes are equal, this is the resulting class. + // + // (b) If one of the classes is NO_CLASS, the resulting class is + // the other class. + // + // (c) If one of the classes is MEMORY, the result is the MEMORY + // class. + // + // (d) If one of the classes is INTEGER, the result is the + // INTEGER. + // + // (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, + // MEMORY is used as class. + // + // (f) Otherwise class SSE is used. + + // Accum should never be memory (we should have returned) or + // ComplexX87 (because this cannot be passed in a structure). + assert((Accum != Class::Memory && Accum != Class::ComplexX87) && + "Invalid accumulated classification during merge."); + if (Accum == Field || Field == Class::NoClass) + return Accum; + if (Field == Class::Memory) + return Class::Memory; + if (Accum == Class::NoClass) + return Field; + if (Accum == Class::Integer || Field == Class::Integer) + return Class::Integer; + if (Field == Class::X87 || Field == Class::X87Up || + Field == Class::ComplexX87 || Accum == Class::X87 || + Accum == Class::X87Up) + return Class::Memory; + return Class::SSE; +} + +void X86_64ABIInfo::postMerge(unsigned AggregateSize, Class &Lo, + Class &Hi) const { + // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done: + // + // (a) If one of the classes is Memory, the whole argument is passed in + // memory. + // + // (b) If X87UP is not preceded by X87, the whole argument is passed in + // memory. + // + // (c) If the size of the aggregate exceeds two eightbytes and the first + // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole + // argument is passed in memory. NOTE: This is necessary to keep the + // ABI working for processors that don't support the __m256 type. + // + // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE. + // + // Some of these are enforced by the merging logic. Others can arise + // only with unions; for example: + // union { _Complex double; unsigned; } + // + // Note that clauses (b) and (c) were added in 0.98. + // + if (Hi == Class::Memory) + Lo = Class::Memory; + if (Hi == Class::X87Up && Lo != Class::X87 && honorsRevision0_98()) + Lo = Class::Memory; + if (AggregateSize > 128 && (Lo != Class::SSE || Hi != Class::SSEUp)) + Lo = Class::Memory; + if (Hi == Class::SSEUp && Lo != Class::SSE) + Hi = Class::SSE; +} + std::unique_ptr createX86_64TargetLoweringInfo(LowerModule &LM, X86AVXABILevel AVXLevel) { return std::make_unique(LM.getTypes(), AVXLevel); diff --git a/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp b/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp index 6eb1189402fc..48345dfc7c0d 100644 --- a/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp +++ b/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp @@ -87,3 +87,32 @@ double Double(double d) { // cir.call @_Z6Doubled(%{{.+}}) : (!cir.double) -> !cir.double return Double(d); } + + +/// Test call conv lowering for struct type coercion scenarios. /// + +struct S1 { + int a, b; +}; + + +/// Validate coerced argument and cast it to the expected type. + +/// Cast arguments to the expected type. +// CHECK: cir.func @_Z2s12S1(%arg0: !u64i loc({{.+}})) -> !u64i +// CHECK: %[[#V0:]] = cir.alloca !ty_22S122, !cir.ptr +// CHECK: %[[#V1:]] = cir.cast(bitcast, %arg0 : !u64i), !ty_22S122 +// CHECK: cir.store %[[#V1]], %[[#V0]] : !ty_22S122, !cir.ptr +S1 s1(S1 arg) { + + /// Cast argument and result of the function call to the expected types. + // CHECK: %[[#V9:]] = cir.cast(bitcast, %{{.+}} : !ty_22S122), !u64i + // CHECK: %[[#V10:]] = cir.call @_Z2s12S1(%[[#V9]]) : (!u64i) -> !u64i + // CHECK: %[[#V11:]] = cir.cast(bitcast, %[[#V10]] : !u64i), !ty_22S122 + s1({1, 2}); + + // CHECK: %[[#V12:]] = cir.load %{{.+}} : !cir.ptr, !ty_22S122 + // CHECK: %[[#V13:]] = cir.cast(bitcast, %[[#V12]] : !ty_22S122), !u64i + // CHECK: cir.return %[[#V13]] : !u64i + return {1, 2}; +} From c7d323b3af1030a1c771bf6d573259afc6e4aefd Mon Sep 17 00:00:00 2001 From: Vinicius Espindola Date: Sun, 11 Aug 2024 19:45:06 -0300 Subject: [PATCH 16/16] Remove uint usage --- clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp index 64de1df2049b..860f66b10f35 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp @@ -183,8 +183,8 @@ llvm::Align CIRDataLayout::getAlignment(mlir::Type Ty, bool abi_or_pref) const { assert(!::cir::MissingFeatures::addressSpace()); // Fetch type alignment from MLIR's data layout. - uint align = abi_or_pref ? layout.getTypeABIAlignment(Ty) - : layout.getTypePreferredAlignment(Ty); + unsigned align = abi_or_pref ? layout.getTypeABIAlignment(Ty) + : layout.getTypePreferredAlignment(Ty); return llvm::Align(align); }