blob: dffc07227065c0e131b5015123d8018d2419b0ed [file] [log] [blame]
Yang Nifb40ee22015-10-13 20:34:06 +00001/*
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
Yang Ni19467492015-10-27 15:24:41 -070028const char KERNEL_LAUNCH_FUNCTION_NAME[] = "rsForEach";
29const char KERNEL_LAUNCH_FUNCTION_NAME_WITH_OPTIONS[] = "rsForEachWithOptions";
Yang Nifb40ee22015-10-13 20:34:06 +000030const char INTERNAL_LAUNCH_FUNCTION_NAME[] =
Yang Ni19467492015-10-27 15:24:41 -070031 "_Z17rsForEachInternaliP14rs_script_calliiz";
Yang Nifb40ee22015-10-13 20:34:06 +000032
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
Yang Nifb40ee22015-10-13 20:34:06 +000073 return FD;
74}
75
Yang Ni19467492015-10-27 15:24:41 -070076// Checks if the call expression is a legal rsForEach call by looking for the
Yang Nifb40ee22015-10-13 20:34:06 +000077// 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 Ni19467492015-10-27 15:24:41 -070083// | `-DeclRefExpr 'void (void *, ...)' 'rsForEach' 'void (void *, ...)'
Yang Nifb40ee22015-10-13 20:34:06 +000084// |
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(
Yang Ni19467492015-10-27 15:24:41 -070095 clang::CallExpr* CE, int* slot, bool* hasOptions) {
Yang Nifb40ee22015-10-13 20:34:06 +000096 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 Ni19467492015-10-27 15:24:41 -0700105 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 Nifb40ee22015-10-13 20:34:06 +0000110 return nullptr;
111 }
112
Yang Ni19467492015-10-27 15:24:41 -0700113 clang::Expr* arg0 = CE->getArg(0);
114 const clang::FunctionDecl* kernel = matchFunctionDesignator(arg0);
Yang Nifb40ee22015-10-13 20:34:06 +0000115
Yang Ni19467492015-10-27 15:24:41 -0700116 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 Nifb40ee22015-10-13 20:34:06 +0000129 }
130
131 return kernel;
132}
133
134// Create an AST node for the declaration of rsForEachInternal
135clang::FunctionDecl* RSForEachLowering::CreateForEachInternalFunctionDecl() {
Yang Nifb40ee22015-10-13 20:34:06 +0000136 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 Ni19467492015-10-27 15:24:41 -0700144 EPI.Variadic = true; // varargs
Yang Nifb40ee22015-10-13 20:34:06 +0000145
146 clang::QualType T = mASTCtxt.getFunctionType(
Yang Ni19467492015-10-27 15:24:41 -0700147 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 Nifb40ee22015-10-13 20:34:06 +0000154 EPI);
155
156 clang::FunctionDecl* FD = clang::FunctionDecl::Create(
157 mASTCtxt, DC, Loc, Loc, N, T, nullptr, clang::SC_Extern);
Yang Ni19467492015-10-27 15:24:41 -0700158
Yang Nifb40ee22015-10-13 20:34:06 +0000159 return FD;
160}
161
162// Create an expression like the following that references the rsForEachInternal to
Yang Ni19467492015-10-27 15:24:41 -0700163// replace the callee in the original call expression that references rsForEach.
Yang Nifb40ee22015-10-13 20:34:06 +0000164//
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
Yang Ni19467492015-10-27 15:24:41 -0700184// rsForEach, and the arguments satisfy the restrictions on the
185// rsForEach API. If so, replace the call with a rsForEachInternal call
Yang Nifb40ee22015-10-13 20:34:06 +0000186// 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) {
Yang Ni19467492015-10-27 15:24:41 -0700191 int slot;
192 bool hasOptions;
193 const clang::FunctionDecl* kernel = matchKernelLaunchCall(CE, &slot, &hasOptions);
Yang Nifb40ee22015-10-13 20:34:06 +0000194 if (kernel == nullptr) {
195 return;
196 }
197
Yang Ni19467492015-10-27 15:24:41 -0700198 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 Nifb40ee22015-10-13 20:34:06 +0000218 clang::Expr* calleeNew = CreateCalleeExprForInternalForEach();
219 CE->setCallee(calleeNew);
220
Yang Ni19467492015-10-27 15:24:41 -0700221 const clang::CanQualType IntTy = mASTCtxt.IntTy;
222 const unsigned IntTySize = mASTCtxt.getTypeSize(IntTy);
223 const llvm::APInt APIntSlot(IntTySize, slot);
Yang Nifb40ee22015-10-13 20:34:06 +0000224 const clang::Expr* arg0 = CE->getArg(0);
225 const clang::SourceLocation Loc(arg0->getLocStart());
226 clang::Expr* IntSlotNum =
Yang Ni19467492015-10-27 15:24:41 -0700227 clang::IntegerLiteral::Create(mASTCtxt, APIntSlot, IntTy, Loc);
Yang Nifb40ee22015-10-13 20:34:06 +0000228 CE->setArg(0, IntSlotNum);
Yang Ni19467492015-10-27 15:24:41 -0700229
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 Nifb40ee22015-10-13 20:34:06 +0000265}
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