diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -122,10 +122,14 @@ /// other when on the same line. Defaults to ';' const char *SeparatorString; - /// This indicates the comment character used by the assembler. Defaults to + /// This indicates the comment string used by the assembler. Defaults to /// "#" StringRef CommentString; + /// This indicates whether the comment string is only accepted as a comment + /// at the beginning of statements. Defaults to false. + bool RestrictCommentStringToStartOfStatement = false; + /// This is appended to emitted labels. Defaults to ":" const char *LabelSuffix; @@ -557,6 +561,9 @@ unsigned getCommentColumn() const { return 40; } StringRef getCommentString() const { return CommentString; } + bool getRestrictCommentStringToStartOfStatement() const { + return RestrictCommentStringToStartOfStatement; + } const char *getLabelSuffix() const { return LabelSuffix; } bool useAssignmentForEHBegin() const { return UseAssignmentForEHBegin; } diff --git a/llvm/lib/MC/MCParser/AsmLexer.cpp b/llvm/lib/MC/MCParser/AsmLexer.cpp --- a/llvm/lib/MC/MCParser/AsmLexer.cpp +++ b/llvm/lib/MC/MCParser/AsmLexer.cpp @@ -659,6 +659,9 @@ } bool AsmLexer::isAtStartOfComment(const char *Ptr) { + if (MAI.getRestrictCommentStringToStartOfStatement() && !IsAtStartOfStatement) + return false; + StringRef CommentString = MAI.getCommentString(); if (CommentString.size() == 1) diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.cpp --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmInfo.cpp @@ -21,7 +21,8 @@ MaxInstLength = 6; - CommentString = "#"; + CommentString = AssemblerDialect == AD_HLASM ? "*" : "#"; + RestrictCommentStringToStartOfStatement = (AssemblerDialect == AD_HLASM); ZeroDirective = "\t.space\t"; Data64bitsDirective = "\t.quad\t"; UsesELFSectionDirectiveForBSS = true; diff --git a/llvm/unittests/MC/SystemZ/CMakeLists.txt b/llvm/unittests/MC/SystemZ/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/MC/SystemZ/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/SystemZ + ) + +set(LLVM_LINK_COMPONENTS + SystemZ + MC + Support + ) + +add_llvm_unittest(SystemZAsmLexer + SystemZAsmLexerTest.cpp + ) diff --git a/llvm/unittests/MC/SystemZ/SystemZAsmLexerTest.cpp b/llvm/unittests/MC/SystemZ/SystemZAsmLexerTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/MC/SystemZ/SystemZAsmLexerTest.cpp @@ -0,0 +1,163 @@ +//===- llvm/unittests/MC/SystemZ/SystemZAsmLexerTest.cpp ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--------------------------------------------------------------------===// +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +// Come up with our hacked version of MCAsmInfo. +// This hacked version derives from the main MCAsmInfo instance. +// Here, we're free to override whatever we want, without polluting +// the main MCAsmInfo interface. +class MockedUpMCAsmInfo : public MCAsmInfo { +public: + void setRestrictCommentStringToStartOfStatement(bool Value) { + RestrictCommentStringToStartOfStatement = Value; + } + void setCommentString(StringRef Value) { CommentString = Value; } +}; + +// Setup a testing class that the GTest framework can call. +class SystemZAsmLexerTest : public ::testing::Test { +protected: + static void SetUpTestCase() { + LLVMInitializeSystemZTargetInfo(); + LLVMInitializeSystemZTargetMC(); + } + + std::unique_ptr MRI; + std::unique_ptr MUPMAI; + std::unique_ptr MII; + std::unique_ptr Str; + std::unique_ptr Parser; + + std::string TripleName; + llvm::Triple Triple; + const Target *TheTarget; + + const MCTargetOptions MCOptions; + MCObjectFileInfo MOFI; + + // Get the SystemZ Target info. + static const Target *getTarget(std::string Triple) { + std::string Error; + llvm::Triple TripleName(Triple); + const Target *TheTarget = TargetRegistry::lookupTarget(Triple, Error); + if (!TheTarget) + return nullptr; + + return TheTarget; + } + + SystemZAsmLexerTest() { + // We will use the SystemZ triple, because of missing + // Object File and Streamer support for the z/OS target. + TripleName = "s390x-ibm-linux"; + Triple = llvm::Triple(TripleName); + + TheTarget = getTarget(TripleName); + EXPECT_NE(TheTarget, nullptr); + + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + EXPECT_NE(MRI, nullptr); + + std::unique_ptr MAI; + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + EXPECT_NE(MAI, nullptr); + + // Now we cast to our mocked up version of MCAsmInfo. + MUPMAI.reset(static_cast(MAI.release())); + // MUPMAI should "hold" MAI. + EXPECT_NE(MUPMAI, nullptr); + // After releasing, MAI should now be null. + EXPECT_EQ(MAI, nullptr); + } + + void setupCallToAsmParser(StringRef AsmStr) { + std::unique_ptr Buffer(MemoryBuffer::getMemBuffer(AsmStr)); + SourceMgr SrcMgr; + SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); + + llvm::Triple Triple(TripleName); + MCContext Ctx(MUPMAI.get(), MRI.get(), &MOFI, &SrcMgr, &MCOptions); + MOFI.InitMCObjectFileInfo(Triple, false, Ctx, false); + + Str.reset(TheTarget->createNullStreamer(Ctx)); + + Parser.reset(createMCAsmParser(SrcMgr, Ctx, *Str, *MUPMAI)); + // Lex initially to get the string. + Parser->getLexer().Lex(); + } + + void lexAndCheckTokens(StringRef AsmStr, + SmallVector ExpectedTokens) { + // Get reference to AsmLexer. + MCAsmLexer &Lexer = Parser->getLexer(); + // Loop through all expected tokens checking one by one. + for (size_t I = 0; I < ExpectedTokens.size(); ++I) { + EXPECT_EQ(Lexer.getTok().getKind(), ExpectedTokens[I]); + Lexer.Lex(); + } + } +}; + +TEST_F(SystemZAsmLexerTest, CheckDontRestrictCommentStringToStartOfStatement) { + StringRef AsmStr = "jne #-4"; + + // Setup. + setupCallToAsmParser(AsmStr); + + SmallVector ExpectedTokens( + {AsmToken::Identifier, AsmToken::EndOfStatement}); + lexAndCheckTokens(AsmStr /* "jne #-4" */, ExpectedTokens); +} + +// Testing MCAsmInfo's RestrictCommentStringToStartOfStatement attribute. +TEST_F(SystemZAsmLexerTest, CheckRestrictCommentStringToStartOfStatement) { + StringRef AsmStr = "jne #-4"; + + // Setup. + MUPMAI->setRestrictCommentStringToStartOfStatement(true); + setupCallToAsmParser(AsmStr); + + // When we are restricting the comment string to only the start of the + // statement, The sequence of tokens we are expecting are: Identifier - "jne" + // Hash - '#' + // Minus - '-' + // Integer - '4' + SmallVector ExpectedTokens( + {AsmToken::Identifier, AsmToken::Hash, AsmToken::Minus, + AsmToken::Integer}); + lexAndCheckTokens(AsmStr /* "jne #-4" */, ExpectedTokens); +} + +// Test HLASM Comment Syntax ('*') +TEST_F(SystemZAsmLexerTest, CheckHLASMComment) { + StringRef AsmStr = "* lhi 1,10"; + + // Setup. + MUPMAI->setCommentString("*"); + setupCallToAsmParser(AsmStr); + + SmallVector ExpectedTokens( + {AsmToken::EndOfStatement, AsmToken::Eof}); + lexAndCheckTokens(AsmStr /* "* lhi 1,10" */, ExpectedTokens); +} +} // end anonymous namespace