diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h index 88e030fb424a..e820d11340ae 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h @@ -15,6 +15,7 @@ #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 { @@ -24,6 +25,18 @@ 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); + } + + /// Parse a data layout string (with fallback to default values). + void reset(llvm::StringRef dataLayout); + + // Free all internal data structures. + void clear(); + CIRDataLayout(mlir::ModuleOp modOp); bool isBigEndian() const { return bigEndian; } diff --git a/clang/include/clang/CIR/FnInfoOpts.h b/clang/include/clang/CIR/FnInfoOpts.h new file mode 100644 index 000000000000..cea4d89f4c14 --- /dev/null +++ b/clang/include/clang/CIR/FnInfoOpts.h @@ -0,0 +1,37 @@ +#ifndef CIR_FNINFOOPTS_H +#define CIR_FNINFOOPTS_H + +#include "llvm/ADT/STLForwardCompat.h" + +namespace cir { + +enum class FnInfoOpts { + None = 0, + IsInstanceMethod = 1 << 0, + IsChainCall = 1 << 1, + IsDelegateCall = 1 << 2, +}; + +inline FnInfoOpts operator|(FnInfoOpts A, FnInfoOpts B) { + return static_cast(llvm::to_underlying(A) | + llvm::to_underlying(B)); +} + +inline FnInfoOpts operator&(FnInfoOpts A, FnInfoOpts B) { + return static_cast(llvm::to_underlying(A) & + llvm::to_underlying(B)); +} + +inline FnInfoOpts operator|=(FnInfoOpts A, FnInfoOpts B) { + A = A | B; + return A; +} + +inline FnInfoOpts operator&=(FnInfoOpts A, FnInfoOpts B) { + A = A & B; + return A; +} + +} // namespace cir + +#endif // CIR_FNINFOOPTS_H diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 12255d409a75..e2f554019422 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -156,7 +156,6 @@ struct MissingFeatures { static bool zeroInitializer() { return false; } static bool targetCodeGenInfoIsProtoCallVariadic() { return false; } static bool targetCodeGenInfoGetNullPointer() { return false; } - static bool chainCalls() { return false; } static bool operandBundles() { return false; } static bool exceptions() { return false; } static bool metaDataNode() { return false; } @@ -190,24 +189,50 @@ struct MissingFeatures { //===--- ABI lowering --===// + //-- Missing AST queries + + static bool recordDeclCanPassInRegisters() { return false; } + + //-- Missing types + + static bool vectorType() { return false; } + + //-- Other missing features + + // Calls with a static chain pointer argument may be optimized (p.e. freeing + // up argument registers), but we do not yet track such cases. + static bool chainCall() { return false; } + + // ABI-lowering has special handling for regcall calling convention (tries to + // pass every argument in regs). We don't support it just yet. + static bool regCall() { return false; } + + // Some ABIs (e.g. x86) require special handling for returning large structs + // by value. The sret argument parameter aids in this, but it is current NYI. + static bool sretArgs() { return false; } + + // Inalloca parameter attributes are mostly used for Windows x86_32 ABI. We + // do not yet support this yet. + static bool inallocaArgs() { return false; } + // Parameters may have additional attributes (e.g. [[noescape]]) that affect // the compiler. This is not yet supported in CIR. - static bool extParamInfo() { return true; } + static bool extParamInfo() { return false; } // LangOpts may affect lowering, but we do not carry this information into CIR // just yet. Right now, it only instantiates the default lang options. - static bool langOpts() { return true; } + static bool langOpts() { return false; } // Several type qualifiers are not yet supported in CIR, but important when // evaluating ABI-specific lowering. - static bool qualifiedTypes() { return true; } + static bool qualifiedTypes() { return false; } // We're ignoring several details regarding ABI-halding for Swift. - static bool swift() { return true; } + static bool swift() { return false; } // Despite carrying some information about variadics, we are currently // ignoring this to focus only on the code necessary to lower non-variadics. - static bool variadicFunctions() { return true; } + static bool variadicFunctions() { return false; } }; } // namespace cir diff --git a/clang/include/clang/CIR/Target/x86.h b/clang/include/clang/CIR/Target/x86.h new file mode 100644 index 000000000000..2aa2d0493aac --- /dev/null +++ b/clang/include/clang/CIR/Target/x86.h @@ -0,0 +1,32 @@ +//==-- x86.h - Definitions common to all x86 ABI variants ------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definitions common to any X86 ABI implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef CIR_X86_H +#define CIR_X86_H + +namespace cir { + +// Possible argument classifications according to the x86 ABI documentation. +enum X86ArgClass { + Integer = 0, + SSE, + SSEUp, + X87, + X87Up, + ComplexX87, + NoClass, + Memory +}; + +} // namespace cir + +#endif // CIR_X86_H diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 346cce788d91..60268b72d9dd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -23,6 +23,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/FnInfoOpts.h" #include "llvm/Support/ErrorHandling.h" #include diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 866ba9af7a3b..ea8e9e546352 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -290,13 +290,6 @@ class ReturnValueSlot { Address getAddress() const { return Addr; } }; -enum class FnInfoOpts { - None = 0, - IsInstanceMethod = 1 << 0, - IsChainCall = 1 << 1, - IsDelegateCall = 1 << 2, -}; - } // namespace cir #endif diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 11807eeedd4a..6e751691696c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1435,7 +1435,7 @@ RValue CIRGenFunction::buildCall(clang::QualType CalleeType, // Chain calls use the same code path to add the inviisble chain parameter to // the function type. if (isa(FnType) || Chain) { - assert(!MissingFeatures::chainCalls()); + assert(!MissingFeatures::chainCall()); assert(!MissingFeatures::addressSpace()); auto CalleeTy = getTypes().GetFunctionType(FnInfo); // get non-variadic function type diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index ca4d18461154..3eb2b1b455e3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -16,6 +16,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/AST/RecordLayout.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/FnInfoOpts.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 51350c9ea70e..d1d547f24a9a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -22,6 +22,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/ABI.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/FnInfoOpts.h" #include "llvm/ADT/SmallPtrSet.h" diff --git a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp index 7ab0cf32c25a..26e8cb48da26 100644 --- a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp @@ -748,3 +748,7 @@ CIRDataLayout::CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} { } } } + +void CIRDataLayout::reset(StringRef Desc) { clear(); } + +void CIRDataLayout::clear() {} diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp index 121bc2f023c1..2973a6ce70d3 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.cpp +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -6,6 +6,7 @@ #include "CallingConv.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Target/x86.h" using namespace cir; using namespace clang; @@ -79,16 +80,7 @@ namespace { enum class X86AVXABILevel { None, AVX, AVX512 }; class X86_64ABIInfo : public ABIInfo { - enum Class { - Integer = 0, - SSE, - SSEUp, - X87, - X87Up, - ComplexX87, - NoClass, - Memory - }; + using Class = X86ArgClass; // X86AVXABILevel AVXLevel; // Some ABIs (e.g. X32 ABI and Native Client OS) use 32 bit pointers on 64-bit diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLowering.cpp b/clang/lib/CIR/Dialect/Transforms/CallConvLowering.cpp index 6130367d91a4..ad35d7835255 100644 --- a/clang/lib/CIR/Dialect/Transforms/CallConvLowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CallConvLowering.cpp @@ -45,7 +45,7 @@ LowerModule createLowerModule(FuncOp op, PatternRewriter &rewriter) { // FIXME(cir): This just uses the default language options. We need to account // for custom options. // Create context. - assert(::cir::MissingFeatures::langOpts()); + assert(!::cir::MissingFeatures::langOpts()); clang::LangOptions langOpts; auto context = CIRLowerContext(module.getContext(), langOpts); context.initBuiltinTypes(*targetInfo); @@ -64,11 +64,27 @@ struct CallConvLoweringPattern : public OpRewritePattern { LogicalResult matchAndRewrite(FuncOp op, PatternRewriter &rewriter) const final { + const auto module = op->getParentOfType(); + if (!op.getAst()) return op.emitError("function has no AST information"); LowerModule lowerModule = createLowerModule(op, rewriter); + // Rewrite function calls before definitions. This should be done before + // lowering the definition. + auto calls = op.getSymbolUses(module); + if (calls.has_value()) { + for (auto call : calls.value()) { + auto callOp = cast(call.getUser()); + if (lowerModule.rewriteFunctionCall(callOp, op).failed()) + return failure(); + } + } + + // Rewrite function definition. + // FIXME(cir): This is a workaround to avoid an infinite loop in the driver. + rewriter.replaceOp(op, rewriter.clone(*op)); return success(); } }; diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp index 46a865da0670..6160174191dc 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "ABIInfo.h" +#include "CIRCXXABI.h" +#include "LowerTypes.h" namespace mlir { namespace cir { @@ -19,5 +21,7 @@ namespace cir { // Pin the vtable to this file. ABIInfo::~ABIInfo() = default; +CIRCXXABI &ABIInfo::getCXXABI() const { return LT.getCXXABI(); } + } // namespace cir } // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h index 3fad01f3d7a8..ef5bae6d13fa 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfo.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_ABIINFO_H #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_ABIINFO_H +#include "CIRCXXABI.h" +#include "LowerFunctionInfo.h" #include "llvm/IR/CallingConv.h" namespace mlir { @@ -32,6 +34,10 @@ class ABIInfo { public: ABIInfo(LowerTypes <) : LT(LT), RuntimeCC(llvm::CallingConv::C) {} virtual ~ABIInfo(); + + CIRCXXABI &getCXXABI() const; + + virtual void computeInfo(LowerFunctionInfo &FI) const = 0; }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp index c51176a99b95..ef90698054e8 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.cpp @@ -10,3 +10,25 @@ // adapted to operate on the CIR dialect, however. // //===----------------------------------------------------------------------===// + +#include "ABIInfo.h" +#include "CIRCXXABI.h" +#include "LowerFunctionInfo.h" +#include "llvm/Support/ErrorHandling.h" + +namespace mlir { +namespace cir { + +bool classifyReturnType(const CIRCXXABI &CXXABI, LowerFunctionInfo &FI, + const ABIInfo &Info) { + Type Ty = FI.getReturnType(); + + if (const auto RT = Ty.dyn_cast()) { + llvm_unreachable("NYI"); + } + + return CXXABI.classifyReturnType(FI); +} + +} // 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 f34d7fb07226..d3ee18f0467b 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ABIInfoImpl.h @@ -14,8 +14,17 @@ #ifndef LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_ABIINFOIMPL_H #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_ABIINFOIMPL_H +#include "ABIInfo.h" +#include "CIRCXXABI.h" +#include "LowerFunctionInfo.h" + namespace mlir { -namespace cir {} // namespace cir +namespace cir { + +bool classifyReturnType(const CIRCXXABI &CXXABI, LowerFunctionInfo &FI, + const ABIInfo &Info); + +} // namespace cir } // namespace mlir #endif // LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_ABIINFOIMPL_H diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h index bf5131a074b8..5496dbbf2327 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRCXXABI_H #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRCXXABI_H +#include "LowerFunctionInfo.h" #include "mlir/IR/Value.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" @@ -34,6 +35,10 @@ class CIRCXXABI { public: virtual ~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; }; /// Creates an Itanium-family ABI. diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp index b75893bfb33f..7152ab081ec5 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRLowerContext.cpp @@ -31,7 +31,7 @@ Type CIRLowerContext::initBuiltinType(clang::BuiltinType::Kind K) { Type Ty; // NOTE(cir): Clang does more stuff here. Not sure if we need to do the same. - assert(::cir::MissingFeatures::qualifiedTypes()); + assert(!::cir::MissingFeatures::qualifiedTypes()); switch (K) { case clang::BuiltinType::Char_S: Ty = IntType::get(getMLIRContext(), 8, true); diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRToCIRArgMapping.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRToCIRArgMapping.h index 9c1dae1f3dbf..6481874bf3ab 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRToCIRArgMapping.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRToCIRArgMapping.h @@ -17,8 +17,10 @@ #define LLVM_CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRTOCIRARGMAPPING_H #include "CIRLowerContext.h" -#include "LoweringFunctionInfo.h" +#include "LowerFunctionInfo.h" +#include "clang/CIR/ABIArgInfo.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/ErrorHandling.h" namespace mlir { namespace cir { @@ -49,9 +51,36 @@ class CIRToCIRArgMapping { public: CIRToCIRArgMapping(const CIRLowerContext &context, const LowerFunctionInfo &FI, bool onlyRequiredArgs = false) - : ArgInfo(onlyRequiredArgs ? FI.getNumRequiredArgs() : FI.arg_size()) {}; + : ArgInfo(onlyRequiredArgs ? FI.getNumRequiredArgs() : FI.arg_size()) { + construct(context, FI, onlyRequiredArgs); + }; unsigned totalIRArgs() const { return TotalIRArgs; } + + void construct(const CIRLowerContext &context, const LowerFunctionInfo &FI, + bool onlyRequiredArgs = false) { + unsigned IRArgNo = 0; + const ::cir::ABIArgInfo &RetAI = FI.getReturnInfo(); + + if (RetAI.getKind() == ::cir::ABIArgInfo::Indirect) { + llvm_unreachable("NYI"); + } + + unsigned ArgNo = 0; + unsigned NumArgs = + onlyRequiredArgs ? FI.getNumRequiredArgs() : FI.arg_size(); + for (LowerFunctionInfo::const_arg_iterator _ = FI.arg_begin(); + ArgNo < NumArgs; ++_, ++ArgNo) { + llvm_unreachable("NYI"); + } + assert(ArgNo == ArgInfo.size()); + + if (::cir::MissingFeatures::inallocaArgs()) { + llvm_unreachable("NYI"); + } + + TotalIRArgs = IRArgNo; + } }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp index 8fc404dda582..b39be9ed02e4 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp @@ -32,10 +32,24 @@ class ItaniumCXXABI : public CIRCXXABI { public: ItaniumCXXABI(LowerModule &LM) : CIRCXXABI(LM) {} + + bool classifyReturnType(LowerFunctionInfo &FI) const override; }; } // namespace +bool ItaniumCXXABI::classifyReturnType(LowerFunctionInfo &FI) const { + const StructType RD = FI.getReturnType().dyn_cast(); + if (!RD) + return false; + + // If C++ prohibits us from making a copy, return by address. + if (::cir::MissingFeatures::recordDeclCanPassInRegisters()) + llvm_unreachable("NYI"); + + return false; +} + CIRCXXABI *CreateItaniumCXXABI(LowerModule &LM) { switch (LM.getCXXABIKind()) { case clang::TargetCXXABI::GenericItanium: diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.cpp index e69de29bb2d1..59d736c0574f 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.cpp @@ -0,0 +1,107 @@ +#include "LowerCall.h" +#include "LowerFunctionInfo.h" +#include "LowerTypes.h" +#include "clang/CIR/FnInfoOpts.h" + +using namespace mlir; +using namespace mlir::cir; + +using FnInfoOpts = ::cir::FnInfoOpts; + +namespace { + +/// Arrange a call as unto a free function, except possibly with an +/// additional number of formal parameters considered required. +const LowerFunctionInfo & +arrangeFreeFunctionLikeCall(LowerTypes <, LowerModule &LM, + const OperandRange &args, const FuncType fnType, + unsigned numExtraRequiredArgs, bool chainCall) { + assert(args.size() >= numExtraRequiredArgs); + + assert(!::cir::MissingFeatures::extParamInfo()); + + // In most cases, there are no optional arguments. + RequiredArgs required = RequiredArgs::All; + + // If we have a variadic prototype, the required arguments are the + // extra prefix plus the arguments in the prototype. + // FIXME(cir): Properly check if function is no-proto. + if (/*IsPrototypedFunction=*/true) { + if (fnType.isVarArg()) + llvm_unreachable("NYI"); + + if (::cir::MissingFeatures::extParamInfo()) + llvm_unreachable("NYI"); + } + + // TODO(cir): There's some CC stuff related to no-proto functions here, but + // I'm skipping it since it requires CodeGen info. Maybe we can embbed this + // information in the FuncOp during CIRGen. + + assert(!::cir::MissingFeatures::chainCall() && !chainCall && "NYI"); + FnInfoOpts opts = chainCall ? FnInfoOpts::IsChainCall : FnInfoOpts::None; + return LT.arrangeLLVMFunctionInfo(fnType.getReturnType(), opts, + fnType.getInputs(), required); +} + +} // namespace + +/// Figure out the rules for calling a function with the given formal +/// type using the given arguments. The arguments are necessary +/// because the function might be unprototyped, in which case it's +/// target-dependent in crazy ways. +const LowerFunctionInfo & +LowerTypes::arrangeFreeFunctionCall(const OperandRange args, + const FuncType fnType, bool chainCall) { + return arrangeFreeFunctionLikeCall(*this, LM, args, fnType, chainCall ? 1 : 0, + chainCall); +} + +/// Arrange the argument and result information for an abstract value +/// of a given function type. This is the method which all of the +/// above functions ultimately defer to. +/// +/// \param resultType - ABI-agnostic CIR result type. +/// \param opts - Options to control the arrangement. +/// \param argTypes - ABI-agnostic CIR argument types. +/// \param required - Information about required/optional arguments. +const LowerFunctionInfo & +LowerTypes::arrangeLLVMFunctionInfo(Type resultType, FnInfoOpts opts, + ArrayRef argTypes, + RequiredArgs required) { + assert(!::cir::MissingFeatures::qualifiedTypes()); + + LowerFunctionInfo *FI = nullptr; + + // FIXME(cir): Allow user-defined CCs (e.g. __attribute__((vectorcall))). + assert(!::cir::MissingFeatures::extParamInfo()); + unsigned CC = clangCallConvToLLVMCallConv(clang::CallingConv::CC_C); + + // Construct the function info. We co-allocate the ArgInfos. + // NOTE(cir): This initial function info might hold incorrect data. + FI = LowerFunctionInfo::create( + CC, /*isInstanceMethod=*/false, /*isChainCall=*/false, + /*isDelegateCall=*/false, resultType, argTypes, required); + + // Compute ABI information. + if (CC == llvm::CallingConv::SPIR_KERNEL) { + llvm_unreachable("NYI"); + } else if (::cir::MissingFeatures::extParamInfo()) { + llvm_unreachable("NYI"); + } else { + // NOTE(cir): This corects the initial function info data. + getABIInfo().computeInfo(*FI); // FIXME(cir): Args should be set to null. + } + + // Loop over all of the computed argument and return value info. If any of + // them are direct or extend without a specified coerce type, specify the + // default now. + ::cir::ABIArgInfo &retInfo = FI->getReturnInfo(); + if (retInfo.canHaveCoerceToType() && retInfo.getCoerceToType() == nullptr) + llvm_unreachable("NYI"); + + for (auto &_ : FI->arguments()) + llvm_unreachable("NYI"); + + return *FI; +} diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.h index ac54490c578f..b579f96fb436 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerCall.h @@ -22,6 +22,8 @@ namespace cir { /// Contains the address where the return value of a function can be stored, and /// whether the address is volatile or not. class ReturnValueSlot { + // FIXME(cir): We should be able to query this directly from CIR at some + // point. This class can then be removed. Value Addr = {}; // Return value slot flags diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp index 6215b6149786..ae341dd03f53 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.cpp @@ -12,11 +12,16 @@ //===----------------------------------------------------------------------===// #include "LowerFunction.h" +#include "CIRToCIRArgMapping.h" #include "LowerCall.h" +#include "LowerFunctionInfo.h" #include "LowerModule.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Support/LogicalResult.h" +#include "clang/CIR/ABIArgInfo.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/Support/ErrorHandling.h" namespace mlir { namespace cir { @@ -32,5 +37,222 @@ LowerFunction::LowerFunction(LowerModule &LM, PatternRewriter &rewriter, : Target(LM.getTarget()), rewriter(rewriter), SrcFn(srcFn), callOp(callOp), LM(LM) {} +/// Rewrite a call operation to abide to the ABI calling convention. +/// +/// FIXME(cir): This method has partial parity to CodeGenFunction's +/// EmitCallEpxr method defined in CGExpr.cpp. This could likely be +/// removed in favor of a more direct approach. +LogicalResult LowerFunction::rewriteCallOp(CallOp op, + ReturnValueSlot retValSlot) { + + // TODO(cir): Check if BlockCall, CXXMemberCall, CUDAKernelCall, or + // CXXOperatorMember require special handling here. These should be handled in + // CIRGen, unless there is call conv or ABI-specific stuff to be handled, them + // we should do it here. + + // TODO(cir): Also check if Builtin and CXXPeseudoDtor need special handling + // here. These should be handled in CIRGen, unless there is call conv or + // ABI-specific stuff to be handled, them we should do it here. + + // NOTE(cir): There is no direct way to fetch the function type from the + // CallOp, so we fetch it from the source function. This assumes the function + // definition has not yet been lowered. + assert(SrcFn && "No source function"); + auto fnType = SrcFn.getFunctionType(); + + // Rewrite the call operation to abide to the ABI calling convention. + auto Ret = rewriteCallOp(fnType, SrcFn, op, retValSlot); + + // Replace the original call result with the new one. + if (Ret) + rewriter.replaceAllUsesWith(op.getResult(), Ret); + + return success(); +} + +/// Rewrite a call operation to abide to the ABI calling convention. +/// +/// FIXME(cir): This method has partial parity to CodeGenFunction's EmitCall +/// method defined in CGExpr.cpp. This could likely be removed in favor of a +/// more direct approach since most of the code here is exclusively CodeGen. +Value LowerFunction::rewriteCallOp(FuncType calleeTy, FuncOp origCallee, + CallOp callOp, ReturnValueSlot retValSlot, + Value Chain) { + // NOTE(cir): Skip a bunch of function pointer stuff and AST declaration + // asserts. Also skip sanitizers, as these should likely be handled at + // CIRGen. + CallArgList Args; + if (Chain) + llvm_unreachable("NYI"); + + // NOTE(cir): Call args were already emitted in CIRGen. Skip the evaluation + // order done in CIRGen and just fetch the exiting arguments here. + Args = callOp.getArgOperands(); + + const LowerFunctionInfo &FnInfo = LM.getTypes().arrangeFreeFunctionCall( + callOp.getArgOperands(), calleeTy, /*chainCall=*/false); + + // C99 6.5.2.2p6: + // If the expression that denotes the called function has a type + // that does not include a prototype, [the default argument + // promotions are performed]. If the number of arguments does not + // equal the number of parameters, the behavior is undefined. If + // the function is defined with a type that includes a prototype, + // and either the prototype ends with an ellipsis (, ...) or the + // types of the arguments after promotion are not compatible with + // the types of the parameters, the behavior is undefined. If the + // function is defined with a type that does not include a + // prototype, and the types of the arguments after promotion are + // not compatible with those of the parameters after promotion, + // the behavior is undefined [except in some trivial cases]. + // That is, in the general case, we should assume that a call + // through an unprototyped function type works like a *non-variadic* + // call. The way we make this work is to cast to the exact type + // of the promoted arguments. + // + // Chain calls use this same code path to add the invisible chain parameter + // to the function type. + if (origCallee.getNoProto() || Chain) { + llvm_unreachable("NYI"); + } + + assert(!::cir::MissingFeatures::CUDA()); + + // TODO(cir): LLVM IR has the concept of "CallBase", which is a base class for + // all types of calls. Perhaps we should have a CIR interface to mimic this + // class. + CallOp CallOrInvoke = {}; + Value CallResult = {}; + rewriteCallOp(FnInfo, origCallee, callOp, retValSlot, Args, CallOrInvoke, + /*isMustTail=*/false, callOp.getLoc()); + + // NOTE(cir): Skipping debug stuff here. + + return CallResult; +} + +// NOTE(cir): This method has partial parity to CodeGenFunction's EmitCall +// method in CGCall.cpp. When incrementing it, use the original codegen as a +// reference: add ABI-specific stuff and skip codegen stuff. +Value LowerFunction::rewriteCallOp(const LowerFunctionInfo &CallInfo, + FuncOp Callee, CallOp Caller, + ReturnValueSlot ReturnValue, + CallArgList &CallArgs, CallOp CallOrInvoke, + bool isMustTail, Location loc) { + // FIXME: We no longer need the types from CallArgs; lift up and simplify. + + // Handle struct-return functions by passing a pointer to the + // location that we would like to return into. + Type RetTy = CallInfo.getReturnType(); // ABI-agnostic type. + const ::cir::ABIArgInfo &RetAI = CallInfo.getReturnInfo(); + + FuncType IRFuncTy = LM.getTypes().getFunctionType(CallInfo); + + // NOTE(cir): Some target/ABI related checks happen here. I'm skipping them + // under the assumption that they are handled in CIRGen. + + // 1. Set up the arguments. + + // If we're using inalloca, insert the allocation after the stack save. + // FIXME: Do this earlier rather than hacking it in here! + if (StructType ArgStruct = CallInfo.getArgStruct()) { + llvm_unreachable("NYI"); + } + + CIRToCIRArgMapping IRFunctionArgs(LM.getContext(), CallInfo); + SmallVector IRCallArgs(IRFunctionArgs.totalIRArgs()); + + // If the call returns a temporary with struct return, create a temporary + // alloca to hold the result, unless one is given to us. + if (RetAI.isIndirect() || RetAI.isCoerceAndExpand() || RetAI.isInAlloca()) { + llvm_unreachable("NYI"); + } + + assert(!::cir::MissingFeatures::swift()); + + // NOTE(cir): Skipping lifetime markers here. + + // Translate all of the arguments as necessary to match the IR lowering. + assert(CallInfo.arg_size() == CallArgs.size() && + "Mismatch between function signature & arguments."); + unsigned ArgNo = 0; + LowerFunctionInfo::const_arg_iterator info_it = CallInfo.arg_begin(); + for (auto I = CallArgs.begin(), E = CallArgs.end(); I != E; + ++I, ++info_it, ++ArgNo) { + llvm_unreachable("NYI"); + } + + // 2. Prepare the function pointer. + // NOTE(cir): This is not needed for CIR. + + // 3. Perform the actual call. + + // NOTE(cir): CIRGen handle when to "deactive" cleanups. We also skip some + // debugging stuff here. + + // Update the largest vector width if any arguments have vector types. + assert(!::cir::MissingFeatures::vectorType()); + + // Compute the calling convention and attributes. + + // FIXME(cir): Skipping call attributes for now. Not sure if we have to do + // this at all since we already do it for the function definition. + + // FIXME(cir): Implement the required procedures for strictfp function and + // fast-math. + + // FIXME(cir): Add missing call-site attributes here if they are + // ABI/target-specific, otherwise, do it in CIRGen. + + // NOTE(cir): Deciding whether to use Call or Invoke is done in CIRGen. + + // Rewrite the actual call operation. + // TODO(cir): Handle other types of CIR calls (e.g. cir.try_call). + // NOTE(cir): We don't know if the callee was already lowered, so we only + // fetch the name from the callee, while the return type is fetch from the + // lowering types manager. + CallOp _ = rewriter.create(loc, Caller.getCalleeAttr(), + IRFuncTy.getReturnType(), IRCallArgs); + + assert(!::cir::MissingFeatures::vectorType()); + + // NOTE(cir): Skipping some ObjC, tail-call, debug, and attribute stuff here. + + // 4. Finish the call. + + // NOTE(cir): Skipping no-return, isMustTail, swift error handling, and + // writebacks here. These should be handled in CIRGen, I think. + + // Convert return value from ABI-agnostic to ABI-aware. + Value Ret = [&] { + // NOTE(cir): CIRGen already handled the emission of the return value. We + // need only to handle the ABI-specific to ABI-agnostic cast here. + switch (RetAI.getKind()) { + case ::cir::ABIArgInfo::Ignore: + // If we are ignoring an argument that had a result, make sure to + // construct the appropriate return value for our caller. + return getUndefRValue(RetTy); + default: + llvm::errs() << "Unhandled ABIArgInfo kind: " << RetAI.getKind() << "\n"; + llvm_unreachable("NYI"); + } + }(); + + // NOTE(cir): Skipping Emissions, lifetime markers, and dtors here that should + // be handled in CIRGen. + + return Ret; +} + +// NOTE(cir): This method has partial parity to CodeGenFunction's GetUndefRValue +// defined in CGExpr.cpp. +Value LowerFunction::getUndefRValue(Type Ty) { + if (Ty.isa()) + return nullptr; + + llvm::outs() << "Missing undef handler for value type: " << Ty << "\n"; + llvm_unreachable("NYI"); +} + } // namespace cir } // namespace mlir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h index 319751790915..6498bd705288 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunction.h @@ -25,6 +25,8 @@ namespace mlir { namespace cir { +using CallArgList = SmallVector; + class LowerFunction { LowerFunction(const LowerFunction &) = delete; void operator=(const LowerFunction &) = delete; @@ -50,6 +52,19 @@ class LowerFunction { ~LowerFunction() = default; LowerModule &LM; // Per-module state. + + /// Rewrite a call operation to abide to the ABI calling convention. + LogicalResult rewriteCallOp(CallOp op, + ReturnValueSlot retValSlot = ReturnValueSlot()); + Value rewriteCallOp(FuncType calleeTy, FuncOp origCallee, CallOp callOp, + ReturnValueSlot retValSlot, Value Chain = nullptr); + Value rewriteCallOp(const LowerFunctionInfo &CallInfo, FuncOp Callee, + CallOp Caller, ReturnValueSlot ReturnValue, + CallArgList &CallArgs, CallOp CallOrInvoke, + bool isMustTail, Location loc); + + /// Get an appropriate 'undef' value for the given type. + Value getUndefRValue(Type Ty); }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunctionInfo.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunctionInfo.h index 4344745f2478..ea7174caf6b9 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunctionInfo.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerFunctionInfo.h @@ -18,6 +18,7 @@ #include "clang/CIR/ABIArgInfo.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/TrailingObjects.h" namespace mlir { @@ -46,6 +47,8 @@ struct LowerFunctionInfoArgInfo { ::cir::ABIArgInfo info; // ABI-specific information. }; +// FIXME(cir): We could likely encode this information within CIR/MLIR, allowing +// us to eliminate this class. class LowerFunctionInfo final : private llvm::TrailingObjects { @@ -89,7 +92,7 @@ class LowerFunctionInfo final ArrayRef argTypes, RequiredArgs required) { // TODO(cir): Add assertions? - assert(::cir::MissingFeatures::extParamInfo()); + assert(!::cir::MissingFeatures::extParamInfo()); void *buffer = operator new(totalSizeToAlloc(argTypes.size() + 1)); LowerFunctionInfo *FI = new (buffer) LowerFunctionInfo(); @@ -115,10 +118,22 @@ class LowerFunctionInfo final return NumArgs + 1; } + typedef const ArgInfo *const_arg_iterator; + typedef ArgInfo *arg_iterator; + + MutableArrayRef arguments() { + return MutableArrayRef(arg_begin(), NumArgs); + } + + const_arg_iterator arg_begin() const { return getArgsBuffer() + 1; } + const_arg_iterator arg_end() const { return getArgsBuffer() + 1 + NumArgs; } + arg_iterator arg_begin() { return getArgsBuffer() + 1; } + arg_iterator arg_end() { return getArgsBuffer() + 1 + NumArgs; } + unsigned arg_size() const { return NumArgs; } bool isVariadic() const { - assert(::cir::MissingFeatures::variadicFunctions()); + assert(!::cir::MissingFeatures::variadicFunctions()); return false; } unsigned getNumRequiredArgs() const { @@ -126,6 +141,20 @@ class LowerFunctionInfo final llvm_unreachable("NYI"); return arg_size(); } + + Type getReturnType() const { return getArgsBuffer()[0].type; } + + ::cir::ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } + const ::cir::ABIArgInfo &getReturnInfo() const { + return getArgsBuffer()[0].info; + } + + /// Return the user specified callingconvention, which has been translated + /// into an LLVM CC. + unsigned getCallingConvention() const { return CallingConvention; } + + /// Get the struct type used to represent all the arguments in memory. + StructType getArgStruct() const { return ArgStruct; } }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp index 44ef32a5ddfa..28760fea585d 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp @@ -13,6 +13,7 @@ #include "LowerModule.h" #include "CIRLowerContext.h" +#include "LowerFunction.h" #include "TargetInfo.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/BuiltinAttributes.h" @@ -81,10 +82,19 @@ LogicalResult LowerModule::rewriteGlobalFunctionDefinition(FuncOp op, return failure(); } -LogicalResult LowerModule::rewriteFunctionCall(CallOp caller, FuncOp callee) { +LogicalResult LowerModule::rewriteFunctionCall(CallOp callOp, FuncOp funcOp) { mlir::OpBuilder::InsertionGuard guard(rewriter); - rewriter.setInsertionPoint(caller); - return failure(); + rewriter.setInsertionPoint(callOp); + + // Create a new function with the ABI-specific calling convention. + if (LowerFunction(*this, rewriter, funcOp, callOp) + .rewriteCallOp(callOp) + .failed()) + return failure(); + + // Erase original ABI-agnostic call. + rewriter.eraseOp(callOp); + return success(); } } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h index d99d40f90554..bb56cb5fef92 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h @@ -60,7 +60,7 @@ class LowerModule { // FIXME(cir): This would be in ASTContext, not CodeGenModule. clang::TargetCXXABI::Kind getCXXABIKind() const { auto kind = getTarget().getCXXABI().getKind(); - assert(::cir::MissingFeatures::langOpts()); + assert(!::cir::MissingFeatures::langOpts()); return kind; } @@ -68,7 +68,7 @@ class LowerModule { LogicalResult rewriteGlobalFunctionDefinition(FuncOp op, LowerModule &state); // Rewrite CIR CallOp to match the target ABI. - LogicalResult rewriteFunctionCall(CallOp caller, FuncOp callee); + LogicalResult rewriteFunctionCall(CallOp callOp, FuncOp funcOp); }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp index 1186da9df1e7..3d8ca6cfe61f 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.cpp @@ -12,13 +12,59 @@ //===----------------------------------------------------------------------===// #include "LowerTypes.h" +#include "CIRToCIRArgMapping.h" #include "LowerModule.h" #include "mlir/Support/LLVM.h" +#include "clang/CIR/ABIArgInfo.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/Support/ErrorHandling.h" using namespace ::mlir::cir; +unsigned LowerTypes::clangCallConvToLLVMCallConv(clang::CallingConv CC) { + switch (CC) { + case clang::CC_C: + return llvm::CallingConv::C; + default: + llvm_unreachable("calling convention NYI"); + } +} + LowerTypes::LowerTypes(LowerModule &LM, StringRef DLString) - : LM(LM), queries(LM.getContext()), Target(LM.getTarget()), + : LM(LM), context(LM.getContext()), Target(LM.getTarget()), CXXABI(LM.getCXXABI()), TheABIInfo(LM.getTargetLoweringInfo().getABIInfo()), - mlirContext(LM.getMLIRContext()), DL(LM.getModule()) {} + mlirContext(LM.getMLIRContext()), DL(DLString, LM.getModule()) {} + +/// Return the ABI-specific function type for a CIR function type. +FuncType LowerTypes::getFunctionType(const LowerFunctionInfo &FI) { + + mlir::Type resultType = {}; + const ::cir::ABIArgInfo &retAI = FI.getReturnInfo(); + switch (retAI.getKind()) { + case ::cir::ABIArgInfo::Ignore: + resultType = VoidType::get(getMLIRContext()); + break; + default: + llvm_unreachable("Missing ABIArgInfo::Kind"); + } + + CIRToCIRArgMapping IRFunctionArgs(getContext(), FI, true); + SmallVector ArgTypes(IRFunctionArgs.totalIRArgs()); + + // Add type for sret argument. + assert(!::cir::MissingFeatures::sretArgs()); + + // Add type for inalloca argument. + assert(!::cir::MissingFeatures::inallocaArgs()); + + // Add in all of the required arguments. + unsigned ArgNo = 0; + LowerFunctionInfo::const_arg_iterator it = FI.arg_begin(), + ie = it + FI.getNumRequiredArgs(); + for (; it != ie; ++it, ++ArgNo) { + llvm_unreachable("NYI"); + } + + return FuncType::get(getMLIRContext(), ArgTypes, resultType, FI.isVariadic()); +} diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h index 395665d47f16..941b3d7aeab7 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerTypes.h @@ -17,8 +17,11 @@ #include "ABIInfo.h" #include "CIRCXXABI.h" #include "CIRLowerContext.h" +#include "LowerCall.h" #include "mlir/IR/MLIRContext.h" +#include "clang/Basic/Specifiers.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" +#include "clang/CIR/FnInfoOpts.h" namespace mlir { namespace cir { @@ -33,7 +36,7 @@ class LowerTypes { private: LowerModule &LM; - CIRLowerContext &queries; + CIRLowerContext &context; const clang::TargetInfo &Target; CIRCXXABI &CXXABI; @@ -46,11 +49,41 @@ class LowerTypes { ::cir::CIRDataLayout DL; + const ABIInfo &getABIInfo() const { return TheABIInfo; } + public: LowerTypes(LowerModule &LM, StringRef DLString); ~LowerTypes() = default; LowerModule &getLM() const { return LM; } + CIRCXXABI &getCXXABI() const { return CXXABI; } + CIRLowerContext &getContext() { return context; } + MLIRContext *getMLIRContext() { return mlirContext; } + + /// Convert clang calling convention to LLVM callilng convention. + unsigned clangCallConvToLLVMCallConv(clang::CallingConv CC); + + /// Free functions are functions that are compatible with an ordinary + /// C function pointer type. + const LowerFunctionInfo &arrangeFreeFunctionCall(const OperandRange args, + const FuncType fnType, + bool chainCall); + + /// Arrange the argument and result information for an abstract value + /// of a given function type. This is the method which all of the + /// above functions ultimately defer to. + /// + /// \param resultType - ABI-agnostic CIR result type. + /// \param opts - Options to control the arrangement. + /// \param argTypes - ABI-agnostic CIR argument types. + /// \param required - Information about required/optional arguments. + const LowerFunctionInfo &arrangeLLVMFunctionInfo(Type resultType, + ::cir::FnInfoOpts opts, + ArrayRef argTypes, + RequiredArgs required); + + /// Return the ABI-specific function type for a CIR function type. + FuncType getFunctionType(const LowerFunctionInfo &FI); }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp index 6d2a329e6d2a..736f3a7ea301 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/Targets/X86.cpp @@ -1,27 +1,164 @@ + +#include "clang/CIR/Target/x86.h" #include "ABIInfo.h" +#include "ABIInfoImpl.h" #include "LowerModule.h" #include "LowerTypes.h" #include "TargetInfo.h" +#include "clang/CIR/ABIArgInfo.h" #include "clang/CIR/MissingFeatures.h" +#include "llvm/Support/ErrorHandling.h" #include namespace mlir { namespace cir { class X86_64ABIInfo : public ABIInfo { + using Class = ::cir::X86ArgClass; + + /// Determine the x86_64 register classes in which the given type T should be + /// passed. + /// + /// \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 high word of the containing object. + /// + /// \param OffsetBase - The bit offset of this type in the + /// containing object. Some parameters are classified different + /// depending on whether they straddle an eightbyte boundary. + /// + /// \param isNamedArg - Whether the argument in question is a "named" + /// argument, as used in AMD64-ABI 3.5.7. + /// + /// \param IsRegCall - Whether the calling conversion is regcall. + /// + /// If a word is unused its result will be NoClass; if a type should + /// be passed in Memory then at least the classification of \arg Lo + /// will be Memory. + /// + /// The \arg Lo class will be NoClass iff the argument is ignored. + /// + /// If the \arg Lo class is ComplexX87, then the \arg Hi class will + /// also be ComplexX87. + void classify(Type T, uint64_t OffsetBase, Class &Lo, Class &Hi, + bool isNamedArg, bool IsRegCall = false) const; public: X86_64ABIInfo(LowerTypes &CGT, X86AVXABILevel AVXLevel) : ABIInfo(CGT) {} + + ::cir::ABIArgInfo classifyReturnType(Type RetTy) const; + + void computeInfo(LowerFunctionInfo &FI) const override; }; class X86_64TargetLoweringInfo : public TargetLoweringInfo { public: X86_64TargetLoweringInfo(LowerTypes &LM, X86AVXABILevel AVXLevel) : TargetLoweringInfo(std::make_unique(LM, AVXLevel)) { - assert(::cir::MissingFeatures::swift()); + assert(!::cir::MissingFeatures::swift()); } }; +void X86_64ABIInfo::classify(Type Ty, uint64_t OffsetBase, Class &Lo, Class &Hi, + bool isNamedArg, bool IsRegCall) const { + // FIXME: This code can be simplified by introducing a simple value class + // for Class pairs with appropriate constructor methods for the various + // situations. + + // FIXME: Some of the split computations are wrong; unaligned vectors + // shouldn't be passed in registers for example, so there is no chance they + // can straddle an eightbyte. Verify & simplify. + + Lo = Hi = Class::NoClass; + + Class &Current = OffsetBase < 64 ? Lo : Hi; + Current = Class::Memory; + + // FIXME(cir): There's currently no direct way to identify if a type is a + // builtin. + if (/*isBuitinType=*/true) { + if (Ty.isa()) { + Current = Class::NoClass; + } else { + llvm::outs() << "Missing X86 classification for type " << Ty << "\n"; + llvm_unreachable("NYI"); + } + // FIXME: _Decimal32 and _Decimal64 are SSE. + // FIXME: _float128 and _Decimal128 are (SSE, SSEUp). + return; + } + + llvm::outs() << "Missing X86 classification for non-builtin types\n"; + llvm_unreachable("NYI"); +} + +::cir::ABIArgInfo X86_64ABIInfo::classifyReturnType(Type RetTy) const { + // AMD64-ABI 3.2.3p4: Rule 1. Classify the return type with the + // classification algorithm. + X86_64ABIInfo::Class Lo, Hi; + classify(RetTy, 0, Lo, Hi, true); + + // Check some invariants. + assert((Hi != Class::Memory || Lo == Class::Memory) && + "Invalid memory classification."); + assert((Hi != Class::SSEUp || Lo == Class::SSE) && + "Invalid SSEUp classification."); + + switch (Lo) { + case Class::NoClass: + if (Hi == Class::NoClass) + return ::cir::ABIArgInfo::getIgnore(); + break; + default: + llvm_unreachable("NYI"); + } + + llvm_unreachable("NYI"); +} + +void X86_64ABIInfo::computeInfo(LowerFunctionInfo &FI) const { + const unsigned CallingConv = FI.getCallingConvention(); + // It is possible to force Win64 calling convention on any x86_64 target by + // using __attribute__((ms_abi)). In such case to correctly emit Win64 + // compatible code delegate this call to WinX86_64ABIInfo::computeInfo. + if (CallingConv == llvm::CallingConv::Win64) { + llvm_unreachable("Win64 CC is NYI"); + } + + bool IsRegCall = CallingConv == llvm::CallingConv::X86_RegCall; + + // Keep track of the number of assigned registers. + unsigned NeededSSE = 0, MaxVectorWidth = 0; + + if (!::mlir::cir::classifyReturnType(getCXXABI(), FI, *this)) { + if (IsRegCall || ::cir::MissingFeatures::regCall()) { + llvm_unreachable("RegCall is NYI"); + } else + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + } + + // If the return value is indirect, then the hidden argument is consuming + // one integer register. + if (FI.getReturnInfo().isIndirect()) + llvm_unreachable("NYI"); + else if (NeededSSE && MaxVectorWidth) + llvm_unreachable("NYI"); + + // The chain argument effectively gives us another free register. + if (::cir::MissingFeatures::chainCall()) + llvm_unreachable("NYI"); + + // AMD64-ABI 3.2.3p3: Once arguments are classified, the registers + // get assigned (in left-to-right order) for passing as follows... + unsigned ArgNo = 0; + for (LowerFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); + it != ie; ++it, ++ArgNo) { + llvm_unreachable("NYI"); + } +} + 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/x86-call-conv-lowering-pass.cpp b/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp similarity index 69% rename from clang/test/CIR/Transforms/Target/x86/x86-call-conv-lowering-pass.cpp rename to clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp index e8772b24c3b8..6bb4d71d4877 100644 --- a/clang/test/CIR/Transforms/Target/x86/x86-call-conv-lowering-pass.cpp +++ b/clang/test/CIR/Transforms/Target/x86_64/x86_64-call-conv-lowering-pass.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -fclangir-call-conv-lowering -emit-cir -mmlir --mlir-print-ir-after=cir-call-conv-lowering %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -// Just check if the pass is called for now. -// CHECK: module +// CHECK: @_Z4Voidv() +void Void(void) { +// CHECK: cir.call @_Z4Voidv() : () -> () + Void(); +}