llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Sirui Mu (Lancern) <details> <summary>Changes</summary> This PR upstreams support for scalar arguments in `cir.call` operation. Related to #<!-- -->132487 . --- Patch is 40.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136810.diff 16 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+5-4) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+44-2) - (modified) clang/include/clang/CIR/Interfaces/CIROpInterfaces.td (+16-1) - (modified) clang/include/clang/CIR/MissingFeatures.h (+11-1) - (modified) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+265-12) - (modified) clang/lib/CIR/CodeGen/CIRGenCall.h (+30-2) - (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+20-4) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+47-1) - (modified) clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h (+25-2) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+10-5) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+5-2) - (modified) clang/lib/CIR/CodeGen/TargetInfo.cpp (+7-1) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+49-9) - (modified) clang/test/CIR/CodeGen/call.cpp (+12) - (modified) clang/test/CIR/IR/call.cir (+15) - (modified) clang/test/CIR/IR/invalid-call.cir (+27) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 539268c6270f4..0a6e47ea43a8c 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -214,14 +214,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { //===--------------------------------------------------------------------===// cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, - mlir::Type returnType) { - auto op = create<cir::CallOp>(loc, callee, returnType); + mlir::Type returnType, mlir::ValueRange operands) { + auto op = create<cir::CallOp>(loc, callee, returnType, operands); return op; } - cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) { + cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee, + mlir::ValueRange operands) { return createCallOp(loc, mlir::SymbolRefAttr::get(callee), - callee.getFunctionType().getReturnType()); + callee.getFunctionType().getReturnType(), operands); } //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bb19de31b4fa5..aa7a9b2de664f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1496,6 +1496,10 @@ def FuncOp : CIR_Op<"func", [ return getFunctionType().getReturnTypes(); } + // TODO(cir): this should be an operand attribute, but for now we just hard- + // wire this as a function. Will later add a $no_proto argument to this op. + bool getNoProto() { return false; } + //===------------------------------------------------------------------===// // SymbolOpInterface Methods //===------------------------------------------------------------------===// @@ -1516,6 +1520,41 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> !listconcat(extra_traits, [DeclareOpInterfaceMethods<CIRCallOpInterface>, DeclareOpInterfaceMethods<SymbolUserOpInterface>])> { + let extraClassDeclaration = [{ + /// Get the argument operands to the called function. + mlir::OperandRange getArgOperands() { + return {arg_operand_begin(), arg_operand_end()}; + } + + mlir::MutableOperandRange getArgOperandsMutable() { + llvm_unreachable("NYI"); + } + + /// Return the callee of this operation + mlir::CallInterfaceCallable getCallableForCallee() { + return (*this)->getAttrOfType<mlir::SymbolRefAttr>("callee"); + } + + /// Set the callee for this operation. + void setCalleeFromCallable(::mlir::CallInterfaceCallable callee) { + (*this)->setAttr(getCalleeAttrName(), + mlir::cast<mlir::SymbolRefAttr>(callee)); + } + + ::mlir::ArrayAttr getArgAttrsAttr() { return {}; } + ::mlir::ArrayAttr getResAttrsAttr() { return {}; } + + void setResAttrsAttr(::mlir::ArrayAttr attrs) {} + void setArgAttrsAttr(::mlir::ArrayAttr attrs) {} + + ::mlir::Attribute removeArgAttrsAttr() { return {}; } + ::mlir::Attribute removeResAttrsAttr() { return {}; } + + void setArg(unsigned index, mlir::Value value) { + setOperand(index, value); + } + }]; + let hasCustomAssemblyFormat = 1; let skipDefaultBuilders = 1; let hasVerifier = 0; @@ -1525,7 +1564,8 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> // the upstreaming process moves on. The verifiers is also missing for now, // will add in the future. - dag commonArgs = (ins FlatSymbolRefAttr:$callee); + dag commonArgs = (ins FlatSymbolRefAttr:$callee, + Variadic<CIR_AnyType>:$args); } def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { @@ -1546,7 +1586,9 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { let arguments = commonArgs; let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee, - "mlir::Type":$resType), [{ + "mlir::Type":$resType, + "mlir::ValueRange":$operands), [{ + $_state.addOperands(operands); $_state.addAttribute("callee", callee); if (resType && !isa<VoidType>(resType)) $_state.addTypes(resType); diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index c6c6356118ac6..8227ce4bea5a3 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -21,9 +21,24 @@ 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", []> { + def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", [CallOpInterface]> { // Currently we don't have any methods defined in CIRCallOpInterface. We'll // add more methods as the upstreaming proceeds. + let methods = [ + InterfaceMethod<"", "mlir::Operation::operand_iterator", + "arg_operand_begin", (ins)>, + InterfaceMethod<"", "mlir::Operation::operand_iterator", + "arg_operand_end", (ins)>, + InterfaceMethod< + "Return the operand at index 'i', accounts for indirect call or " + "exception info", + "mlir::Value", "getArgOperand", + (ins "unsigned":$i)>, + InterfaceMethod< + "Return the number of operands, accounts for indirect call or " + "exception info", + "unsigned", "getNumArgOperands", (ins)>, + ]; } def CIRGlobalValueInterface diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 6bfc1199aea55..370d82d26ebe7 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -76,7 +76,13 @@ struct MissingFeatures { // CallOp handling static bool opCallBuiltinFunc() { return false; } static bool opCallPseudoDtor() { return false; } - static bool opCallArgs() { return false; } + static bool opCallAggregateArgs() { return false; } + static bool opCallPaddingArgs() { return false; } + static bool opCallABIExtendArg() { return false; } + static bool opCallABIIndirectArg() { return false; } + static bool opCallWidenArg() { return false; } + static bool opCallBitcastArg() { return false; } + static bool opCallImplicitObjectSizeArgs() { return false; } static bool opCallReturn() { return false; } static bool opCallArgEvaluationOrder() { return false; } static bool opCallCallConv() { return false; } @@ -90,6 +96,9 @@ struct MissingFeatures { static bool opCallAttrs() { return false; } static bool opCallSurroundingTry() { return false; } static bool opCallASTAttr() { return false; } + static bool opCallVariadic() { return false; } + static bool opCallObjCMethod() { return false; } + static bool opCallExtParameterInfo() { return false; } // ScopeOp handling static bool opScopeCleanupRegion() { return false; } @@ -157,6 +166,7 @@ struct MissingFeatures { static bool emitCheckedInBoundsGEP() { return false; } static bool preservedAccessIndexRegion() { return false; } static bool bitfields() { return false; } + static bool msabi() { return false; } static bool typeChecks() { return false; } static bool lambdaFieldToName() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 69266f79a88a5..bea91f8ec0ec7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -18,15 +18,110 @@ using namespace clang; using namespace clang::CIRGen; -CIRGenFunctionInfo *CIRGenFunctionInfo::create(CanQualType resultType) { - void *buffer = operator new(totalSizeToAlloc<ArgInfo>(1)); +CIRGenFunctionInfo * +CIRGenFunctionInfo::create(CanQualType resultType, + llvm::ArrayRef<CanQualType> argTypes) { + void *buffer = operator new(totalSizeToAlloc<ArgInfo>(argTypes.size() + 1)); CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo(); + fi->numArgs = argTypes.size(); fi->getArgsBuffer()[0].type = resultType; + for (unsigned i = 0; i < argTypes.size(); ++i) + fi->getArgsBuffer()[i + 1].type = argTypes[i]; return fi; } +namespace { + +/// Encapsulates information about the way function arguments from +/// CIRGenFunctionInfo should be passed to actual CIR function. +class ClangToCIRArgMapping { + static constexpr unsigned invalidIndex = ~0U; + unsigned totalNumCIRArgs; + + /// Arguments of CIR function corresponding to single Clang argument. + struct CIRArgs { + // Argument is expanded to CIR arguments at positions + // [FirstArgIndex, FirstArgIndex + NumberOfArgs). + unsigned firstArgIndex = 0; + unsigned numberOfArgs = 0; + + CIRArgs() : firstArgIndex(invalidIndex), numberOfArgs(0) {} + }; + + SmallVector<CIRArgs, 8> argInfo; + +public: + ClangToCIRArgMapping(const ASTContext &astContext, + const CIRGenFunctionInfo &funcInfo) + : totalNumCIRArgs(0), argInfo(funcInfo.arg_size()) { + construct(astContext, funcInfo); + } + + unsigned totalCIRArgs() const { return totalNumCIRArgs; } + + /// Returns index of first CIR argument corresponding to argNo, and their + /// quantity. + std::pair<unsigned, unsigned> getCIRArgs(unsigned argNo) const { + assert(argNo < argInfo.size()); + return std::make_pair(argInfo[argNo].firstArgIndex, + argInfo[argNo].numberOfArgs); + } + +private: + void construct(const ASTContext &astContext, + const CIRGenFunctionInfo &funcInfo); +}; + +void ClangToCIRArgMapping::construct(const ASTContext &astContext, + const CIRGenFunctionInfo &funcInfo) { + unsigned cirArgNo = 0; + + assert(!cir::MissingFeatures::opCallABIIndirectArg()); + + unsigned argNo = 0; + unsigned numArgs = funcInfo.arg_size(); + for (const auto *i = funcInfo.arg_begin(); argNo < numArgs; ++i, ++argNo) { + assert(i != funcInfo.arg_end()); + const cir::ABIArgInfo &ai = i->info; + // Collect data about CIR arguments corresponding to Clang argument ArgNo. + auto &cirArgs = argInfo[argNo]; + + assert(!cir::MissingFeatures::opCallPaddingArgs()); + + switch (ai.getKind()) { + default: + assert(!cir::MissingFeatures::abiArgInfo()); + // For now we just fall through. More argument kinds will be added later + // as the upstreaming proceeds. + [[fallthrough]]; + case cir::ABIArgInfo::Direct: + // Postpone splitting structs into elements since this makes it way + // more complicated for analysis to obtain information on the original + // arguments. + // + // TODO(cir): a LLVM lowering prepare pass should break this down into + // the appropriated pieces. + assert(!cir::MissingFeatures::opCallABIExtendArg()); + cirArgs.numberOfArgs = 1; + break; + } + + if (cirArgs.numberOfArgs > 0) { + cirArgs.firstArgIndex = cirArgNo; + cirArgNo += cirArgs.numberOfArgs; + } + } + + assert(argNo == argInfo.size()); + assert(!cir::MissingFeatures::opCallInAlloca()); + + totalNumCIRArgs = cirArgNo; +} + +} // namespace + CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { assert(!cir::MissingFeatures::opCallVirtual()); return *this; @@ -34,6 +129,7 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { static const CIRGenFunctionInfo & arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, + const CallArgList &args, const FunctionType *fnType) { if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) { if (proto->isVariadic()) @@ -44,22 +140,26 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, cast<FunctionNoProtoType>(fnType))) cgm.errorNYI("call to function without a prototype"); - assert(!cir::MissingFeatures::opCallArgs()); + SmallVector<CanQualType, 16> argTypes; + for (const CallArg &arg : args) + argTypes.push_back(cgt.getASTContext().getCanonicalParamType(arg.ty)); CanQualType retType = fnType->getReturnType() ->getCanonicalTypeUnqualified() .getUnqualifiedType(); - return cgt.arrangeCIRFunctionInfo(retType); + return cgt.arrangeCIRFunctionInfo(retType, argTypes); } const CIRGenFunctionInfo & -CIRGenTypes::arrangeFreeFunctionCall(const FunctionType *fnType) { - return arrangeFreeFunctionLikeCall(*this, cgm, fnType); +CIRGenTypes::arrangeFreeFunctionCall(const CallArgList &args, + const FunctionType *fnType) { + return arrangeFreeFunctionLikeCall(*this, cgm, args, fnType); } -static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, - mlir::Location callLoc, - cir::FuncOp directFuncOp) { +static cir::CIRCallOpInterface +emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, + cir::FuncOp directFuncOp, + const SmallVectorImpl<mlir::Value> &cirCallArgs) { CIRGenBuilderTy &builder = cgf.getBuilder(); assert(!cir::MissingFeatures::opCallSurroundingTry()); @@ -68,20 +168,70 @@ static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, assert(builder.getInsertionBlock() && "expected valid basic block"); assert(!cir::MissingFeatures::opCallIndirect()); - return builder.createCallOp(callLoc, directFuncOp); + return builder.createCallOp(callLoc, directFuncOp, cirCallArgs); } RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, + const CallArgList &args, cir::CIRCallOpInterface *callOp, mlir::Location loc) { QualType retTy = funcInfo.getReturnType(); const cir::ABIArgInfo &retInfo = funcInfo.getReturnInfo(); - assert(!cir::MissingFeatures::opCallArgs()); + ClangToCIRArgMapping cirFuncArgs(cgm.getASTContext(), funcInfo); + SmallVector<mlir::Value, 16> cirCallArgs(cirFuncArgs.totalCIRArgs()); + assert(!cir::MissingFeatures::emitLifetimeMarkers()); + // Translate all of the arguments as necessary to match the CIR lowering. + assert(funcInfo.arg_size() == args.size() && + "Mismatch between function signature & arguments."); + unsigned argNo = 0; + const auto *infoIter = funcInfo.arg_begin(); + for (auto i = args.begin(), e = args.end(); i != e; + ++i, ++infoIter, ++argNo) { + const cir::ABIArgInfo &argInfo = infoIter->info; + + // Insert a padding argument to ensure proper alignment. + assert(!cir::MissingFeatures::opCallPaddingArgs()); + + unsigned firstCIRArg; + unsigned numCIRArgs; + std::tie(firstCIRArg, numCIRArgs) = cirFuncArgs.getCIRArgs(argNo); + + switch (argInfo.getKind()) { + case cir::ABIArgInfo::Direct: { + if (!mlir::isa<cir::RecordType>(argInfo.getCoerceToType()) && + argInfo.getCoerceToType() == convertType(infoIter->type) && + argInfo.getDirectOffset() == 0) { + assert(numCIRArgs == 1); + assert(!cir::MissingFeatures::opCallAggregateArgs()); + mlir::Value v = i->getKnownRValue().getScalarVal(); + + assert(!cir::MissingFeatures::opCallExtParameterInfo()); + + // We might have to widen integers, but we should never truncate. + assert(!cir::MissingFeatures::opCallWidenArg()); + + // If the argument doesn't match, perform a bitcast to coerce it. This + // can happen due to trivial type mismatches. + assert(!cir::MissingFeatures::opCallBitcastArg()); + + cirCallArgs[firstCIRArg] = v; + break; + } + + assert(!cir::MissingFeatures::opCallAggregateArgs()); + cgm.errorNYI("aggregate function call argument"); + break; + } + default: + cgm.errorNYI("unsupported argument kind"); + } + } + const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this); mlir::Operation *calleePtr = concreteCallee.getFunctionPointer(); @@ -102,7 +252,8 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, assert(!cir::MissingFeatures::opCallIndirect()); assert(!cir::MissingFeatures::opCallAttrs()); - cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, directFuncOp); + cir::CIRCallOpInterface theCall = + emitCallLikeOp(*this, loc, directFuncOp, cirCallArgs); if (callOp) *callOp = theCall; @@ -152,3 +303,105 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, return ret; } + +void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e, + clang::QualType argType) { + assert(argType->isReferenceType() == e->isGLValue() && + "reference binding to unmaterialized r-value!"); + + if (e->isGLValue()) { + assert(e->getObjectKind() == OK_Ordinary); + args.add(emitReferenceBindingToExpr(e), argType); + } + + bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType); + + if (hasAggregateEvalKind) { + assert(!cir::MissingFeatures::opCallAggregateArgs()); + cgm.errorNYI(e->getSourceRange(), "aggregate function call argument"); + } + + args.add(emitAnyExprToTemp(e), argType); +} + +/// Similar to emitAnyExpr(), however, the result will always be accessible +/// even if no aggregate location is provided. +RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) { + assert(!cir::MissingFeatures::opCallAggregateArgs()); + + if (hasAggregateEvaluationKind(e->getType())) + cgm.errorNYI(e->getSourceRange(), "emit aggregate value to temp"); + + return emitAnyExpr(e); +} + +void CIRGenFunction::emitCallArgs( + CallArgList &args, PrototypeWrapper prototype, + llvm::iterator_range<clang::CallExpr::const_arg_iterator> argRange, + AbstractCallee callee, unsigned paramsToSkip) { + llvm::SmallVector<QualType, 16> argTypes; + + assert(!cir::MissingFeatures::opCallCallConv()); + + // First, if a prototype was provided, use those argument types. + assert(!cir::MissingFeatures::opCallVariadic()); + if (prototype.p) { + assert(!cir::MissingFeatures::opCallObjCMethod()); + + const auto *fpt = cast<const FunctionProtoType *>(prototype.p); + argTypes.assign(fpt->param_type_begin() + paramsToSkip, + fpt->param_type_end()); + } + + // If we still have any arguments, emit them using the type of the argument. + for (auto *a : llvm::drop_begin(argRange, argTypes.size())) + argTypes.push_back(a->getType()); + assert(argTypes.size() == (size_t)(argRange.end() - argRange.begin())); + + // We must evaluate arguments from right to left in the MS C++ ABI, because + // arguments are destroyed left to right in the callee. As a special case, + // there are certain language constructs taht require left-to-right + // evaluation, and in those cases we consider the evaluation order requirement + // to trump the "destruction order is reverse construction order" guarantee. + auto leftToRight = true; + assert(!cir::MissingFeatures::msabi()); + + auto maybeEmitImplicitObjectSize = [&](size_t i, const Expr *arg, + RValue emittedArg) { + if (callee.hasFunctionDecl() || i >= callee.getNumParams()) + return; + auto *ps = callee.getParamDecl(i)->getAttr<PassObjectSizeAttr>(); + if (!ps) + return; + + assert(!cir::MissingFeatures::opCallImplicitObjectSizeArgs()); + cgm.errorNYI("emit implicit object size for call arg"); + }; + + // Evaluate each argument in the appropriate order. + size_t callArgsStart = args.size(); + for (size_t i = 0; i != argTypes.size(); ++i) { +... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/136810 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits