slang_rs_foreach_lowering.cpp revision 1946749cebf4a64341d8210890688fef7d958c22
1/* 2 * Copyright 2015, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "slang_rs_foreach_lowering.h" 18 19#include "clang/AST/ASTContext.h" 20#include "llvm/Support/raw_ostream.h" 21#include "slang_rs_context.h" 22#include "slang_rs_export_foreach.h" 23 24namespace slang { 25 26namespace { 27 28const char KERNEL_LAUNCH_FUNCTION_NAME[] = "rsForEach"; 29const char KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS[] = "rsForEachWithOptions"; 30const char INTERNAL_LAUNCH_FUNCTION_NAME[] = 31 "_Z17rsForEachInternaliP14rs_script_calliiz"; 32 33} // anonymous namespace 34 35RSForEachLowering::RSForEachLowering(RSContext* ctxt) 36 : mCtxt(ctxt), mASTCtxt(ctxt->getASTContext()) {} 37 38// Check if the passed-in expr references a kernel function in the following 39// pattern in the AST. 40// 41// ImplicitCastExpr 'void *' <BitCast> 42// `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay> 43// `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)' 44const clang::FunctionDecl* RSForEachLowering::matchFunctionDesignator( 45 clang::Expr* expr) { 46 clang::ImplicitCastExpr* ToVoidPtr = 47 clang::dyn_cast<clang::ImplicitCastExpr>(expr); 48 if (ToVoidPtr == nullptr) { 49 return nullptr; 50 } 51 52 clang::ImplicitCastExpr* Decay = 53 clang::dyn_cast<clang::ImplicitCastExpr>(ToVoidPtr->getSubExpr()); 54 55 if (Decay == nullptr) { 56 return nullptr; 57 } 58 59 clang::DeclRefExpr* DRE = 60 clang::dyn_cast<clang::DeclRefExpr>(Decay->getSubExpr()); 61 62 if (DRE == nullptr) { 63 return nullptr; 64 } 65 66 const clang::FunctionDecl* FD = 67 clang::dyn_cast<clang::FunctionDecl>(DRE->getDecl()); 68 69 if (FD == nullptr) { 70 return nullptr; 71 } 72 73 return FD; 74} 75 76// Checks if the call expression is a legal rsForEach call by looking for the 77// following pattern in the AST. On success, returns the first argument that is 78// a FunctionDecl of a kernel function. 79// 80// CallExpr 'void' 81// | 82// |-ImplicitCastExpr 'void (*)(void *, ...)' <FunctionToPointerDecay> 83// | `-DeclRefExpr 'void (void *, ...)' 'rsForEach' 'void (void *, ...)' 84// | 85// |-ImplicitCastExpr 'void *' <BitCast> 86// | `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay> 87// | `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)' 88// | 89// |-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue> 90// | `-DeclRefExpr 'rs_allocation':'rs_allocation' lvalue ParmVar 'in' 'rs_allocation':'rs_allocation' 91// | 92// `-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue> 93// `-DeclRefExpr 'rs_allocation':'rs_allocation' lvalue ParmVar 'out' 'rs_allocation':'rs_allocation' 94const clang::FunctionDecl* RSForEachLowering::matchKernelLaunchCall( 95 clang::CallExpr* CE, int* slot, bool* hasOptions) { 96 const clang::Decl* D = CE->getCalleeDecl(); 97 const clang::FunctionDecl* FD = clang::dyn_cast<clang::FunctionDecl>(D); 98 99 if (FD == nullptr) { 100 return nullptr; 101 } 102 103 const clang::StringRef& funcName = FD->getName(); 104 105 if (funcName.equals(KERNEL_LAUNCH_FUNCTION_NAME)) { 106 *hasOptions = false; 107 } else if (funcName.equals(KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS)) { 108 *hasOptions = true; 109 } else { 110 return nullptr; 111 } 112 113 clang::Expr* arg0 = CE->getArg(0); 114 const clang::FunctionDecl* kernel = matchFunctionDesignator(arg0); 115 116 if (kernel == nullptr) { 117 mCtxt->ReportError(arg0->getExprLoc(), 118 "Invalid kernel launch call. " 119 "Expects a function designator for the first argument."); 120 return nullptr; 121 } 122 123 // Verifies that kernel is indeed a "kernel" function. 124 *slot = mCtxt->getForEachSlotNumber(kernel); 125 if (*slot == -1) { 126 mCtxt->ReportError(CE->getExprLoc(), "%0 applied to non kernel function %1") 127 << funcName << kernel->getName(); 128 return nullptr; 129 } 130 131 return kernel; 132} 133 134// Create an AST node for the declaration of rsForEachInternal 135clang::FunctionDecl* RSForEachLowering::CreateForEachInternalFunctionDecl() { 136 clang::DeclContext* DC = mASTCtxt.getTranslationUnitDecl(); 137 clang::SourceLocation Loc; 138 139 llvm::StringRef SR(INTERNAL_LAUNCH_FUNCTION_NAME); 140 clang::IdentifierInfo& II = mASTCtxt.Idents.get(SR); 141 clang::DeclarationName N(&II); 142 143 clang::FunctionProtoType::ExtProtoInfo EPI; 144 EPI.Variadic = true; // varargs 145 146 clang::QualType T = mASTCtxt.getFunctionType( 147 mASTCtxt.VoidTy, // Return type 148 // Argument types: 149 { mASTCtxt.IntTy, // int slot 150 mASTCtxt.VoidPtrTy, // rs_script_call_t* launch_options 151 mASTCtxt.IntTy, // int numOutput 152 mASTCtxt.IntTy // int numInputs 153 }, 154 EPI); 155 156 clang::FunctionDecl* FD = clang::FunctionDecl::Create( 157 mASTCtxt, DC, Loc, Loc, N, T, nullptr, clang::SC_Extern); 158 159 return FD; 160} 161 162// Create an expression like the following that references the rsForEachInternal to 163// replace the callee in the original call expression that references rsForEach. 164// 165// ImplicitCastExpr 'void (*)(int, rs_allocation, rs_allocation)' <FunctionToPointerDecay> 166// `-DeclRefExpr 'void' Function '_Z17rsForEachInternali13rs_allocationS_' 'void (int, rs_allocation, rs_allocation)' 167clang::Expr* RSForEachLowering::CreateCalleeExprForInternalForEach() { 168 clang::FunctionDecl* FDNew = CreateForEachInternalFunctionDecl(); 169 170 clang::DeclRefExpr* refExpr = clang::DeclRefExpr::Create( 171 mASTCtxt, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), FDNew, 172 false, clang::SourceLocation(), mASTCtxt.VoidTy, clang::VK_RValue); 173 174 const clang::QualType FDNewType = FDNew->getType(); 175 176 clang::Expr* calleeNew = clang::ImplicitCastExpr::Create( 177 mASTCtxt, mASTCtxt.getPointerType(FDNewType), 178 clang::CK_FunctionToPointerDecay, refExpr, nullptr, clang::VK_RValue); 179 180 return calleeNew; 181} 182 183// This visit method checks (via pattern matching) if the call expression is to 184// rsForEach, and the arguments satisfy the restrictions on the 185// rsForEach API. If so, replace the call with a rsForEachInternal call 186// with the first argument replaced by the slot number of the kernel function 187// referenced in the original first argument. 188// 189// See comments to the helper methods defined above for details. 190void RSForEachLowering::VisitCallExpr(clang::CallExpr* CE) { 191 int slot; 192 bool hasOptions; 193 const clang::FunctionDecl* kernel = matchKernelLaunchCall(CE, &slot, &hasOptions); 194 if (kernel == nullptr) { 195 return; 196 } 197 198 slangAssert(slot >= 0); 199 200 const unsigned numArgsOrig = CE->getNumArgs(); 201 202 clang::QualType resultType = kernel->getReturnType().getCanonicalType(); 203 int numOutput = resultType->isVoidType() ? 0 : 1; 204 205 unsigned numInputs = RSExportForEach::getNumInputs(mCtxt->getTargetAPI(), kernel); 206 207 // Verifies that rsForEach takes the right number of input and output allocations. 208 // TODO: Check input/output allocation types match kernel function expectation. 209 const unsigned expectedNumAllocations = numArgsOrig - (hasOptions ? 2 : 1); 210 if (numInputs + numOutput != expectedNumAllocations) { 211 mCtxt->ReportError( 212 CE->getExprLoc(), 213 "Number of input and output allocations unexpected for kernel function %0") 214 << kernel->getName(); 215 return; 216 } 217 218 clang::Expr* calleeNew = CreateCalleeExprForInternalForEach(); 219 CE->setCallee(calleeNew); 220 221 const clang::CanQualType IntTy = mASTCtxt.IntTy; 222 const unsigned IntTySize = mASTCtxt.getTypeSize(IntTy); 223 const llvm::APInt APIntSlot(IntTySize, slot); 224 const clang::Expr* arg0 = CE->getArg(0); 225 const clang::SourceLocation Loc(arg0->getLocStart()); 226 clang::Expr* IntSlotNum = 227 clang::IntegerLiteral::Create(mASTCtxt, APIntSlot, IntTy, Loc); 228 CE->setArg(0, IntSlotNum); 229 230 const unsigned numAdditionalArgs = 231 hasOptions ? 232 2 : // numOutputs and numInputs 233 3; // launchOptions, numOutputs and numInputs 234 235 const unsigned numArgs = numArgsOrig + numAdditionalArgs; 236 237 // Makes extra room in the argument list for new arguments to add at position 238 // 1, 2, and 3. The last (numInputs + numOutput) arguments, i.e., the input 239 // and output allocations, are moved up numAdditionalArgs (2 or 3) positions 240 // in the argument list. 241 CE->setNumArgs(mASTCtxt, numArgs); 242 for (unsigned i = numArgs - 1; i >= 4; i--) { 243 CE->setArg(i, CE->getArg(i - numAdditionalArgs)); 244 } 245 246 // Sets the new arguments for NULL launch option (if the user does not set one), 247 // the number of outputs, and the number of inputs. 248 249 if (!hasOptions) { 250 const llvm::APInt APIntZero(IntTySize, 0); 251 clang::Expr* IntNull = 252 clang::IntegerLiteral::Create(mASTCtxt, APIntZero, IntTy, Loc); 253 CE->setArg(1, IntNull); 254 } 255 256 const llvm::APInt APIntNumOutput(IntTySize, numOutput); 257 clang::Expr* IntNumOutput = 258 clang::IntegerLiteral::Create(mASTCtxt, APIntNumOutput, IntTy, Loc); 259 CE->setArg(2, IntNumOutput); 260 261 const llvm::APInt APIntNumInputs(IntTySize, numInputs); 262 clang::Expr* IntNumInputs = 263 clang::IntegerLiteral::Create(mASTCtxt, APIntNumInputs, IntTy, Loc); 264 CE->setArg(3, IntNumInputs); 265} 266 267void RSForEachLowering::VisitStmt(clang::Stmt* S) { 268 for (clang::Stmt* Child : S->children()) { 269 if (Child) { 270 Visit(Child); 271 } 272 } 273} 274 275} // namespace slang 276