https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/135552
This PR introduces support for calling functions with a scalar return type to the upstream. This PR also includes an initial version of `CIRGenTargetInfo` and related definitions which are essential for the CIRGen of call ops. Related to #132487 . >From fcd100485e1a589be20ddd6b9050cdd5e5281fa6 Mon Sep 17 00:00:00 2001 From: Sirui Mu <msrlanc...@gmail.com> Date: Sun, 13 Apr 2025 23:34:21 +0800 Subject: [PATCH] [CIR] cir.call with scalar return type This patch introduces support for calling functions with a scalar return type to the upstream. This patch also includes an initial version of CIRGenTargetInfo and related definitions which are essential for the CIRGen of call ops. --- clang/include/clang/CIR/ABIArgInfo.h | 89 +++++++++++++++++++ .../CIR/Dialect/Builder/CIRBaseBuilder.h | 8 +- clang/include/clang/CIR/Dialect/IR/CIROps.td | 6 +- clang/include/clang/CIR/MissingFeatures.h | 4 + clang/lib/CIR/CodeGen/ABIInfo.h | 32 +++++++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 82 ++++++++++++++--- clang/lib/CIR/CodeGen/CIRGenCall.h | 4 + clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 24 +++-- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 7 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 15 +++- clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 39 +++++++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 30 +++++++ clang/lib/CIR/CodeGen/CIRGenModule.h | 6 ++ clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 19 +++- clang/lib/CIR/CodeGen/CIRGenTypes.h | 9 +- clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/CodeGen/TargetInfo.cpp | 50 +++++++++++ clang/lib/CIR/CodeGen/TargetInfo.h | 41 +++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 29 +++++- clang/test/CIR/CodeGen/call.cpp | 10 +++ clang/test/CIR/IR/call.cir | 14 +++ 21 files changed, 480 insertions(+), 39 deletions(-) create mode 100644 clang/include/clang/CIR/ABIArgInfo.h create mode 100644 clang/lib/CIR/CodeGen/ABIInfo.h create mode 100644 clang/lib/CIR/CodeGen/TargetInfo.cpp create mode 100644 clang/lib/CIR/CodeGen/TargetInfo.h diff --git a/clang/include/clang/CIR/ABIArgInfo.h b/clang/include/clang/CIR/ABIArgInfo.h new file mode 100644 index 0000000000000..0c2cd85915aa7 --- /dev/null +++ b/clang/include/clang/CIR/ABIArgInfo.h @@ -0,0 +1,89 @@ +//==-- ABIArgInfo.h - Abstract info regarding ABI-specific arguments -------==// +// +// 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 ABIArgInfo and associated types used by CIR to track information +// regarding ABI-coerced types for function arguments and return values. This +// was moved to the common library as it might be used by both CIRGen and +// passes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_ABIARGINFO_H +#define LLVM_CLANG_CIR_ABIARGINFO_H + +#include "MissingFeatures.h" +#include "mlir/IR/Types.h" + +namespace cir { + +class ABIArgInfo { +public: + enum Kind : uint8_t { + /// Pass the argument directly using the normal converted CIR type, + /// or by coercing to another specified type stored in 'CoerceToType'). If + /// an offset is specified (in UIntData), then the argument passed is offset + /// by some number of bytes in the memory representation. A dummy argument + /// is emitted before the real argument if the specified type stored in + /// "PaddingType" is not zero. + Direct, + + /// Ignore the argument (treat as void). Useful for void and empty + /// structs. + Ignore, + }; + +private: + mlir::Type typeData; + struct DirectAttrInfo { + unsigned offset; + unsigned align; + }; + union { + DirectAttrInfo directAttr; + }; + Kind theKind; + +public: + ABIArgInfo(Kind k = Direct) : directAttr{0, 0}, theKind(k) {} + + static ABIArgInfo getDirect(mlir::Type ty = nullptr) { + ABIArgInfo info(Direct); + info.setCoerceToType(ty); + return info; + } + + static ABIArgInfo getIgnore() { return ABIArgInfo(Ignore); } + + Kind getKind() const { return theKind; } + bool isDirect() const { return theKind == Direct; } + bool isIgnore() const { return theKind == Ignore; } + + bool canHaveCoerceToType() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return isDirect(); + } + + unsigned getDirectOffset() const { + assert(!cir::MissingFeatures::abiArgInfo()); + return directAttr.offset; + } + + mlir::Type getCoerceToType() const { + assert(canHaveCoerceToType() && "invalid kind!"); + return typeData; + } + + void setCoerceToType(mlir::Type ty) { + assert(canHaveCoerceToType() && "invalid kind!"); + typeData = ty; + } +}; + +} // namespace cir + +#endif // LLVM_CLANG_CIR_ABIARGINFO_H diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 68a4505ca7a5a..a24006810c1f5 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -205,13 +205,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { // Call operators //===--------------------------------------------------------------------===// - cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee) { - auto op = create<cir::CallOp>(loc, callee); + cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, + mlir::Type returnType = cir::VoidType()) { + auto op = create<cir::CallOp>(loc, callee, /*resType=*/returnType); return op; } cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) { - return createCallOp(loc, mlir::SymbolRefAttr::get(callee)); + return createCallOp(loc, mlir::SymbolRefAttr::get(callee), + callee.getFunctionType().getReturnType()); } //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 0d3c2065cd58c..5ba4b33dc1a12 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1408,10 +1408,14 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { ``` }]; + let results = (outs Optional<CIR_AnyType>:$result); let arguments = commonArgs; - let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee), [{ + let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee, + "mlir::Type":$resType), [{ $_state.addAttribute("callee", callee); + if (resType && !isa<VoidType>(resType)) + $_state.addTypes(resType); }]>]; } diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index d6a28d4324b32..f692dc661e9d5 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -103,6 +103,8 @@ struct MissingFeatures { // Misc static bool cxxABI() { return false; } + static bool cirgenABIInfo() { return false; } + static bool abiArgInfo() { return false; } static bool tryEmitAsConstant() { return false; } static bool constructABIArgDirectExtend() { return false; } static bool opGlobalViewAttr() { return false; } @@ -121,6 +123,8 @@ struct MissingFeatures { static bool fpConstraints() { return false; } static bool sanitizers() { return false; } static bool addHeapAllocSiteMetadata() { return false; } + static bool targetCIRGenInfoArch() { return false; } + static bool targetCIRGenInfoOS() { return false; } static bool targetCodeGenInfoGetNullPointer() { return false; } static bool loopInfoStack() { return false; } static bool requiresCleanups() { return false; } diff --git a/clang/lib/CIR/CodeGen/ABIInfo.h b/clang/lib/CIR/CodeGen/ABIInfo.h new file mode 100644 index 0000000000000..157e80f67a67c --- /dev/null +++ b/clang/lib/CIR/CodeGen/ABIInfo.h @@ -0,0 +1,32 @@ +//===----- ABIInfo.h - ABI information access & encapsulation ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_ABIINFO_H +#define LLVM_CLANG_LIB_CIR_ABIINFO_H + +namespace clang::CIRGen { + +class CIRGenFunctionInfo; +class CIRGenTypes; + +class ABIInfo { + ABIInfo() = delete; + +public: + CIRGenTypes &cgt; + + ABIInfo(CIRGenTypes &cgt) : cgt(cgt) {} + + virtual ~ABIInfo(); + + virtual void computeInfo(CIRGenFunctionInfo &funcInfo) const = 0; +}; + +} // namespace clang::CIRGen + +#endif // LLVM_CLANG_LIB_CIR_ABIINFO_H diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 1a936458782ea..811750ebfc8b4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -18,9 +18,12 @@ using namespace clang; using namespace clang::CIRGen; -CIRGenFunctionInfo *CIRGenFunctionInfo::create() { - // For now we just create an empty CIRGenFunctionInfo. - CIRGenFunctionInfo *fi = new CIRGenFunctionInfo(); +CIRGenFunctionInfo *CIRGenFunctionInfo::create(CanQualType resultType) { + void *buffer = operator new(totalSizeToAlloc<ArgInfo>(1)); + + CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo(); + fi->getArgsBuffer()[0].type = resultType; + return fi; } @@ -29,13 +32,29 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { return *this; } -static const CIRGenFunctionInfo &arrangeFreeFunctionLikeCall(CIRGenTypes &cgt) { +static const CIRGenFunctionInfo & +arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, + const FunctionType *fnType) { + if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) { + if (proto->isVariadic()) + cgm.errorNYI("call to variadic function"); + if (proto->hasExtParameterInfos()) + cgm.errorNYI("call to functions with extra parameter info"); + } else if (isa<FunctionNoProtoType>(fnType)) { + cgm.errorNYI("call to function without a prototype"); + } + assert(!cir::MissingFeatures::opCallArgs()); - return cgt.arrangeCIRFunctionInfo(); + + CanQualType retType = fnType->getReturnType() + ->getCanonicalTypeUnqualified() + .getUnqualifiedType(); + return cgt.arrangeCIRFunctionInfo(retType); } -const CIRGenFunctionInfo &CIRGenTypes::arrangeFreeFunctionCall() { - return arrangeFreeFunctionLikeCall(*this); +const CIRGenFunctionInfo & +CIRGenTypes::arrangeFreeFunctionCall(const FunctionType *fnType) { + return arrangeFreeFunctionLikeCall(*this, cgm, fnType); } static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, @@ -54,8 +73,12 @@ static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, + ReturnValueSlot returnValue, cir::CIRCallOpInterface *callOp, mlir::Location loc) { + QualType retTy = funcInfo.getReturnType(); + const cir::ABIArgInfo &retInfo = funcInfo.getReturnInfo(); + assert(!cir::MissingFeatures::opCallArgs()); assert(!cir::MissingFeatures::emitLifetimeMarkers()); @@ -87,9 +110,48 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, 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); + RValue ret; + switch (retInfo.getKind()) { + case cir::ABIArgInfo::Direct: { + mlir::Type retCIRTy = convertType(retTy); + if (retInfo.getCoerceToType() == retCIRTy && + retInfo.getDirectOffset() == 0) { + switch (getEvaluationKind(retTy)) { + case cir::TEK_Scalar: { + mlir::ResultRange results = theCall->getOpResults(); + assert(results.size() == 1 && "unexpected number of returns"); + + // If the argument doesn't match, perform a bitcast to coerce it. This + // can happen due to trivial type mismatches. + if (results[0].getType() != retCIRTy) { + cgm.errorNYI(loc, "bitcast on function return value"); + } + + mlir::Region *region = builder.getBlock()->getParent(); + if (region != theCall->getParentRegion()) { + cgm.errorNYI(loc, "function calls with cleanup"); + } + + return RValue::get(results[0]); + } + default: + cgm.errorNYI(loc, + "unsupported evaluation kind of function call result"); + } + } else { + cgm.errorNYI(loc, "unsupported function call form"); + } + + break; + } + 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. + ret = getUndefRValue(retTy); + break; + default: + cgm.errorNYI(loc, "unsupported return value information"); + } return ret; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 76fefdca9e45e..4427fda863d7e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -81,6 +81,10 @@ struct CallArg {}; class CallArgList : public llvm::SmallVector<CallArg, 8> {}; +/// Contains the address where the return value of a function can be stored, and +/// whether the address is volatile or not. +class ReturnValueSlot {}; + } // 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 f0732a8ea60af..550231132ab53 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -662,23 +662,36 @@ static CIRGenCallee emitDirectCallee(CIRGenModule &cgm, GlobalDecl gd) { return CIRGenCallee::forDirect(callee, gd); } +RValue CIRGenFunction::getUndefRValue(QualType ty) { + if (ty->isVoidType()) + return RValue::get(nullptr); + + cgm.errorNYI("unsupported type for undef rvalue"); + return RValue::get(nullptr); +} + RValue CIRGenFunction::emitCall(clang::QualType calleeTy, const CIRGenCallee &callee, - const clang::CallExpr *e) { + const clang::CallExpr *e, + ReturnValueSlot returnValue) { // 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); + auto pointeeTy = cast<PointerType>(calleeTy)->getPointeeType(); if (getLangOpts().CPlusPlus) assert(!cir::MissingFeatures::sanitizers()); + const auto *fnType = cast<FunctionType>(pointeeTy); + assert(!cir::MissingFeatures::sanitizers()); assert(!cir::MissingFeatures::opCallArgs()); - const CIRGenFunctionInfo &funcInfo = cgm.getTypes().arrangeFreeFunctionCall(); + const CIRGenFunctionInfo &funcInfo = + cgm.getTypes().arrangeFreeFunctionCall(fnType); assert(!cir::MissingFeatures::opCallNoPrototypeFunc()); assert(!cir::MissingFeatures::opCallChainCall()); @@ -687,7 +700,7 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy, cir::CIRCallOpInterface callOp; RValue callResult = - emitCall(funcInfo, callee, &callOp, getLoc(e->getExprLoc())); + emitCall(funcInfo, callee, returnValue, &callOp, getLoc(e->getExprLoc())); assert(!cir::MissingFeatures::generateDebugInfo()); @@ -713,7 +726,8 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) { return {}; } -RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) { +RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e, + ReturnValueSlot returnValue) { assert(!cir::MissingFeatures::objCBlocks()); if (isa<CXXMemberCallExpr>(e)) { @@ -745,7 +759,7 @@ RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) { } assert(!cir::MissingFeatures::opCallPseudoDtor()); - return emitCall(e->getCallee()->getType(), callee, e); + return emitCall(e->getCallee()->getType(), callee, e, returnValue); } /// Emit code to compute the specified expression, ignoring the result. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 38104f8533c7d..3dae26dc86f85 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1519,11 +1519,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { } 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 {}; - } + if (e->getCallReturnType(cgf.getContext())->isReferenceType()) + return emitLoadOfLValue(e); auto v = cgf.emitCallExpr(e).getScalarVal(); assert(!cir::MissingFeatures::emitLValueAlignmentAssumption()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index a96d277d0bc0b..01abd84ce1c85 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -269,6 +269,12 @@ class CIRGenFunction : public CIRGenTypeCache { return LValue::makeAddr(addr, ty, baseInfo); } + /// Get an appropriate 'undef' rvalue for the given type. + /// TODO: What's the equivalent for MLIR? Currently we're only using this for + /// void types so it just returns RValue::get(nullptr) but it'll need + /// addressed later. + RValue getUndefRValue(clang::QualType ty); + cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType); @@ -451,11 +457,12 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s); RValue emitCall(const CIRGenFunctionInfo &funcInfo, - const CIRGenCallee &callee, cir::CIRCallOpInterface *callOp, - mlir::Location loc); + const CIRGenCallee &callee, ReturnValueSlot returnValue, + cir::CIRCallOpInterface *callOp, mlir::Location loc); RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee, - const clang::CallExpr *e); - RValue emitCallExpr(const clang::CallExpr *e); + const clang::CallExpr *e, ReturnValueSlot returnValue); + RValue emitCallExpr(const clang::CallExpr *e, + ReturnValueSlot returnValue = ReturnValueSlot()); CIRGenCallee emitCallee(const clang::Expr *e); mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h index da73e7a7a9059..c4a2b238c96ae 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h @@ -15,18 +15,49 @@ #ifndef LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H #define LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H +#include "clang/AST/CanonicalType.h" +#include "clang/CIR/ABIArgInfo.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/TrailingObjects.h" namespace clang::CIRGen { -class CIRGenFunctionInfo final : public llvm::FoldingSetNode { +struct CIRGenFunctionInfoArgInfo { + CanQualType type; + cir::ABIArgInfo info; +}; + +class CIRGenFunctionInfo final + : public llvm::FoldingSetNode, + private llvm::TrailingObjects<CIRGenFunctionInfo, + CIRGenFunctionInfoArgInfo> { + using ArgInfo = CIRGenFunctionInfoArgInfo; + + ArgInfo *getArgsBuffer() { return getTrailingObjects<ArgInfo>(); } + const ArgInfo *getArgsBuffer() const { return getTrailingObjects<ArgInfo>(); } + public: - static CIRGenFunctionInfo *create(); + static CIRGenFunctionInfo *create(CanQualType resultType); + + void operator delete(void *p) { ::operator delete(p); } + + // Friending class TrailingObjects is apparantly not good enough for MSVC, so + // these have to be public. + friend class TrailingObjects; // This function has to be CamelCase because llvm::FoldingSet requires so. // NOLINTNEXTLINE(readability-identifier-naming) - static void Profile(llvm::FoldingSetNodeID &id) { - // We don't have anything to profile yet. + static void Profile(llvm::FoldingSetNodeID &id, CanQualType resultType) { + resultType.Profile(id); + } + + void Profile(llvm::FoldingSetNodeID &id) { getReturnType().Profile(id); } + + CanQualType getReturnType() const { return getArgsBuffer()[0].type; } + + cir::ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } + const cir::ABIArgInfo &getReturnInfo() const { + return getArgsBuffer()[0].info; } }; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index fd11523ebba61..cbfaa3d89836b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -129,6 +129,36 @@ CharUnits CIRGenModule::getNaturalTypeAlignment(QualType t, return alignment; } +const TargetCIRGenInfo &CIRGenModule::getTargetCIRGenInfo() { + if (theTargetCIRGenInfo) + return *theTargetCIRGenInfo; + + const llvm::Triple &triple = getTarget().getTriple(); + switch (triple.getArch()) { + default: + assert(!cir::MissingFeatures::targetCIRGenInfoArch()); + errorNYI("unsupported target arch"); + + // Currently we just fall through to x86_64. + [[fallthrough]]; + + case llvm::Triple::x86_64: { + switch (triple.getOS()) { + default: + assert(!cir::MissingFeatures::targetCIRGenInfoOS()); + errorNYI("unsupported target OS"); + + // Currently we just fall through to x86_64. + [[fallthrough]]; + + case llvm::Triple::Linux: + theTargetCIRGenInfo = createX8664TargetCIRGenInfo(genTypes); + return *theTargetCIRGenInfo; + } + } + } +} + mlir::Location CIRGenModule::getLoc(SourceLocation cLoc) { assert(cLoc.isValid() && "expected valid source location"); const SourceManager &sm = astContext.getSourceManager(); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 764ad1d7592aa..1e0d6623c4f40 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -21,6 +21,7 @@ #include "clang/AST/CharUnits.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "TargetInfo.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" @@ -60,6 +61,8 @@ class CIRGenModule : public CIRGenTypeCache { ~CIRGenModule() = default; private: + mutable std::unique_ptr<TargetCIRGenInfo> theTargetCIRGenInfo; + CIRGenBuilderTy builder; /// Hold Clang AST information. @@ -86,6 +89,7 @@ class CIRGenModule : public CIRGenTypeCache { mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } clang::ASTContext &getASTContext() const { return astContext; } + const clang::TargetInfo &getTarget() const { return target; } const clang::CodeGenOptions &getCodeGenOpts() const { return codeGenOpts; } CIRGenTypes &getTypes() { return genTypes; } const clang::LangOptions &getLangOpts() const { return langOpts; } @@ -116,6 +120,8 @@ class CIRGenModule : public CIRGenTypeCache { getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {}, ForDefinition_t isForDefinition = NotForDefinition); + const TargetCIRGenInfo &getTargetCIRGenInfo(); + /// Helpers to convert the presumed location of Clang's SourceLocation to an /// MLIR Location. mlir::Location getLoc(clang::SourceLocation cLoc); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index a5978a4ad9085..ccc5b5fc070d2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -14,7 +14,8 @@ using namespace clang::CIRGen; CIRGenTypes::CIRGenTypes(CIRGenModule &genModule) : cgm(genModule), astContext(genModule.getASTContext()), - builder(cgm.getBuilder()) {} + builder(cgm.getBuilder()), + theABIInfo(cgm.getTargetCIRGenInfo().getABIInfo()) {} CIRGenTypes::~CIRGenTypes() {} @@ -290,10 +291,11 @@ bool CIRGenTypes::isZeroInitializable(clang::QualType t) { return true; } -const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo() { +const CIRGenFunctionInfo & +CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType) { // Lookup or create unique function info. llvm::FoldingSetNodeID id; - CIRGenFunctionInfo::Profile(id); + CIRGenFunctionInfo::Profile(id, returnType); void *insertPos = nullptr; CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos); @@ -303,7 +305,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo() { assert(!cir::MissingFeatures::opCallCallConv()); // Construction the function info. We co-allocate the ArgInfos. - fi = CIRGenFunctionInfo::create(); + fi = CIRGenFunctionInfo::create(returnType); functionInfos.InsertNode(fi, insertPos); bool inserted = functionsBeingProcessed.insert(fi).second; @@ -311,6 +313,15 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo() { assert(inserted && "Are functions being processed recursively?"); assert(!cir::MissingFeatures::opCallCallConv()); + getABIInfo().computeInfo(*fi); + + // 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) + retInfo.setCoerceToType(convertType(fi->getReturnType())); + assert(!cir::MissingFeatures::opCallArgs()); bool erased = functionsBeingProcessed.erase(fi); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 60661ba0a3beb..59548f5c1f0b3 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 "ABIInfo.h" #include "CIRGenFunctionInfo.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" @@ -45,6 +46,8 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; + const ABIInfo &theABIInfo; + /// Hold memoized CIRGenFunctionInfo results llvm::FoldingSet<CIRGenFunctionInfo> functionInfos; @@ -69,6 +72,8 @@ class CIRGenTypes { mlir::MLIRContext &getMLIRContext() const; + const ABIInfo &getABIInfo() const { return theABIInfo; } + /// Convert a Clang type into a mlir::Type. mlir::Type convertType(clang::QualType type); @@ -83,9 +88,9 @@ class CIRGenTypes { /// LLVM zeroinitializer. bool isZeroInitializable(clang::QualType ty); - const CIRGenFunctionInfo &arrangeFreeFunctionCall(); + const CIRGenFunctionInfo &arrangeFreeFunctionCall(const FunctionType *fnType); - const CIRGenFunctionInfo &arrangeCIRFunctionInfo(); + const CIRGenFunctionInfo &arrangeCIRFunctionInfo(CanQualType returnType); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index dc18f7f2af160..59834eac3049f 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangCIR CIRGenStmt.cpp CIRGenStmtOpenACC.cpp CIRGenTypes.cpp + TargetInfo.cpp DEPENDS MLIRCIR diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp new file mode 100644 index 0000000000000..8b89f8dc8b431 --- /dev/null +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -0,0 +1,50 @@ +#include "TargetInfo.h" +#include "ABIInfo.h" +#include "CIRGenFunctionInfo.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +static bool testIfIsVoidTy(QualType ty) { + const auto *builtinTy = ty->getAs<BuiltinType>(); + return builtinTy && builtinTy->getKind() == BuiltinType::Void; +} + +namespace { + +class X8664ABIInfo : public ABIInfo { +public: + X8664ABIInfo(CIRGenTypes &cgt) : ABIInfo(cgt) {} + + void computeInfo(CIRGenFunctionInfo &funcInfo) const override; +}; + +class X8664TargetCIRGenInfo : public TargetCIRGenInfo { +public: + X8664TargetCIRGenInfo(CIRGenTypes &cgt) + : TargetCIRGenInfo(std::make_unique<X8664ABIInfo>(cgt)) {} +}; + +} // namespace + +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. + assert(!cir::MissingFeatures::opCallArgs()); + + CanQualType retTy = funcInfo.getReturnType(); + if (testIfIsVoidTy(retTy)) + funcInfo.getReturnInfo() = cir::ABIArgInfo::getIgnore(); + else + funcInfo.getReturnInfo() = + cir::ABIArgInfo::getDirect(cgt.convertType(retTy)); +} + +std::unique_ptr<TargetCIRGenInfo> +clang::CIRGen::createX8664TargetCIRGenInfo(CIRGenTypes &cgt) { + return std::make_unique<X8664TargetCIRGenInfo>(cgt); +} + +ABIInfo::~ABIInfo() noexcept = default; diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h new file mode 100644 index 0000000000000..70590c3c65ebb --- /dev/null +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -0,0 +1,41 @@ +//===---- TargetInfo.h - Encapsulate target details -------------*- C++ -*-===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_TARGETINFO_H +#define LLVM_CLANG_LIB_CIR_TARGETINFO_H + +#include "ABIInfo.h" +#include "CIRGenTypes.h" + +#include <memory> +#include <utility> + +namespace clang::CIRGen { + +class TargetCIRGenInfo { + std::unique_ptr<ABIInfo> info; + +public: + TargetCIRGenInfo(std::unique_ptr<ABIInfo> info) : info(std::move(info)) {} + + virtual ~TargetCIRGenInfo() = default; + + /// Returns ABI info helper for the target. + const ABIInfo &getABIInfo() const { return *info; } +}; + +std::unique_ptr<TargetCIRGenInfo> createX8664TargetCIRGenInfo(CIRGenTypes &cgt); + +} // namespace clang::CIRGen + +#endif // LLVM_CLANG_LIB_CIR_TARGETINFO_H diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index f3e5e572653da..5e910f92002d0 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -449,6 +449,7 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) { static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, mlir::OperationState &result) { mlir::FlatSymbolRefAttr calleeAttr; + llvm::ArrayRef<mlir::Type> allResultTypes; if (!parser.parseOptionalAttribute(calleeAttr, "callee", result.attributes) .has_value()) @@ -473,6 +474,9 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, if (parser.parseType(opsFnTy)) return mlir::failure(); + allResultTypes = opsFnTy.getResults(); + result.addTypes(allResultTypes); + return mlir::success(); } @@ -515,9 +519,32 @@ verifyCallCommInSymbolUses(mlir::Operation *op, return op->emitOpError() << "'" << fnAttr.getValue() << "' does not reference a valid function"; - // TODO(cir): verify function arguments and return type + auto callIf = dyn_cast<cir::CIRCallOpInterface>(op); + assert(callIf && "expected CIR call interface to be always available"); + + // Verify that the operand and result types match the callee. Note that + // argument-checking is disabled for functions without a prototype. + auto fnType = fn.getFunctionType(); + + // TODO(cir): verify function arguments assert(!cir::MissingFeatures::opCallArgs()); + // Void function must not return any results. + if (fnType.hasVoidReturn() && op->getNumResults() != 0) + return op->emitOpError("callee returns void but call has results"); + + // Non-void function calls must return exactly one result. + if (!fnType.hasVoidReturn() && op->getNumResults() != 1) + return op->emitOpError("incorrect number of results for callee"); + + // Parent function and return value types must match. + if (!fnType.hasVoidReturn() && + op->getResultTypes().front() != fnType.getReturnType()) { + return op->emitOpError("result type mismatch: expected ") + << fnType.getReturnType() << ", but provided " + << op->getResult(0).getType(); + } + return mlir::success(); } diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp index e69b347c2ca99..9082fbc9f6860 100644 --- a/clang/test/CIR/CodeGen/call.cpp +++ b/clang/test/CIR/CodeGen/call.cpp @@ -7,3 +7,13 @@ void f2() { // CHECK-LABEL: cir.func @f2 // CHECK: cir.call @f1() : () -> () + +int f3(); +int f4() { + int x = f3(); + return x; +} + +// CHECK-LABEL: cir.func @f4() -> !s32i +// CHECK: %[[#x:]] = cir.call @f3() : () -> !s32i +// CHECK-NEXT: cir.store %[[#x]], %{{.+}} : !s32i, !cir.ptr<!s32i> diff --git a/clang/test/CIR/IR/call.cir b/clang/test/CIR/IR/call.cir index 8630bb80eb14a..3c3fbf3d4d987 100644 --- a/clang/test/CIR/IR/call.cir +++ b/clang/test/CIR/IR/call.cir @@ -1,5 +1,7 @@ // RUN: cir-opt %s | FileCheck %s +!s32i = !cir.int<s, 32> + module { cir.func @f1() @@ -14,4 +16,16 @@ cir.func @f2() { // CHECK-NEXT: cir.return // CHECK-NEXT: } +cir.func @f3() -> !s32i + +cir.func @f4() -> !s32i { + %0 = cir.call @f3() : () -> !s32i + cir.return %0 : !s32i +} + +// CHECK: cir.func @f4() -> !s32i { +// CHECK-NEXT: %[[#x:]] = cir.call @f3() : () -> !s32i +// CHECK-NEXT: cir.return %[[#x]] : !s32i +// CHECK-NEXT: } + } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits