https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/214789

When these appear in an AttributedStmt, we have to annotate the call with these 
attributes.  This patch implements them for all of the CallOpInterface types.

>From b378b9fc224cfb5ba9b3a8374e610e92245d051b Mon Sep 17 00:00:00 2001
From: Erich Keane <[email protected]>
Date: Fri, 7 Aug 2026 08:09:34 -0700
Subject: [PATCH] [CIR] Implement alwaysinline/noinline on call sites

When these appear in an AttributedStmt, we have to annotate the call
with these attributes.  This patch implements them for all of the
CallOpInterface types.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  1 +
 .../clang/CIR/Interfaces/CIROpInterfaces.td   | 20 ++++++-
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          | 15 +++++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  6 ++
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp          | 14 ++++-
 clang/lib/CIR/CodeGen/TargetInfo.h            | 18 ++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  8 +++
 .../CodeGen/callsite-inline-attributes.cpp    | 55 +++++++++++++++++++
 8 files changed, 133 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/callsite-inline-attributes.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 49ecec207cd45..cf2a3f0bdad88 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4540,6 +4540,7 @@ class CIR_CallOpBase<string mnemonic, list<Trait> 
extra_traits = []>
   dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee,
       Variadic<CIR_AnyType>:$args,
       UnitAttr:$nothrow,
+      OptionalAttr<CIR_InlineKind>:$inline_kind,
       UnitAttr:$musttail,
       DefaultValuedAttr<CIR_SideEffect, "SideEffect::All">:$side_effect,
       OptionalAttr<DictArrayAttr>:$arg_attrs,
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td 
b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index 9f0e91c0ac40c..08f6bdc024866 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -23,8 +23,6 @@ let cppNamespace = "::cir" in {
   // that's useful for handling indirect calls and other details.
   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<
           "Return the operand at index 'i', accounts for indirect call or "
@@ -47,6 +45,24 @@ let cppNamespace = "::cir" in {
                       "bool", "getNothrow", (ins)>,
       InterfaceMethod<"Return the side effects of the call operation",
                       "cir::SideEffect", "getSideEffect", (ins)>,
+      InterfaceMethod<[{"Set the inline-kind of a call operation"}],
+                      "void", "setInlineKind", 
+                      (ins "std::optional<cir::InlineKind>":$kind), [{}], 
+                      /*defaultImplementation=*/[{
+                        $_op.setInlineKind(kind);
+                      }]>,
+      InterfaceMethod<[{"Get the inline-kind of a call operation"}],
+                      "std::optional<cir::InlineKind>", "getInlineKind", 
+                      (ins), [{}], 
+                      /*defaultImplementation=*/[{
+                        $_op.getInlineKind();
+                      }]>,
+      InterfaceMethod<[{"Get the inline-kind attribute name"}], 
+                      "mlir::StringAttr", "getInlineKindAttrName",
+                      (ins), [{}],
+                      /*defaultImplementation=*/[{
+                        $_op.getInlineKindAttrName();
+                      }]>,
     ];
   }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 28670cf31694a..940294c923179 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1179,6 +1179,10 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo 
&funcInfo,
 
   SmallVector<mlir::Value, 16> cirCallArgs(args.size());
 
+  const Decl *targetDecl = callee.getAbstractInfo().getCalleeDecl().getDecl();
+  const FunctionDecl *callerDecl = dyn_cast_or_null<FunctionDecl>(curCodeDecl);
+  const FunctionDecl *calleeDecl = dyn_cast_or_null<FunctionDecl>(targetDecl);
+
   assert(!cir::MissingFeatures::emitLifetimeMarkers());
 
   // Translate all of the arguments as necessary to match the CIR lowering.
@@ -1350,6 +1354,17 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo 
&funcInfo,
   if (callOp)
     *callOp = theCall;
 
+  // Sema/emitAttributedStmt (see
+  // https://github.com/llvm/llvm-project/issues/214764) should one-day enforce
+  // that only one of these is valid at a time. For now, we have the same 'bug'
+  // as classic codegen where we can end up having BOTH of these.
+  if (inNoInlineAttributedStmt)
+    theCall.setInlineKind(cir::InlineKind::NoInline);
+  if (inAlwaysInlineAttributedStmt &&
+      !cgm.getTargetCIRGenInfo().wouldInliningViolateFunctionCallABI(
+          callerDecl, calleeDecl))
+    theCall.setInlineKind(cir::InlineKind::AlwaysInline);
+
   if (isMustTail) {
     // PPC/MIPS have some diagnostics for classic-codegen, but we don't support
     // them yet.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index d318338187f12..9f8454309f13a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -617,6 +617,12 @@ class CIRGenFunction : public CIRGenTypeCache {
     }
   };
 
+  /// True if the current statement has noinline attribute.
+  bool inNoInlineAttributedStmt = false;
+
+  /// True if the current statement has always_inline attribute.
+  bool inAlwaysInlineAttributedStmt = false;
+
   // The CallExpr within the current statement that the musttail attribute
   // applies to.  nullptr if there is no 'musttail' on the current statement.
   const CallExpr *mustTailCall = nullptr;
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index ceda5811cd065..38b65f16197eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -90,6 +90,8 @@ mlir::LogicalResult 
CIRGenFunction::emitCompoundStmtWithoutScope(
 mlir::LogicalResult
 CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
 
+  bool noinline = false;
+  bool alwaysinline = false;
   const CallExpr *musttail = nullptr;
 
   for (const Attr *attr : s.getAttrs()) {
@@ -97,14 +99,19 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) 
{
     default:
       break;
     case attr::NoMerge:
-    case attr::NoInline:
-    case attr::AlwaysInline:
     case attr::NoConvergent:
     case attr::Atomic:
+    case attr::AMDGPUAvailableVisible:
     case attr::HLSLControlFlowHint:
       cgm.errorNYI(s.getSourceRange(),
                    "Unimplemented statement attribute: ", attr->getKind());
       break;
+    case attr::NoInline:
+      noinline = true;
+      break;
+    case attr::AlwaysInline:
+      alwaysinline = true;
+      break;
     case attr::MustTail: {
       const Stmt *sub = s.getSubStmt();
       const ReturnStmt *ret = cast<ReturnStmt>(sub);
@@ -124,6 +131,9 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) 
{
     }
   }
 
+  SaveAndRestore save_noinline(inNoInlineAttributedStmt, noinline);
+  SaveAndRestore save_alwaysinline(inAlwaysInlineAttributedStmt, alwaysinline);
+
   SaveAndRestore save_musttail(mustTailCall, musttail);
 
   return emitStmt(s.getSubStmt(), /*useCurrentScope=*/true, s.getAttrs());
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h 
b/clang/lib/CIR/CodeGen/TargetInfo.h
index 308d472234f99..e720a4ad2ec5c 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -114,6 +114,24 @@ class TargetCIRGenInfo {
   /// right thing when calling a function with no know signature.
   virtual bool isNoProtoCallVariadic(const FunctionNoProtoType *fnType) const;
 
+  /// Returns true if inlining the function call would produce incorrect code
+  /// for the current target and should be ignored (even with the always_inline
+  /// or flatten attributes).
+  ///
+  /// Note: This probably should be handled in LLVM. However, the LLVM
+  /// `alwaysinline` attribute currently means the inliner will ignore
+  /// mismatched attributes (which sometimes can generate invalid code). So,
+  /// this hook allows targets to avoid adding the LLVM `alwaysinline` 
attribute
+  /// based on C/C++ attributes or other target-specific reasons.
+  ///
+  /// See previous discussion here:
+  /// 
https://discourse.llvm.org/t/rfc-avoid-inlining-alwaysinline-functions-when-they-cannot-be-inlined/79528
+  virtual bool
+  wouldInliningViolateFunctionCallABI(const FunctionDecl *Caller,
+                                      const FunctionDecl *Callee) const {
+    return false;
+  }
+
   /// Provides a convenient hook to handle extra target-specific attributes
   /// for the given global.
   /// In OG, the function receives an llvm::GlobalValue. However, functions
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4e923f2bcee28..cc5a49d0effcb 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2044,6 +2044,7 @@ static void lowerCallAttributes(cir::CIRCallOpInterface 
op,
         attr.getName() == CIRDialect::getNoThrowAttrName() ||
         attr.getName() == CIRDialect::getNoUnwindAttrName() ||
         attr.getName() == CIRDialect::getNoReturnAttrName() ||
+        attr.getName() == op.getInlineKindAttrName() ||
         attr.getName() == CIRDialect::getMustTailAttrName())
       continue;
 
@@ -2155,6 +2156,13 @@ rewriteCallOrInvoke(mlir::Operation *op, 
mlir::ValueRange callOperands,
     newOp.setNoreturn(noReturn);
     if (op->hasAttr(CIRDialect::getMustTailAttrName()))
       newOp.setTailCallKind(mlir::LLVM::TailCallKind::MustTail);
+
+         if (std::optional<cir::InlineKind> inlineKind = call.getInlineKind()) 
{
+           newOp.setNoInline(*inlineKind == cir::InlineKind::NoInline);
+           newOp.setInlineHint(*inlineKind == cir::InlineKind::InlineHint);
+           newOp.setAlwaysInline(*inlineKind == cir::InlineKind::AlwaysInline);
+         }
+
   }
 
   return mlir::success();
diff --git a/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp 
b/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
new file mode 100644
index 0000000000000..06422d36539c6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+
+void callee();
+
+void (*fptr)(void) = &callee;
+
+void caller() {
+  // CIR-LABEL: cir.func{{.*}}@_Z6callerv()
+ 
+  [[clang::always_inline]]
+  callee();
+  // CIR: cir.call @_Z6calleev() {inline_kind = 2 : i32}
+  // LLVM: call void @_Z6calleev() #[[ALWAYSINLINE:.*]]
+  [[clang::noinline]]
+  callee();
+  // CIR: cir.call @_Z6calleev() {inline_kind = 1 : i32}
+  // LLVM: call void @_Z6calleev() #[[NOINLINE:.*]]
+
+  [[clang::always_inline]]
+  fptr();
+  // CIR: cir.call %{{.*}}() {inline_kind = 2 : i32}
+  // LLVM: call void %{{.*}}() #[[ALWAYSINLINE]]
+  [[clang::noinline]]
+  fptr();
+  // CIR: cir.call %{{.*}}() {inline_kind = 1 : i32}
+  // LLVM: call void %{{.*}}() #[[NOINLINE]]
+
+  [[clang::always_inline]]
+  {
+    callee();
+    // CIR: cir.call @_Z6calleev() {inline_kind = 2 : i32}
+    // LLVM: call void @_Z6calleev() #[[ALWAYSINLINE]]
+    fptr();
+    // CIR: cir.call %{{.*}}() {inline_kind = 2 : i32}
+    // LLVM: call void %{{.*}}() #[[ALWAYSINLINE]]
+  }
+
+  [[clang::noinline]]
+  {
+    callee();
+    // CIR: cir.call @_Z6calleev() {inline_kind = 1 : i32}
+    // LLVM: call void @_Z6calleev() #[[NOINLINE]]
+    fptr();
+    // CIR: cir.call %{{.*}}() {inline_kind = 1 : i32}
+    // LLVM: call void %{{.*}}() #[[NOINLINE]]
+  }
+}
+
+// LLVM: attributes #[[ALWAYSINLINE]] = { alwaysinline }
+// LLVM: attributes #[[NOINLINE]] = { noinline }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to