llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Sirui Mu (Lancern) <details> <summary>Changes</summary> 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 --- Patch is 27.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/134673.diff 17 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+13) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+37) - (modified) clang/include/clang/CIR/Interfaces/CIROpInterfaces.td (+9) - (modified) clang/include/clang/CIR/MissingFeatures.h (+24) - (added) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+95) - (modified) clang/lib/CIR/CodeGen/CIRGenCall.h (+58) - (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+97) - (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+13) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+20) - (added) clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h (+34) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+12-1) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+6) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+30) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+11) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+81) - (added) clang/test/CIR/CodeGen/call.cpp (+9) ``````````diff 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 @@... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/134673 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits