|
@@ -0,0 +1,169 @@
|
|
|
+//===--- HLSLMacroExpander.cpp - Standalone Macro expansion -----*- C++ -*-===//
|
|
|
+// //
|
|
|
+// HLSLMacroExpander.cpp //
|
|
|
+// Copyright (C) Microsoft Corporation. All rights reserved. //
|
|
|
+// This file is distributed under the University of Illinois Open Source //
|
|
|
+// License. See LICENSE.TXT for details. //
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+//
|
|
|
+// This file implements the MacroExpander class.
|
|
|
+//
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+#include "clang/Lex/HLSLMacroExpander.h"
|
|
|
+
|
|
|
+#include "clang/Basic/SourceLocation.h"
|
|
|
+#include "clang/Lex/Lexer.h"
|
|
|
+#include "clang/Lex/MacroInfo.h"
|
|
|
+#include "clang/Lex/ModuleMap.h"
|
|
|
+#include "clang/Lex/PPCallbacks.h"
|
|
|
+#include "clang/Lex/Preprocessor.h"
|
|
|
+#include "clang/Lex/PTHLexer.h"
|
|
|
+#include "clang/Lex/PTHManager.h"
|
|
|
+#include "clang/Lex/Token.h"
|
|
|
+#include "clang/Lex/TokenLexer.h"
|
|
|
+#include "llvm/ADT/StringRef.h"
|
|
|
+
|
|
|
+#include "dxc/Support/Global.h"
|
|
|
+using namespace clang;
|
|
|
+using namespace llvm;
|
|
|
+using namespace hlsl;
|
|
|
+
|
|
|
+MacroExpander::MacroExpander(Preprocessor &PP_, unsigned options)
|
|
|
+ : PP(PP_)
|
|
|
+ , m_expansionFileId()
|
|
|
+ , m_stripQuotes(false)
|
|
|
+{
|
|
|
+ if (options & STRIP_QUOTES)
|
|
|
+ m_stripQuotes = true;
|
|
|
+
|
|
|
+ // The preprocess requires a file to be on the lexing stack when we
|
|
|
+ // call ExpandMacro. We add an empty in-memory buffer that we use
|
|
|
+ // just for expanding macros.
|
|
|
+ std::unique_ptr<llvm::MemoryBuffer> SB = llvm::MemoryBuffer::getMemBuffer("", "<hlsl-semantic-defines>");
|
|
|
+ if (!SB) {
|
|
|
+ DXASSERT(false, "Cannot create macro expansion source buffer");
|
|
|
+ throw hlsl::Exception(DXC_E_MACRO_EXPANSION_FAILURE);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Unfortunately, there is no api in the SourceManager to lookup a
|
|
|
+ // previously added file, so we have to add the empty file every time
|
|
|
+ // we expand macros. We could modify source manager to get/set the
|
|
|
+ // macro file id similar to the one we have for getPreambleFileID.
|
|
|
+ // Macros should only be expanded once (if needed for a root signature)
|
|
|
+ // or twice (for semantic defines) so adding an empty file every time
|
|
|
+ // is probably not a big deal.
|
|
|
+ m_expansionFileId = PP.getSourceManager().createFileID(std::move(SB));
|
|
|
+ if (m_expansionFileId.isInvalid()) {
|
|
|
+ DXASSERT(false, "Could not create FileID for macro expnasion?");
|
|
|
+ throw hlsl::Exception(DXC_E_MACRO_EXPANSION_FAILURE);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Simple struct to hold a data/length pair.
|
|
|
+struct LiteralData {
|
|
|
+ const char *Data;
|
|
|
+ unsigned Length;
|
|
|
+};
|
|
|
+
|
|
|
+// Get the literal data from a literal token.
|
|
|
+// If stripQuotes flag is true the quotes (and string literal type) will
|
|
|
+// be removed from the data and only the raw string literal value will be
|
|
|
+// returned.
|
|
|
+static LiteralData GetLiteralData(const Token &Tok, bool stripQuotes) {
|
|
|
+ if (!tok::isStringLiteral(Tok.getKind()))
|
|
|
+ return LiteralData{ Tok.getLiteralData(), Tok.getLength() };
|
|
|
+
|
|
|
+ unsigned start_offset = 0;
|
|
|
+ unsigned end_offset = 0;
|
|
|
+ switch (Tok.getKind()) {
|
|
|
+ case tok::string_literal: start_offset = 1; end_offset = 1; break; // "foo"
|
|
|
+ case tok::wide_string_literal: start_offset = 2; end_offset = 1; break; // L"foo"
|
|
|
+ case tok::utf8_string_literal: start_offset = 3; end_offset = 1; break; // u8"foo"
|
|
|
+ case tok::utf16_string_literal: start_offset = 2; end_offset = 1; break; // u"foo"
|
|
|
+ case tok::utf32_string_literal: start_offset = 2; end_offset = 1; break; // U"foo"
|
|
|
+ default: break;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned length = Tok.getLength() - (start_offset + end_offset);
|
|
|
+ if (length > Tok.getLength()) { // Check for unsigned underflow.
|
|
|
+ DXASSERT(false, "string literal quote count is wrong?");
|
|
|
+ start_offset = 0;
|
|
|
+ length = Tok.getLength();
|
|
|
+ }
|
|
|
+
|
|
|
+ return LiteralData {Tok.getLiteralData() + start_offset, length};
|
|
|
+}
|
|
|
+
|
|
|
+// Print leading spaces if needed by the token.
|
|
|
+// Take care when stripping string literal quoates that we do not add extra
|
|
|
+// spaces to the output.
|
|
|
+static bool ShouldPrintLeadingSpace(const Token &Tok, const Token &PrevTok, bool stripQuotes) {
|
|
|
+ if (!Tok.hasLeadingSpace())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // Token has leading spaces, but the previous token was a sting literal
|
|
|
+ // and we are stripping quotes to paste the strings together so do not
|
|
|
+ // add a space between the string literal values.
|
|
|
+ if (tok::isStringLiteral(PrevTok.getKind()) && stripQuotes)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// Macro expansion implementation.
|
|
|
+// We re-lex the macro using the preprocessors lexer.
|
|
|
+bool MacroExpander::ExpandMacro(MacroInfo *pMacro, std::string *out) {
|
|
|
+ if (!pMacro || !out)
|
|
|
+ return false;
|
|
|
+ MacroInfo ¯o = *pMacro;
|
|
|
+
|
|
|
+ // Initialize the token from the macro definition location.
|
|
|
+ Token Tok;
|
|
|
+ bool failed = PP.getRawToken(macro.getDefinitionLoc(), Tok);
|
|
|
+ if (failed)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // Start the lexing process. Use an outer file to make the preprocessor happy.
|
|
|
+ PP.EnterSourceFile(m_expansionFileId, nullptr, PP.getSourceManager().getLocForStartOfFile(m_expansionFileId));
|
|
|
+ PP.EnterMacro(Tok, macro.getDefinitionEndLoc(), ¯o, nullptr);
|
|
|
+ PP.Lex(Tok);
|
|
|
+ llvm::raw_string_ostream OS(*out);
|
|
|
+
|
|
|
+ // Keep track of previous token to print spaces correctly.
|
|
|
+ Token PrevTok;
|
|
|
+ PrevTok.startToken();
|
|
|
+
|
|
|
+ // Lex all the tokens from the macro and add them to the output.
|
|
|
+ while (!Tok.is(tok::eof)) {
|
|
|
+ if (ShouldPrintLeadingSpace(Tok, PrevTok, m_stripQuotes)) {
|
|
|
+ OS << ' ';
|
|
|
+ }
|
|
|
+ if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
|
|
|
+ OS << II->getName();
|
|
|
+ }
|
|
|
+ else if (Tok.isLiteral() && !Tok.needsCleaning() &&
|
|
|
+ Tok.getLiteralData()) {
|
|
|
+ LiteralData literalData = GetLiteralData(Tok, m_stripQuotes);
|
|
|
+ OS.write(literalData.Data, literalData.Length);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ std::string S = PP.getSpelling(Tok);
|
|
|
+ OS.write(&S[0], S.size());
|
|
|
+ }
|
|
|
+ PrevTok = Tok;
|
|
|
+ PP.Lex(Tok);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// Search for the macro info by the given name.
|
|
|
+MacroInfo *MacroExpander::FindMacroInfo(clang::Preprocessor &PP, StringRef macroName) {
|
|
|
+ // Lookup macro identifier.
|
|
|
+ IdentifierInfo *ii = PP.getIdentifierInfo(macroName);
|
|
|
+ if (!ii)
|
|
|
+ return nullptr;
|
|
|
+
|
|
|
+ // Lookup macro info.
|
|
|
+ return PP.getMacroInfo(ii);
|
|
|
+}
|