clang 24.0.0git
LowerToLLVMOffloadPrintf.cpp
Go to the documentation of this file.
1//===- LowerToLLVMOffloadPrintf.cpp - Lower cir.offload.printf ------------===//
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 the printf runtime interface of each
10// device target. The expansions follow the lowerings of the GPU dialect's
11// gpu.printf in mlir/lib/Conversion/GPUCommon/GPUOpsLowering.cpp.
12//
13//===----------------------------------------------------------------------===//
14
15#include "LowerToLLVM.h"
16
17#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
18#include "mlir/IR/BuiltinOps.h"
20#include "llvm/TargetParser/Triple.h"
21
22namespace cir {
23namespace direct {
24
25namespace {
26
27// vprintf takes two args: A format string, and a pointer to a buffer containing
28// the varargs.
29//
30// For example, the call
31//
32// printf("format string", arg1, arg2, arg3);
33//
34// is converted into something resembling
35//
36// struct Tmp {
37// Arg1 a1;
38// Arg2 a2;
39// Arg3 a3;
40// };
41// char* buf = alloca(sizeof(Tmp));
42// *(Tmp*)buf = {a1, a2, a3};
43// vprintf("format string", buf);
44//
45// `buf` is aligned to the max of {alignof(Arg1), ...}. Furthermore, each of
46// the args is itself aligned to its ABI alignment.
47//
48// Note that by the time this function runs, the arguments have already
49// undergone the standard C vararg promotion (short -> int, float -> double
50// etc). In this function we pack the arguments into the buffer described above.
51mlir::Value
52packArgsIntoNVPTXFormatBuffer(cir::OffloadPrintfOp op, mlir::ValueRange args,
53 mlir::ConversionPatternRewriter &rewriter,
54 const mlir::DataLayout &dataLayout) {
55 mlir::Location loc = op.getLoc();
56 auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
57
58 if (args.empty())
59 // If there are no arguments other than the format string,
60 // pass a nullptr to vprintf.
61 return mlir::LLVM::ZeroOp::create(rewriter, loc, ptrTy);
62
63 // We can directly store the arguments into a struct, and the alignment
64 // would automatically be correct. That's because vprintf does not
65 // accept aggregates.
66 auto allocaTy = mlir::LLVM::LLVMStructType::getLiteral(
67 rewriter.getContext(), llvm::to_vector(args.getTypes()));
68
69 // Allocate the buffer in the entry block, so that a printf in a loop does
70 // not grow the stack on every iteration.
71 mlir::Value alloca;
72 {
73 mlir::OpBuilder::InsertionGuard guard(rewriter);
74 auto fn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
75 assert(fn && "lowering expects printf to be inside a function");
76 rewriter.setInsertionPointToStart(&fn.getBody().front());
77 mlir::Value one =
78 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), 1);
79 alloca =
80 mlir::LLVM::AllocaOp::create(rewriter, loc, ptrTy, allocaTy, one,
81 dataLayout.getTypeABIAlignment(allocaTy));
82 }
83
84 // Member accesses are inbounds and nuw, as for cir.get_member.
85 mlir::LLVM::GEPNoWrapFlags flags =
86 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
87 for (auto [i, arg] : llvm::enumerate(args)) {
88 mlir::Value member = mlir::LLVM::GEPOp::create(
89 rewriter, loc, ptrTy, allocaTy, alloca,
90 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, static_cast<int32_t>(i)}, flags);
91 mlir::LLVM::StoreOp::create(rewriter, loc, arg, member,
92 dataLayout.getTypeABIAlignment(arg.getType()));
93 }
94
95 return alloca;
96}
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
127
128mlir::LogicalResult CIRToLLVMOffloadPrintfOpLowering::matchAndRewrite(
129 cir::OffloadPrintfOp op, OpAdaptor adaptor,
130 mlir::ConversionPatternRewriter &rewriter) const {
131 auto mod = op->getParentOfType<mlir::ModuleOp>();
132 llvm::StringRef tripleStr;
133 if (auto tripleAttr = mod->getAttrOfType<mlir::StringAttr>(
134 cir::CIRDialect::getTripleAttrName()))
135 tripleStr = tripleAttr.getValue();
136 llvm::Triple triple(tripleStr);
137
138 if (triple.isNVPTX())
139 return lowerNVPTXPrintf(op, adaptor.getFormat(), adaptor.getArgs(),
140 rewriter, dataLayout, symbolTables);
141
142 return op.emitError() << "offload printf lowering is NYI for target '"
143 << tripleStr << "'";
144}
145
146} // namespace direct
147} // 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...
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