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

Reply via email to