Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2010, 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_pragma_handler.h"
     18 
     19 #include <map>
     20 #include <sstream>
     21 #include <string>
     22 
     23 #include "clang/AST/ASTContext.h"
     24 
     25 #include "clang/Basic/TokenKinds.h"
     26 
     27 #include "clang/Lex/LiteralSupport.h"
     28 #include "clang/Lex/Preprocessor.h"
     29 #include "clang/Lex/Token.h"
     30 
     31 #include "slang_assert.h"
     32 #include "slang_rs_context.h"
     33 #include "slang_rs_export_reduce.h"
     34 #include "slang_version.h"
     35 
     36 namespace slang {
     37 
     38 namespace {  // Anonymous namespace
     39 
     40 class RSExportTypePragmaHandler : public RSPragmaHandler {
     41  private:
     42   void handleItem(const std::string &Item) {
     43     mContext->addPragma(this->getName(), Item);
     44     mContext->addExportType(Item);
     45   }
     46 
     47  public:
     48   RSExportTypePragmaHandler(llvm::StringRef Name, RSContext *Context)
     49       : RSPragmaHandler(Name, Context) { }
     50 
     51   void HandlePragma(clang::Preprocessor &PP,
     52                     clang::PragmaIntroducerKind Introducer,
     53                     clang::Token &FirstToken) {
     54     this->handleItemListPragma(PP, FirstToken);
     55   }
     56 };
     57 
     58 class RSJavaPackageNamePragmaHandler : public RSPragmaHandler {
     59  public:
     60   RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context)
     61       : RSPragmaHandler(Name, Context) { }
     62 
     63   void HandlePragma(clang::Preprocessor &PP,
     64                     clang::PragmaIntroducerKind Introducer,
     65                     clang::Token &FirstToken) {
     66     // FIXME: Need to validate the extracted package name from pragma.
     67     // Currently "all chars" specified in pragma will be treated as package
     68     // name.
     69     //
     70     // 18.1 The Grammar of the Java Programming Language
     71     // (http://java.sun.com/docs/books/jls/third_edition/html/syntax.html#18.1)
     72     //
     73     // CompilationUnit:
     74     //     [[Annotations] package QualifiedIdentifier   ;  ] {ImportDeclaration}
     75     //     {TypeDeclaration}
     76     //
     77     // QualifiedIdentifier:
     78     //     Identifier { . Identifier }
     79     //
     80     // Identifier:
     81     //     IDENTIFIER
     82     //
     83     // 3.8 Identifiers
     84     // (http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.8)
     85     //
     86     //
     87 
     88     clang::Token &PragmaToken = FirstToken;
     89     std::string PackageName;
     90 
     91     // Skip first token, "java_package_name"
     92     PP.LexUnexpandedToken(PragmaToken);
     93 
     94     // Now, the current token must be clang::tok::lpara
     95     if (PragmaToken.isNot(clang::tok::l_paren))
     96       return;
     97 
     98     while (PragmaToken.isNot(clang::tok::eod)) {
     99       // Lex package name
    100       PP.LexUnexpandedToken(PragmaToken);
    101 
    102       bool Invalid;
    103       std::string Spelling = PP.getSpelling(PragmaToken, &Invalid);
    104       if (!Invalid)
    105         PackageName.append(Spelling);
    106 
    107       // Pre-mature end (syntax error will be triggered by preprocessor later)
    108       if (PragmaToken.is(clang::tok::eod) || PragmaToken.is(clang::tok::eof)) {
    109         break;
    110       } else {
    111         // Next token is ')' (end of pragma)
    112         const clang::Token &NextTok = PP.LookAhead(0);
    113         if (NextTok.is(clang::tok::r_paren)) {
    114           mContext->addPragma(this->getName(), PackageName);
    115           mContext->setReflectJavaPackageName(PackageName);
    116           // Lex until meets clang::tok::eod
    117           do {
    118             PP.LexUnexpandedToken(PragmaToken);
    119           } while (PragmaToken.isNot(clang::tok::eod));
    120           break;
    121         }
    122       }
    123     }
    124   }
    125 };
    126 
    127 class RSReducePragmaHandler : public RSPragmaHandler {
    128  public:
    129   RSReducePragmaHandler(llvm::StringRef Name, RSContext *Context)
    130       : RSPragmaHandler(Name, Context) { }
    131 
    132   void HandlePragma(clang::Preprocessor &PP,
    133                     clang::PragmaIntroducerKind Introducer,
    134                     clang::Token &FirstToken) override {
    135     // #pragma rs reduce(name)
    136     //   initializer(initializename)
    137     //   accumulator(accumulatename)
    138     //   combiner(combinename)
    139     //   outconverter(outconvertname)
    140     //   halter(haltname)
    141 
    142     const clang::SourceLocation PragmaLocation = FirstToken.getLocation();
    143 
    144     clang::Token &PragmaToken = FirstToken;
    145 
    146     // Grab "reduce(name)" ("reduce" is already known to be the first
    147     // token) and all the "keyword(value)" contributions
    148     KeywordValueMapType KeywordValueMap({std::make_pair(RSExportReduce::KeyReduce, ""),
    149                                          std::make_pair(RSExportReduce::KeyInitializer, ""),
    150                                          std::make_pair(RSExportReduce::KeyAccumulator, ""),
    151                                          std::make_pair(RSExportReduce::KeyCombiner, ""),
    152                                          std::make_pair(RSExportReduce::KeyOutConverter, "")});
    153     if (mContext->getTargetAPI() >= SLANG_FEATURE_GENERAL_REDUCTION_HALTER_API) {
    154       // Halter functionality has not been released, nor has its
    155       // specification been finalized with partners.  We do not have a
    156       // specification that extends through the full RenderScript
    157       // software stack, either.
    158       KeywordValueMap.insert(std::make_pair(RSExportReduce::KeyHalter, ""));
    159     }
    160     while (PragmaToken.is(clang::tok::identifier)) {
    161       if (!ProcessKeywordAndValue(PP, PragmaToken, KeywordValueMap))
    162         return;
    163     }
    164 
    165     // Make sure there's no end-of-line garbage
    166     if (PragmaToken.isNot(clang::tok::eod)) {
    167       PP.Diag(PragmaToken.getLocation(),
    168               PP.getDiagnostics().getCustomDiagID(
    169                 clang::DiagnosticsEngine::Error,
    170                 "did not expect '%0' here for '#pragma rs %1'"))
    171           << PP.getSpelling(PragmaToken) << getName();
    172       return;
    173     }
    174 
    175     // Make sure we have an accumulator
    176     if (KeywordValueMap[RSExportReduce::KeyAccumulator].empty()) {
    177       PP.Diag(PragmaLocation, PP.getDiagnostics().getCustomDiagID(
    178                                 clang::DiagnosticsEngine::Error,
    179                                 "missing '%0' for '#pragma rs %1'"))
    180           << RSExportReduce::KeyAccumulator << getName();
    181       return;
    182     }
    183 
    184     // Make sure the reduction kernel name is unique.  (If we were
    185     // worried there might be a VERY large number of pragmas, then we
    186     // could do something more efficient than walking a list to search
    187     // for duplicates.)
    188     for (auto I = mContext->export_reduce_begin(),
    189               E = mContext->export_reduce_end();
    190          I != E; ++I) {
    191       if ((*I)->getNameReduce() == KeywordValueMap[RSExportReduce::KeyReduce]) {
    192         PP.Diag(PragmaLocation, PP.getDiagnostics().getCustomDiagID(
    193                                   clang::DiagnosticsEngine::Error,
    194                                   "reduction kernel '%0' declared multiple "
    195                                   "times (first one is at %1)"))
    196             << KeywordValueMap[RSExportReduce::KeyReduce]
    197             << (*I)->getLocation().printToString(PP.getSourceManager());
    198         return;
    199       }
    200     }
    201 
    202     // Check API version.
    203     if (mContext->getTargetAPI() < SLANG_FEATURE_GENERAL_REDUCTION_API) {
    204       PP.Diag(PragmaLocation,
    205               PP.getDiagnostics().getCustomDiagID(
    206                 clang::DiagnosticsEngine::Error,
    207                 "reduction kernels are not supported in SDK levels %0-%1"))
    208           << SLANG_MINIMUM_TARGET_API
    209           << (SLANG_FEATURE_GENERAL_REDUCTION_API - 1);
    210       return;
    211     }
    212 
    213     // Handle backward reference from pragma (see Backend::HandleTopLevelDecl for forward reference).
    214     MarkUsed(PP, KeywordValueMap[RSExportReduce::KeyInitializer]);
    215     MarkUsed(PP, KeywordValueMap[RSExportReduce::KeyAccumulator]);
    216     MarkUsed(PP, KeywordValueMap[RSExportReduce::KeyCombiner]);
    217     MarkUsed(PP, KeywordValueMap[RSExportReduce::KeyOutConverter]);
    218     MarkUsed(PP, KeywordValueMap[RSExportReduce::KeyHalter]);
    219 
    220     mContext->addExportReduce(RSExportReduce::Create(mContext, PragmaLocation,
    221                                                      KeywordValueMap[RSExportReduce::KeyReduce],
    222                                                      KeywordValueMap[RSExportReduce::KeyInitializer],
    223                                                      KeywordValueMap[RSExportReduce::KeyAccumulator],
    224                                                      KeywordValueMap[RSExportReduce::KeyCombiner],
    225                                                      KeywordValueMap[RSExportReduce::KeyOutConverter],
    226                                                      KeywordValueMap[RSExportReduce::KeyHalter]));
    227   }
    228 
    229  private:
    230   typedef std::map<std::string, std::string> KeywordValueMapType;
    231 
    232   void MarkUsed(clang::Preprocessor &PP, const std::string &FunctionName) {
    233     if (FunctionName.empty())
    234       return;
    235 
    236     clang::ASTContext &ASTC = mContext->getASTContext();
    237     clang::TranslationUnitDecl *TUDecl = ASTC.getTranslationUnitDecl();
    238     slangAssert(TUDecl);
    239     if (const clang::IdentifierInfo *II = PP.getIdentifierInfo(FunctionName)) {
    240       for (auto Decl : TUDecl->lookup(II)) {
    241         clang::FunctionDecl *FDecl = Decl->getAsFunction();
    242         if (!FDecl || !FDecl->isThisDeclarationADefinition())
    243           continue;
    244         // Handle backward reference from pragma (see
    245         // Backend::HandleTopLevelDecl for forward reference).
    246         mContext->markUsedByReducePragma(FDecl, RSContext::CheckNameNo);
    247       }
    248     }
    249   }
    250 
    251   // Return comma-separated list of all keys in the map
    252   static std::string ListKeywords(const KeywordValueMapType &KeywordValueMap) {
    253     std::string Ret;
    254     bool First = true;
    255     for (auto const &entry : KeywordValueMap) {
    256       if (First)
    257         First = false;
    258       else
    259         Ret += ", ";
    260       Ret += "'";
    261       Ret += entry.first;
    262       Ret += "'";
    263     }
    264     return Ret;
    265   }
    266 
    267   // Parse "keyword(value)" and set KeywordValueMap[keyword] = value.  (Both
    268   // "keyword" and "value" are identifiers.)
    269   // Does both syntactic validation and the following semantic validation:
    270   // - The keyword must be present in the map.
    271   // - The map entry for the keyword must not contain a value.
    272   bool ProcessKeywordAndValue(clang::Preprocessor &PP,
    273                               clang::Token &PragmaToken,
    274                               KeywordValueMapType &KeywordValueMap) {
    275     // The current token must be an identifier in KeywordValueMap
    276     KeywordValueMapType::iterator Entry;
    277     if (PragmaToken.isNot(clang::tok::identifier) ||
    278         ((Entry = KeywordValueMap.find(
    279             PragmaToken.getIdentifierInfo()->getName())) ==
    280          KeywordValueMap.end())) {
    281       // Note that we should never get here for the "reduce" token
    282       // itself, which should already have been recognized.
    283       PP.Diag(PragmaToken.getLocation(),
    284               PP.getDiagnostics().getCustomDiagID(
    285                 clang::DiagnosticsEngine::Error,
    286                 "did not recognize '%0' for '#pragma %1'; expected one of "
    287                 "the following keywords: %2"))
    288           << PragmaToken.getIdentifierInfo()->getName() << getName()
    289           << ListKeywords(KeywordValueMap);
    290       return false;
    291     }
    292     // ... and there must be no value for this keyword yet
    293     if (!Entry->second.empty()) {
    294       PP.Diag(PragmaToken.getLocation(),
    295               PP.getDiagnostics().getCustomDiagID(
    296                 clang::DiagnosticsEngine::Error,
    297                 "more than one '%0' for '#pragma rs %1'"))
    298           << Entry->first << getName();
    299       return false;
    300     }
    301     PP.LexUnexpandedToken(PragmaToken);
    302 
    303     // The current token must be clang::tok::l_paren
    304     if (PragmaToken.isNot(clang::tok::l_paren)) {
    305       PP.Diag(PragmaToken.getLocation(),
    306               PP.getDiagnostics().getCustomDiagID(
    307                 clang::DiagnosticsEngine::Error,
    308                 "missing '(' after '%0' for '#pragma rs %1'"))
    309           << Entry->first << getName();
    310       return false;
    311     }
    312     PP.LexUnexpandedToken(PragmaToken);
    313 
    314     // The current token must be an identifier (a name)
    315     if (PragmaToken.isNot(clang::tok::identifier)) {
    316       PP.Diag(PragmaToken.getLocation(),
    317               PP.getDiagnostics().getCustomDiagID(
    318                 clang::DiagnosticsEngine::Error,
    319                 "missing name after '%0(' for '#pragma rs %1'"))
    320           << Entry->first << getName();
    321       return false;
    322     }
    323     const std::string Name = PragmaToken.getIdentifierInfo()->getName();
    324     PP.LexUnexpandedToken(PragmaToken);
    325 
    326     // The current token must be clang::tok::r_paren
    327     if (PragmaToken.isNot(clang::tok::r_paren)) {
    328       PP.Diag(PragmaToken.getLocation(),
    329               PP.getDiagnostics().getCustomDiagID(
    330                 clang::DiagnosticsEngine::Error,
    331                 "missing ')' after '%0(%1' for '#pragma rs %2'"))
    332           << Entry->first << Name << getName();
    333       return false;
    334     }
    335     PP.LexUnexpandedToken(PragmaToken);
    336 
    337     // Success
    338     Entry->second = Name;
    339     return true;
    340   }
    341 };
    342 
    343 class RSReflectLicensePragmaHandler : public RSPragmaHandler {
    344  private:
    345   void handleItem(const std::string &Item) {
    346     mContext->addPragma(this->getName(), Item);
    347     mContext->setLicenseNote(Item);
    348   }
    349 
    350  public:
    351   RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
    352       : RSPragmaHandler(Name, Context) { }
    353 
    354   void HandlePragma(clang::Preprocessor &PP,
    355                     clang::PragmaIntroducerKind Introducer,
    356                     clang::Token &FirstToken) {
    357     this->handleOptionalStringLiteralParamPragma(PP, FirstToken);
    358   }
    359 };
    360 
    361 class RSVersionPragmaHandler : public RSPragmaHandler {
    362  private:
    363   void handleInt(clang::Preprocessor &PP,
    364                  clang::Token &Tok,
    365                  const int v) {
    366     if (v != 1) {
    367       PP.Diag(Tok,
    368               PP.getDiagnostics().getCustomDiagID(
    369                   clang::DiagnosticsEngine::Error,
    370                   "pragma for version in source file must be set to 1"));
    371       mContext->setVersion(1);
    372       return;
    373     }
    374     std::stringstream ss;
    375     ss << v;
    376     mContext->addPragma(this->getName(), ss.str());
    377     mContext->setVersion(v);
    378   }
    379 
    380  public:
    381   RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
    382       : RSPragmaHandler(Name, Context) { }
    383 
    384   void HandlePragma(clang::Preprocessor &PP,
    385                     clang::PragmaIntroducerKind Introducer,
    386                     clang::Token &FirstToken) {
    387     this->handleIntegerParamPragma(PP, FirstToken);
    388   }
    389 };
    390 
    391 // Handles the pragmas rs_fp_full, rs_fp_relaxed, and rs_fp_imprecise.
    392 // There's one instance of this handler for each of the above values.
    393 // Only getName() differs between the instances.
    394 class RSPrecisionPragmaHandler : public RSPragmaHandler {
    395 public:
    396   RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context)
    397       : RSPragmaHandler(Name, Context) {}
    398 
    399   void HandlePragma(clang::Preprocessor &PP,
    400                     clang::PragmaIntroducerKind Introducer,
    401                     clang::Token &Token) {
    402     std::string Precision = getName();
    403     // We are deprecating rs_fp_imprecise.
    404     if (Precision == "rs_fp_imprecise") {
    405       PP.Diag(Token, PP.getDiagnostics().getCustomDiagID(
    406                          clang::DiagnosticsEngine::Warning,
    407                          "rs_fp_imprecise is deprecated.  Assuming "
    408                          "rs_fp_relaxed instead."));
    409       Precision = "rs_fp_relaxed";
    410     }
    411     // Check if we have already encountered a precision pragma already.
    412     std::string PreviousPrecision = mContext->getPrecision();
    413     if (!PreviousPrecision.empty()) {
    414       // If the previous specified a different value, it's an error.
    415       if (PreviousPrecision != Precision) {
    416         PP.Diag(Token, PP.getDiagnostics().getCustomDiagID(
    417                            clang::DiagnosticsEngine::Error,
    418                            "Multiple float precisions specified.  Encountered "
    419                            "%0 previously."))
    420             << PreviousPrecision;
    421       }
    422       // Otherwise we ignore redundant entries.
    423       return;
    424     }
    425 
    426     mContext->addPragma(Precision, "");
    427     mContext->setPrecision(Precision);
    428   }
    429 };
    430 
    431 }  // namespace
    432 
    433 void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP,
    434                                            clang::Token &FirstToken) {
    435   clang::Token &PragmaToken = FirstToken;
    436 
    437   // Skip first token, like "export_var"
    438   PP.LexUnexpandedToken(PragmaToken);
    439 
    440   // Now, the current token must be clang::tok::lpara
    441   if (PragmaToken.isNot(clang::tok::l_paren))
    442     return;
    443 
    444   while (PragmaToken.isNot(clang::tok::eod)) {
    445     // Lex variable name
    446     PP.LexUnexpandedToken(PragmaToken);
    447     if (PragmaToken.is(clang::tok::identifier))
    448       this->handleItem(PP.getSpelling(PragmaToken));
    449     else
    450       break;
    451 
    452     slangAssert(PragmaToken.isNot(clang::tok::eod));
    453 
    454     PP.LexUnexpandedToken(PragmaToken);
    455 
    456     if (PragmaToken.isNot(clang::tok::comma))
    457       break;
    458   }
    459 }
    460 
    461 void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP,
    462                                            clang::Token &FirstToken) {
    463   clang::Token &PragmaToken = FirstToken;
    464 
    465   // Skip first token, like "export_var_all"
    466   PP.LexUnexpandedToken(PragmaToken);
    467 
    468   // Should be end immediately
    469   if (PragmaToken.isNot(clang::tok::eod))
    470     if (PragmaToken.isNot(clang::tok::r_paren)) {
    471       PP.Diag(PragmaToken,
    472               PP.getDiagnostics().getCustomDiagID(
    473                   clang::DiagnosticsEngine::Error,
    474                   "expected a ')'"));
    475       return;
    476     }
    477 }
    478 
    479 void RSPragmaHandler::handleOptionalStringLiteralParamPragma(
    480     clang::Preprocessor &PP, clang::Token &FirstToken) {
    481   clang::Token &PragmaToken = FirstToken;
    482 
    483   // Skip first token, like "set_reflect_license"
    484   PP.LexUnexpandedToken(PragmaToken);
    485 
    486   // Now, the current token must be clang::tok::lpara
    487   if (PragmaToken.isNot(clang::tok::l_paren))
    488     return;
    489 
    490   // If not ')', eat the following string literal as the license
    491   PP.LexUnexpandedToken(PragmaToken);
    492   if (PragmaToken.isNot(clang::tok::r_paren)) {
    493     // Eat the whole string literal
    494     clang::StringLiteralParser StringLiteral(PragmaToken, PP);
    495     if (StringLiteral.hadError) {
    496       // Diagnostics will be generated automatically
    497       return;
    498     } else {
    499       this->handleItem(std::string(StringLiteral.GetString()));
    500     }
    501 
    502     // The current token should be clang::tok::r_para
    503     PP.LexUnexpandedToken(PragmaToken);
    504     if (PragmaToken.isNot(clang::tok::r_paren)) {
    505       PP.Diag(PragmaToken,
    506               PP.getDiagnostics().getCustomDiagID(
    507                   clang::DiagnosticsEngine::Error,
    508                   "expected a ')'"));
    509       return;
    510     }
    511   } else {
    512     // If no argument, remove the license
    513     this->handleItem("");
    514   }
    515 }
    516 
    517 void RSPragmaHandler::handleIntegerParamPragma(
    518     clang::Preprocessor &PP, clang::Token &FirstToken) {
    519   clang::Token &PragmaToken = FirstToken;
    520 
    521   // Skip first token, like "version"
    522   PP.LexUnexpandedToken(PragmaToken);
    523 
    524   // Now, the current token must be clang::tok::lpara
    525   if (PragmaToken.isNot(clang::tok::l_paren)) {
    526     // If no argument, set the version to 0
    527     this->handleInt(PP, PragmaToken, 0);
    528     return;
    529   }
    530   PP.LexUnexpandedToken(PragmaToken);
    531 
    532   if (PragmaToken.is(clang::tok::numeric_constant)) {
    533     llvm::SmallString<128> SpellingBuffer;
    534     SpellingBuffer.resize(PragmaToken.getLength() + 1);
    535     llvm::StringRef TokSpelling = PP.getSpelling(PragmaToken, SpellingBuffer);
    536     clang::NumericLiteralParser NumericLiteral(TokSpelling,
    537         PragmaToken.getLocation(), PP);
    538     if (NumericLiteral.hadError) {
    539       // Diagnostics will be generated automatically
    540       return;
    541     } else {
    542       llvm::APInt Val(32, 0);
    543       NumericLiteral.GetIntegerValue(Val);
    544       this->handleInt(PP, PragmaToken, static_cast<int>(Val.getSExtValue()));
    545     }
    546     PP.LexUnexpandedToken(PragmaToken);
    547   } else {
    548     // If no argument, set the version to 0
    549     this->handleInt(PP, PragmaToken, 0);
    550   }
    551 
    552   if (PragmaToken.isNot(clang::tok::r_paren)) {
    553     PP.Diag(PragmaToken,
    554             PP.getDiagnostics().getCustomDiagID(
    555                 clang::DiagnosticsEngine::Error,
    556                 "expected a ')'"));
    557     return;
    558   }
    559 
    560   do {
    561     PP.LexUnexpandedToken(PragmaToken);
    562   } while (PragmaToken.isNot(clang::tok::eod));
    563 }
    564 
    565 void AddPragmaHandlers(clang::Preprocessor &PP, RSContext *RsContext) {
    566   // For #pragma rs export_type
    567   PP.AddPragmaHandler("rs",
    568                       new RSExportTypePragmaHandler("export_type", RsContext));
    569 
    570   // For #pragma rs java_package_name
    571   PP.AddPragmaHandler(
    572       "rs", new RSJavaPackageNamePragmaHandler("java_package_name", RsContext));
    573 
    574   // For #pragma rs reduce
    575   PP.AddPragmaHandler(
    576       "rs", new RSReducePragmaHandler(RSExportReduce::KeyReduce, RsContext));
    577 
    578   // For #pragma rs set_reflect_license
    579   PP.AddPragmaHandler(
    580       "rs", new RSReflectLicensePragmaHandler("set_reflect_license", RsContext));
    581 
    582   // For #pragma version
    583   PP.AddPragmaHandler(new RSVersionPragmaHandler("version", RsContext));
    584 
    585   // For #pragma rs_fp*
    586   PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_full", RsContext));
    587   PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_relaxed", RsContext));
    588   PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_imprecise", RsContext));
    589 }
    590 
    591 
    592 }  // namespace slang
    593