Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[CIR] Upstream cir.call with scalar arguments #136810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2025

Conversation

Lancern
Copy link
Member

@Lancern Lancern commented Apr 23, 2025

This PR upstreams support for scalar arguments in cir.call operation.

Related to #132487 .

@Lancern Lancern requested a review from andykaylor April 23, 2025 04:18
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Apr 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 23, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Sirui Mu (Lancern)

Changes

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 --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]

@andykaylor andykaylor requested review from erichkeane and mmha April 23, 2025 17:54
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop is using a bunch of traditional for loops instead of 'range-for' (and loops instead of algorithms!). It would be a vast improvement to replace those with std::algorithms if they end up being 'simple' enough, and at least range-for loops.

}

// If we still have any arguments, emit them using the type of the argument.
for (auto *a : llvm::drop_begin(argRange, argTypes.size()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop could be a std::copy with predicate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this achievable? This loop does not copy the range elements directly, it copies a transformation (i.e. getType()) of them.

}

mlir::MutableOperandRange getArgOperandsMutable() {
llvm_unreachable("NYI");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just not add this now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is required by CallOpInterface and we have to keep it here. I have added the implementation for this function in the latest change, though.

CIRGenFunctionInfo *CIRGenFunctionInfo::create(CanQualType resultType) {
void *buffer = operator new(totalSizeToAlloc<ArgInfo>(1));
CIRGenFunctionInfo *
CIRGenFunctionInfo::create(CanQualType resultType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This same change is being made in #136854.

@el-ev I think it makes sense to hold your change until this one has been merged. That will help with the concern I had about limited testing in your PR.

@@ -446,8 +446,31 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
// CallOp
//===----------------------------------------------------------------------===//

mlir::Operation::operand_iterator cir::CallOp::arg_operand_begin() {
assert(!cir::MissingFeatures::opCallIndirect());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we issue a diagnostic somewhere for indirect calls? This seems like a significant gap.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has been removed in the latest change.

// CHECK: %[[#b:]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
// CHECK: %[[#a:]] = cir.const #cir.int<2> : !s32i
// CHECK-NEXT: %[[#c:]] = cir.const #false
// CHECK-NEXT: %5 = cir.call @f5(%[[#a]], %[[#b:]], %[[#c]]) : (!s32i, !cir.ptr<!s32i>, !cir.bool) -> !s32i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add checks for the LLVM IR generated from this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LLVM IR lowering is NYI implemented for cir.call. Should I add an empty implementation of cir.call LLVMIR lowering?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, if that's not implemented yet, it's fine to leave these tests until it is. It would be good to add the lowering as soon as this PR is finished.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I add an empty implementation of cir.call LLVMIR lowering?

The fact that lowering will automatically reject with a diagnostic is already good enough

@andykaylor andykaylor requested a review from xlauko April 23, 2025 18:41
Comment on lines 28 to 31
InterfaceMethod<"", "mlir::Operation::operand_iterator",
"arg_operand_begin", (ins)>,
InterfaceMethod<"", "mlir::Operation::operand_iterator",
"arg_operand_end", (ins)>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bcardosolopes any reason why this is part of interface and not just normal method on ops as is in func::CallOp?

Interface methods and methods in generally should not use snake_case, but camelCase.
Though it is unfortunately named using a snake_case in other mlir dialects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason why this is part of interface and not just normal method on ops as is in func::CallOp?

My recollection is that there was a problem with inherited interfaces and that wouldn't work. All we need here is the ability to override those differently from the inherited interface. If it works without the explicit declaration here I'm all for it!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have confirmed that these two functions are not used in this PR and I removed them for now. We could add them back if they're proven necessary in future upstreaming works.

@Lancern Lancern force-pushed the clangir/call-args branch 2 times, most recently from 8f7b72f to 6fa0792 Compare April 26, 2025 16:35
Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like there's only nits left? LGTM after they get addressed.

@Lancern Lancern force-pushed the clangir/call-args branch from 6fa0792 to e933413 Compare May 1, 2025 16:31
@Lancern Lancern force-pushed the clangir/call-args branch from e933413 to e5e7bd9 Compare May 1, 2025 17:45
@Lancern
Copy link
Member Author

Lancern commented May 1, 2025

Rebased onto the latest main. Will land later if no one have more comments.

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Lancern Lancern merged commit 44c4b4c into llvm:main May 2, 2025
11 checks passed
@Lancern Lancern deleted the clangir/call-args branch May 2, 2025 02:04
@llvm-ci
Copy link
Collaborator

llvm-ci commented May 2, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-aarch64-darwin running on doug-worker-4 while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/19343

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang-Unit :: ./AllClangUnitTests/8/48' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:/Users/buildbot/buildbot-root/aarch64-darwin/build/tools/clang/unittests/./AllClangUnitTests-Clang-Unit-89969-8-48.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=48 GTEST_SHARD_INDEX=8 /Users/buildbot/buildbot-root/aarch64-darwin/build/tools/clang/unittests/./AllClangUnitTests
--

Script:
--
/Users/buildbot/buildbot-root/aarch64-darwin/build/tools/clang/unittests/./AllClangUnitTests --gtest_filter=TimeProfilerTest.ConstantEvaluationC99
--
/Users/buildbot/buildbot-root/aarch64-darwin/llvm-project/clang/unittests/Support/TimeProfilerTest.cpp:349: Failure
Expected equality of these values:
  R"(
Frontend (test.c)
| ParseDeclarationOrFunctionDefinition (test.c:2:1)
| | isIntegerConstantExpr (<test.c:3:18>)
| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
| PerformPendingInstantiations
)"
    Which is: "\nFrontend (test.c)\n| ParseDeclarationOrFunctionDefinition (test.c:2:1)\n| | isIntegerConstantExpr (<test.c:3:18>)\n| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)\n| PerformPendingInstantiations\n"
  buildTraceGraph(Json)
    Which is: "\nFrontend (test.c)\n| ParseDeclarationOrFunctionDefinition (test.c:2:1)\n| | isIntegerConstantExpr (<test.c:3:18>)\n| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)\n| | PerformPendingInstantiations\n"
With diff:
@@ -4,3 +4,3 @@
 | | isIntegerConstantExpr (<test.c:3:18>)
 | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
-| PerformPendingInstantiations\n
+| | PerformPendingInstantiations\n



/Users/buildbot/buildbot-root/aarch64-darwin/llvm-project/clang/unittests/Support/TimeProfilerTest.cpp:349
Expected equality of these values:
  R"(
Frontend (test.c)
| ParseDeclarationOrFunctionDefinition (test.c:2:1)
| | isIntegerConstantExpr (<test.c:3:18>)
| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
| PerformPendingInstantiations
)"
    Which is: "\nFrontend (test.c)\n| ParseDeclarationOrFunctionDefinition (test.c:2:1)\n| | isIntegerConstantExpr (<test.c:3:18>)\n| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)\n| PerformPendingInstantiations\n"
  buildTraceGraph(Json)
    Which is: "\nFrontend (test.c)\n| ParseDeclarationOrFunctionDefinition (test.c:2:1)\n| | isIntegerConstantExpr (<test.c:3:18>)\n| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)\n| | PerformPendingInstantiations\n"
With diff:
@@ -4,3 +4,3 @@
 | | isIntegerConstantExpr (<test.c:3:18>)
 | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
-| PerformPendingInstantiations\n
+| | PerformPendingInstantiations\n

...

IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This PR upstreams support for scalar arguments in `cir.call` operation.

Related to llvm#132487 .
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This PR upstreams support for scalar arguments in `cir.call` operation.

Related to llvm#132487 .
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This PR upstreams support for scalar arguments in `cir.call` operation.

Related to llvm#132487 .
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
This PR upstreams support for scalar arguments in `cir.call` operation.

Related to llvm#132487 .
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants