Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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 | |
| 24 | namespace slang { |
| 25 | |
| 26 | namespace { |
| 27 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 28 | const char KERNEL_LAUNCH_FUNCTION_NAME[] = "rsForEach"; |
| 29 | const char KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS[] = "rsForEachWithOptions"; |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 30 | const char INTERNAL_LAUNCH_FUNCTION_NAME[] = |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 31 | "_Z17rsForEachInternaliP14rs_script_calliiz"; |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 32 | |
| 33 | } // anonymous namespace |
| 34 | |
| 35 | RSForEachLowering::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)' |
| 44 | const 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 | |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 73 | return FD; |
| 74 | } |
| 75 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 76 | // Checks if the call expression is a legal rsForEach call by looking for the |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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> |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 83 | // | `-DeclRefExpr 'void (void *, ...)' 'rsForEach' 'void (void *, ...)' |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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' |
| 94 | const clang::FunctionDecl* RSForEachLowering::matchKernelLaunchCall( |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 95 | clang::CallExpr* CE, int* slot, bool* hasOptions) { |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 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 { |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 110 | return nullptr; |
| 111 | } |
| 112 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 113 | clang::Expr* arg0 = CE->getArg(0); |
| 114 | const clang::FunctionDecl* kernel = matchFunctionDesignator(arg0); |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 115 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 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; |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | return kernel; |
| 132 | } |
| 133 | |
| 134 | // Create an AST node for the declaration of rsForEachInternal |
| 135 | clang::FunctionDecl* RSForEachLowering::CreateForEachInternalFunctionDecl() { |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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; |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 144 | EPI.Variadic = true; // varargs |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 145 | |
| 146 | clang::QualType T = mASTCtxt.getFunctionType( |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 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 | }, |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 154 | EPI); |
| 155 | |
| 156 | clang::FunctionDecl* FD = clang::FunctionDecl::Create( |
| 157 | mASTCtxt, DC, Loc, Loc, N, T, nullptr, clang::SC_Extern); |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 158 | |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 159 | return FD; |
| 160 | } |
| 161 | |
| 162 | // Create an expression like the following that references the rsForEachInternal to |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 163 | // replace the callee in the original call expression that references rsForEach. |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 164 | // |
| 165 | // ImplicitCastExpr 'void (*)(int, rs_allocation, rs_allocation)' <FunctionToPointerDecay> |
| 166 | // `-DeclRefExpr 'void' Function '_Z17rsForEachInternali13rs_allocationS_' 'void (int, rs_allocation, rs_allocation)' |
| 167 | clang::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 |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 184 | // rsForEach, and the arguments satisfy the restrictions on the |
| 185 | // rsForEach API. If so, replace the call with a rsForEachInternal call |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 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. |
| 190 | void RSForEachLowering::VisitCallExpr(clang::CallExpr* CE) { |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 191 | int slot; |
| 192 | bool hasOptions; |
| 193 | const clang::FunctionDecl* kernel = matchKernelLaunchCall(CE, &slot, &hasOptions); |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 194 | if (kernel == nullptr) { |
| 195 | return; |
| 196 | } |
| 197 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 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 | |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 218 | clang::Expr* calleeNew = CreateCalleeExprForInternalForEach(); |
| 219 | CE->setCallee(calleeNew); |
| 220 | |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 221 | const clang::CanQualType IntTy = mASTCtxt.IntTy; |
| 222 | const unsigned IntTySize = mASTCtxt.getTypeSize(IntTy); |
| 223 | const llvm::APInt APIntSlot(IntTySize, slot); |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 224 | const clang::Expr* arg0 = CE->getArg(0); |
| 225 | const clang::SourceLocation Loc(arg0->getLocStart()); |
| 226 | clang::Expr* IntSlotNum = |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 227 | clang::IntegerLiteral::Create(mASTCtxt, APIntSlot, IntTy, Loc); |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 228 | CE->setArg(0, IntSlotNum); |
Yang Ni | 1946749 | 2015-10-27 15:24:41 -0700 | [diff] [blame^] | 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); |
Yang Ni | fb40ee2 | 2015-10-13 20:34:06 +0000 | [diff] [blame] | 265 | } |
| 266 | |
| 267 | void 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 |