[clang] [CIR][CUDA] Support device-side printf for NVPTX (PR #196573)
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)
@@ -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)
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)
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)
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)
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)
@@ -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)
@@ -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)
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)
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)
@@ -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)
@@ -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)
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)
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)
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)
@@ -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)
@@ -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)
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)
@@ -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
