https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/136854
>From dbe66e5e48f769589592e22bcfb8c122910d91d1 Mon Sep 17 00:00:00 2001 From: Iris Shi <0...@owo.li> Date: Thu, 8 May 2025 23:40:15 +0800 Subject: [PATCH] [CIR] Cleanup support for C functions --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 110 ++++++++++++++++++--- clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 103 +++++++++++++++---- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 18 +++- clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 40 ++++---- clang/lib/CIR/CodeGen/CIRGenTypes.h | 10 +- clang/lib/CIR/CodeGen/TargetInfo.cpp | 8 +- clang/test/CIR/CodeGen/basic.c | 13 +++ 7 files changed, 244 insertions(+), 58 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index bed0db28818f1..c8c12b509dca8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -13,6 +13,7 @@ #include "CIRGenCall.h" #include "CIRGenFunction.h" +#include "CIRGenFunctionInfo.h" #include "clang/CIR/MissingFeatures.h" using namespace clang; @@ -20,20 +21,22 @@ using namespace clang::CIRGen; CIRGenFunctionInfo * CIRGenFunctionInfo::create(CanQualType resultType, - llvm::ArrayRef<CanQualType> argTypes) { + llvm::ArrayRef<CanQualType> argTypes, + RequiredArgs required) { // The first slot allocated for ArgInfo is for the return value. void *buffer = operator new(totalSizeToAlloc<ArgInfo>(argTypes.size() + 1)); + assert(!cir::MissingFeatures::opCallCIRGenFuncInfoParamInfo()); + CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo(); - fi->numArgs = argTypes.size(); - assert(!cir::MissingFeatures::opCallCIRGenFuncInfoParamInfo()); + fi->required = required; + fi->numArgs = argTypes.size(); ArgInfo *argsBuffer = fi->getArgsBuffer(); (argsBuffer++)->type = resultType; for (CanQualType ty : argTypes) (argsBuffer++)->type = ty; - assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo()); return fi; @@ -45,7 +48,7 @@ namespace { /// CIRGenFunctionInfo should be passed to actual CIR function. class ClangToCIRArgMapping { static constexpr unsigned invalidIndex = ~0U; - unsigned totalNumCIRArgs; + unsigned totalNumCIRArgs = 0; /// Arguments of CIR function corresponding to single Clang argument. struct CIRArgs { @@ -61,14 +64,20 @@ class ClangToCIRArgMapping { public: ClangToCIRArgMapping(const ASTContext &astContext, - const CIRGenFunctionInfo &funcInfo) - : totalNumCIRArgs(0), argInfo(funcInfo.arg_size()) { + const CIRGenFunctionInfo &funcInfo, + bool onlyRequiredArgs) + : argInfo(onlyRequiredArgs ? funcInfo.getNumRequiredArgs() + : funcInfo.argInfoSize()) { unsigned cirArgNo = 0; assert(!cir::MissingFeatures::opCallABIIndirectArg()); unsigned argNo = 0; - for (const CIRGenFunctionInfoArgInfo &i : funcInfo.arguments()) { + llvm::ArrayRef<CIRGenFunctionInfoArgInfo> argInfos( + funcInfo.argInfoBegin(), onlyRequiredArgs + ? funcInfo.getNumRequiredArgs() + : funcInfo.argInfoSize()); + for (const CIRGenFunctionInfoArgInfo &i : argInfos) { // Collect data about CIR arguments corresponding to Clang argument ArgNo. CIRArgs &cirArgs = argInfo[argNo]; @@ -119,6 +128,63 @@ class ClangToCIRArgMapping { } // namespace +cir::FuncType CIRGenTypes::getFunctionType(const CIRGenFunctionInfo &fi) { + bool inserted = functionsBeingProcessed.insert(&fi).second; + (void)inserted; + assert(inserted && "Recursively being processed?"); + + mlir::Type resultType; + const cir::ABIArgInfo &retInfo = fi.getReturnInfo(); + + switch (retInfo.getKind()) { + case cir::ABIArgInfo::Ignore: + // TODO(CIR): This should probably be the None type from the builtin + // dialect. + resultType = nullptr; + break; + case cir::ABIArgInfo::Direct: + resultType = retInfo.getCoerceToType(); + break; + } + + ClangToCIRArgMapping cirFunctionArgs(getASTContext(), fi, true); + SmallVector<mlir::Type, 8> argTypes(cirFunctionArgs.totalCIRArgs()); + + unsigned argNo = 0; + llvm::ArrayRef<CIRGenFunctionInfoArgInfo> argInfos(fi.argInfoBegin(), + fi.getNumRequiredArgs()); + for (const auto &argInfo : argInfos) { + const auto &abiArgInfo = argInfo.info; + + unsigned firstCIRArg, numCIRArgs; + std::tie(firstCIRArg, numCIRArgs) = cirFunctionArgs.getCIRArgs(argNo); + + switch (abiArgInfo.getKind()) { + case cir::ABIArgInfo::Direct: { + mlir::Type argType = abiArgInfo.getCoerceToType(); + // TODO: handle the test against llvm::RecordType from codegen + assert(numCIRArgs == 1); + argTypes[firstCIRArg] = argType; + break; + } + default: + cgm.errorNYI("getFunctionType: unhandled argument kind"); + } + + ++argNo; + } + assert(argNo == fi.argInfoSize() && + "Mismatch between function info and args"); + + bool erased = functionsBeingProcessed.erase(&fi); + (void)erased; + assert(erased && "Not in set?"); + + return cir::FuncType::get(argTypes, + (resultType ? resultType : builder.getVoidTy()), + fi.isVariadic()); +} + CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { assert(!cir::MissingFeatures::opCallVirtual()); return *this; @@ -128,6 +194,9 @@ static const CIRGenFunctionInfo & arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, const CallArgList &args, const FunctionType *fnType) { + + RequiredArgs required = RequiredArgs::All; + if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) { if (proto->isVariadic()) cgm.errorNYI("call to variadic function"); @@ -144,7 +213,7 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, CanQualType retType = fnType->getReturnType() ->getCanonicalTypeUnqualified() .getUnqualifiedType(); - return cgt.arrangeCIRFunctionInfo(retType, argTypes); + return cgt.arrangeCIRFunctionInfo(retType, argTypes, required); } const CIRGenFunctionInfo & @@ -168,6 +237,23 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, return builder.createCallOp(callLoc, directFuncOp, cirCallArgs); } +const CIRGenFunctionInfo & +CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionProtoType> fpt) { + SmallVector<CanQualType, 8> argTypes; + for (unsigned i = 0, e = fpt->getNumParams(); i != e; ++i) + argTypes.push_back(fpt->getParamType(i)); + RequiredArgs required = RequiredArgs::forPrototypePlus(fpt); + + CanQualType resultType = fpt->getReturnType().getUnqualifiedType(); + return arrangeCIRFunctionInfo(resultType, argTypes, required); +} + +const CIRGenFunctionInfo & +CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> fnpt) { + CanQualType resultType = fnpt->getReturnType().getUnqualifiedType(); + return arrangeCIRFunctionInfo(resultType, {}, RequiredArgs(0)); +} + RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, @@ -177,16 +263,16 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, QualType retTy = funcInfo.getReturnType(); const cir::ABIArgInfo &retInfo = funcInfo.getReturnInfo(); - ClangToCIRArgMapping cirFuncArgs(cgm.getASTContext(), funcInfo); + ClangToCIRArgMapping cirFuncArgs(cgm.getASTContext(), funcInfo, false); SmallVector<mlir::Value, 16> cirCallArgs(cirFuncArgs.totalCIRArgs()); assert(!cir::MissingFeatures::emitLifetimeMarkers()); // Translate all of the arguments as necessary to match the CIR lowering. - assert(funcInfo.arg_size() == args.size() && + assert(funcInfo.argInfoSize() == args.size() && "Mismatch between function signature & arguments."); unsigned argNo = 0; - for (const auto &[arg, argInfo] : llvm::zip(args, funcInfo.arguments())) { + for (const auto &[arg, argInfo] : llvm::zip(args, funcInfo.argInfos())) { // Insert a padding argument to ensure proper alignment. assert(!cir::MissingFeatures::opCallPaddingArgs()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h index 4319f7a2be225..50be94dd7ee6f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h @@ -27,20 +27,68 @@ struct CIRGenFunctionInfoArgInfo { cir::ABIArgInfo info; }; +/// A class for recording the number of arguments that a function signature +/// requires. +class RequiredArgs { + /// The number of required arguments, or ~0 if the signature does not permit + /// optional arguments. + unsigned numRequired; + +public: + enum All_t { All }; + + RequiredArgs(All_t _) : numRequired(~0U) {} + explicit RequiredArgs(unsigned n) : numRequired(n) { assert(n != ~0U); } + + unsigned getOpaqueData() const { return numRequired; } + + bool allowsOptionalArgs() const { return numRequired != ~0U; } + + /// Compute the arguments required by the given formal prototype, given that + /// there may be some additional, non-formal arguments in play. + /// + /// If FD is not null, this will consider pass_object_size params in FD. + static RequiredArgs + forPrototypePlus(const clang::FunctionProtoType *prototype) { + if (!prototype->isVariadic()) + return All; + + if (prototype->hasExtParameterInfos()) + llvm_unreachable("NYI"); + + return RequiredArgs(prototype->getNumParams()); + } + + static RequiredArgs + forPrototypePlus(clang::CanQual<clang::FunctionProtoType> prototype) { + return forPrototypePlus(prototype.getTypePtr()); + } + + unsigned getNumRequiredArgs() const { + assert(allowsOptionalArgs()); + return numRequired; + } +}; + class CIRGenFunctionInfo final : public llvm::FoldingSetNode, private llvm::TrailingObjects<CIRGenFunctionInfo, CIRGenFunctionInfoArgInfo> { using ArgInfo = CIRGenFunctionInfoArgInfo; + RequiredArgs required; + unsigned numArgs; ArgInfo *getArgsBuffer() { return getTrailingObjects<ArgInfo>(); } const ArgInfo *getArgsBuffer() const { return getTrailingObjects<ArgInfo>(); } + CIRGenFunctionInfo() : required(RequiredArgs::All) {} + public: static CIRGenFunctionInfo *create(CanQualType resultType, - llvm::ArrayRef<CanQualType> argTypes); + llvm::ArrayRef<CanQualType> argTypes, + RequiredArgs required); void operator delete(void *p) { ::operator delete(p); } @@ -53,35 +101,52 @@ class CIRGenFunctionInfo final // This function has to be CamelCase because llvm::FoldingSet requires so. // NOLINTNEXTLINE(readability-identifier-naming) - static void Profile(llvm::FoldingSetNodeID &id, CanQualType resultType, - llvm::ArrayRef<clang::CanQualType> argTypes) { + static void Profile(llvm::FoldingSetNodeID &id, RequiredArgs required, + CanQualType resultType, + llvm::ArrayRef<CanQualType> argTypes) { + id.AddBoolean(required.getOpaqueData()); resultType.Profile(id); - for (auto i : argTypes) - i.Profile(id); + for (const CanQualType &arg : argTypes) + arg.Profile(id); } - void Profile(llvm::FoldingSetNodeID &id) { getReturnType().Profile(id); } - - llvm::MutableArrayRef<ArgInfo> arguments() { - return llvm::MutableArrayRef<ArgInfo>(arg_begin(), numArgs); - } - llvm::ArrayRef<ArgInfo> arguments() const { - return llvm::ArrayRef<ArgInfo>(arg_begin(), numArgs); + // NOLINTNEXTLINE(readability-identifier-naming) + void Profile(llvm::FoldingSetNodeID &id) { + id.AddBoolean(required.getOpaqueData()); + getReturnType().Profile(id); + for (const ArgInfo &argInfo : argInfos()) + argInfo.type.Profile(id); } - 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; } - CanQualType getReturnType() const { return getArgsBuffer()[0].type; } cir::ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } const cir::ABIArgInfo &getReturnInfo() const { return getArgsBuffer()[0].info; } + + const_arg_iterator argInfoBegin() const { return getArgsBuffer() + 1; } + const_arg_iterator argInfoEnd() const { + return getArgsBuffer() + 1 + numArgs; + } + arg_iterator argInfoBegin() { return getArgsBuffer() + 1; } + arg_iterator argInfoEnd() { return getArgsBuffer() + 1 + numArgs; } + + unsigned argInfoSize() const { return numArgs; } + + llvm::MutableArrayRef<ArgInfo> argInfos() { + return llvm::MutableArrayRef<ArgInfo>(argInfoBegin(), numArgs); + } + llvm::ArrayRef<ArgInfo> argInfos() const { + return llvm::ArrayRef<ArgInfo>(argInfoBegin(), numArgs); + } + + bool isVariadic() const { return required.allowsOptionalArgs(); } + RequiredArgs getRequiredArgs() const { return required; } + unsigned getNumRequiredArgs() const { + return isVariadic() ? getRequiredArgs().getNumRequiredArgs() + : argInfoSize(); + } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 82bd139295b10..61ba5aa16a5a0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -23,6 +23,7 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" +#include "CIRGenFunctionInfo.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Location.h" #include "mlir/IR/MLIRContext.h" @@ -247,8 +248,21 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, "function definition with a non-identifier for a name"); return; } - cir::FuncType funcType = - cast<cir::FuncType>(convertType(funcDecl->getType())); + + cir::FuncType funcType; + // TODO: Move this to arrangeFunctionDeclaration when it is + // implemented. + // When declaring a function without a prototype, always use a + // non-variadic type. + if (CanQual<FunctionNoProtoType> noProto = + funcDecl->getType() + ->getCanonicalTypeUnqualified() + .getAs<FunctionNoProtoType>()) { + const CIRGenFunctionInfo &fi = getTypes().arrangeCIRFunctionInfo( + noProto->getReturnType(), {}, RequiredArgs::All); + funcType = getTypes().getFunctionType(fi); + } else + funcType = cast<cir::FuncType>(convertType(funcDecl->getType())); cir::FuncOp funcOp = dyn_cast_if_present<cir::FuncOp>(op); if (!funcOp || funcOp.getFunctionType() != funcType) { diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 89dc5eea7f028..ea25aca8e6ff8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -1,5 +1,6 @@ #include "CIRGenTypes.h" +#include "CIRGenFunctionInfo.h" #include "CIRGenModule.h" #include "clang/AST/ASTContext.h" @@ -73,21 +74,19 @@ mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) { return cir::FuncType::get(SmallVector<mlir::Type, 1>{}, cgm.VoidTy); } - // TODO(CIR): This is a stub of what the final code will be. See the - // implementation of this function and the implementation of class - // CIRGenFunction in the ClangIR incubator project. - + const CIRGenFunctionInfo *fi; if (const auto *fpt = dyn_cast<FunctionProtoType>(ft)) { - SmallVector<mlir::Type> mlirParamTypes; - for (unsigned i = 0; i < fpt->getNumParams(); ++i) { - mlirParamTypes.push_back(convertType(fpt->getParamType(i))); - } - return cir::FuncType::get( - mlirParamTypes, convertType(fpt->getReturnType().getUnqualifiedType()), - fpt->isVariadic()); + fi = &arrangeFreeFunctionType( + CanQual<FunctionProtoType>::CreateUnsafe(QualType(fpt, 0))); + } else { + const FunctionNoProtoType *fnpt = cast<FunctionNoProtoType>(ft); + fi = &arrangeFreeFunctionType( + CanQual<FunctionNoProtoType>::CreateUnsafe(QualType(fnpt, 0))); } - cgm.errorNYI(SourceLocation(), "non-prototype function type", qft); - return cir::FuncType::get(SmallVector<mlir::Type, 1>{}, cgm.VoidTy); + + mlir::Type resultType = getFunctionType(*fi); + + return resultType; } // This is CIR's version of CodeGenTypes::addRecordTypeName. It isn't shareable @@ -518,14 +517,15 @@ bool CIRGenTypes::isZeroInitializable(const RecordDecl *rd) { return getCIRGenRecordLayout(rd).isZeroInitializable(); } -const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo( - CanQualType returnType, llvm::ArrayRef<clang::CanQualType> argTypes) { +const CIRGenFunctionInfo & +CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType, + llvm::ArrayRef<CanQualType> argTypes, + RequiredArgs required) { assert(llvm::all_of(argTypes, - [](CanQualType T) { return T.isCanonicalAsParam(); })); - + [](CanQualType t) { return t.isCanonicalAsParam(); })); // Lookup or create unique function info. llvm::FoldingSetNodeID id; - CIRGenFunctionInfo::Profile(id, returnType, argTypes); + CIRGenFunctionInfo::Profile(id, required, returnType, argTypes); void *insertPos = nullptr; CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos); @@ -535,7 +535,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo( assert(!cir::MissingFeatures::opCallCallConv()); // Construction the function info. We co-allocate the ArgInfos. - fi = CIRGenFunctionInfo::create(returnType, argTypes); + fi = CIRGenFunctionInfo::create(returnType, argTypes, required); functionInfos.InsertNode(fi, insertPos); bool inserted = functionsBeingProcessed.insert(fi).second; @@ -552,7 +552,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo( if (retInfo.canHaveCoerceToType() && retInfo.getCoerceToType() == nullptr) retInfo.setCoerceToType(convertType(fi->getReturnType())); - for (CIRGenFunctionInfoArgInfo &i : fi->arguments()) + for (CIRGenFunctionInfoArgInfo &i : fi->argInfos()) if (i.info.canHaveCoerceToType() && i.info.getCoerceToType() == nullptr) i.info.setCoerceToType(convertType(i.type)); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index cf94375d17e12..ff8ce3f87f362 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -127,7 +127,15 @@ class CIRGenTypes { const CIRGenFunctionInfo & arrangeCIRFunctionInfo(CanQualType returnType, - llvm::ArrayRef<clang::CanQualType> argTypes); + llvm::ArrayRef<CanQualType> argTypes, + RequiredArgs required); + + const CIRGenFunctionInfo & + arrangeFreeFunctionType(CanQual<FunctionProtoType> fpt); + const CIRGenFunctionInfo & + arrangeFreeFunctionType(CanQual<FunctionNoProtoType> fnpt); + + cir::FuncType getFunctionType(const CIRGenFunctionInfo &fi); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp index 0b70170cadb69..a2b71876026c9 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.cpp +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -32,11 +32,11 @@ void X8664ABIInfo::computeInfo(CIRGenFunctionInfo &funcInfo) const { // Top level CIR has unlimited arguments and return types. Lowering for ABI // specific concerns should happen during a lowering phase. Assume everything // is direct for now. - for (CIRGenFunctionInfoArgInfo &info : funcInfo.arguments()) { - if (testIfIsVoidTy(info.type)) - info.info = cir::ABIArgInfo::getIgnore(); + for (CIRGenFunctionInfoArgInfo &argInfo : funcInfo.argInfos()) { + if (testIfIsVoidTy(argInfo.type)) + argInfo.info = cir::ABIArgInfo::getIgnore(); else - info.info = cir::ABIArgInfo::getDirect(cgt.convertType(info.type)); + argInfo.info = cir::ABIArgInfo::getDirect(cgt.convertType(argInfo.type)); } CanQualType retTy = funcInfo.getReturnType(); diff --git a/clang/test/CIR/CodeGen/basic.c b/clang/test/CIR/CodeGen/basic.c index 1845d3b64bf68..70dd885f4f6d7 100644 --- a/clang/test/CIR/CodeGen/basic.c +++ b/clang/test/CIR/CodeGen/basic.c @@ -233,6 +233,19 @@ int f8(int *p) { // OGCG: %[[P2:.*]] = load ptr, ptr %[[P_PTR]], align 8 // OGCG: %[[STAR_P:.*]] = load i32, ptr %[[P2]], align 4 + +void f9() {} + +// CIR: cir.func @f9() +// CIR-NEXT: cir.return + +// LLVM: define void @f9() +// LLVM-NEXT: ret void + +// OGCG: define{{.*}} void @f9() +// OGCG-NEXT: entry: +// OGCG-NEXT: ret void + typedef unsigned long size_type; typedef unsigned long _Tp; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits