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:
handleItem(const std::string & Item)42 void handleItem(const std::string &Item) {
43 mContext->addPragma(this->getName(), Item);
44 mContext->addExportType(Item);
45 }
46
47 public:
RSExportTypePragmaHandler(llvm::StringRef Name,RSContext * Context)48 RSExportTypePragmaHandler(llvm::StringRef Name, RSContext *Context)
49 : RSPragmaHandler(Name, Context) { }
50
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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:
RSJavaPackageNamePragmaHandler(llvm::StringRef Name,RSContext * Context)60 RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context)
61 : RSPragmaHandler(Name, Context) { }
62
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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:
RSReducePragmaHandler(llvm::StringRef Name,RSContext * Context)129 RSReducePragmaHandler(llvm::StringRef Name, RSContext *Context)
130 : RSPragmaHandler(Name, Context) { }
131
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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
MarkUsed(clang::Preprocessor & PP,const std::string & FunctionName)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
ListKeywords(const KeywordValueMapType & KeywordValueMap)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.
ProcessKeywordAndValue(clang::Preprocessor & PP,clang::Token & PragmaToken,KeywordValueMapType & KeywordValueMap)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:
handleItem(const std::string & Item)345 void handleItem(const std::string &Item) {
346 mContext->addPragma(this->getName(), Item);
347 mContext->setLicenseNote(Item);
348 }
349
350 public:
RSReflectLicensePragmaHandler(llvm::StringRef Name,RSContext * Context)351 RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
352 : RSPragmaHandler(Name, Context) { }
353
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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:
handleInt(clang::Preprocessor & PP,clang::Token & Tok,const int v)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:
RSVersionPragmaHandler(llvm::StringRef Name,RSContext * Context)381 RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
382 : RSPragmaHandler(Name, Context) { }
383
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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:
RSPrecisionPragmaHandler(llvm::StringRef Name,RSContext * Context)396 RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context)
397 : RSPragmaHandler(Name, Context) {}
398
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & Token)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
handleItemListPragma(clang::Preprocessor & PP,clang::Token & FirstToken)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
handleNonParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)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
handleOptionalStringLiteralParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)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
handleIntegerParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)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
AddPragmaHandlers(clang::Preprocessor & PP,RSContext * RsContext)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