HLSLMacroExpander.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //===--- HLSLMacroExpander.cpp - Standalone Macro expansion -----*- C++ -*-===//
  2. // //
  3. // HLSLMacroExpander.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file implements the MacroExpander class.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/Lex/HLSLMacroExpander.h"
  13. #include "clang/Basic/SourceLocation.h"
  14. #include "clang/Lex/Lexer.h"
  15. #include "clang/Lex/MacroInfo.h"
  16. #include "clang/Lex/ModuleMap.h"
  17. #include "clang/Lex/PPCallbacks.h"
  18. #include "clang/Lex/Preprocessor.h"
  19. #include "clang/Lex/PTHLexer.h"
  20. #include "clang/Lex/PTHManager.h"
  21. #include "clang/Lex/Token.h"
  22. #include "clang/Lex/TokenLexer.h"
  23. #include "llvm/ADT/StringRef.h"
  24. #include "dxc/Support/Global.h"
  25. using namespace clang;
  26. using namespace llvm;
  27. using namespace hlsl;
  28. MacroExpander::MacroExpander(Preprocessor &PP_, unsigned options)
  29. : PP(PP_)
  30. , m_expansionFileId()
  31. , m_stripQuotes(false)
  32. {
  33. if (options & STRIP_QUOTES)
  34. m_stripQuotes = true;
  35. // The preprocess requires a file to be on the lexing stack when we
  36. // call ExpandMacro. We add an empty in-memory buffer that we use
  37. // just for expanding macros.
  38. std::unique_ptr<llvm::MemoryBuffer> SB = llvm::MemoryBuffer::getMemBuffer("", "<hlsl-semantic-defines>");
  39. if (!SB) {
  40. DXASSERT(false, "Cannot create macro expansion source buffer");
  41. throw hlsl::Exception(DXC_E_MACRO_EXPANSION_FAILURE);
  42. }
  43. // Unfortunately, there is no api in the SourceManager to lookup a
  44. // previously added file, so we have to add the empty file every time
  45. // we expand macros. We could modify source manager to get/set the
  46. // macro file id similar to the one we have for getPreambleFileID.
  47. // Macros should only be expanded once (if needed for a root signature)
  48. // or twice (for semantic defines) so adding an empty file every time
  49. // is probably not a big deal.
  50. m_expansionFileId = PP.getSourceManager().createFileID(std::move(SB));
  51. if (m_expansionFileId.isInvalid()) {
  52. DXASSERT(false, "Could not create FileID for macro expnasion?");
  53. throw hlsl::Exception(DXC_E_MACRO_EXPANSION_FAILURE);
  54. }
  55. }
  56. // Simple struct to hold a data/length pair.
  57. struct LiteralData {
  58. const char *Data;
  59. unsigned Length;
  60. };
  61. // Get the literal data from a literal token.
  62. // If stripQuotes flag is true the quotes (and string literal type) will
  63. // be removed from the data and only the raw string literal value will be
  64. // returned.
  65. static LiteralData GetLiteralData(const Token &Tok, bool stripQuotes) {
  66. if (!tok::isStringLiteral(Tok.getKind()))
  67. return LiteralData{ Tok.getLiteralData(), Tok.getLength() };
  68. unsigned start_offset = 0;
  69. unsigned end_offset = 0;
  70. switch (Tok.getKind()) {
  71. case tok::string_literal: start_offset = 1; end_offset = 1; break; // "foo"
  72. case tok::wide_string_literal: start_offset = 2; end_offset = 1; break; // L"foo"
  73. case tok::utf8_string_literal: start_offset = 3; end_offset = 1; break; // u8"foo"
  74. case tok::utf16_string_literal: start_offset = 2; end_offset = 1; break; // u"foo"
  75. case tok::utf32_string_literal: start_offset = 2; end_offset = 1; break; // U"foo"
  76. default: break;
  77. }
  78. unsigned length = Tok.getLength() - (start_offset + end_offset);
  79. if (length > Tok.getLength()) { // Check for unsigned underflow.
  80. DXASSERT(false, "string literal quote count is wrong?");
  81. start_offset = 0;
  82. length = Tok.getLength();
  83. }
  84. return LiteralData {Tok.getLiteralData() + start_offset, length};
  85. }
  86. // Print leading spaces if needed by the token.
  87. // Take care when stripping string literal quoates that we do not add extra
  88. // spaces to the output.
  89. static bool ShouldPrintLeadingSpace(const Token &Tok, const Token &PrevTok, bool stripQuotes) {
  90. if (!Tok.hasLeadingSpace())
  91. return false;
  92. // Token has leading spaces, but the previous token was a sting literal
  93. // and we are stripping quotes to paste the strings together so do not
  94. // add a space between the string literal values.
  95. if (tok::isStringLiteral(PrevTok.getKind()) && stripQuotes)
  96. return false;
  97. return true;
  98. }
  99. // Macro expansion implementation.
  100. // We re-lex the macro using the preprocessors lexer.
  101. bool MacroExpander::ExpandMacro(MacroInfo *pMacro, std::string *out) {
  102. if (!pMacro || !out)
  103. return false;
  104. MacroInfo &macro = *pMacro;
  105. // Initialize the token from the macro definition location.
  106. Token Tok;
  107. bool failed = PP.getRawToken(macro.getDefinitionLoc(), Tok);
  108. if (failed)
  109. return false;
  110. // Start the lexing process. Use an outer file to make the preprocessor happy.
  111. PP.EnterSourceFile(m_expansionFileId, nullptr, PP.getSourceManager().getLocForStartOfFile(m_expansionFileId));
  112. PP.EnterMacro(Tok, macro.getDefinitionEndLoc(), &macro, nullptr);
  113. PP.Lex(Tok);
  114. llvm::raw_string_ostream OS(*out);
  115. // Keep track of previous token to print spaces correctly.
  116. Token PrevTok;
  117. PrevTok.startToken();
  118. // Lex all the tokens from the macro and add them to the output.
  119. while (!Tok.is(tok::eof)) {
  120. if (ShouldPrintLeadingSpace(Tok, PrevTok, m_stripQuotes)) {
  121. OS << ' ';
  122. }
  123. if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
  124. OS << II->getName();
  125. }
  126. else if (Tok.isLiteral() && !Tok.needsCleaning() &&
  127. Tok.getLiteralData()) {
  128. LiteralData literalData = GetLiteralData(Tok, m_stripQuotes);
  129. OS.write(literalData.Data, literalData.Length);
  130. }
  131. else {
  132. std::string S = PP.getSpelling(Tok);
  133. OS.write(&S[0], S.size());
  134. }
  135. PrevTok = Tok;
  136. PP.Lex(Tok);
  137. }
  138. return true;
  139. }
  140. // Search for the macro info by the given name.
  141. MacroInfo *MacroExpander::FindMacroInfo(clang::Preprocessor &PP, StringRef macroName) {
  142. // Lookup macro identifier.
  143. IdentifierInfo *ii = PP.getIdentifierInfo(macroName);
  144. if (!ii)
  145. return nullptr;
  146. // Lookup macro info.
  147. return PP.getMacroInfo(ii);
  148. }