Author: adams381
Date: 2026-07-22T10:52:33-05:00
New Revision: 0590a36b312b950cb16dadfe349d07e73be1809a

URL: 
https://github.com/llvm/llvm-project/commit/0590a36b312b950cb16dadfe349d07e73be1809a
DIFF: 
https://github.com/llvm/llvm-project/commit/0590a36b312b950cb16dadfe349d07e73be1809a.diff

LOG: [CIR] Convert byval/sret/byref arg-attr types when lowering to LLVM 
(#210764)

The CallConvLowering pass lowers an indirectly-passed aggregate to a pointer 
argument and records the pointee record type on the `llvm.byval`, `llvm.sret`, 
or `llvm.byref` argument attribute.  That type payload is still a CIR record 
after the pass runs, so once the module reaches the LLVM dialect and is 
translated to LLVM IR, the translation hits a CIR type inside the attribute and 
fails.

LowerToLLVM already routes every operand and result type through the type 
converter.  It now does the same for the type carried by those three argument 
attributes, in the attribute lowering shared by the function definition and by 
the call and invoke sites.  With the conversion in place, a byval or sret 
parameter translates to `byval(%struct.X)` / `sret(%struct.X)` carrying the 
lowered LLVM struct type, and byref does the same.  CallConvLowering does not 
classify `cir.try_call` yet, so no invoke carries these attributes today, but 
the shared path already covers the invoke once it is classified.

The test injects the three attributes directly, lowers to the LLVM dialect, and 
translates to LLVM IR, checking the emitted attributes carry the LLVM struct 
type.  It uses no aggregate classifier, so it stands alone from the x86_64 
classifier stack.

Added: 
    clang/test/CIR/Transforms/abi-lowering/byval-sret-arg-attr-lowering.cir

Modified: 
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index f3f77a9151c35..88706bace854d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1868,7 +1868,45 @@ mlir::LogicalResult 
CIRToLLVMRotateOpLowering::matchAndRewrite(
   return mlir::LogicalResult::success();
 }
 
+/// The `llvm.byval`, `llvm.sret`, and `llvm.byref` argument attributes carry
+/// the pointee type as a TypeAttr.  After the CallConvLowering pass that type
+/// is still a CIR record; remap it to the lowered LLVM type so translation to
+/// LLVM IR does not encounter a CIR type in an attribute.  Returns the input
+/// unchanged when there is nothing to convert.
+static mlir::ArrayAttr
+convertTypedArgAttrs(mlir::ArrayAttr argAttrs,
+                     const mlir::TypeConverter &converter,
+                     mlir::MLIRContext *ctx) {
+  if (!argAttrs)
+    return argAttrs;
+  bool changed = false;
+  SmallVector<mlir::Attribute> loweredArgAttrs;
+  loweredArgAttrs.reserve(argAttrs.size());
+  for (mlir::Attribute a : argAttrs) {
+    auto dict = cast<mlir::DictionaryAttr>(a);
+    SmallVector<mlir::NamedAttribute> entries(dict.begin(), dict.end());
+    for (mlir::NamedAttribute &entry : entries) {
+      StringRef name = entry.getName().strref();
+      if (name != mlir::LLVM::LLVMDialect::getByValAttrName() &&
+          name != mlir::LLVM::LLVMDialect::getStructRetAttrName() &&
+          name != mlir::LLVM::LLVMDialect::getByRefAttrName())
+        continue;
+      auto typeAttr = dyn_cast<mlir::TypeAttr>(entry.getValue());
+      if (!typeAttr)
+        continue;
+      mlir::Type lowered = converter.convertType(typeAttr.getValue());
+      if (lowered && lowered != typeAttr.getValue()) {
+        entry.setValue(mlir::TypeAttr::get(lowered));
+        changed = true;
+      }
+    }
+    loweredArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, entries));
+  }
+  return changed ? mlir::ArrayAttr::get(ctx, loweredArgAttrs) : argAttrs;
+}
+
 static void lowerCallAttributes(cir::CIRCallOpInterface op,
+                                const mlir::TypeConverter &converter,
                                 SmallVectorImpl<mlir::NamedAttribute> &result) 
{
   for (mlir::NamedAttribute attr : op->getAttrs()) {
     if (attr.getName() == CIRDialect::getCalleeAttrName() ||
@@ -1880,6 +1918,13 @@ static void lowerCallAttributes(cir::CIRCallOpInterface 
op,
       continue;
 
     assert(!cir::MissingFeatures::opFuncExtraAttrs());
+    if (attr.getName() == CIRDialect::getArgAttrsAttrName()) {
+      auto argAttrs = cast<mlir::ArrayAttr>(attr.getValue());
+      result.emplace_back(
+          attr.getName(),
+          convertTypedArgAttrs(argAttrs, converter, op->getContext()));
+      continue;
+    }
     result.push_back(attr);
   }
 }
@@ -1909,7 +1954,7 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange 
callOperands,
                            memoryEffects, noUnwind, willReturn, noReturn);
 
   SmallVector<mlir::NamedAttribute, 4> attributes;
-  lowerCallAttributes(call, attributes);
+  lowerCallAttributes(call, *converter, attributes);
 
   mlir::LLVM::LLVMFunctionType llvmFnTy;
 
@@ -2398,6 +2443,13 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
       continue;
 
     assert(!cir::MissingFeatures::opFuncExtraAttrs());
+    if (attr.getName() == func.getArgAttrsAttrName()) {
+      auto argAttrs = cast<mlir::ArrayAttr>(attr.getValue());
+      result.emplace_back(
+          attr.getName(),
+          convertTypedArgAttrs(argAttrs, *getTypeConverter(), getContext()));
+      continue;
+    }
     result.push_back(attr);
   }
 }

diff  --git 
a/clang/test/CIR/Transforms/abi-lowering/byval-sret-arg-attr-lowering.cir 
b/clang/test/CIR/Transforms/abi-lowering/byval-sret-arg-attr-lowering.cir
new file mode 100644
index 0000000000000..8f121e5ed78dd
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/byval-sret-arg-attr-lowering.cir
@@ -0,0 +1,85 @@
+// RUN: cir-opt %s -cir-call-conv-lowering="classification-attr=test_classify" 
\
+// RUN:   | FileCheck %s
+// RUN: cir-opt %s -cir-call-conv-lowering="classification-attr=test_classify" 
\
+// RUN:     -cir-to-llvm -o - 2>/dev/null \
+// RUN:   | mlir-translate -mlir-to-llvmir --allow-unregistered-dialect \
+// RUN:   | FileCheck %s --check-prefix=LLVM
+
+!s64i = !cir.int<s, 64>
+!rec_Big = !cir.struct<"Big" {!s64i, !s64i, !s64i}>
+
+#byval_arg = {
+  return = { kind = "direct" },
+  args   = [ { kind = "indirect", indirect_align = 8 } ]
+}
+
+#sret_ret = {
+  return = { kind = "indirect", indirect_align = 8 },
+  args   = [ ]
+}
+
+#byref_arg = {
+  return = { kind = "direct" },
+  args   = [ { kind = "indirect", indirect_align = 8, byval = false } ]
+}
+
+#passthrough = {
+  return = { kind = "direct" },
+  args   = [ ]
+}
+
+module attributes {
+  cir.triple = "x86_64-unknown-linux-gnu",
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>
+} {
+
+  // The byval argument attribute carries the record type.  cir-to-llvm must
+  // convert that type to the LLVM struct so translation emits byval(%struct).
+  cir.func @takes_big(%arg0: !rec_Big) attributes { test_classify = #byval_arg 
} {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @takes_big(%{{.*}}: !cir.ptr<!rec_Big> 
{{{.*}}llvm.byval = !rec_Big{{.*}}})
+  // LLVM: define void @takes_big(ptr noalias noundef byval(%struct.Big) align 
8 %{{.+}})
+
+  // The sret return attribute is converted the same way.
+  cir.func @ret_big() -> !rec_Big attributes { test_classify = #sret_ret } {
+    %0 = cir.alloca "r" align(8) : !cir.ptr<!rec_Big>
+    %z = cir.const #cir.zero : !rec_Big
+    cir.store %z, %0 : !rec_Big, !cir.ptr<!rec_Big>
+    %1 = cir.load %0 : !cir.ptr<!rec_Big>, !rec_Big
+    cir.return %1 : !rec_Big
+  }
+
+  // CHECK: cir.func{{.*}} @ret_big(%{{.*}}: !cir.ptr<!rec_Big> 
{{{.*}}llvm.sret = !rec_Big{{.*}}})
+  // LLVM: define void @ret_big(ptr dead_on_unwind noalias writable 
sret(%struct.Big) align 8 %{{.+}})
+
+  // byref carries the type the same way and is converted identically.
+  cir.func @takes_byref(%arg0: !rec_Big) attributes { test_classify = 
#byref_arg } {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @takes_byref(%{{.*}}: !cir.ptr<!rec_Big> 
{{{.*}}llvm.byref = !rec_Big{{.*}}})
+  // LLVM: define void @takes_byref(ptr byref(%struct.Big) align 8 %{{.+}})
+
+  // The call site's byval operand attribute is converted too.
+  cir.func @caller(%s: !rec_Big) attributes { test_classify = #passthrough } {
+    cir.call @takes_big(%s) : (!rec_Big) -> ()
+    cir.return
+  }
+
+  // LLVM: define void @caller(%struct.Big %{{.+}})
+  // LLVM:   call void @takes_big(ptr noalias noundef byval(%struct.Big) align 
8 %{{.+}})
+
+  // A caller of an sret function carries the sret attribute on the call
+  // operand; its type payload is converted as well.
+  cir.func @caller_sret() attributes { test_classify = #passthrough } {
+    %r = cir.call @ret_big() : () -> !rec_Big
+    cir.return
+  }
+
+  // LLVM: define void @caller_sret()
+  // LLVM:   call void @ret_big(ptr {{.*}}sret(%struct.Big) align 8 %{{.+}})
+}


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

Reply via email to