[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed converted_to_draft 
https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits


@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());

AbdallahRashed wrote:

Thank you,done

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed created 
https://github.com/llvm/llvm-project/pull/196573

Implement device-side printf lowering for NVPTX targets in CIR codegen. The 
variadic arguments are packed into a stack-allocated struct and passed to 
vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf, we 
route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
part of https://github.com/llvm/llvm-project/issues/179278

>From 2c8f237741352026fda4cbca57a3015257d38127 Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  42 
 4 files changed, 152 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::d

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed updated 
https://github.com/llvm/llvm-project/pull/196573

>From 7c1ed3c9ac49d13862f42cf512ec3d6cffef2bcd Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH 1/2] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  46 +
 4 files changed, 156 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed updated 
https://github.com/llvm/llvm-project/pull/196573

>From 7c1ed3c9ac49d13862f42cf512ec3d6cffef2bcd Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH 1/2] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  46 +
 4 files changed, 156 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed updated 
https://github.com/llvm/llvm-project/pull/196573

>From 7c1ed3c9ac49d13862f42cf512ec3d6cffef2bcd Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH 1/2] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  46 +
 4 files changed, 156 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread Andy Kaylor via cfe-commits


@@ -1008,3 +1009,105 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getKnownRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr{});
+  }
+
+  return builder.createBitcast(alloca, builder.getVoidPtrTy());
+}
+
+mlir::Value
+CIRGenFunction::emitNVPTXDevicePrintfCallExpr(const CallExpr *expr) {
+  assert(cgm.getTriple().isNVPTX());
+  assert(expr->getBuiltinCallee() == Builtin::BIprintf ||
+ expr->getBuiltinCallee() == Builtin::BI__builtin_printf);
+  assert(expr->getNumArgs() >= 1); // printf always has at least one arg.
+  CallArgList args;
+  emitCallArgs(args,
+   expr->getDirectCallee()->getType()->getAs(),
+   expr->arguments(), expr->getDirectCallee());
+
+  mlir::Location loc = getLoc(expr->getBeginLoc());
+
+  // We don't know how to emit non-scalar varargs.
+  bool hasNonScalar =
+  llvm::any_of(llvm::drop_begin(args), [&](const CallArg &a) {
+return !a.getRValue(*this, loc).isScalar();
+  });
+  if (hasNonScalar) {
+cgm.errorUnsupported(expr, "non-scalar args to printf");
+return builder.getConstInt(loc, builder.getSInt32Ty(), 0);
+  }
+
+  mlir::Value packedData = packArgsIntoNVPTXFormatBuffer(*this, args, loc);
+
+  // int vprintf(char *format, void *packedData);
+  auto vprintf = cgm.createRuntimeFunction(
+  cir::FuncType::get(
+  {cir::PointerType::get(builder.getSInt8Ty()), 
builder.getVoidPtrTy()},
+  builder.getSInt32Ty()),
+  "vprintf");
+  auto formatString = args[0].getRValue(*this, loc).getValue();

andykaylor wrote:

Yes, of course. Sloppy suggestion editing on my part.

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits


@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr{});
+  }
+
+  return builder.createBitcast(alloca, builder.getVoidPtrTy());
+}
+
+mlir::Value
+CIRGenFunction::emitNVPTXDevicePrintfCallExpr(const CallExpr *expr) {
+  assert(cgm.getTriple().isNVPTX());
+  CallArgList args;
+  emitCallArgs(args,
+   expr->getDirectCallee()->getType()->getAs(),
+   expr->arguments(), expr->getDirectCallee());
+
+  mlir::Location loc = getLoc(expr->getBeginLoc());
+
+  // Except the format string, no non-scalar arguments are allowed for
+  // device-side printf.

AbdallahRashed wrote:

updated, but that is how it was in the incubator ,
https://github.com/llvm/clangir/pull/1475/changes#diff-6dd5edd50afb829e886a51ed6f37fbaafdf57be2d589901fc9224eb601ebae5dR155

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread Andy Kaylor via cfe-commits

https://github.com/andykaylor approved this pull request.

lgtm

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

llvmorg-github-actions[bot] wrote:




@llvm/pr-subscribers-clangir

Author: AbdallahRashed (AbdallahRashed)


Changes

Implement device-side printf lowering for NVPTX targets in CIR codegen. The 
variadic arguments are packed into a stack-allocated struct and passed to 
vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf, we 
route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
part of https://github.com/llvm/llvm-project/issues/179278

---
Full diff: https://github.com/llvm/llvm-project/pull/196573.diff


4 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+7) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp (+100) 
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+3) 
- (added) clang/test/CIR/CodeGenCUDA/device-printf.cu (+42) 


``diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrd

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread Andy Kaylor via cfe-commits


@@ -1008,3 +1009,105 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getKnownRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr{});
+  }
+
+  return builder.createBitcast(alloca, builder.getVoidPtrTy());
+}
+
+mlir::Value
+CIRGenFunction::emitNVPTXDevicePrintfCallExpr(const CallExpr *expr) {
+  assert(cgm.getTriple().isNVPTX());
+  assert(expr->getBuiltinCallee() == Builtin::BIprintf ||
+ expr->getBuiltinCallee() == Builtin::BI__builtin_printf);
+  assert(expr->getNumArgs() >= 1); // printf always has at least one arg.
+  CallArgList args;
+  emitCallArgs(args,
+   expr->getDirectCallee()->getType()->getAs(),
+   expr->arguments(), expr->getDirectCallee());
+
+  mlir::Location loc = getLoc(expr->getBeginLoc());
+
+  // We don't know how to emit non-scalar varargs.
+  bool hasNonScalar =
+  llvm::any_of(llvm::drop_begin(args), [&](const CallArg &a) {
+return !a.getRValue(*this, loc).isScalar();
+  });
+  if (hasNonScalar) {
+cgm.errorUnsupported(expr, "non-scalar args to printf");
+return builder.getConstInt(loc, builder.getSInt32Ty(), 0);
+  }
+
+  mlir::Value packedData = packArgsIntoNVPTXFormatBuffer(*this, args, loc);
+
+  // int vprintf(char *format, void *packedData);
+  auto vprintf = cgm.createRuntimeFunction(
+  cir::FuncType::get(
+  {cir::PointerType::get(builder.getSInt8Ty()), 
builder.getVoidPtrTy()},
+  builder.getSInt32Ty()),
+  "vprintf");
+  auto formatString = args[0].getRValue(*this, loc).getValue();

andykaylor wrote:

```suggestion
  auto formatString = args[0].getKnownRValue(*this, loc).getValue();
```

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits


@@ -1008,3 +1009,105 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getKnownRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr{});
+  }
+
+  return builder.createBitcast(alloca, builder.getVoidPtrTy());
+}
+
+mlir::Value
+CIRGenFunction::emitNVPTXDevicePrintfCallExpr(const CallExpr *expr) {
+  assert(cgm.getTriple().isNVPTX());
+  assert(expr->getBuiltinCallee() == Builtin::BIprintf ||
+ expr->getBuiltinCallee() == Builtin::BI__builtin_printf);
+  assert(expr->getNumArgs() >= 1); // printf always has at least one arg.
+  CallArgList args;
+  emitCallArgs(args,
+   expr->getDirectCallee()->getType()->getAs(),
+   expr->arguments(), expr->getDirectCallee());
+
+  mlir::Location loc = getLoc(expr->getBeginLoc());
+
+  // We don't know how to emit non-scalar varargs.
+  bool hasNonScalar =
+  llvm::any_of(llvm::drop_begin(args), [&](const CallArg &a) {
+return !a.getRValue(*this, loc).isScalar();
+  });
+  if (hasNonScalar) {
+cgm.errorUnsupported(expr, "non-scalar args to printf");
+return builder.getConstInt(loc, builder.getSInt32Ty(), 0);
+  }
+
+  mlir::Value packedData = packArgsIntoNVPTXFormatBuffer(*this, args, loc);
+
+  // int vprintf(char *format, void *packedData);
+  auto vprintf = cgm.createRuntimeFunction(
+  cir::FuncType::get(
+  {cir::PointerType::get(builder.getSInt8Ty()), 
builder.getVoidPtrTy()},
+  builder.getSInt32Ty()),
+  "vprintf");
+  auto formatString = args[0].getRValue(*this, loc).getValue();

AbdallahRashed wrote:

done but I believe getKnownRValue takes no arguments. 

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed ready_for_review 
https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed updated 
https://github.com/llvm/llvm-project/pull/196573

>From 529abed9f9018ad62699b7e3cd18f105f9e6ec3f Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  42 
 4 files changed, 152 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ 

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

https://github.com/AbdallahRashed updated 
https://github.com/llvm/llvm-project/pull/196573

>From 7c1ed3c9ac49d13862f42cf512ec3d6cffef2bcd Mon Sep 17 00:00:00 2001
From: AbdallahRashed 
Date: Fri, 8 May 2026 18:19:23 +0200
Subject: [PATCH] [CIR][CUDA] Support device-side printf for NVPTX

Implement device-side printf lowering for NVPTX targets in CIR codegen.
The variadic arguments are packed into a stack-allocated struct and passed
to vprintf, matching the classic codegen behavior in CGGPUBuiltin.cpp

When the target triple is NVPTX and the builtin is printf/__builtin_printf,
we route to emitNVPTXDevicePrintfCallExpr
The no-varargs case passes a null pointer directly.

AMDGCN device printf remains NYI.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp  |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp | 100 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h   |   3 +
 clang/test/CIR/CodeGenCUDA/device-printf.cu  |  46 +
 4 files changed, 156 insertions(+)
 create mode 100644 clang/test/CIR/CodeGenCUDA/device-printf.cu

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index fe932834e9b55..9752d2571ed86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {
+  return errorBuiltinNYI(*this, e, builtinID);
+}
+if (getTarget().getTriple().isNVPTX()) {
+  return RValue::get(emitNVPTXDevicePrintfCallExpr(e));
+}
 break;
   case Builtin::BI__builtin_canonicalize:
   case Builtin::BI__builtin_canonicalizef:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index 52f98af8028b4..bd0607261e88b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -14,6 +14,7 @@
 
 #include "mlir/IR/Value.h"
 #include "clang/Basic/TargetBuiltins.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 using namespace clang;
@@ -1008,3 +1009,102 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread Andy Kaylor via cfe-commits


@@ -1008,3 +1009,105 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getKnownRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),

andykaylor wrote:

This should also be `getKnownRValue`

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits


@@ -2400,6 +2400,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
 return errorBuiltinNYI(*this, e, builtinID);
   case Builtin::BI__builtin_printf:
   case Builtin::BIprintf:
+assert(e->getNumArgs() >= 1); // printf always has at least one arg.
+if (getTarget().getTriple().isAMDGCN()) {

AbdallahRashed wrote:

done. please check, is it matching? 

https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread via cfe-commits

github-actions[bot] wrote:


# :penguin: Linux x64 Test Results

* 89373 tests passed
* 831 tests skipped
* 6 tests failed

## Failed Tests
(click on a test name to see its output)

### Clang

Clang.CIR/CodeGen/delete-array-throwing-dtor.cpp

```
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 
-internal-isystem 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/23/include
 -nostdsysteminc -triple x86_64-unknown-linux-gnu -std=c++20 -fcxx-exceptions 
-fexceptions -fclangir -emit-cir -mmlir 
-mlir-print-ir-after=cir-cxxabi-lowering -mmlir 
-mlir-print-ir-before=cir-cxxabi-lowering -mmlir 
-mlir-print-ir-after=cir-cxxabi-lowering 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp
 -o 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp.cir
 2> 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir
# executed command: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 
-internal-isystem 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/23/include
 -nostdsysteminc -triple x86_64-unknown-linux-gnu -std=c++20 -fcxx-exceptions 
-fexceptions -fclangir -emit-cir -mmlir 
-mlir-print-ir-after=cir-cxxabi-lowering -mmlir 
-mlir-print-ir-before=cir-cxxabi-lowering -mmlir 
-mlir-print-ir-after=cir-cxxabi-lowering 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp
 -o 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp.cir
# note: command had no output on stdout or stderr
# RUN: at line 2
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck 
--input-file=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir
 --check-prefix=CIR-BEFORE-CXXABI 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp
# executed command: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck 
--input-file=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir
 --check-prefix=CIR-BEFORE-CXXABI 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp
# .---command stderr
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp:19:23:
 error: CIR-BEFORE-CXXABI: expected string not found in input
# | // CIR-BEFORE-CXXABI: IR Dump Before CXXABILowering (cir-cxxabi-lowering)
# |   ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir:1:1:
 note: scanning from here
# | // -// IR Dump Before CXXABILowering: cir-cxxabi-lowering //- //
# | ^
# | 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir:1:12:
 note: possible intended match here
# | // -// IR Dump Before CXXABILowering: cir-cxxabi-lowering //- //
# |^
# | 
# | Input file: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/test/CIR/CodeGen/Output/delete-array-throwing-dtor.cpp.tmp-cxxabi.cir
# | Check file: 
/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<
# | 1: // -// IR Dump Before CXXABILowering: 
cir-cxxabi-lowering //- // 
# | check:19'0 
X 
error: no match found
# | check:19'1? 
 possible intended match
# | 2: !s32i = !cir.int 
# | check:19'0 
# | 3: !u64i = !cir.int 
# | check:19'0 
# | 4: !void = !cir.void 
# | check:19'0 ~~
# | 5: !rec_ThrowingDtor = !cir.record 
# | check:19'0 
~~~
# | 6: module 
@"/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CIR/CodeGen/delete-array-throwing-dtor.cpp"
 attributes {cir.lang = #cir.lang, cir.module_asm = [], cir.record_layouts 
= {ThrowingDtor = #cir.record_layout}, cir.triple = 
"x86_64-unknown-linux-gnu", dlti.dl_spec 

[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)

2026-05-09 Thread Andy Kaylor via cfe-commits


@@ -1008,3 +1009,105 @@ CIRGenFunction::emitNVPTXBuiltinExpr(unsigned 
builtinId, const CallExpr *expr) {
 return std::nullopt;
   }
 }
+
+// vprintf takes two args: A format string, and a pointer to a buffer 
containing
+// the varargs.
+//
+// For example, the call
+//
+//   printf("format string", arg1, arg2, arg3);
+//
+// is converted into something resembling
+//
+//   struct Tmp {
+// Arg1 a1;
+// Arg2 a2;
+// Arg3 a3;
+//   };
+//   char* buf = alloca(sizeof(Tmp));
+//   *(Tmp*)buf = {a1, a2, a3};
+//   vprintf("format string", buf);
+//
+// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
+// the args is itself aligned to its preferred alignment.
+//
+// Note that by the time this function runs, the arguments have already
+// undergone the standard C vararg promotion (short -> int, float -> double
+// etc). In this function we pack the arguments into the buffer described 
above.
+static mlir::Value packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
+ const CallArgList &args,
+ mlir::Location loc) {
+  const cir::CIRDataLayout dataLayout = cgf.cgm.getDataLayout();
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  if (args.size() <= 1)
+// If there are no arguments other than the format string,
+// pass a nullptr to vprintf.
+return builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+  llvm::SmallVector argTypes;
+  for (const auto &arg : llvm::drop_begin(args))
+argTypes.push_back(arg.getKnownRValue(cgf, loc).getValue().getType());
+
+  // We can directly store the arguments into a struct, and the alignment
+  // would automatically be correct. That's because vprintf does not
+  // accept aggregates.
+  mlir::Type allocaTy = builder.getAnonRecordTy(argTypes);
+  auto allocaAlign = clang::CharUnits::fromQuantity(
+  dataLayout.getABITypeAlign(allocaTy).value());
+  Address allocaAddr =
+  cgf.createTempAlloca(allocaTy, allocaAlign, loc, "printf_args");
+  mlir::Value alloca = allocaAddr.getPointer();
+
+  for (auto [i, arg] : llvm::enumerate(llvm::drop_begin(args))) {
+mlir::Value member = builder.createGetMember(
+loc, cir::PointerType::get(argTypes[i]), alloca, /*name=*/"",
+/*index=*/i);
+auto abiAlign = clang::CharUnits::fromQuantity(
+dataLayout.getABITypeAlign(argTypes[i]).value());
+cir::StoreOp::create(builder, loc, arg.getRValue(cgf, loc).getValue(),
+ member, /*is_volatile=*/false,
+ builder.getAlignmentAttr(abiAlign),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr{});
+  }
+
+  return builder.createBitcast(alloca, builder.getVoidPtrTy());
+}
+
+mlir::Value
+CIRGenFunction::emitNVPTXDevicePrintfCallExpr(const CallExpr *expr) {
+  assert(cgm.getTriple().isNVPTX());
+  assert(expr->getBuiltinCallee() == Builtin::BIprintf ||
+ expr->getBuiltinCallee() == Builtin::BI__builtin_printf);
+  assert(expr->getNumArgs() >= 1); // printf always has at least one arg.
+  CallArgList args;
+  emitCallArgs(args,
+   expr->getDirectCallee()->getType()->getAs(),
+   expr->arguments(), expr->getDirectCallee());
+
+  mlir::Location loc = getLoc(expr->getBeginLoc());
+
+  // We don't know how to emit non-scalar varargs.
+  bool hasNonScalar =
+  llvm::any_of(llvm::drop_begin(args), [&](const CallArg &a) {
+return !a.getRValue(*this, loc).isScalar();

andykaylor wrote:

```suggestion
return a.hasLValue || !a.getKnownRValue.isScalar();
```
This was actually quite problematic since it's checking to see if this is a 
scalar value. If it isn't this will generate a copy. I guess that's not 
terrible since we're about to emit an error anyway, but it's bad form.


https://github.com/llvm/llvm-project/pull/196573
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits