clang 24.0.0git
LowerToLLVMOffloadPrintfNVPTX.cpp
Go to the documentation of this file.
1//===- LowerToLLVMOffloadPrintfNVPTX.cpp - NVPTX printf lowering ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file lowers cir.offload.printf to vprintf, the printf runtime interface
10// of NVPTX.
11//
12//===----------------------------------------------------------------------===//
13
14#include "LowerToLLVM.h"
16
17#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
19
20namespace cir {
21namespace direct {
22
23namespace {
24
25// vprintf takes two args: A format string, and a pointer to a buffer containing
26// the varargs.
27//
28// For example, the call
29//
30// printf("format string", arg1, arg2, arg3);
31//
32// is converted into something resembling
33//
34// struct Tmp {
35// Arg1 a1;
36// Arg2 a2;
37// Arg3 a3;
38// };
39// char* buf = alloca(sizeof(Tmp));
40// *(Tmp*)buf = {a1, a2, a3};
41// vprintf("format string", buf);
42//
43// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
44// the args is itself aligned to its ABI alignment.
45//
46// Note that by the time this function runs, the arguments have already
47// undergone the standard C vararg promotion (short -> int, float -> double
48// etc). In this function we pack the arguments into the buffer described above.
49mlir::Value
50packArgsIntoNVPTXFormatBuffer(cir::OffloadPrintfOp op, mlir::ValueRange args,
51 mlir::ConversionPatternRewriter &rewriter,
52 const mlir::DataLayout &dataLayout) {
53 mlir::Location loc = op.getLoc();
54 auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
55
56 if (args.empty())
57 // If there are no arguments other than the format string,
58 // pass a nullptr to vprintf.
59 return mlir::LLVM::ZeroOp::create(rewriter, loc, ptrTy);
60
61 // We can directly store the arguments into a struct, and the alignment
62 // would automatically be correct. That's because vprintf does not
63 // accept aggregates.
64 auto allocaTy = mlir::LLVM::LLVMStructType::getLiteral(
65 rewriter.getContext(), llvm::to_vector(args.getTypes()));
66
67 // Allocate the buffer in the entry block, so that a printf in a loop does
68 // not grow the stack on every iteration.
69 mlir::Value alloca;
70 {
71 mlir::OpBuilder::InsertionGuard guard(rewriter);
72 auto fn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
73 assert(fn && "lowering expects printf to be inside a function");
74 rewriter.setInsertionPointToStart(&fn.getBody().front());
75 mlir::Value one =
76 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), 1);
77 alloca =
78 mlir::LLVM::AllocaOp::create(rewriter, loc, ptrTy, allocaTy, one,
79 dataLayout.getTypeABIAlignment(allocaTy));
80 }
81
82 // Member accesses are inbounds and nuw, as for cir.get_member.
83 mlir::LLVM::GEPNoWrapFlags flags =
84 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
85 for (auto [i, arg] : llvm::enumerate(args)) {
86 mlir::Value member = mlir::LLVM::GEPOp::create(
87 rewriter, loc, ptrTy, allocaTy, alloca,
88 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, static_cast<int32_t>(i)}, flags);
89 mlir::LLVM::StoreOp::create(rewriter, loc, arg, member,
90 dataLayout.getTypeABIAlignment(arg.getType()));
91 }
92
93 return alloca;
94}
95
96} // namespace
97
98// Lowers a printf to a call to vprintf, as GPUPrintfOpToVPrintfLowering does
99// for gpu.printf.
100mlir::LogicalResult
101lowerNVPTXPrintf(cir::OffloadPrintfOp op, mlir::Value format,
102 mlir::ValueRange args,
103 mlir::ConversionPatternRewriter &rewriter,
104 const mlir::DataLayout &dataLayout,
105 mlir::SymbolTableCollection &symbolTables) {
106 mlir::Value packedData =
107 packArgsIntoNVPTXFormatBuffer(op, args, rewriter, dataLayout);
108
109 // int vprintf(char *format, void *packedData);
110 auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
111 mlir::Type i32Ty = rewriter.getI32Type();
112 const llvm::StringRef fnName = "vprintf";
114 rewriter, symbolTables, op, fnName,
115 mlir::LLVM::LLVMFunctionType::get(i32Ty, {ptrTy, ptrTy}));
116 // vprintf takes a generic pointer, which can address a format string in any
117 // address space.
118 if (format.getType() != ptrTy)
119 format = mlir::LLVM::AddrSpaceCastOp::create(rewriter, op.getLoc(), ptrTy,
120 format);
121 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
122 op, mlir::TypeRange{i32Ty}, fnName, mlir::ValueRange{format, packedData});
123 return mlir::success();
124}
125
126} // namespace direct
127} // namespace cir
void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter, mlir::SymbolTableCollection &symbolTables, mlir::Operation *srcOp, llvm::StringRef fnName, mlir::Type fnTy, mlir::ArrayAttr argAttrs, mlir::ArrayAttr resAttrs)
Declare fnName with type fnTy before the function enclosing srcOp, unless the module already has a sy...
mlir::LogicalResult lowerNVPTXPrintf(cir::OffloadPrintfOp op, mlir::Value format, mlir::ValueRange args, mlir::ConversionPatternRewriter &rewriter, const mlir::DataLayout &dataLayout, mlir::SymbolTableCollection &symbolTables)
Lowers op, whose lowered format and arguments are format and args, to a call to vprintf.
RangeSelector member(std::string ID)
Given a MemberExpr, selects the member token. ID is the node's binding in the match result.
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)
__builtin_bitreverse32 uint32_t __builtin_riscv_aaddu_u32 __builtin_riscv_asubu_u32 __builtin_elementwise_add_sat __builtin_elementwise_sub_sat int32_t