slang_rs_export_foreach.cpp revision 9999ec3aa0c4d7a6befd3a300dc07f0cea91cb6c
1/*
2 * Copyright 2011-2012, 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_export_foreach.h"
18
19#include <string>
20
21#include "clang/AST/ASTContext.h"
22#include "clang/AST/Decl.h"
23#include "clang/AST/TypeLoc.h"
24
25#include "llvm/DerivedTypes.h"
26#include "llvm/Target/TargetData.h"
27
28#include "slang_assert.h"
29#include "slang_rs_context.h"
30#include "slang_rs_export_type.h"
31#include "slang_version.h"
32
33namespace slang {
34
35namespace {
36
37static void ReportNameError(clang::DiagnosticsEngine *DiagEngine,
38                            clang::ParmVarDecl const *PVD) {
39  slangAssert(DiagEngine && PVD);
40  const clang::SourceManager &SM = DiagEngine->getSourceManager();
41
42  DiagEngine->Report(
43    clang::FullSourceLoc(PVD->getLocation(), SM),
44    DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
45                                "Duplicate parameter entry "
46                                "(by position/name): '%0'"))
47    << PVD->getName();
48  return;
49}
50
51}  // namespace
52
53// This function takes care of additional validation and construction of
54// parameters related to forEach_* reflection.
55bool RSExportForEach::validateAndConstructParams(
56    RSContext *Context, const clang::FunctionDecl *FD) {
57  slangAssert(Context && FD);
58  bool valid = true;
59  clang::ASTContext &C = Context->getASTContext();
60  clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics();
61
62  if (!isRootRSFunc(FD)) {
63    slangAssert(false && "must be called on compute root function!");
64  }
65
66  numParams = FD->getNumParams();
67  slangAssert(numParams > 0);
68
69  // Compute root functions are required to return a void type for now
70  if (FD->getResultType().getCanonicalType() != C.VoidTy) {
71    DiagEngine->Report(
72      clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()),
73      DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
74                                  "compute root() is required to return a "
75                                  "void type"));
76    valid = false;
77  }
78
79  // Validate remaining parameter types
80  // TODO(all): Add support for LOD/face when we have them
81
82  size_t i = 0;
83  const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
84  clang::QualType QT = PVD->getType().getCanonicalType();
85
86  // Check for const T1 *in
87  if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
88    mIn = PVD;
89    i++;  // advance parameter pointer
90  }
91
92  // Check for T2 *out
93  if (i < numParams) {
94    PVD = FD->getParamDecl(i);
95    QT = PVD->getType().getCanonicalType();
96    if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) {
97      mOut = PVD;
98      i++;  // advance parameter pointer
99    }
100  }
101
102  if (!mIn && !mOut) {
103    DiagEngine->Report(
104      clang::FullSourceLoc(FD->getLocation(),
105                           DiagEngine->getSourceManager()),
106      DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
107                                  "Compute root() must have at least one "
108                                  "parameter for in or out"));
109    valid = false;
110  }
111
112  // Check for T3 *usrData
113  if (i < numParams) {
114    PVD = FD->getParamDecl(i);
115    QT = PVD->getType().getCanonicalType();
116    if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
117      mUsrData = PVD;
118      i++;  // advance parameter pointer
119    }
120  }
121
122  while (i < numParams) {
123    PVD = FD->getParamDecl(i);
124    QT = PVD->getType().getCanonicalType();
125
126    if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
127      DiagEngine->Report(
128        clang::FullSourceLoc(PVD->getLocation(),
129                             DiagEngine->getSourceManager()),
130        DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
131                                    "Unexpected root() parameter '%0' "
132                                    "of type '%1'"))
133        << PVD->getName() << PVD->getType().getAsString();
134      valid = false;
135    } else {
136      llvm::StringRef ParamName = PVD->getName();
137      if (ParamName.equals("x")) {
138        if (mX) {
139          ReportNameError(DiagEngine, PVD);
140          valid = false;
141        } else if (mY) {
142          // Can't go back to X after skipping Y
143          ReportNameError(DiagEngine, PVD);
144          valid = false;
145        } else {
146          mX = PVD;
147        }
148      } else if (ParamName.equals("y")) {
149        if (mY) {
150          ReportNameError(DiagEngine, PVD);
151          valid = false;
152        } else {
153          mY = PVD;
154        }
155      } else {
156        if (!mX && !mY) {
157          mX = PVD;
158        } else if (!mY) {
159          mY = PVD;
160        } else {
161          DiagEngine->Report(
162            clang::FullSourceLoc(PVD->getLocation(),
163                                 DiagEngine->getSourceManager()),
164            DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
165                                        "Unexpected root() parameter '%0' "
166                                        "of type '%1'"))
167            << PVD->getName() << PVD->getType().getAsString();
168          valid = false;
169        }
170      }
171    }
172
173    i++;
174  }
175
176  mMetadataEncoding = 0;
177  if (valid) {
178    // Set up the bitwise metadata encoding for runtime argument passing.
179    mMetadataEncoding |= (mIn ?       0x01 : 0);
180    mMetadataEncoding |= (mOut ?      0x02 : 0);
181    mMetadataEncoding |= (mUsrData ?  0x04 : 0);
182    mMetadataEncoding |= (mX ?        0x08 : 0);
183    mMetadataEncoding |= (mY ?        0x10 : 0);
184  }
185
186  if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
187    // APIs before ICS cannot skip between parameters. It is ok, however, for
188    // them to omit further parameters (i.e. skipping X is ok if you skip Y).
189    if (mMetadataEncoding != 0x1f &&  // In, Out, UsrData, X, Y
190        mMetadataEncoding != 0x0f &&  // In, Out, UsrData, X
191        mMetadataEncoding != 0x07 &&  // In, Out, UsrData
192        mMetadataEncoding != 0x03 &&  // In, Out
193        mMetadataEncoding != 0x01) {  // In
194      DiagEngine->Report(
195        clang::FullSourceLoc(FD->getLocation(),
196                             DiagEngine->getSourceManager()),
197        DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
198                                    "Compute root() targeting SDK levels "
199                                    "%0-%1 may not skip parameters"))
200        << SLANG_MINIMUM_TARGET_API << (SLANG_ICS_TARGET_API-1);
201      valid = false;
202    }
203  }
204
205
206  return valid;
207}
208
209RSExportForEach *RSExportForEach::Create(RSContext *Context,
210                                         const clang::FunctionDecl *FD) {
211  slangAssert(Context && FD);
212  llvm::StringRef Name = FD->getName();
213  RSExportForEach *FE;
214
215  slangAssert(!Name.empty() && "Function must have a name");
216
217  FE = new RSExportForEach(Context, Name, FD);
218
219  if (!FE->validateAndConstructParams(Context, FD)) {
220    return NULL;
221  }
222
223  clang::ASTContext &Ctx = Context->getASTContext();
224
225  std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:");
226  Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX);
227
228  // Extract the usrData parameter (if we have one)
229  if (FE->mUsrData) {
230    const clang::ParmVarDecl *PVD = FE->mUsrData;
231    clang::QualType QT = PVD->getType().getCanonicalType();
232    slangAssert(QT->isPointerType() &&
233                QT->getPointeeType().isConstQualified());
234
235    const clang::ASTContext &C = Context->getASTContext();
236    if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
237        C.VoidTy) {
238      // In the case of using const void*, we can't reflect an appopriate
239      // Java type, so we fall back to just reflecting the ain/aout parameters
240      FE->mUsrData = NULL;
241    } else {
242      clang::RecordDecl *RD =
243          clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
244                                    Ctx.getTranslationUnitDecl(),
245                                    clang::SourceLocation(),
246                                    clang::SourceLocation(),
247                                    &Ctx.Idents.get(Id));
248
249      clang::FieldDecl *FD =
250          clang::FieldDecl::Create(Ctx,
251                                   RD,
252                                   clang::SourceLocation(),
253                                   clang::SourceLocation(),
254                                   PVD->getIdentifier(),
255                                   QT->getPointeeType(),
256                                   NULL,
257                                   /* BitWidth = */ NULL,
258                                   /* Mutable = */ false,
259                                   /* HasInit = */ false);
260      RD->addDecl(FD);
261      RD->completeDefinition();
262
263      // Create an export type iff we have a valid usrData type
264      clang::QualType T = Ctx.getTagDeclType(RD);
265      slangAssert(!T.isNull());
266
267      RSExportType *ET = RSExportType::Create(Context, T.getTypePtr());
268
269      if (ET == NULL) {
270        fprintf(stderr, "Failed to export the function %s. There's at least "
271                        "one parameter whose type is not supported by the "
272                        "reflection\n", FE->getName().c_str());
273        return NULL;
274      }
275
276      slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
277                  "Parameter packet must be a record");
278
279      FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
280    }
281  }
282
283  if (FE->mIn) {
284    const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr();
285    FE->mInType = RSExportType::Create(Context, T);
286  }
287
288  if (FE->mOut) {
289    const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
290    FE->mOutType = RSExportType::Create(Context, T);
291  }
292
293  return FE;
294}
295
296bool RSExportForEach::isGraphicsRootRSFunc(int targetAPI,
297                                           const clang::FunctionDecl *FD) {
298  if (!isRootRSFunc(FD)) {
299    return false;
300  }
301
302  if (FD->getNumParams() == 0) {
303    // Graphics root function
304    return true;
305  }
306
307  // Check for legacy graphics root function (with single parameter).
308  if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
309    const clang::QualType &IntType = FD->getASTContext().IntTy;
310    if (FD->getResultType().getCanonicalType() == IntType) {
311      return true;
312    }
313  }
314
315  return false;
316}
317
318bool RSExportForEach::isRSForEachFunc(int targetAPI,
319    const clang::FunctionDecl *FD) {
320  // We currently support only compute root() being exported via forEach
321  if (!isRootRSFunc(FD)) {
322    return false;
323  }
324
325  if (isGraphicsRootRSFunc(targetAPI, FD)) {
326    return false;
327  }
328
329  return true;
330}
331
332bool
333RSExportForEach::validateSpecialFuncDecl(int targetAPI,
334                                         clang::DiagnosticsEngine *DiagEngine,
335                                         clang::FunctionDecl const *FD) {
336  slangAssert(DiagEngine && FD);
337  bool valid = true;
338  const clang::ASTContext &C = FD->getASTContext();
339  const clang::QualType &IntType = FD->getASTContext().IntTy;
340
341  if (isGraphicsRootRSFunc(targetAPI, FD)) {
342    if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
343      // Legacy graphics root function
344      const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
345      clang::QualType QT = PVD->getType().getCanonicalType();
346      if (QT != IntType) {
347        DiagEngine->Report(
348          clang::FullSourceLoc(PVD->getLocation(),
349                               DiagEngine->getSourceManager()),
350          DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
351                                      "invalid parameter type for legacy "
352                                      "graphics root() function: %0"))
353          << PVD->getType();
354        valid = false;
355      }
356    }
357
358    // Graphics root function, so verify that it returns an int
359    if (FD->getResultType().getCanonicalType() != IntType) {
360      DiagEngine->Report(
361        clang::FullSourceLoc(FD->getLocation(),
362                             DiagEngine->getSourceManager()),
363        DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
364                                    "root() is required to return "
365                                    "an int for graphics usage"));
366      valid = false;
367    }
368  } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) {
369    if (FD->getNumParams() != 0) {
370      DiagEngine->Report(
371          clang::FullSourceLoc(FD->getLocation(),
372                               DiagEngine->getSourceManager()),
373          DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
374                                      "%0(void) is required to have no "
375                                      "parameters")) << FD->getName();
376      valid = false;
377    }
378
379    if (FD->getResultType().getCanonicalType() != C.VoidTy) {
380      DiagEngine->Report(
381          clang::FullSourceLoc(FD->getLocation(),
382                               DiagEngine->getSourceManager()),
383          DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
384                                      "%0(void) is required to have a void "
385                                      "return type")) << FD->getName();
386      valid = false;
387    }
388  } else {
389    slangAssert(false && "must be called on root, init or .rs.dtor function!");
390  }
391
392  return valid;
393}
394
395}  // namespace slang
396