Author: Sirui Mu Date: 2025-04-17T22:38:37+08:00 New Revision: 78857e7263ba555fb40b286c6b40fcd35a85a65a
URL: https://github.com/llvm/llvm-project/commit/78857e7263ba555fb40b286c6b40fcd35a85a65a DIFF: https://github.com/llvm/llvm-project/commit/78857e7263ba555fb40b286c6b40fcd35a85a65a.diff LOG: [CIR] cir.call with scalar return type (#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 . Added: clang/include/clang/CIR/ABIArgInfo.h clang/lib/CIR/CodeGen/ABIInfo.h clang/lib/CIR/CodeGen/TargetInfo.cpp clang/lib/CIR/CodeGen/TargetInfo.h clang/test/CIR/IR/invalid-call.cir Modified: clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenCall.cpp clang/lib/CIR/CodeGen/CIRGenCall.h clang/lib/CIR/CodeGen/CIRGenExpr.cpp clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/CodeGen/CIRGenModule.h clang/lib/CIR/CodeGen/CIRGenTypes.cpp clang/lib/CIR/CodeGen/CIRGenTypes.h clang/lib/CIR/CodeGen/CMakeLists.txt clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/test/CIR/CodeGen/call.cpp clang/test/CIR/IR/call.cir Removed: ################################################################################ diff --git a/clang/include/clang/CIR/ABIArgInfo.h b/clang/include/clang/CIR/ABIArgInfo.h new file mode 100644 index 0000000000000..b8d10445f9586 --- /dev/null +++ b/clang/include/clang/CIR/ABIArgInfo.h @@ -0,0 +1,92 @@ +//==-- 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 CLANG_CIR_ABIARGINFO_H +#define CLANG_CIR_ABIARGINFO_H + +#include "mlir/IR/Types.h" +#include "clang/CIR/MissingFeatures.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, + + // TODO: more argument kinds will be added as the upstreaming proceeds. + }; + +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); + assert(!cir::MissingFeatures::abiArgInfo()); + 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 // 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 a787908fb9f8d..d2a241964f34f 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -207,13 +207,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) { + auto op = create<cir::CallOp>(loc, callee, 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 5ba55c53dfc4d..0b66ed31d01f1 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -114,6 +114,9 @@ struct MissingFeatures { // Misc static bool cxxABI() { return false; } + static bool cirgenABIInfo() { return false; } + static bool cirgenTargetInfo() { return false; } + static bool abiArgInfo() { return false; } static bool tryEmitAsConstant() { return false; } static bool constructABIArgDirectExtend() { return false; } static bool opGlobalViewAttr() { return false; } @@ -132,6 +135,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..69266f79a88a5 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 (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic( + cast<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,45 @@ 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 cffe5c5cd1ec3..aca26526b79f2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -695,23 +695,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()); @@ -720,7 +733,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()); @@ -746,7 +759,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)) { @@ -778,7 +792,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 ddcdc0d269708..1b504546162c1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -129,6 +129,34 @@ 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()); + + // Currently we just fall through to x86_64. + [[fallthrough]]; + + case llvm::Triple::x86_64: { + switch (triple.getOS()) { + default: + assert(!cir::MissingFeatures::targetCIRGenInfoOS()); + + // 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 ec77c4428d43b..a896b9dce14d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -14,9 +14,13 @@ 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() {} +CIRGenTypes::~CIRGenTypes() { + for (auto i = functionInfos.begin(), e = functionInfos.end(); i != e;) + delete &*i++; +} mlir::MLIRContext &CIRGenTypes::getMLIRContext() const { return *builder.getContext(); @@ -392,10 +396,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); @@ -405,7 +410,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; @@ -413,6 +418,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 38e4bb2f688ab..fc6e9cf621cb3 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 "CIRGenRecordLayout.h" @@ -46,6 +47,8 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; + const ABIInfo &theABIInfo; + /// Contains the CIR type for any converted RecordDecl. llvm::DenseMap<const clang::Type *, std::unique_ptr<CIRGenRecordLayout>> cirGenRecordLayouts; @@ -88,6 +91,8 @@ class CIRGenTypes { bool noRecordsBeingLaidOut() const { return recordsBeingLaidOut.empty(); } + const ABIInfo &getABIInfo() const { return theABIInfo; } + /// Convert a Clang type into a mlir::Type. mlir::Type convertType(clang::QualType type); @@ -110,9 +115,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 418bc2db408cb..400af0237d588 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -22,6 +22,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..0d0ffb93d4e7e --- /dev/null +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -0,0 +1,59 @@ +#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; + +bool TargetCIRGenInfo::isNoProtoCallVariadic( + const FunctionNoProtoType *fnType) const { + // The following conventions are known to require this to be false: + // x86_stdcall + // MIPS + // For everything else, we just prefer false unless we opt out. + return false; +} diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h new file mode 100644 index 0000000000000..d31d1ee82d90a --- /dev/null +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -0,0 +1,85 @@ +//===---- 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; } + + /// Determine whether a call to an unprototyped functions under + /// the given calling convention should use the variadic + /// convention or the non-variadic convention. + /// + /// There's a good reason to make a platform's variadic calling + /// convention be diff erent from its non-variadic calling + /// convention: the non-variadic arguments can be passed in + /// registers (better for performance), and the variadic arguments + /// can be passed on the stack (also better for performance). If + /// this is done, however, unprototyped functions *must* use the + /// non-variadic convention, because C99 states that a call + /// through an unprototyped function type must succeed if the + /// function was defined with a non-variadic prototype with + /// compatible parameters. Therefore, splitting the conventions + /// makes it impossible to call a variadic function through an + /// unprototyped type. Since function prototypes came out in the + /// late 1970s, this is probably an acceptable trade-off. + /// Nonetheless, not all platforms are willing to make it, and in + /// particularly x86-64 bends over backwards to make the + /// conventions compatible. + /// + /// The default is false. This is correct whenever: + /// - the conventions are exactly the same, because it does not + /// matter and the resulting IR will be somewhat prettier in + /// certain cases; or + /// - the conventions are substantively diff erent in how they pass + /// arguments, because in this case using the variadic convention + /// will lead to C99 violations. + /// + /// However, some platforms make the conventions identical except + /// for passing additional out-of-band information to a variadic + /// function: for example, x86-64 passes the number of SSE + /// arguments in %al. On these platforms, it is desirable to + /// call unprototyped functions using the variadic convention so + /// that unprototyped calls to varargs functions still succeed. + /// + /// Relatedly, platforms which pass the fixed arguments to this: + /// A foo(B, C, D); + /// diff erently than they would pass them to this: + /// A foo(B, C, D, ...); + /// may need to adjust the debugger-support code in Sema to do the + /// right thing when calling a function with no know signature. + virtual bool isNoProtoCallVariadic(const FunctionNoProtoType *fnType) const; +}; + +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 1f4232b9e29ec..d2313e75870b4 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: } + } diff --git a/clang/test/CIR/IR/invalid-call.cir b/clang/test/CIR/IR/invalid-call.cir new file mode 100644 index 0000000000000..64b6d56e0fa88 --- /dev/null +++ b/clang/test/CIR/IR/invalid-call.cir @@ -0,0 +1,43 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +!u32i = !cir.int<u, 32> + +cir.func @f1() { + // expected-error @below {{'f' does not reference a valid function}} + cir.call @f() : () -> () + cir.return +} + +// ----- + +!u32i = !cir.int<u, 32> + +cir.func @f2() +cir.func @f3() { + // expected-error @below {{callee returns void but call has results}} + %0 = cir.call @f2() : () -> !u32i + cir.return +} + +// ----- + +!u32i = !cir.int<u, 32> + +cir.func @f4() -> !u32i +cir.func @f5() { + // expected-error @below {{incorrect number of results for callee}} + cir.call @f4() : () -> () + cir.return +} + +// ----- + +!s32i = !cir.int<s, 32> +!u32i = !cir.int<u, 32> + +cir.func @f6() -> !u32i +cir.func @f7() { + // expected-error @below {{result type mismatch}} + %0 = cir.call @f6() : () -> !s32i + cir.return +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits