| Arpith Chacko Jacob | cdda3daa | 2017-01-29 20:49:31 +0000 | [diff] [blame] | 1 | //===------ CGGPUBuiltin.cpp - Codegen for GPU builtins -------------------===// | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 2 | // | 
| Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 6 | // | 
|  | 7 | //===----------------------------------------------------------------------===// | 
|  | 8 | // | 
| Arpith Chacko Jacob | cdda3daa | 2017-01-29 20:49:31 +0000 | [diff] [blame] | 9 | // Generates code for built-in GPU calls which are not runtime-specific. | 
|  | 10 | // (Runtime-specific codegen lives in programming model specific files.) | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 11 | // | 
|  | 12 | //===----------------------------------------------------------------------===// | 
|  | 13 |  | 
|  | 14 | #include "CodeGenFunction.h" | 
|  | 15 | #include "clang/Basic/Builtins.h" | 
|  | 16 | #include "llvm/IR/DataLayout.h" | 
|  | 17 | #include "llvm/IR/Instruction.h" | 
|  | 18 | #include "llvm/Support/MathExtras.h" | 
| Sameer Sahasrabuddhe | ed181ef | 2019-08-22 15:34:35 +0530 | [diff] [blame] | 19 | #include "llvm/Transforms/Utils/AMDGPUEmitPrintf.h" | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 20 |  | 
|  | 21 | using namespace clang; | 
|  | 22 | using namespace CodeGen; | 
|  | 23 |  | 
|  | 24 | static llvm::Function *GetVprintfDeclaration(llvm::Module &M) { | 
|  | 25 | llvm::Type *ArgTypes[] = {llvm::Type::getInt8PtrTy(M.getContext()), | 
|  | 26 | llvm::Type::getInt8PtrTy(M.getContext())}; | 
|  | 27 | llvm::FunctionType *VprintfFuncType = llvm::FunctionType::get( | 
|  | 28 | llvm::Type::getInt32Ty(M.getContext()), ArgTypes, false); | 
|  | 29 |  | 
|  | 30 | if (auto* F = M.getFunction("vprintf")) { | 
|  | 31 | // Our CUDA system header declares vprintf with the right signature, so | 
|  | 32 | // nobody else should have been able to declare vprintf with a bogus | 
|  | 33 | // signature. | 
|  | 34 | assert(F->getFunctionType() == VprintfFuncType); | 
|  | 35 | return F; | 
|  | 36 | } | 
|  | 37 |  | 
|  | 38 | // vprintf doesn't already exist; create a declaration and insert it into the | 
|  | 39 | // module. | 
|  | 40 | return llvm::Function::Create( | 
|  | 41 | VprintfFuncType, llvm::GlobalVariable::ExternalLinkage, "vprintf", &M); | 
|  | 42 | } | 
|  | 43 |  | 
|  | 44 | // Transforms a call to printf into a call to the NVPTX vprintf syscall (which | 
|  | 45 | // isn't particularly special; it's invoked just like a regular function). | 
|  | 46 | // vprintf takes two args: A format string, and a pointer to a buffer containing | 
|  | 47 | // the varargs. | 
|  | 48 | // | 
|  | 49 | // For example, the call | 
|  | 50 | // | 
|  | 51 | //   printf("format string", arg1, arg2, arg3); | 
|  | 52 | // | 
|  | 53 | // is converted into something resembling | 
|  | 54 | // | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 55 | //   struct Tmp { | 
|  | 56 | //     Arg1 a1; | 
|  | 57 | //     Arg2 a2; | 
|  | 58 | //     Arg3 a3; | 
|  | 59 | //   }; | 
|  | 60 | //   char* buf = alloca(sizeof(Tmp)); | 
|  | 61 | //   *(Tmp*)buf = {a1, a2, a3}; | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 62 | //   vprintf("format string", buf); | 
|  | 63 | // | 
|  | 64 | // buf is aligned to the max of {alignof(Arg1), ...}.  Furthermore, each of the | 
|  | 65 | // args is itself aligned to its preferred alignment. | 
|  | 66 | // | 
|  | 67 | // Note that by the time this function runs, E's args have already undergone the | 
|  | 68 | // standard C vararg promotion (short -> int, float -> double, etc.). | 
|  | 69 | RValue | 
| Arpith Chacko Jacob | cdda3daa | 2017-01-29 20:49:31 +0000 | [diff] [blame] | 70 | CodeGenFunction::EmitNVPTXDevicePrintfCallExpr(const CallExpr *E, | 
|  | 71 | ReturnValueSlot ReturnValue) { | 
|  | 72 | assert(getTarget().getTriple().isNVPTX()); | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 73 | assert(E->getBuiltinCallee() == Builtin::BIprintf); | 
|  | 74 | assert(E->getNumArgs() >= 1); // printf always has at least one arg. | 
|  | 75 |  | 
|  | 76 | const llvm::DataLayout &DL = CGM.getDataLayout(); | 
|  | 77 | llvm::LLVMContext &Ctx = CGM.getLLVMContext(); | 
|  | 78 |  | 
|  | 79 | CallArgList Args; | 
|  | 80 | EmitCallArgs(Args, | 
|  | 81 | E->getDirectCallee()->getType()->getAs<FunctionProtoType>(), | 
|  | 82 | E->arguments(), E->getDirectCallee(), | 
|  | 83 | /* ParamsToSkip = */ 0); | 
|  | 84 |  | 
| Justin Lebar | 9a2c0fb | 2016-02-11 02:00:52 +0000 | [diff] [blame] | 85 | // We don't know how to emit non-scalar varargs. | 
| Yaxun Liu | 5b330e8 | 2018-03-15 15:25:19 +0000 | [diff] [blame] | 86 | if (std::any_of(Args.begin() + 1, Args.end(), [&](const CallArg &A) { | 
|  | 87 | return !A.getRValue(*this).isScalar(); | 
|  | 88 | })) { | 
| Justin Lebar | 9a2c0fb | 2016-02-11 02:00:52 +0000 | [diff] [blame] | 89 | CGM.ErrorUnsupported(E, "non-scalar arg to printf"); | 
|  | 90 | return RValue::get(llvm::ConstantInt::get(IntTy, 0)); | 
|  | 91 | } | 
|  | 92 |  | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 93 | // Construct and fill the args buffer that we'll pass to vprintf. | 
|  | 94 | llvm::Value *BufferPtr; | 
|  | 95 | if (Args.size() <= 1) { | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 96 | // If there are no args, pass a null pointer to vprintf. | 
|  | 97 | BufferPtr = llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(Ctx)); | 
|  | 98 | } else { | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 99 | llvm::SmallVector<llvm::Type *, 8> ArgTypes; | 
|  | 100 | for (unsigned I = 1, NumArgs = Args.size(); I < NumArgs; ++I) | 
| Yaxun Liu | 5b330e8 | 2018-03-15 15:25:19 +0000 | [diff] [blame] | 101 | ArgTypes.push_back(Args[I].getRValue(*this).getScalarVal()->getType()); | 
| Justin Lebar | e56360a | 2016-07-27 22:36:21 +0000 | [diff] [blame] | 102 |  | 
|  | 103 | // Using llvm::StructType is correct only because printf doesn't accept | 
|  | 104 | // aggregates.  If we had to handle aggregates here, we'd have to manually | 
|  | 105 | // compute the offsets within the alloca -- we wouldn't be able to assume | 
|  | 106 | // that the alignment of the llvm type was the same as the alignment of the | 
|  | 107 | // clang type. | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 108 | llvm::Type *AllocaTy = llvm::StructType::create(ArgTypes, "printf_args"); | 
|  | 109 | llvm::Value *Alloca = CreateTempAlloca(AllocaTy); | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 110 |  | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 111 | for (unsigned I = 1, NumArgs = Args.size(); I < NumArgs; ++I) { | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 112 | llvm::Value *P = Builder.CreateStructGEP(AllocaTy, Alloca, I - 1); | 
| Yaxun Liu | 5b330e8 | 2018-03-15 15:25:19 +0000 | [diff] [blame] | 113 | llvm::Value *Arg = Args[I].getRValue(*this).getScalarVal(); | 
| Guillaume Chatelet | 59f9522 | 2020-01-23 16:18:34 +0100 | [diff] [blame] | 114 | Builder.CreateAlignedStore(Arg, P, DL.getPrefTypeAlign(Arg->getType())); | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 115 | } | 
| Justin Lebar | c0e4275 | 2016-01-28 23:58:28 +0000 | [diff] [blame] | 116 | BufferPtr = Builder.CreatePointerCast(Alloca, llvm::Type::getInt8PtrTy(Ctx)); | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 117 | } | 
|  | 118 |  | 
|  | 119 | // Invoke vprintf and return. | 
|  | 120 | llvm::Function* VprintfFunc = GetVprintfDeclaration(CGM.getModule()); | 
| Yaxun Liu | 5b330e8 | 2018-03-15 15:25:19 +0000 | [diff] [blame] | 121 | return RValue::get(Builder.CreateCall( | 
|  | 122 | VprintfFunc, {Args[0].getRValue(*this).getScalarVal(), BufferPtr})); | 
| Justin Lebar | 3039a59 | 2016-01-23 21:28:14 +0000 | [diff] [blame] | 123 | } | 
| Sameer Sahasrabuddhe | ed181ef | 2019-08-22 15:34:35 +0530 | [diff] [blame] | 124 |  | 
|  | 125 | RValue | 
|  | 126 | CodeGenFunction::EmitAMDGPUDevicePrintfCallExpr(const CallExpr *E, | 
|  | 127 | ReturnValueSlot ReturnValue) { | 
|  | 128 | assert(getTarget().getTriple().getArch() == llvm::Triple::amdgcn); | 
|  | 129 | assert(E->getBuiltinCallee() == Builtin::BIprintf || | 
|  | 130 | E->getBuiltinCallee() == Builtin::BI__builtin_printf); | 
|  | 131 | assert(E->getNumArgs() >= 1); // printf always has at least one arg. | 
|  | 132 |  | 
|  | 133 | CallArgList CallArgs; | 
|  | 134 | EmitCallArgs(CallArgs, | 
|  | 135 | E->getDirectCallee()->getType()->getAs<FunctionProtoType>(), | 
|  | 136 | E->arguments(), E->getDirectCallee(), | 
|  | 137 | /* ParamsToSkip = */ 0); | 
|  | 138 |  | 
|  | 139 | SmallVector<llvm::Value *, 8> Args; | 
|  | 140 | for (auto A : CallArgs) { | 
|  | 141 | // We don't know how to emit non-scalar varargs. | 
|  | 142 | if (!A.getRValue(*this).isScalar()) { | 
|  | 143 | CGM.ErrorUnsupported(E, "non-scalar arg to printf"); | 
|  | 144 | return RValue::get(llvm::ConstantInt::get(IntTy, -1)); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | llvm::Value *Arg = A.getRValue(*this).getScalarVal(); | 
|  | 148 | Args.push_back(Arg); | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | llvm::IRBuilder<> IRB(Builder.GetInsertBlock(), Builder.GetInsertPoint()); | 
|  | 152 | IRB.SetCurrentDebugLocation(Builder.getCurrentDebugLocation()); | 
|  | 153 | auto Printf = llvm::emitAMDGPUPrintfCall(IRB, Args); | 
|  | 154 | Builder.SetInsertPoint(IRB.GetInsertBlock(), IRB.GetInsertPoint()); | 
|  | 155 | return RValue::get(Printf); | 
|  | 156 | } |