diff --git a/bolt/CMakeLists.txt b/bolt/CMakeLists.txt --- a/bolt/CMakeLists.txt +++ b/bolt/CMakeLists.txt @@ -76,6 +76,10 @@ add_subdirectory(tools) if (BOLT_INCLUDE_TESTS) + if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include/gtest/gtest.h) + add_subdirectory(unittests) + list(APPEND BOLT_TEST_DEPS BoltUnitTests) + endif() add_subdirectory(test) endif() diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -1886,6 +1886,14 @@ } }; +MCPlusBuilder *createX86MCPlusBuilder(const MCInstrAnalysis *, + const MCInstrInfo *, + const MCRegisterInfo *); + +MCPlusBuilder *createAArch64MCPlusBuilder(const MCInstrAnalysis *, + const MCInstrInfo *, + const MCRegisterInfo *); + } // namespace bolt } // namespace llvm diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -547,6 +547,11 @@ friend class RewriteInstanceDiff; }; +MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch, + const MCInstrAnalysis *Analysis, + const MCInstrInfo *Info, + const MCRegisterInfo *RegInfo); + } // namespace bolt } // namespace llvm diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -44,7 +44,6 @@ extern llvm::cl::opt HotData; extern llvm::cl::opt HotFunctionsAtEnd; extern llvm::cl::opt HotText; -extern llvm::cl::opt InputFilename; extern llvm::cl::opt Instrument; extern llvm::cl::opt OutputFilename; extern llvm::cl::opt PerfData; diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp --- a/bolt/lib/Core/MCPlusBuilder.cpp +++ b/bolt/lib/Core/MCPlusBuilder.cpp @@ -416,49 +416,46 @@ // AliasMap caches a mapping of registers to the set of registers that // alias (are sub or superregs of itself, including itself). static std::vector AliasMap; - static std::vector SuperReg; + static std::vector SmallerAliasMap; if (AliasMap.size() > 0) { if (OnlySmaller) - return AliasMap[Reg]; - return AliasMap[SuperReg[Reg]]; + return SmallerAliasMap[Reg]; + return AliasMap[Reg]; } + // Build alias map for (MCPhysReg I = 0, E = RegInfo->getNumRegs(); I != E; ++I) { BitVector BV(RegInfo->getNumRegs(), false); BV.set(I); - AliasMap.emplace_back(std::move(BV)); - SuperReg.emplace_back(I); + AliasMap.emplace_back(BV); + SmallerAliasMap.emplace_back(BV); + } + + // Cache all aliases for each register + for (MCPhysReg I = 1, E = RegInfo->getNumRegs(); I != E; ++I) { + for (MCRegAliasIterator AI(I, RegInfo, true); AI.isValid(); ++AI) + AliasMap[I].set(*AI); } + + // Propagate smaller alias info upwards. Skip reg 0 (mapped to NoRegister) std::queue Worklist; - // Propagate alias info upwards. Skip reg 0 (mapped to NoRegister) for (MCPhysReg I = 1, E = RegInfo->getNumRegs(); I < E; ++I) Worklist.push(I); while (!Worklist.empty()) { MCPhysReg I = Worklist.front(); Worklist.pop(); for (MCSubRegIterator SI(I, RegInfo); SI.isValid(); ++SI) - AliasMap[I] |= AliasMap[*SI]; + SmallerAliasMap[I] |= SmallerAliasMap[*SI]; for (MCSuperRegIterator SI(I, RegInfo); SI.isValid(); ++SI) Worklist.push(*SI); } - // Propagate parent reg downwards - for (MCPhysReg I = 1, E = RegInfo->getNumRegs(); I < E; ++I) - Worklist.push(I); - while (!Worklist.empty()) { - MCPhysReg I = Worklist.front(); - Worklist.pop(); - for (MCSubRegIterator SI(I, RegInfo); SI.isValid(); ++SI) { - SuperReg[*SI] = SuperReg[I]; - Worklist.push(*SI); - } - } LLVM_DEBUG({ dbgs() << "Dumping reg alias table:\n"; for (MCPhysReg I = 0, E = RegInfo->getNumRegs(); I != E; ++I) { dbgs() << "Reg " << I << ": "; - const BitVector &BV = AliasMap[SuperReg[I]]; + const BitVector &BV = AliasMap[I]; int Idx = BV.find_first(); while (Idx != -1) { dbgs() << Idx << " "; @@ -469,8 +466,8 @@ }); if (OnlySmaller) - return AliasMap[Reg]; - return AliasMap[SuperReg[Reg]]; + return SmallerAliasMap[Reg]; + return AliasMap[Reg]; } uint8_t MCPlusBuilder::getRegSize(MCPhysReg Reg) const { diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -321,27 +321,6 @@ extern const char *BoltRevision; -extern MCPlusBuilder *createX86MCPlusBuilder(const MCInstrAnalysis *, - const MCInstrInfo *, - const MCRegisterInfo *); -extern MCPlusBuilder *createAArch64MCPlusBuilder(const MCInstrAnalysis *, - const MCInstrInfo *, - const MCRegisterInfo *); - -} // namespace bolt -} // namespace llvm - -namespace { - -bool refersToReorderedSection(ErrorOr Section) { - auto Itr = - std::find_if(opts::ReorderData.begin(), opts::ReorderData.end(), - [&](const std::string &SectionName) { - return (Section && Section->getName() == SectionName); - }); - return Itr != opts::ReorderData.end(); -} - MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch, const MCInstrAnalysis *Analysis, const MCInstrInfo *Info, @@ -359,6 +338,20 @@ llvm_unreachable("architecture unsupported by MCPlusBuilder"); } +} // namespace bolt +} // namespace llvm + +namespace { + +bool refersToReorderedSection(ErrorOr Section) { + auto Itr = + std::find_if(opts::ReorderData.begin(), opts::ReorderData.end(), + [&](const std::string &SectionName) { + return (Section && Section->getName() == SectionName); + }); + return Itr != opts::ReorderData.end(); +} + } // anonymous namespace RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc, diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -131,14 +131,6 @@ "will put hot code into 2M pages. This requires relocation."), cl::ZeroOrMore, cl::cat(BoltCategory)); -cl::opt -InputFilename( - cl::Positional, - cl::desc(""), - cl::Required, - cl::cat(BoltCategory), - cl::sub(*cl::AllSubCommands)); - cl::opt Instrument("instrument", cl::desc("instrument code to generate accurate profile data"), diff --git a/bolt/test/CMakeLists.txt b/bolt/test/CMakeLists.txt --- a/bolt/test/CMakeLists.txt +++ b/bolt/test/CMakeLists.txt @@ -8,6 +8,12 @@ MAIN_CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py ) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg.py + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.cfg.py + ) set(BOLT_TEST_PARAMS bolt_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg diff --git a/bolt/test/Unit/CMakeLists.txt b/bolt/test/Unit/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/bolt/test/Unit/CMakeLists.txt @@ -0,0 +1,17 @@ +add_custom_target(bolt-unit-test-deps) +add_dependencies(bolt-unit-test-deps bolt-test-depends) + +add_lit_testsuites(BOLT-UNIT + ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS bolt-unit-test-deps) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py) + +add_lit_testsuite(check-golt-unit "Running bolt unit test suite" + ${CMAKE_CURRENT_BINARY_DIR} + EXCLUDE_FROM_CHECK_ALL + DEPENDS bolt-unit-test-deps) diff --git a/bolt/test/Unit/lit.cfg.py b/bolt/test/Unit/lit.cfg.py new file mode 100644 --- /dev/null +++ b/bolt/test/Unit/lit.cfg.py @@ -0,0 +1,22 @@ +# -*- Python -*- + +# Configuration file for the 'lit' test runner. + +import os +import subprocess + +import lit.formats + +# name: The name of this test suite. +config.name = 'BOLT-Unit' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [] + +# test_source_root: The root path where tests are located. +# test_exec_root: The root path where tests should be run. +config.test_exec_root = os.path.join(config.bolt_obj_root, 'unittests') +config.test_source_root = config.test_exec_root + +# testFormat: The test format to use to interpret tests. +config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests') diff --git a/bolt/test/Unit/lit.site.cfg.py.in b/bolt/test/Unit/lit.site.cfg.py.in new file mode 100644 --- /dev/null +++ b/bolt/test/Unit/lit.site.cfg.py.in @@ -0,0 +1,28 @@ +@LIT_SITE_CFG_IN_HEADER@ + +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.bolt_obj_root = "@BOLT_BINARY_DIR@" +config.bolt_src_root = "@BOLT_SOURCE_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.python_executable = "@Python3_EXECUTABLE@" + +# Support substitution of the tools and libs dirs with user parameters. This is +# used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params + config.llvm_build_mode = config.llvm_build_mode % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +import lit.llvm +lit.llvm.initialize(lit_config, config) + +# Let the main config do the real work. +lit_config.load_config(config, "@BOLT_SOURCE_DIR@/test/Unit/lit.cfg.py") diff --git a/bolt/tools/driver/llvm-bolt.cpp b/bolt/tools/driver/llvm-bolt.cpp --- a/bolt/tools/driver/llvm-bolt.cpp +++ b/bolt/tools/driver/llvm-bolt.cpp @@ -45,6 +45,11 @@ static cl::OptionCategory *Perf2BoltCategories[] = {&AggregatorCategory, &BoltOutputCategory}; +static cl::opt InputFilename(cl::Positional, + cl::desc(""), + cl::Required, cl::cat(BoltCategory), + cl::sub(*cl::AllSubCommands)); + static cl::opt InputDataFilename("data", cl::desc(""), diff --git a/bolt/unittests/CMakeLists.txt b/bolt/unittests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/bolt/unittests/CMakeLists.txt @@ -0,0 +1,8 @@ +add_custom_target(BoltUnitTests) +set_target_properties(BoltUnitTests PROPERTIES FOLDER "Bolt tests") + +function(add_bolt_unittest test_dirname) + add_unittest(BoltUnitTests ${test_dirname} ${ARGN}) +endfunction() + +add_subdirectory(Core) diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/bolt/unittests/Core/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/lib/Target/AArch64 + ${CMAKE_BINARY_DIR}/lib/Target/AArch64 + ) + +set(LLVM_LINK_COMPONENTS + BOLTRewrite + ) + +add_bolt_unittest(CoreTests + MCPlusBuilder.cpp + ) + +string(FIND "${LLVM_TARGETS_TO_BUILD}" "AArch64" POSITION) +if (NOT ${POSITION} EQUAL -1) + target_compile_definitions(CoreTests PRIVATE AARCH64_AVAILABLE) +endif() diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp new file mode 100644 --- /dev/null +++ b/bolt/unittests/Core/MCPlusBuilder.cpp @@ -0,0 +1,82 @@ +#include "AArch64Subtarget.h" +#include "bolt/Rewrite/RewriteInstance.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; +using namespace bolt; + +namespace { +struct MCPlusBuilderTester : public testing::TestWithParam { + void SetUp() override { + initalizeLLVM(); + prepareElf(); + initializeBolt(); + } + +protected: + void initalizeLLVM() { + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + } + + void prepareElf() { + memcpy(ElfBuf, "\177ELF", 4); + ELF64LE::Ehdr *EHdr = reinterpret_cast(ElfBuf); + EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64; + EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB; + EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64; + MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF"); + ObjFile = cantFail(ObjectFile::createObjectFile(Source)); + } + + void initializeBolt() { + BC = BinaryContext::createBinaryContext( + ObjFile.get(), true, DWARFContext::create(*ObjFile.get())); + ASSERT_FALSE(!BC); + BC->initializeTarget(std::unique_ptr(createMCPlusBuilder( + GetParam(), BC->MIA.get(), BC->MII.get(), BC->MRI.get()))); + } + + char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {}; + std::unique_ptr ObjFile; + std::unique_ptr BC; +}; +} // namespace + +#ifdef AARCH64_AVAILABLE + +INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester, + ::testing::Values(Triple::aarch64)); + +TEST_P(MCPlusBuilderTester, AliasX0) { + uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0, AArch64::W0_W1, + AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7}; + size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0); + const BitVector &BV = BC->MIB->getAliases(AArch64::X0); + ASSERT_EQ(BV.count(), AliasesX0Count); + for (size_t I = 0; I < AliasesX0Count; ++I) { + ASSERT_TRUE(BV[AliasesX0[I]]); + } +} + +TEST_P(MCPlusBuilderTester, AliasSmallerX0) { + uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0}; + size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0); + const BitVector &BV = BC->MIB->getAliases(AArch64::X0, /*OnlySmaller*/ true); + ASSERT_EQ(BV.count(), AliasesX0Count); + for (size_t I = 0; I < AliasesX0Count; ++I) { + ASSERT_TRUE(BV[AliasesX0[I]]); + } +} + +#endif // AARCH64_AVAILABLE