https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/134673
This PR upstreams initial support for making function calls in CIR. Function arguments and return values are not included to keep the patch small for review. Related to #132487 >From 82f6ce93ca22dd778173100231523706e2739546 Mon Sep 17 00:00:00 2001 From: Sirui Mu <msrlanc...@gmail.com> Date: Mon, 7 Apr 2025 22:54:49 +0800 Subject: [PATCH] [CIR] Upstream initial function call support --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 13 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 37 +++++++ .../clang/CIR/Interfaces/CIROpInterfaces.td | 9 ++ clang/include/clang/CIR/MissingFeatures.h | 24 +++++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 95 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenCall.h | 58 +++++++++++ clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 97 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 13 +++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 20 ++++ clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 34 +++++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 13 ++- clang/lib/CIR/CodeGen/CIRGenModule.h | 6 ++ clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 30 ++++++ clang/lib/CIR/CodeGen/CIRGenTypes.h | 11 +++ clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 81 ++++++++++++++++ clang/test/CIR/CodeGen/call.cpp | 9 ++ 17 files changed, 550 insertions(+), 1 deletion(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenCall.cpp create mode 100644 clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h create mode 100644 clang/test/CIR/CodeGen/call.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index c1e93fe790c08..dda3ecf492506 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -201,6 +201,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::PtrStrideOp>(loc, base.getType(), base, stride); } + //===--------------------------------------------------------------------===// + // Call operators + //===--------------------------------------------------------------------===// + + cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee) { + auto op = create<cir::CallOp>(loc, callee); + return op; + } + + cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) { + return createCallOp(loc, mlir::SymbolRefAttr::get(callee)); + } + //===--------------------------------------------------------------------===// // Cast/Conversion Operators //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 609e60ca74b49..aa805ee4108da 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1242,6 +1242,43 @@ def FuncOp : CIR_Op<"func", [ let hasVerifier = 1; } +//===----------------------------------------------------------------------===// +// CallOp +//===----------------------------------------------------------------------===// + +class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []> + : Op<CIR_Dialect, mnemonic, + !listconcat(extra_traits, + [DeclareOpInterfaceMethods<CIRCallOpInterface>, + DeclareOpInterfaceMethods<SymbolUserOpInterface>])> { + let hasCustomAssemblyFormat = 1; + let skipDefaultBuilders = 1; + let hasVerifier = 0; + + dag commonArgs = (ins FlatSymbolRefAttr:$callee); +} + +def CallOp : CIR_CallOp<"call", [NoRegionArguments]> { + let summary = "call a function"; + let description = [{ + The `cir.call` operation represents a direct call to a function that is + within the same symbol scope as the call. The callee is encoded as a symbol + reference attribute named `callee`. + + Example: + + ```mlir + %0 = cir.call @foo() + ``` + }]; + + let arguments = commonArgs; + + let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee), [{ + $_state.addAttribute("callee", callee); + }]>]; +} + //===----------------------------------------------------------------------===// // UnreachableOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index 39ef402c59e43..c6c6356118ac6 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -15,8 +15,17 @@ include "mlir/IR/OpBase.td" include "mlir/IR/SymbolInterfaces.td" +include "mlir/Interfaces/CallInterfaces.td" let cppNamespace = "::cir" in { + // The CIRCallOpInterface must be used instead of CallOpInterface when looking + // at arguments and other bits of CallOp. This creates a level of abstraction + // that's useful for handling indirect calls and other details. + def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", []> { + // Currently we don't have any methods defined in CIRCallOpInterface. We'll + // add more methods as the upstreaming proceeds. + } + def CIRGlobalValueInterface : OpInterface<"CIRGlobalValueInterface", [Symbol]> { diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 86fdaf1ddaf51..491fb31c4b7e2 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -72,6 +72,24 @@ struct MissingFeatures { static bool opFuncLinkage() { return false; } static bool opFuncVisibility() { return false; } + // CallOp handling + static bool opCallBuiltinFunc() { return false; } + static bool opCallPseudoDtor() { return false; } + static bool opCallArgs() { return false; } + static bool opCallReturn() { return false; } + static bool opCallArgEvaluationOrder() { return false; } + static bool opCallCallConv() { return false; } + static bool opCallSideEffect() { return false; } + static bool opCallChainCall() { return false; } + static bool opCallNoPrototypeFunc() { return false; } + static bool opCallMustTail() { return false; } + static bool opCallIndirect() { return false; } + static bool opCallVirtual() { return false; } + static bool opCallInAlloca() { return false; } + static bool opCallAttrs() { return false; } + static bool opCallSurroundingTry() { return false; } + static bool opCallASTAttr() { return false; } + // ScopeOp handling static bool opScopeCleanupRegion() { return false; } @@ -90,7 +108,10 @@ struct MissingFeatures { static bool opTBAA() { return false; } static bool opCmp() { return false; } static bool objCLifetime() { return false; } + static bool objCBlocks() { return false; } static bool emitNullabilityCheck() { return false; } + static bool emitLValueAlignmentAssumption() { return false; } + static bool emitLifetimeMarkers() { return false; } static bool astVarDeclInterface() { return false; } static bool stackSaveOp() { return false; } static bool aggValueSlot() { return false; } @@ -108,6 +129,8 @@ struct MissingFeatures { static bool cgFPOptionsRAII() { return false; } static bool metaDataNode() { return false; } static bool fastMathFlags() { return false; } + static bool weakRefReference() { return false; } + static bool hip() { return false; } // Missing types static bool dataMemberType() { return false; } @@ -127,6 +150,7 @@ struct MissingFeatures { static bool complexImagOp() { return false; } static bool complexRealOp() { return false; } static bool ifOp() { return false; } + static bool invokeOp() { return false; } static bool labelOp() { return false; } static bool ptrDiffOp() { return false; } static bool ptrStrideOp() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp new file mode 100644 index 0000000000000..1a936458782ea --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -0,0 +1,95 @@ +//===--- CIRGenCall.cpp - Encapsulate calling convention details ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// These classes wrap the information about a call or function definition used +// to handle ABI compliancy. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenCall.h" +#include "CIRGenFunction.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +CIRGenFunctionInfo *CIRGenFunctionInfo::create() { + // For now we just create an empty CIRGenFunctionInfo. + CIRGenFunctionInfo *fi = new CIRGenFunctionInfo(); + return fi; +} + +CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { + assert(!cir::MissingFeatures::opCallVirtual()); + return *this; +} + +static const CIRGenFunctionInfo &arrangeFreeFunctionLikeCall(CIRGenTypes &cgt) { + assert(!cir::MissingFeatures::opCallArgs()); + return cgt.arrangeCIRFunctionInfo(); +} + +const CIRGenFunctionInfo &CIRGenTypes::arrangeFreeFunctionCall() { + return arrangeFreeFunctionLikeCall(*this); +} + +static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, + mlir::Location callLoc, + cir::FuncOp directFuncOp) { + CIRGenBuilderTy &builder = cgf.getBuilder(); + + assert(!cir::MissingFeatures::opCallSurroundingTry()); + assert(!cir::MissingFeatures::invokeOp()); + + assert(builder.getInsertionBlock() && "expected valid basic block"); + assert(!cir::MissingFeatures::opCallIndirect()); + + return builder.createCallOp(callLoc, directFuncOp); +} + +RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, + const CIRGenCallee &callee, + cir::CIRCallOpInterface *callOp, + mlir::Location loc) { + assert(!cir::MissingFeatures::opCallArgs()); + assert(!cir::MissingFeatures::emitLifetimeMarkers()); + + const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this); + mlir::Operation *calleePtr = concreteCallee.getFunctionPointer(); + + assert(!cir::MissingFeatures::opCallInAlloca()); + + mlir::NamedAttrList attrs; + StringRef funcName; + if (auto calleeFuncOp = dyn_cast<cir::FuncOp>(calleePtr)) + funcName = calleeFuncOp.getName(); + + assert(!cir::MissingFeatures::opCallCallConv()); + assert(!cir::MissingFeatures::opCallSideEffect()); + assert(!cir::MissingFeatures::opCallAttrs()); + + assert(!cir::MissingFeatures::invokeOp()); + + auto directFuncOp = dyn_cast<cir::FuncOp>(calleePtr); + assert(!cir::MissingFeatures::opCallIndirect()); + assert(!cir::MissingFeatures::opCallAttrs()); + + cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, directFuncOp); + + if (callOp) + *callOp = theCall; + + assert(!cir::MissingFeatures::opCallMustTail()); + assert(!cir::MissingFeatures::opCallReturn()); + + // For now we just return nothing because we don't have support for return + // values yet. + RValue ret = RValue::get(nullptr); + + return ret; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 0996167feeef6..76fefdca9e45e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -14,15 +14,73 @@ #ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H #define CLANG_LIB_CODEGEN_CIRGENCALL_H +#include "mlir/IR/Operation.h" #include "clang/AST/GlobalDecl.h" #include "llvm/ADT/SmallVector.h" namespace clang::CIRGen { +class CIRGenFunction; + +/// Abstract information about a function or function prototype. +class CIRGenCalleeInfo { + clang::GlobalDecl calleeDecl; + +public: + explicit CIRGenCalleeInfo() : calleeDecl() {} + CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) : calleeDecl(calleeDecl) {} +}; + +class CIRGenCallee { + enum class SpecialKind : uintptr_t { + Invalid, + + Last = Invalid, + }; + + SpecialKind kindOrFunctionPtr; + + union { + CIRGenCalleeInfo abstractInfo; + }; + +public: + CIRGenCallee() : kindOrFunctionPtr(SpecialKind::Invalid) {} + + CIRGenCallee(const CIRGenCalleeInfo &abstractInfo, mlir::Operation *funcPtr) + : kindOrFunctionPtr(SpecialKind(reinterpret_cast<uintptr_t>(funcPtr))), + abstractInfo(abstractInfo) { + assert(funcPtr && "configuring callee without function pointer"); + } + + static CIRGenCallee + forDirect(mlir::Operation *funcPtr, + const CIRGenCalleeInfo &abstractInfo = CIRGenCalleeInfo()) { + return CIRGenCallee(abstractInfo, funcPtr); + } + + bool isOrdinary() const { + return uintptr_t(kindOrFunctionPtr) > uintptr_t(SpecialKind::Last); + } + + /// If this is a delayed callee computation of some sort, prepare a concrete + /// callee + CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const; + + mlir::Operation *getFunctionPointer() const { + assert(isOrdinary()); + return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr); + } +}; + /// Type for representing both the decl and type of parameters to a function. /// The decl must be either a ParmVarDecl or ImplicitParamDecl. class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {}; +struct CallArg {}; + +class CallArgList : public llvm::SmallVector<CallArg, 8> {}; + } // namespace clang::CIRGen #endif // CLANG_LIB_CODEGEN_CIRGENCALL_H diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index f01e03a89981d..1e5fb985b0b8c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -18,6 +18,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" @@ -304,6 +305,102 @@ RValue CIRGenFunction::emitAnyExpr(const Expr *e) { llvm_unreachable("bad evaluation kind"); } +static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) { + assert(!cir::MissingFeatures::weakRefReference()); + return cgm.getAddrOfFunction(gd); +} + +static CIRGenCallee emitDirectCallee(CIRGenModule &cgm, GlobalDecl gd) { + assert(!cir::MissingFeatures::opCallBuiltinFunc()); + + cir::FuncOp callee = emitFunctionDeclPointer(cgm, gd); + + assert(!cir::MissingFeatures::hip()); + + return CIRGenCallee::forDirect(callee, gd); +} + +RValue CIRGenFunction::emitCall(clang::QualType calleeTy, + const CIRGenCallee &callee, + const clang::CallExpr *e) { + // Get the actual function type. The callee type will always be a pointer to + // function type or a block pointer type. + assert(calleeTy->isFunctionPointerType() && + "Callee must have function pointer type!"); + + calleeTy = getContext().getCanonicalType(calleeTy); + + if (getLangOpts().CPlusPlus) + assert(!cir::MissingFeatures::sanitizers()); + + assert(!cir::MissingFeatures::sanitizers()); + assert(!cir::MissingFeatures::opCallArgs()); + + const CIRGenFunctionInfo &funcInfo = cgm.getTypes().arrangeFreeFunctionCall(); + + assert(!cir::MissingFeatures::opCallNoPrototypeFunc()); + assert(!cir::MissingFeatures::opCallChainCall()); + assert(!cir::MissingFeatures::hip()); + assert(!cir::MissingFeatures::opCallMustTail()); + + cir::CIRCallOpInterface callOp; + RValue callResult = + emitCall(funcInfo, callee, &callOp, getLoc(e->getExprLoc())); + + assert(!cir::MissingFeatures::generateDebugInfo()); + + return callResult; +} + +CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) { + e = e->IgnoreParens(); + + // Look through function-to-pointer decay. + if (const auto *implicitCast = dyn_cast<ImplicitCastExpr>(e)) { + if (implicitCast->getCastKind() == CK_FunctionToPointerDecay || + implicitCast->getCastKind() == CK_BuiltinFnToFnPtr) { + return emitCallee(implicitCast->getSubExpr()); + } + // Resolve direct calls. + } else if (const auto *declRef = dyn_cast<DeclRefExpr>(e)) { + const auto *funcDecl = dyn_cast<FunctionDecl>(declRef->getDecl()); + assert( + funcDecl && + "DeclRef referring to FunctionDecl is the only thing supported so far"); + return emitDirectCallee(cgm, funcDecl); + } + + llvm_unreachable("Nothing else supported yet!"); +} + +RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) { + assert(!cir::MissingFeatures::objCBlocks()); + + if (isa<CXXMemberCallExpr>(e)) { + cgm.errorNYI(e->getSourceRange(), "call to member function"); + return RValue::get(nullptr); + } + + if (isa<CUDAKernelCallExpr>(e)) { + cgm.errorNYI(e->getSourceRange(), "call to CUDA kernel"); + return RValue::get(nullptr); + } + + if (const auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(e)) { + if (isa_and_nonnull<CXXMethodDecl>(operatorCall->getCalleeDecl())) { + cgm.errorNYI(e->getSourceRange(), "call to member operator"); + return RValue::get(nullptr); + } + } + + CIRGenCallee callee = emitCallee(e->getCallee()); + + assert(!cir::MissingFeatures::opCallBuiltinFunc()); + assert(!cir::MissingFeatures::opCallPseudoDtor()); + + return emitCall(e->getCallee()->getType(), callee, e); +} + /// Emit code to compute the specified expression, ignoring the result. void CIRGenFunction::emitIgnoredExpr(const Expr *e) { if (e->isPRValue()) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 3863d21487531..ca6f35e0f7319 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -156,6 +156,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitCastExpr(CastExpr *e); + mlir::Value VisitCallExpr(const CallExpr *e); mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { return VisitCastExpr(e); @@ -1345,6 +1346,18 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { return {}; } +mlir::Value ScalarExprEmitter::VisitCallExpr(const CallExpr *e) { + if (e->getCallReturnType(cgf.getContext())->isReferenceType()) { + cgf.getCIRGenModule().errorNYI( + e->getSourceRange(), "call to function with non-void return type"); + return {}; + } + + auto v = cgf.emitCallExpr(e).getScalarVal(); + assert(!cir::MissingFeatures::emitLValueAlignmentAssumption()); + return v; +} + mlir::Value CIRGenFunction::emitScalarConversion(mlir::Value src, QualType srcTy, QualType dstTy, SourceLocation loc) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 1bedbe28ae625..1a0ab51c759f5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -403,6 +403,26 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); + /// An abstract representation of regular/ObjC call/message targets. + class AbstractCallee { + /// The function declaration of the callee. + const clang::Decl *calleeDecl; + + public: + AbstractCallee() : calleeDecl(nullptr) {} + AbstractCallee(const clang::FunctionDecl *fd) : calleeDecl(fd) {} + }; + + RValue emitCall(const CIRGenFunctionInfo &funcInfo, + const CIRGenCallee &callee, cir::CIRCallOpInterface *callOp, + mlir::Location loc); + RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee, + const clang::CallExpr *e); + + CIRGenCallee emitCallee(const clang::Expr *e); + + RValue emitCallExpr(const clang::CallExpr *e); + /// Emit an expression as an initializer for an object (variable, field, etc.) /// at the given location. The expression is not necessarily the normal /// initializer for the object, and the address is not necessarily diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h new file mode 100644 index 0000000000000..37191ee300eda --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h @@ -0,0 +1,34 @@ +//==-- CIRGenFunctionInfo.h - Representation of fn argument/return types ---==// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines CIRGenFunctionInfo and associated types used in representing the +// CIR source types and ABI-coerced types for function arguments and +// return values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H +#define LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H + +#include "llvm/ADT/FoldingSet.h" + +namespace clang::CIRGen { + +class CIRGenFunctionInfo final : public llvm::FoldingSetNode { +public: + static CIRGenFunctionInfo *create(); + + // NOLINTNEXTLINE(readability-identifier-naming) + static void Profile(llvm::FoldingSetNodeID &id) { + // We don't have anything to profile yet. + } +}; + +} // namespace clang::CIRGen + +#endif diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d3b3b0632c2f0..a07a1e3d52e2c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -133,10 +133,12 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, } CIRGenFunction cgf(*this, builder); + curCGF = &cgf; { mlir::OpBuilder::InsertionGuard guard(builder); cgf.generateCode(gd, funcOp, funcType); } + curCGF = nullptr; } void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, @@ -466,8 +468,17 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, { mlir::OpBuilder::InsertionGuard guard(builder); + // Some global emissions are triggered while emitting a function, e.g. + // void s() { x.method() } + // + // Be sure to insert a new function before a current one. + if (curCGF) + builder.setInsertionPoint(curCGF->curFn); + func = builder.create<cir::FuncOp>(loc, name, funcType); - theModule.push_back(func); + + if (!curCGF) + theModule.push_back(func); } return func; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 6ba1ccc4ddd9f..7ee24699b521e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -41,6 +41,8 @@ class VarDecl; namespace CIRGen { +class CIRGenFunction; + enum ForDefinition_t : bool { NotForDefinition = false, ForDefinition = true }; /// This class organizes the cross-function state that is used while generating @@ -75,6 +77,10 @@ class CIRGenModule : public CIRGenTypeCache { CIRGenTypes genTypes; + /// Per-function codegen information. Updated everytime emitCIR is called + /// for FunctionDecls's. + CIRGenFunction *curCGF = nullptr; + public: mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 1e47ccc451b86..6be11a9d5a7ff 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -281,3 +281,33 @@ bool CIRGenTypes::isZeroInitializable(clang::QualType t) { return true; } + +const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo() { + // Lookup or create unique function info. + llvm::FoldingSetNodeID id; + CIRGenFunctionInfo::Profile(id); + + void *insertPos = nullptr; + CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos); + if (fi) + return *fi; + + assert(!cir::MissingFeatures::opCallCallConv()); + + // Construction the function info. We co-allocate the ArgInfos. + fi = CIRGenFunctionInfo::create(); + functionInfos.InsertNode(fi, insertPos); + + bool inserted = functionsBeingProcessed.insert(fi).second; + (void)inserted; + assert(inserted && "Recursively being processed?"); + + assert(!cir::MissingFeatures::opCallCallConv()); + assert(!cir::MissingFeatures::opCallArgs()); + + bool erased = functionsBeingProcessed.erase(fi); + (void)erased; + assert(erased && "Not in set?"); + + return *fi; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 73948f5c63e6a..ca43f96ed78a6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CODEGENTYPES_H #define LLVM_CLANG_LIB_CODEGEN_CODEGENTYPES_H +#include "CIRGenFunctionInfo.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/AST/Type.h" @@ -33,6 +34,7 @@ class Type; namespace clang::CIRGen { +class CallArgList; class CIRGenBuilderTy; class CIRGenModule; @@ -43,6 +45,11 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; + /// Hold memoized CIRGenFunctionInfo results + llvm::FoldingSet<CIRGenFunctionInfo> functionInfos; + + llvm::SmallPtrSet<const CIRGenFunctionInfo *, 4> functionsBeingProcessed; + /// Heper for convertType. mlir::Type convertFunctionTypeInternal(clang::QualType ft); @@ -75,6 +82,10 @@ class CIRGenTypes { /// Return whether a type can be zero-initialized (in the C++ sense) with an /// LLVM zeroinitializer. bool isZeroInitializable(clang::QualType t); + + const CIRGenFunctionInfo &arrangeFreeFunctionCall(); + + const CIRGenFunctionInfo &arrangeCIRFunctionInfo(); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index da8d63ca569af..794a63ba36d50 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIR CIRGenerator.cpp + CIRGenCall.cpp CIRGenDecl.cpp CIRGenExpr.cpp CIRGenExprAggregate.cpp diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 798aca602c4e3..6ff798c46691e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -441,6 +441,87 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) { return tryFoldCastChain(*this); } +//===----------------------------------------------------------------------===// +// CallOp +//===----------------------------------------------------------------------===// + +static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + mlir::FlatSymbolRefAttr calleeAttr; + + if (!parser.parseOptionalAttribute(calleeAttr, "callee", result.attributes) + .has_value()) + return mlir::failure(); + + if (parser.parseLParen()) + return mlir::failure(); + + // TODO(cir): parse argument list here + + if (parser.parseRParen()) + return mlir::failure(); + + if (parser.parseOptionalAttrDict(result.attributes)) + return ::mlir::failure(); + + if (parser.parseColon()) + return ::mlir::failure(); + + mlir::FunctionType opsFnTy; + if (parser.parseType(opsFnTy)) + return mlir::failure(); + + return mlir::success(); +} + +static void printCallCommon(mlir::Operation *op, + mlir::FlatSymbolRefAttr calleeSym, + mlir::OpAsmPrinter &printer) { + printer << ' '; + + printer.printAttributeWithoutType(calleeSym); + printer << "("; + // TODO(cir): print call args here + printer << ")"; + + printer.printOptionalAttrDict(op->getAttrs(), {"callee"}); + + printer << " : "; + printer.printFunctionalType(op->getOperands().getTypes(), + op->getResultTypes()); +} + +mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + return parseCallCommon(parser, result); +} + +void cir::CallOp::print(mlir::OpAsmPrinter &p) { + printCallCommon(*this, getCalleeAttr(), p); +} + +static LogicalResult +verifyCallCommInSymbolUses(mlir::Operation *op, + SymbolTableCollection &symbolTable) { + auto fnAttr = op->getAttrOfType<FlatSymbolRefAttr>("callee"); + if (!fnAttr) + return mlir::failure(); + + auto fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr); + if (!fn) + return op->emitOpError() << "'" << fnAttr.getValue() + << "' does not reference a valid function"; + + // TODO(cir): verify function arguments and return type + + return mlir::success(); +} + +LogicalResult +cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) { + return verifyCallCommInSymbolUses(*this, symbolTable); +} + //===----------------------------------------------------------------------===// // ReturnOp //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp new file mode 100644 index 0000000000000..e69b347c2ca99 --- /dev/null +++ b/clang/test/CIR/CodeGen/call.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s + +void f1(); +void f2() { + f1(); +} + +// CHECK-LABEL: cir.func @f2 +// CHECK: cir.call @f1() : () -> () _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits