Index: llvm/CMakeLists.txt =================================================================== --- llvm/CMakeLists.txt +++ llvm/CMakeLists.txt @@ -978,6 +978,7 @@ add_subdirectory(utils/count) add_subdirectory(utils/not) add_subdirectory(utils/yaml-bench) + add_subdirectory(utils/compiledtestboilerplate) else() if ( LLVM_INCLUDE_TESTS ) message(FATAL_ERROR "Including tests when not building utils will not work. Index: llvm/include/llvm/ADT/STLExtras.h =================================================================== --- llvm/include/llvm/ADT/STLExtras.h +++ llvm/include/llvm/ADT/STLExtras.h @@ -288,9 +288,13 @@ decltype(std::declval()(*std::declval()))> class mapped_iterator : public iterator_adaptor_base< - mapped_iterator, ItTy, - typename std::iterator_traits::iterator_category, - typename std::remove_reference::type> { + mapped_iterator, ItTy, + typename std::iterator_traits::iterator_category, + typename std::remove_reference::type, // value_type + typename std::iterator_traits::difference_type, // difference_type + typename std::remove_reference::type *, // pointer + FuncReturnTy // reference (if Func returns an r-value, cannot convert it to a reference) + > { public: mapped_iterator(ItTy U, FuncTy F) : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {} Index: llvm/test/CMakeLists.txt =================================================================== --- llvm/test/CMakeLists.txt +++ llvm/test/CMakeLists.txt @@ -1,3 +1,5 @@ +include(ExternalProject) + llvm_canonicalize_cmake_booleans( BUILD_SHARED_LIBS HAVE_LIBXAR @@ -121,6 +123,35 @@ yaml2obj ) +option(LLVM_EXTERNAL_COMPILED_TESTS "Build compiled regression tests separately" OFF) +if (LLVM_EXTERNAL_COMPILED_TESTS) + llvm_ExternalProject_Add(llvm-compiled-tests-external "${CMAKE_CURRENT_SOURCE_DIR}/compiled" + BINARY_DIR + "${LLVM_BINARY_DIR}/compiledtests/${CMAKE_CFG_INTDIR}" + CMAKE_ARGS + "-DLLVM_COMPILED_TEST_BINARY_DIR=${LLVM_BINARY_DIR}/compiledtests/${CMAKE_CFG_INTDIR}" + "-DLLVM_CMAKE_PATH=${LLVM_BINARY_DIR}" + + # Only CMake 3.12 + # "-DLLVM_ROOT=${LLVM_BINARY_DIR}" + EXTRA_TARGETS + llvm-compiled-tests + DEPENDS + compiledtestboilerplate + NO_INSTALL + ) +else () + add_subdirectory(compiled) +endif () +list(APPEND LLVM_TEST_DEPENDS llvm-compiled-tests) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Compiled/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/Compiled/lit.site.cfg.py + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/Compiled/lit.cfg.py +) + if(TARGET llvm-lto) set(LLVM_TEST_DEPENDS ${LLVM_TEST_DEPENDS} llvm-lto) endif() Index: llvm/test/Compiled/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/test/Compiled/CMakeLists.txt @@ -0,0 +1,119 @@ + +# Add the compilation of abstestpath to testtargetname. +# If testtargetname does not exist yet, a new gtest execulable target is created. +function(ensure_compile_test_target testtargetname abstestpath) + if (TARGET "${testtargetname}") + # target already created, only append to list of tests + target_sources("${testtargetname}" PUBLIC "${testfile}") + return () + endif () + + # The following is inspired by add_unittest + + if( NOT LLVM_BUILD_TESTS ) + set(EXCLUDE_FROM_ALL ON) + endif() + + # Our current version of gtest does not properly recognize C++11 support + # with MSVC, so it falls back to tr1 / experimental classes. Since LLVM + # itself requires C++11, we can safely force it on unconditionally so that + # we don't have to fight with the buggy gtest check. + add_definitions(-DGTEST_LANG_CXX11=1) + add_definitions(-DGTEST_HAS_TR1_TUPLE=0) + + if (NOT LLVM_ENABLE_THREADS) + list(APPEND LLVM_COMPILE_DEFINITIONS GTEST_HAS_PTHREAD=0) + endif () + + if (SUPPORTS_VARIADIC_MACROS_FLAG) + list(APPEND LLVM_COMPILE_FLAGS "-Wno-variadic-macros") + endif () + # Some parts of gtest rely on this GNU extension, don't warn on it. + if(SUPPORTS_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS_FLAG) + list(APPEND LLVM_COMPILE_FLAGS "-Wno-gnu-zero-variadic-macro-arguments") + endif() + + set(LLVM_REQUIRES_RTTI OFF) + list(APPEND LLVM_LINK_COMPONENTS Support) # gtest needs it for raw_ostream + + add_llvm_executable("${testtargetname}" "${abstestpath}" NO_INSTALL_RPATH) + + set(outdir "${LLVM_COMPILED_TEST_BINARY_DIR}") + + set_output_directory(${testtargetname} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir}) + # libpthreads overrides some standard library symbols, so main + # executable must be linked with it in order to provide consistent + # API for all shared libaries loaded by this executable. + target_link_libraries(${testtargetname} PRIVATE compiledtestboilerplate gtest_main gtest ${LLVM_PTHREAD_LIB}) + + add_dependencies(llvm-compiled-tests ${testtargetname}) + set_property(TARGET ${testtargetname} PROPERTY FOLDER "compiled tests") +endfunction() + + +# Called when a compiled regression test was found; Add the source file +# to a gtest binary. +function(add_compiled_test testfile abstestpath) + get_filename_component(testdir "${testfile}" DIRECTORY) + + set(testtargetname "${testdir}") + string(REPLACE "/" "_" testtargetname "${testdir}") + string(REPLACE "\\" "_" testtargetname "${testtargetname}") + + ensure_compile_test_target("${testtargetname}CompiledTests" "${abstestpath}") +endfunction() + + +# Find all compiled regression tests in a folder and add them to a +# compiled target. +function(find_compiled_tests testsrcdir) + cmake_parse_arguments(ARG + "" + "GLOBBING" + "" + ${ARGN}) + + if (ARG_GLOBBING) + file(GLOB_RECURSE compiled_tests FOLLOW_SYMLINKS RELATIVE "${testsrcdir}" CONFIGURE_DEPENDS "${testsrcdir}/*.cxx") + else () + file(GLOB_RECURSE compiled_tests FOLLOW_SYMLINKS RELATIVE "${testsrcdir}" "${testsrcdir}/*.cxx") + endif () + + # Group by folder + foreach(compiled_test IN LISTS compiled_tests) + set(abstestpath "${testsrcdir}/${compiled_test}") + add_compiled_test("${compiled_test}" "${abstestpath}") + endforeach() +endfunction() + + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + # Compile regression tests as a separate build + + project(llvm-regression CXX) + cmake_minimum_required(VERSION 3.4.3) + + # Look for the CMake package of LLVM's build configuration. + set(llvm_cmake_config_dir ${LLVM_BINARY_DIR}) + find_package(LLVM REQUIRED CONFIG + PATHS "${llvm_cmake_config_dir}" + NO_DEFAULT_PATH + ) + + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") + include(AddLLVM) + + add_custom_target(llvm-compiled-tests) + + find_compiled_tests("${CMAKE_CURRENT_SOURCE_DIR}/.." GLOBBING ON) +else() + # Compile regression tests in the same build + + option(LLVM_TEST_GLOBBING "Research for new regression test before each compilation" ON) + + add_custom_target(llvm-compiled-tests) + + set(LLVM_COMPILED_TEST_BINARY_DIR "${LLVM_BINARY_DIR}/compiledtests/${CMAKE_CFG_INTDIR}") + find_compiled_tests("${CMAKE_CURRENT_SOURCE_DIR}/.." GLOBBING ${LLVM_TEST_GLOBBING}) +endif() + Index: llvm/test/Compiled/lit.cfg.py =================================================================== --- /dev/null +++ llvm/test/Compiled/lit.cfg.py @@ -0,0 +1,52 @@ +# -*- Python -*- +# Configuration file for the 'lit' test runner. + +import os +import subprocess + +import lit.formats + +# name: The name of this test suite. +config.name = 'LLVM-Compiled' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [] + +# is_early; Request to run this suite early. +config.is_early = True + +# test_source_root: The root path where tests are located. +# test_exec_root: The root path where tests should be run. +print("config.llvm_obj_root = " , config.llvm_obj_root) +config.test_exec_root = os.path.join(config.llvm_obj_root, 'compiledtests') +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, 'CompiledTests') + +# Propagate the temp directory. Windows requires this because it uses \Windows\ +# if none of these are present. +if 'TMP' in os.environ: + config.environment['TMP'] = os.environ['TMP'] +if 'TEMP' in os.environ: + config.environment['TEMP'] = os.environ['TEMP'] + +# Propagate HOME as it can be used to override incorrect homedir in passwd +# that causes the tests to fail. +if 'HOME' in os.environ: + config.environment['HOME'] = os.environ['HOME'] + +# Propagate path to symbolizer for ASan/MSan. +for symbolizer in ['ASAN_SYMBOLIZER_PATH', 'MSAN_SYMBOLIZER_PATH']: + if symbolizer in os.environ: + config.environment[symbolizer] = os.environ[symbolizer] + +# Win32 seeks DLLs along %PATH%. +if sys.platform in ['win32', 'cygwin'] and os.path.isdir(config.shlibdir): + config.environment['PATH'] = os.path.pathsep.join(( + config.shlibdir, config.environment['PATH'])) + +# Win32 may use %SYSTEMDRIVE% during file system shell operations, so propogate. +if sys.platform == 'win32' and 'SYSTEMDRIVE' in os.environ: + config.environment['SYSTEMDRIVE'] = os.environ['SYSTEMDRIVE'] + Index: llvm/test/Compiled/lit.site.cfg.py.in =================================================================== --- /dev/null +++ llvm/test/Compiled/lit.site.cfg.py.in @@ -0,0 +1,24 @@ +@LIT_SITE_CFG_IN_HEADER@ + +import sys + +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.enable_shared = @ENABLE_SHARED@ +config.shlibdir = "@SHLIBDIR@" + +# Support substitution of the tools_dir and build_mode 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_build_mode = config.llvm_build_mode % lit_config.params +except KeyError: + e = sys.exc_info()[1] + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +# Let the main config do the real work. +lit_config.load_config(config, "@LLVM_SOURCE_DIR@/test/Compiled/lit.cfg.py") + Index: llvm/test/Transforms/LoopVectorize/vectorizeVFone.cxx =================================================================== --- /dev/null +++ llvm/test/Transforms/LoopVectorize/vectorizeVFone.cxx @@ -0,0 +1,59 @@ +#ifdef IR +% type = type{[3 x double]} + +define void @getScalarFunc(double * % A, double * % C, % type * % B) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %inc, %for.body ], [ 0, %entry ] + %dummyload2 = load double, double* %A, align 8 + %arrayidx.i24 = getelementptr inbounds %type, %type* %B, i64 %i, i32 0, i32 0 + %_15 = load double, double* %arrayidx.i24, align 8 + %call10 = tail call fast double @atan(double %_15) #0 + %inc = add i64 %i, 1 + %cmp = icmp ugt i64 1000, %inc + br i1 %cmp, label %for.body, label %for.end + +for.end: + ret void +} + +declare double @atan(double) local_unnamed_addr + declare<2 x double> @vector_atan(<2 x double>) #0 attributes #0 = {nounwind readnone "vector-function-abi-variant" = "_ZGV_LLVM_N2v_atan(vector_atan)"} +#else /* IR */ + +#include "compiledtestboilerplate.h" +using namespace llvm; + +TEST(VectorizeVFone, getScalarFunc) { + auto M = run_opt(__FILE__, "IR", "-passes=loop-vectorize"); + auto getScalarFunc = M->getFunc("getScalarFunc"); + + // Set of calls to atan. + auto Calls = getScalarFunc.call_insts(); + + // Expect at least one call in the output. + Calls.expectMinCount(1); + + // Return type must not be a vector. + ASSERT_TRUE( + Calls.all_of([](CallInst *CI) -> bool { return !isa(CI->getType()); }) + ); + + // Arguments must not be vectors. + ASSERT_TRUE( + Calls.operands().all_of([](const llvm::Use &V) -> bool { + return !isa(V.get()->getType()); + }) + ); + + // Actually, there must be no vector at all. + ASSERT_TRUE( + getScalarFunc.operands().all_of([](const llvm::Use &U) -> bool { + return isa(U.get()->getType()); + }) + ); +} + +#endif /* IR */ Index: llvm/test/Transforms/LoopVectorize/vectorizeVFone.ll =================================================================== --- llvm/test/Transforms/LoopVectorize/vectorizeVFone.ll +++ /dev/null @@ -1,28 +0,0 @@ -; RUN: opt < %s -passes=loop-vectorize -S 2>&1 | FileCheck %s - -%type = type { [3 x double] } - -define void @getScalarFunc(double* %A, double* %C, %type* %B) { -; CHECK-LABEL: getScalarFunc -; This check will catch also the massv version of the function. -; CHECK-NOT: call fast <{{[0-9]+}} x double> @{{.*}}atan(<{{[0-9]+}} x double> %{{[0-9]+}}) -entry: - br label %for.body - -for.body: - %i = phi i64 [ %inc, %for.body ], [ 0, %entry ] - %dummyload2 = load double, double* %A, align 8 - %arrayidx.i24 = getelementptr inbounds %type, %type* %B, i64 %i, i32 0, i32 0 - %_15 = load double, double* %arrayidx.i24, align 8 - %call10 = tail call fast double @atan(double %_15) #0 - %inc = add i64 %i, 1 - %cmp = icmp ugt i64 1000, %inc - br i1 %cmp, label %for.body, label %for.end - -for.end: - ret void -} - -declare double @atan(double) local_unnamed_addr -declare <2 x double> @vector_atan(<2 x double>) #0 -attributes #0 = { nounwind readnone "vector-function-abi-variant"="_ZGV_LLVM_N2v_atan(vector_atan)" } Index: llvm/test/lit.cfg.py =================================================================== --- llvm/test/lit.cfg.py +++ llvm/test/lit.cfg.py @@ -22,7 +22,7 @@ # suffixes: A list of file extensions to treat as test files. This is overriden # by individual lit.local.cfg files in the test subdirectories. -config.suffixes = ['.ll', '.c', '.cxx', '.test', '.txt', '.s', '.mir'] +config.suffixes = ['.ll', '.c', '.test', '.txt', '.s', '.mir'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent Index: llvm/unittests/ADT/TestGraph.h =================================================================== --- llvm/unittests/ADT/TestGraph.h +++ llvm/unittests/ADT/TestGraph.h @@ -160,7 +160,8 @@ } /// ChildIterator - Visit all children of a node. - class ChildIterator { + class ChildIterator + : public std::iterator { friend class Graph; /// FirstNode - Pointer to first node in the graph's Nodes array. Index: llvm/utils/compiledtestboilerplate/AnalysisWrappers.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/AnalysisWrappers.cpp @@ -0,0 +1,74 @@ +//===- AnalysisWrappers.cpp - Wrappers around non-pass analyses -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines pass wrappers around LLVM analyses that don't make sense to +// be passes. It provides a nice standard pass interface to these classes so +// that they can be printed out by analyze. +// +// These classes are separated out of analyze.cpp so that it is more clear which +// code is the integral part of the analyze tool, and which part of the code is +// just making it so more passes are available. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +namespace { +/// ExternalFunctionsPassedConstants - This pass prints out call sites to +/// external functions that are called with constant arguments. This can be +/// useful when looking for standard library functions we should constant fold +/// or handle in alias analyses. +struct ExternalFunctionsPassedConstants : public ModulePass { + static char ID; // Pass ID, replacement for typeid + ExternalFunctionsPassedConstants() : ModulePass(ID) {} + bool runOnModule(Module &M) override { + for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { + if (!I->isDeclaration()) + continue; + + bool PrintedFn = false; + for (User *U : I->users()) { + Instruction *UI = dyn_cast(U); + if (!UI) + continue; + + CallBase *CB = dyn_cast(UI); + if (!CB) + continue; + + for (auto AI = CB->arg_begin(), E = CB->arg_end(); AI != E; ++AI) { + if (!isa(*AI)) + continue; + + if (!PrintedFn) { + errs() << "Function '" << I->getName() << "':\n"; + PrintedFn = true; + } + errs() << *UI; + break; + } + } + } + + return false; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } +}; +} // namespace + +char ExternalFunctionsPassedConstants::ID = 0; +static RegisterPass + P1("print-externalfnconstants", + "Print external fn callsites passed constants"); Index: llvm/utils/compiledtestboilerplate/BreakpointPrinter.h =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/BreakpointPrinter.h @@ -0,0 +1,24 @@ +//===- BreakpointPrinter.h - Breakpoint location printer ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Breakpoint location printer. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H +#define LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H + +namespace llvm { + +class ModulePass; +class raw_ostream; + +ModulePass *createBreakpointPrinter(raw_ostream &out); +} // namespace llvm + +#endif // LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H Index: llvm/utils/compiledtestboilerplate/BreakpointPrinter.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/BreakpointPrinter.cpp @@ -0,0 +1,71 @@ +//===- BreakpointPrinter.cpp - Breakpoint location printer ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Breakpoint location printer. +/// +//===----------------------------------------------------------------------===// +#include "BreakpointPrinter.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +struct BreakpointPrinter : public ModulePass { + raw_ostream &Out; + static char ID; + + BreakpointPrinter(raw_ostream &out) : ModulePass(ID), Out(out) {} + + void getContextName(const DIScope *Context, std::string &N) { + if (auto *NS = dyn_cast(Context)) { + if (!NS->getName().empty()) { + getContextName(NS->getScope(), N); + N = N + NS->getName().str() + "::"; + } + } else if (auto *TY = dyn_cast(Context)) { + if (!TY->getName().empty()) { + getContextName(TY->getScope(), N); + N = N + TY->getName().str() + "::"; + } + } + } + + bool runOnModule(Module &M) override { + StringSet<> Processed; + if (NamedMDNode *NMD = M.getNamedMetadata("llvm.dbg.sp")) + for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { + std::string Name; + auto *SP = cast_or_null(NMD->getOperand(i)); + if (!SP) + continue; + getContextName(SP->getScope(), Name); + Name = Name + SP->getName().str(); + if (!Name.empty() && Processed.insert(Name).second) { + Out << Name << "\n"; + } + } + return false; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } +}; + +char BreakpointPrinter::ID = 0; +} // namespace + +ModulePass *llvm::createBreakpointPrinter(raw_ostream &out) { + return new BreakpointPrinter(out); +} Index: llvm/utils/compiledtestboilerplate/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/CMakeLists.txt @@ -0,0 +1,68 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmParsers + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos + AggressiveInstCombine + Analysis + AsmParser + BitWriter + CodeGen + Core + Coroutines + Extensions + IPO + IRReader + InstCombine + Instrumentation + MC + ObjCARCOpts + Remarks + ScalarOpts + Support + Target + TransformUtils + Vectorize + Passes + ) + +# Our current version of gtest does not properly recognize C++11 support +# with MSVC, so it falls back to tr1 / experimental classes. Since LLVM +# itself requires C++11, we can safely force it on unconditionally so that +# we don't have to fight with the buggy gtest check. +add_definitions(-DGTEST_LANG_CXX11=1) +add_definitions(-DGTEST_HAS_TR1_TUPLE=0) + +include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include) +include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock/include) +if (NOT LLVM_ENABLE_THREADS) + list(APPEND LLVM_COMPILE_DEFINITIONS GTEST_HAS_PTHREAD=0) +endif () + + +add_llvm_library(compiledtestboilerplate + AnalysisWrappers.cpp + BreakpointPrinter.cpp + GraphPrinters.cpp + NewPMDriver.cpp + PassPrinters.cpp + PrintSCC.cpp + compiledtestboilerplate.cpp + + DEPENDS + intrinsics_gen + #SUPPORT_PLUGINS + ) +export_executable_symbols_for_plugins(compiledtestboilerplate) + +if(LLVM_BUILD_EXAMPLES) + target_link_libraries(compiledtestboilerplate PRIVATE ExampleIRTransforms) +endif(LLVM_BUILD_EXAMPLES) + +# This library is not intended to be installed +target_include_directories(compiledtestboilerplate INTERFACE $) +target_include_directories(compiledtestboilerplate INTERFACE $) +target_include_directories(compiledtestboilerplate INTERFACE $) +target_include_directories(compiledtestboilerplate INTERFACE $) +#target_link_libraries(compiledtestboilerplate PUBLIC gtest_main gtest ${LLVM_PTHREAD_LIB}) + Index: llvm/utils/compiledtestboilerplate/GraphPrinters.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/GraphPrinters.cpp @@ -0,0 +1,45 @@ +//===- GraphPrinters.cpp - DOT printers for various graph types -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines several printers for various different types of graphs used +// by the LLVM infrastructure. It uses the generic graph interface to convert +// the graph into a .dot graph. These graphs can then be processed with the +// "dot" tool to convert them to postscript or some other suitable format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Dominators.h" +#include "llvm/Pass.h" + +using namespace llvm; + +//===----------------------------------------------------------------------===// +// DomInfoPrinter Pass +//===----------------------------------------------------------------------===// + +namespace { +class DomInfoPrinter : public FunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + DomInfoPrinter() : FunctionPass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired(); + } + + bool runOnFunction(Function &F) override { + getAnalysis().print(dbgs()); + return false; + } +}; +} // namespace + +char DomInfoPrinter::ID = 0; +static RegisterPass DIP("print-dom-info", + "Dominator Info Printer", true, true); Index: llvm/utils/compiledtestboilerplate/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/LLVMBuild.txt @@ -0,0 +1,33 @@ +;===- ./tools/opt/LLVMBuild.txt --------------------------------*- Conf -*--===; +; +; 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 +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = opt +parent = Tools +required_libraries = + AsmParser + BitReader + BitWriter + CodeGen + IRReader + IPO + Instrumentation + Scalar + ObjCARC + Passes + LOF + all-targets Index: llvm/utils/compiledtestboilerplate/NewPMDriver.h =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/NewPMDriver.h @@ -0,0 +1,60 @@ +//===- NewPMDriver.h - Function to drive opt with the new PM ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// A single function which is called to drive the opt behavior for the new +/// PassManager. +/// +/// This is only in a separate TU with a header to avoid including all of the +/// old pass manager headers and the new pass manager headers into the same +/// file. Eventually all of the routines here will get folded back into +/// opt.cpp. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H +#define LLVM_TOOLS_OPT_NEWPMDRIVER_H + +namespace llvm { +class StringRef; +class Module; +class TargetMachine; +class ToolOutputFile; + +namespace opt_tool { +enum OutputKind { + OK_NoOutput, + OK_OutputAssembly, + OK_OutputBitcode, + OK_OutputThinLTOBitcode, +}; +enum VerifierKind { VK_NoVerifier, VK_VerifyInAndOut, VK_VerifyEachPass }; +enum PGOKind { NoPGO, InstrGen, InstrUse, SampleUse }; +enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse }; +} // namespace opt_tool + +/// Driver function to run the new pass manager over a module. +/// +/// This function only exists factored away from opt.cpp in order to prevent +/// inclusion of the new pass manager headers and the old headers into the same +/// file. It's interface is consequentially somewhat ad-hoc, but will go away +/// when the transition finishes. +/// +/// ThinLTOLinkOut is only used when OK is OK_OutputThinLTOBitcode, and can be +/// nullptr. +bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, + ToolOutputFile *Out, ToolOutputFile *ThinLinkOut, + ToolOutputFile *OptRemarkFile, StringRef PassPipeline, + opt_tool::OutputKind OK, opt_tool::VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder, + bool EmitSummaryIndex, bool EmitModuleHash, + bool EnableDebugify, bool Coroutines); +} // namespace llvm + +#endif Index: llvm/utils/compiledtestboilerplate/NewPMDriver.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/NewPMDriver.cpp @@ -0,0 +1,381 @@ +//===- NewPMDriver.cpp - Driver for opt with new PM -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file is just a split of the code that logically belongs in opt.cpp but +/// that includes the new pass manager headers. +/// +//===----------------------------------------------------------------------===// + +#include "NewPMDriver.h" +#include "PassPrinters.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" +#include "llvm/Transforms/Scalar/LoopPassManager.h" +#include "llvm/Transforms/Utils/Debugify.h" + +using namespace llvm; +using namespace opt_tool; + +static cl::opt + DebugPM("debug-pass-manager", cl::Hidden, + cl::desc("Print pass management debugging information")); + +static cl::list + PassPlugins("load-pass-plugin", + cl::desc("Load passes from plugin library")); + +// This flag specifies a textual description of the alias analysis pipeline to +// use when querying for aliasing information. It only works in concert with +// the "passes" flag above. +static cl::opt + AAPipeline("aa-pipeline", + cl::desc("A textual description of the alias analysis " + "pipeline for handling managed aliasing queries"), + cl::Hidden); + +/// {{@ These options accept textual pipeline descriptions which will be +/// inserted into default pipelines at the respective extension points +static cl::opt PeepholeEPPipeline( + "passes-ep-peephole", + cl::desc("A textual description of the function pass pipeline inserted at " + "the Peephole extension points into default pipelines"), + cl::Hidden); +static cl::opt LateLoopOptimizationsEPPipeline( + "passes-ep-late-loop-optimizations", + cl::desc( + "A textual description of the loop pass pipeline inserted at " + "the LateLoopOptimizations extension point into default pipelines"), + cl::Hidden); +static cl::opt LoopOptimizerEndEPPipeline( + "passes-ep-loop-optimizer-end", + cl::desc("A textual description of the loop pass pipeline inserted at " + "the LoopOptimizerEnd extension point into default pipelines"), + cl::Hidden); +static cl::opt ScalarOptimizerLateEPPipeline( + "passes-ep-scalar-optimizer-late", + cl::desc("A textual description of the function pass pipeline inserted at " + "the ScalarOptimizerLate extension point into default pipelines"), + cl::Hidden); +static cl::opt CGSCCOptimizerLateEPPipeline( + "passes-ep-cgscc-optimizer-late", + cl::desc("A textual description of the cgscc pass pipeline inserted at " + "the CGSCCOptimizerLate extension point into default pipelines"), + cl::Hidden); +static cl::opt VectorizerStartEPPipeline( + "passes-ep-vectorizer-start", + cl::desc("A textual description of the function pass pipeline inserted at " + "the VectorizerStart extension point into default pipelines"), + cl::Hidden); +static cl::opt PipelineStartEPPipeline( + "passes-ep-pipeline-start", + cl::desc("A textual description of the function pass pipeline inserted at " + "the PipelineStart extension point into default pipelines"), + cl::Hidden); +static cl::opt OptimizerLastEPPipeline( + "passes-ep-optimizer-last", + cl::desc("A textual description of the function pass pipeline inserted at " + "the OptimizerLast extension point into default pipelines"), + cl::Hidden); + +// Individual pipeline tuning options. +static cl::opt DisableLoopUnrolling( + "new-pm-disable-loop-unrolling", + cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false)); + +extern cl::opt PGOKindFlag; +extern cl::opt ProfileFile; +extern cl::opt CSPGOKindFlag; +extern cl::opt CSProfileGenFile; + +static cl::opt + ProfileRemappingFile("profile-remapping-file", + cl::desc("Path to the profile remapping file."), + cl::Hidden); +static cl::opt DebugInfoForProfiling( + "new-pm-debug-info-for-profiling", cl::init(false), cl::Hidden, + cl::desc("Emit special debug info to enable PGO profile generation.")); +/// @}} + +template +bool tryParsePipelineText(PassBuilder &PB, + const cl::opt &PipelineOpt) { + if (PipelineOpt.empty()) + return false; + + // Verify the pipeline is parseable: + PassManagerT PM; + if (auto Err = PB.parsePassPipeline(PM, PipelineOpt)) { + errs() << "Could not parse -" << PipelineOpt.ArgStr + << " pipeline: " << toString(std::move(Err)) + << "... I'm going to ignore it.\n"; + return false; + } + return true; +} + +/// If one of the EPPipeline command line options was given, register callbacks +/// for parsing and inserting the given pipeline +static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass, + bool DebugLogging) { + if (tryParsePipelineText(PB, PeepholeEPPipeline)) + PB.registerPeepholeEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse PeepholeEP pipeline: "); + Err(PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass, + DebugLogging)); + }); + if (tryParsePipelineText(PB, + LateLoopOptimizationsEPPipeline)) + PB.registerLateLoopOptimizationsEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse LateLoopOptimizationsEP pipeline: "); + Err(PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline, + VerifyEachPass, DebugLogging)); + }); + if (tryParsePipelineText(PB, LoopOptimizerEndEPPipeline)) + PB.registerLoopOptimizerEndEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse LoopOptimizerEndEP pipeline: "); + Err(PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, + VerifyEachPass, DebugLogging)); + }); + if (tryParsePipelineText(PB, + ScalarOptimizerLateEPPipeline)) + PB.registerScalarOptimizerLateEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse ScalarOptimizerLateEP pipeline: "); + Err(PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline, + VerifyEachPass, DebugLogging)); + }); + if (tryParsePipelineText(PB, CGSCCOptimizerLateEPPipeline)) + PB.registerCGSCCOptimizerLateEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse CGSCCOptimizerLateEP pipeline: "); + Err(PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, + VerifyEachPass, DebugLogging)); + }); + if (tryParsePipelineText(PB, VectorizerStartEPPipeline)) + PB.registerVectorizerStartEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse VectorizerStartEP pipeline: "); + Err(PB.parsePassPipeline(PM, VectorizerStartEPPipeline, + VerifyEachPass, DebugLogging)); + }); + if (tryParsePipelineText(PB, PipelineStartEPPipeline)) + PB.registerPipelineStartEPCallback( + [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM) { + ExitOnError Err("Unable to parse PipelineStartEP pipeline: "); + Err(PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass, + DebugLogging)); + }); + if (tryParsePipelineText(PB, OptimizerLastEPPipeline)) + PB.registerOptimizerLastEPCallback( + [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM, + PassBuilder::OptimizationLevel) { + ExitOnError Err("Unable to parse OptimizerLastEP pipeline: "); + Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline, VerifyEachPass, + DebugLogging)); + }); +} + +#define HANDLE_EXTENSION(Ext) \ + llvm::PassPluginLibraryInfo get##Ext##PluginInfo(); +#include "llvm/Support/Extension.def" + +bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, + ToolOutputFile *Out, ToolOutputFile *ThinLTOLinkOut, + ToolOutputFile *OptRemarkFile, + StringRef PassPipeline, OutputKind OK, + VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder, + bool EmitSummaryIndex, bool EmitModuleHash, + bool EnableDebugify, bool Coroutines) { + bool VerifyEachPass = VK == VK_VerifyEachPass; + + Optional P; + switch (PGOKindFlag) { + case InstrGen: + P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr); + break; + case InstrUse: + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse); + break; + case SampleUse: + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, + PGOOptions::SampleUse); + break; + case NoPGO: + if (DebugInfoForProfiling) + P = PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, + true); + else + P = None; + } + if (CSPGOKindFlag != NoCSPGO) { + if (P && (P->Action == PGOOptions::IRInstr || + P->Action == PGOOptions::SampleUse)) + errs() << "CSPGOKind cannot be used with IRInstr or SampleUse"; + if (CSPGOKindFlag == CSInstrGen) { + if (CSProfileGenFile.empty()) + errs() << "CSInstrGen needs to specify CSProfileGenFile"; + if (P) { + P->CSAction = PGOOptions::CSIRInstr; + P->CSProfileGenFile = CSProfileGenFile; + } else + P = PGOOptions("", CSProfileGenFile, ProfileRemappingFile, + PGOOptions::NoAction, PGOOptions::CSIRInstr); + } else /* CSPGOKindFlag == CSInstrUse */ { + if (!P) + errs() << "CSInstrUse needs to be together with InstrUse"; + P->CSAction = PGOOptions::CSIRUse; + } + } + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + PipelineTuningOptions PTO; + // LoopUnrolling defaults on to true and DisableLoopUnrolling is initialized + // to false above so we shouldn't necessarily need to check whether or not the + // option has been enabled. + PTO.LoopUnrolling = !DisableLoopUnrolling; + PTO.Coroutines = Coroutines; + PassBuilder PB(TM, PTO, P, &PIC); + registerEPCallbacks(PB, VerifyEachPass, DebugPM); + + // Load requested pass plugins and let them register pass builder callbacks + for (auto &PluginFN : PassPlugins) { + auto PassPlugin = PassPlugin::Load(PluginFN); + if (!PassPlugin) { + errs() << "Failed to load passes from '" << PluginFN + << "'. Request ignored.\n"; + continue; + } + + PassPlugin->registerPassBuilderCallbacks(PB); + } + + // Register a callback that creates the debugify passes as needed. + PB.registerPipelineParsingCallback( + [](StringRef Name, ModulePassManager &MPM, + ArrayRef) { + if (Name == "debugify") { + MPM.addPass(NewPMDebugifyPass()); + return true; + } else if (Name == "check-debugify") { + MPM.addPass(NewPMCheckDebugifyPass()); + return true; + } + return false; + }); + +#define HANDLE_EXTENSION(Ext) \ + get##Ext##PluginInfo().RegisterPassBuilderCallbacks(PB); +#include "llvm/Support/Extension.def" + + // Specially handle the alias analysis manager so that we can register + // a custom pipeline of AA passes with it. + AAManager AA; + if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } + + LoopAnalysisManager LAM(DebugPM); + FunctionAnalysisManager FAM(DebugPM); + CGSCCAnalysisManager CGAM(DebugPM); + ModuleAnalysisManager MAM(DebugPM); + + // Register the AA manager first so that our version is the one used. + FAM.registerPass([&] { return std::move(AA); }); + + // Register all the basic analyses with the managers. + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + ModulePassManager MPM(DebugPM); + if (VK > VK_NoVerifier) + MPM.addPass(VerifierPass()); + if (EnableDebugify) + MPM.addPass(NewPMDebugifyPass()); + + if (auto Err = + PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } + + if (VK > VK_NoVerifier) + MPM.addPass(VerifierPass()); + if (EnableDebugify) + MPM.addPass(NewPMCheckDebugifyPass()); + + // Add any relevant output pass at the end of the pipeline. + switch (OK) { + case OK_NoOutput: + break; // No output pass needed. + case OK_OutputAssembly: + MPM.addPass( + PrintModulePass(Out->os(), "", ShouldPreserveAssemblyUseListOrder)); + break; + case OK_OutputBitcode: + MPM.addPass(BitcodeWriterPass(Out->os(), ShouldPreserveBitcodeUseListOrder, + EmitSummaryIndex, EmitModuleHash)); + break; + case OK_OutputThinLTOBitcode: + MPM.addPass(ThinLTOBitcodeWriterPass( + Out->os(), ThinLTOLinkOut ? &ThinLTOLinkOut->os() : nullptr)); + break; + } + + // Before executing passes, print the final values of the LLVM options. + cl::PrintOptionValues(); + + // Now that we have all of the passes ready, run them. + MPM.run(M, MAM); + + // Declare success. + if (OK != OK_NoOutput) { + Out->keep(); + if (OK == OK_OutputThinLTOBitcode && ThinLTOLinkOut) + ThinLTOLinkOut->keep(); + } + + if (OptRemarkFile) + OptRemarkFile->keep(); + + return true; +} Index: llvm/utils/compiledtestboilerplate/PassPrinters.h =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/PassPrinters.h @@ -0,0 +1,44 @@ +//=- PassPrinters.h - Utilities to print analysis info for passes -*- C++ -*-=// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utilities to print analysis info for various kinds of passes. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H +#define LLVM_TOOLS_OPT_PASSPRINTERS_H + +namespace llvm { + +class CallGraphSCCPass; +class FunctionPass; +class ModulePass; +class LoopPass; +class PassInfo; +class raw_ostream; +class RegionPass; + +FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out, + bool Quiet); + +CallGraphSCCPass *createCallGraphPassPrinter(const PassInfo *PI, + raw_ostream &out, bool Quiet); + +ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out, + bool Quiet); + +LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out, + bool Quiet); + +RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out, + bool Quiet); + +} // end namespace llvm + +#endif // LLVM_TOOLS_OPT_PASSPRINTERS_H Index: llvm/utils/compiledtestboilerplate/PassPrinters.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/PassPrinters.cpp @@ -0,0 +1,227 @@ +//===- PassPrinters.cpp - Utilities to print analysis info for passes -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utilities to print analysis info for various kinds of passes. +/// +//===----------------------------------------------------------------------===// + +#include "PassPrinters.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/RegionInfo.h" +#include "llvm/Analysis/RegionPass.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +namespace { + +struct FunctionPassPrinter : public FunctionPass { + const PassInfo *PassToPrint; + raw_ostream &Out; + static char ID; + std::string PassName; + bool QuietPass; + + FunctionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) + : FunctionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); + PassName = "FunctionPass Printer: " + PassToPrintName; + } + + bool runOnFunction(Function &F) override { + if (!QuietPass) + Out << "Printing analysis '" << PassToPrint->getPassName() + << "' for function '" << F.getName() << "':\n"; + + // Get and print pass... + getAnalysisID(PassToPrint->getTypeInfo()).print(Out, F.getParent()); + return false; + } + + StringRef getPassName() const override { return PassName; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequiredID(PassToPrint->getTypeInfo()); + AU.setPreservesAll(); + } +}; + +char FunctionPassPrinter::ID = 0; + +struct CallGraphSCCPassPrinter : public CallGraphSCCPass { + static char ID; + const PassInfo *PassToPrint; + raw_ostream &Out; + std::string PassName; + bool QuietPass; + + CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) + : CallGraphSCCPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); + PassName = "CallGraphSCCPass Printer: " + PassToPrintName; + } + + bool runOnSCC(CallGraphSCC &SCC) override { + if (!QuietPass) + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + + // Get and print pass... + for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) { + Function *F = (*I)->getFunction(); + if (F) + getAnalysisID(PassToPrint->getTypeInfo()) + .print(Out, F->getParent()); + } + return false; + } + + StringRef getPassName() const override { return PassName; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequiredID(PassToPrint->getTypeInfo()); + AU.setPreservesAll(); + } +}; + +char CallGraphSCCPassPrinter::ID = 0; + +struct ModulePassPrinter : public ModulePass { + static char ID; + const PassInfo *PassToPrint; + raw_ostream &Out; + std::string PassName; + bool QuietPass; + + ModulePassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) + : ModulePass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); + PassName = "ModulePass Printer: " + PassToPrintName; + } + + bool runOnModule(Module &M) override { + if (!QuietPass) + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + + // Get and print pass... + getAnalysisID(PassToPrint->getTypeInfo()).print(Out, &M); + return false; + } + + StringRef getPassName() const override { return PassName; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequiredID(PassToPrint->getTypeInfo()); + AU.setPreservesAll(); + } +}; + +char ModulePassPrinter::ID = 0; + +struct LoopPassPrinter : public LoopPass { + static char ID; + const PassInfo *PassToPrint; + raw_ostream &Out; + std::string PassName; + bool QuietPass; + + LoopPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) + : LoopPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); + PassName = "LoopPass Printer: " + PassToPrintName; + } + + bool runOnLoop(Loop *L, LPPassManager &LPM) override { + if (!QuietPass) + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + + // Get and print pass... + getAnalysisID(PassToPrint->getTypeInfo()) + .print(Out, L->getHeader()->getParent()->getParent()); + return false; + } + + StringRef getPassName() const override { return PassName; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequiredID(PassToPrint->getTypeInfo()); + AU.setPreservesAll(); + } +}; + +char LoopPassPrinter::ID = 0; + +struct RegionPassPrinter : public RegionPass { + static char ID; + const PassInfo *PassToPrint; + raw_ostream &Out; + std::string PassName; + bool QuietPass; + + RegionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) + : RegionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); + PassName = "RegionPass Printer: " + PassToPrintName; + } + + bool runOnRegion(Region *R, RGPassManager &RGM) override { + if (!QuietPass) { + Out << "Printing analysis '" << PassToPrint->getPassName() << "' for " + << "region: '" << R->getNameStr() << "' in function '" + << R->getEntry()->getParent()->getName() << "':\n"; + } + // Get and print pass... + getAnalysisID(PassToPrint->getTypeInfo()) + .print(Out, R->getEntry()->getParent()->getParent()); + return false; + } + + StringRef getPassName() const override { return PassName; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequiredID(PassToPrint->getTypeInfo()); + AU.setPreservesAll(); + } +}; + +char RegionPassPrinter::ID = 0; + +} // end anonymous namespace + +FunctionPass *llvm::createFunctionPassPrinter(const PassInfo *PI, + raw_ostream &OS, bool Quiet) { + return new FunctionPassPrinter(PI, OS, Quiet); +} + +CallGraphSCCPass *llvm::createCallGraphPassPrinter(const PassInfo *PI, + raw_ostream &OS, + bool Quiet) { + return new CallGraphSCCPassPrinter(PI, OS, Quiet); +} + +ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS, + bool Quiet) { + return new ModulePassPrinter(PI, OS, Quiet); +} + +LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS, + bool Quiet) { + return new LoopPassPrinter(PI, OS, Quiet); +} + +RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS, + bool Quiet) { + return new RegionPassPrinter(PI, OS, Quiet); +} Index: llvm/utils/compiledtestboilerplate/PrintSCC.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/PrintSCC.cpp @@ -0,0 +1,114 @@ +//===- PrintSCC.cpp - Enumerate SCCs in some key graphs -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides passes to print out SCCs in a CFG or a CallGraph. +// Normally, you would not use these passes; instead, you would use the +// scc_iterator directly to enumerate SCCs and process them in some way. These +// passes serve three purposes: +// +// (1) As a reference for how to use the scc_iterator. +// (2) To print out the SCCs for a CFG or a CallGraph: +// analyze -print-cfg-sccs to print the SCCs in each CFG of a +// module. analyze -print-cfg-sccs -stats to print the #SCCs and the +// maximum SCC size. analyze -print-cfg-sccs -debug > /dev/null to watch +// the algorithm in action. +// +// and similarly: +// analyze -print-callgraph-sccs [-stats] [-debug] to print SCCs in the +// CallGraph +// +// (3) To test the scc_iterator. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SCCIterator.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +namespace { +struct CFGSCC : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + CFGSCC() : FunctionPass(ID) {} + bool runOnFunction(Function &func) override; + + void print(raw_ostream &O, const Module * = nullptr) const override {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } +}; + +struct CallGraphSCC : public ModulePass { + static char ID; // Pass identification, replacement for typeid + CallGraphSCC() : ModulePass(ID) {} + + // run - Print out SCCs in the call graph for the specified module. + bool runOnModule(Module &M) override; + + void print(raw_ostream &O, const Module * = nullptr) const override {} + + // getAnalysisUsage - This pass requires the CallGraph. + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired(); + } +}; +} // namespace + +char CFGSCC::ID = 0; +static RegisterPass Y("print-cfg-sccs", + "Print SCCs of each function CFG"); + +char CallGraphSCC::ID = 0; +static RegisterPass Z("print-callgraph-sccs", + "Print SCCs of the Call Graph"); + +bool CFGSCC::runOnFunction(Function &F) { + unsigned sccNum = 0; + errs() << "SCCs for Function " << F.getName() << " in PostOrder:"; + for (scc_iterator SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI) { + const std::vector &nextSCC = *SCCI; + errs() << "\nSCC #" << ++sccNum << " : "; + for (BasicBlock *BB : nextSCC) { + BB->printAsOperand(errs(), false); + errs() << ", "; + } + if (nextSCC.size() == 1 && SCCI.hasCycle()) + errs() << " (Has self-loop)."; + } + errs() << "\n"; + + return true; +} + +// run - Print out SCCs in the call graph for the specified module. +bool CallGraphSCC::runOnModule(Module &M) { + CallGraph &CG = getAnalysis().getCallGraph(); + unsigned sccNum = 0; + errs() << "SCCs for the program in PostOrder:"; + for (scc_iterator SCCI = scc_begin(&CG); !SCCI.isAtEnd(); + ++SCCI) { + const std::vector &nextSCC = *SCCI; + errs() << "\nSCC #" << ++sccNum << " : "; + for (std::vector::const_iterator I = nextSCC.begin(), + E = nextSCC.end(); + I != E; ++I) + errs() << ((*I)->getFunction() ? (*I)->getFunction()->getName() + : "external node") + << ", "; + if (nextSCC.size() == 1 && SCCI.hasCycle()) + errs() << " (Has self-loop)."; + } + errs() << "\n"; + + return true; +} Index: llvm/utils/compiledtestboilerplate/compiledtestboilerplate.h =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/compiledtestboilerplate.h @@ -0,0 +1,284 @@ +//===- compiledtestboilerplate.h --------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// Definitions common for compiled regression tests. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/iterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "gtest/gtest.h" +#include + +#ifndef LLVM_UTILS_COMPILEDTESTBOILERPLATE_H +#define LLVM_UTILS_COMPILEDTESTBOILERPLATE_H + +template +class iterator_range_set : public llvm::iterator_range { +public: + iterator_range_set(Ty x, Ty y) + : llvm::iterator_range(std::move(x), std::move(y)) {} + + bool + all_of(std::function::reference)> Pred, + int AssertMinMatches = 0) const { + expectMinCount(AssertMinMatches); + return llvm::all_of(*this, Pred); + } + + bool any_of(std::function::reference)> + Pred) const { + return llvm::any_of(*this, Pred); + } + + auto count() const { + return count_if(*this, [](const auto &) { return true; }); + } + + // TODO: This should generally not require another complete iteration over + // everything + void expectMinCount(int AssertMinMatches) const { + if (AssertMinMatches <= 0) + return; + EXPECT_GE(count(), AssertMinMatches); + } +}; + +template iterator_range_set make_range_set(T x, T y) { + return iterator_range_set(std::move(x), std::move(y)); +} + +template +class inst_iterator_range_set : public iterator_range_set { +public: + inst_iterator_range_set(Ty x, Ty y) + : iterator_range_set(std::move(x), std::move(y)) {} + + class range_op_iterator + : public llvm::iterator_facade_base< + range_op_iterator, std::forward_iterator_tag, llvm::Use> { + private: + llvm::User::op_iterator OpIter; + llvm::Optional InstIter; + llvm::Optional InstEnd; + + public: + range_op_iterator(llvm::User::op_iterator OpIter, Ty InstIter, Ty InstEnd) + : OpIter(std::move(OpIter)), InstIter(std::move(InstIter)), + InstEnd(std::move(InstEnd)) {} + range_op_iterator() : OpIter{}, InstIter{llvm::None}, InstEnd{llvm::None} {} + + range_op_iterator(const range_op_iterator &R) = default; + range_op_iterator &operator=(const range_op_iterator &R) { + this->OpIter = R.OpIter; + this->InstIter = R.InstIter; + this->InstEnd = R.InstEnd; + return *this; + } + bool operator==(const range_op_iterator &R) const { + return this->OpIter == R.OpIter; + } + llvm::Use &operator*() const { return *OpIter; } + + range_op_iterator &operator++() { + ++OpIter; + + retry_op: + llvm::Instruction *I = *InstIter.getValue(); + if (OpIter != I->op_end()) + return *this; + + retry_inst: + ++InstIter.getValue(); + if (InstIter != InstEnd) { + OpIter = I->op_begin(); + goto retry_op; + } + + OpIter = llvm::User::op_iterator{}; + return *this; + } + }; + + auto operands() { + auto FirstInst = this->begin(); + auto FirstOp = (*FirstInst)->op_begin(); + auto EndInst = this->end(); + auto B = range_op_iterator(FirstOp, FirstInst, EndInst); + auto E = range_op_iterator{}; + return make_range_set(B, E); + } +}; + +template +inst_iterator_range_set make_inst_iterator_range_set(T x, T y) { + return inst_iterator_range_set(std::move(x), std::move(y)); +} + +template auto distance(R &&Range) { + return std::distance(Range.begin(), Range.end()); +} + +struct FunctionResult { + llvm::Function *F; + + FunctionResult(llvm::Function *F) : F(F) {} + + class all_insts_iterator + : public llvm::iterator_facade_base< + all_insts_iterator, std::forward_iterator_tag, llvm::Instruction *, + ptrdiff_t, llvm::Instruction **, llvm::Instruction *> { + private: + llvm::BasicBlock::iterator InstIter; + + public: + all_insts_iterator(llvm::BasicBlock::iterator InstIter) + : InstIter(std::move(InstIter)) {} + all_insts_iterator() {} + + all_insts_iterator(const all_insts_iterator &R) = default; + all_insts_iterator &operator=(const all_insts_iterator &R) { + this->InstIter = R.InstIter; + return *this; + } + bool operator==(const all_insts_iterator &R) const { + return this->InstIter == R.InstIter; + } + llvm::Instruction *operator*() const { return &*InstIter; } + + all_insts_iterator &operator++() { + auto P = InstIter->getParent(); + ++InstIter; + if (InstIter == P->end()) { + auto NextBB = P->getNextNode(); + if (NextBB) { + InstIter = NextBB->begin(); + } else { + InstIter = llvm::BasicBlock::iterator{}; + } + } + return *this; + } + }; + + auto insts() { + auto FirstBB = F->begin(); + auto FirstInst = FirstBB->begin(); + return make_inst_iterator_range_set(all_insts_iterator{FirstInst}, + all_insts_iterator{}); + } + + auto insts(std::function Pred) { + auto Range = llvm::make_filter_range(insts(), Pred); + return make_inst_iterator_range_set(Range.begin(), Range.end()); + } + + auto call_insts() { + auto Filter = + llvm::make_filter_range(insts(), [](llvm::Instruction *I) -> bool { + return llvm::isa(I); + }); + auto Map = llvm::map_range( + Filter, [](llvm::Instruction * I) -> auto { + return llvm::cast(I); + }); + return make_inst_iterator_range_set(Map.begin(), Map.end()); + } + + auto call_insts(std::function Pred, + int ExpectAtLeast = 0) { + auto Filter = llvm::make_filter_range(call_insts(), Pred); + auto Result = make_inst_iterator_range_set(Filter.begin(), Filter.end()); + Result.expectMinCount(ExpectAtLeast); + return Result; + } + + class all_op_iterator + : public llvm::iterator_facade_base< + all_op_iterator, std::forward_iterator_tag, llvm::Use> { + private: + llvm::User::op_iterator OpIter; + + public: + all_op_iterator(llvm::User::op_iterator OpIter) + : OpIter(std::move(OpIter)) {} + all_op_iterator() : OpIter{} {} + + all_op_iterator(const all_op_iterator &R) = default; + all_op_iterator &operator=(const all_op_iterator &R) { + this->OpIter = R.OpIter; + return *this; + } + bool operator==(const all_op_iterator &R) const { + return this->OpIter == R.OpIter; + } + llvm::Use &operator*() const { return *OpIter; } + + all_op_iterator &operator++() { + auto I = llvm::cast(OpIter->getUser()); + auto BB = I->getParent(); + + ++OpIter; + + retry_op: + if (OpIter != I->op_end()) + return *this; + + retry_inst: + I = I->getNextNode(); + if (I) { + OpIter = I->op_begin(); + goto retry_op; + } + + BB = BB->getNextNode(); + if (BB) { + I = &BB->front(); + goto retry_inst; + } + + OpIter = llvm::User::op_iterator{}; + return *this; + } + }; + + auto operands() { + auto FirstBB = F->begin(); + auto FirstInst = FirstBB->begin(); + // TODO: Instruction without operands? + auto FirstOp = FirstInst->operands().begin(); + return make_range_set(all_op_iterator{FirstOp}, all_op_iterator{}); + } + + auto operands(std::function Pred, + int AssertMinCount = 0) { + auto Ops = operands(); + auto Filter = llvm::make_filter_range(Ops, Pred); + auto Result = make_range_set(Filter.begin(), Filter.end()); + Result.expectMinCount(AssertMinCount); + return Result; + } +}; + +struct ModuleResult { + llvm::LLVMContext Context; + std::unique_ptr M; + + FunctionResult getFunc(llvm::StringRef Name) { + return {M->getFunction(Name)}; + } +}; + +std::unique_ptr run_opt(llvm::StringRef FileName, + llvm::StringRef Def, + llvm::ArrayRef Args); + +#endif /* LLVM_UTILS_COMPILEDTESTBOILERPLATE_H */ Index: llvm/utils/compiledtestboilerplate/compiledtestboilerplate.cpp =================================================================== --- /dev/null +++ llvm/utils/compiledtestboilerplate/compiledtestboilerplate.cpp @@ -0,0 +1,1033 @@ +//===- compiledtestboilerplate.cpp ------------------------------*- C++ -*-===// +// +// 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 "compiledtestboilerplate.h" +#include "NewPMDriver.h" + +#include "BreakpointPrinter.h" +#include "NewPMDriver.h" +#include "PassPrinters.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace opt_tool; + +static codegen::RegisterCodeGenFlags CFG; + +// The OptimizationList is automatically populated with registered Passes by the +// PassNameParser. +// +static cl::list + PassList(cl::desc("Optimizations available:")); + +// This flag specifies a textual description of the optimization pass pipeline +// to run over the module. This flag switches opt to use the new pass manager +// infrastructure, completely disabling all of the flags specific to the old +// pass management. +static cl::opt PassPipeline( + "passes", + cl::desc("A textual description of the pass pipeline for optimizing"), + cl::Hidden); + +static cl::opt NoVerify("disable-verify", + cl::desc("Do not run the verifier"), cl::Hidden); + +static cl::opt VerifyEach("verify-each", + cl::desc("Verify after each transform")); + +static cl::opt + DisableDITypeMap("disable-debug-info-type-map", + cl::desc("Don't use a uniquing type map for debug info")); + +static cl::opt + StripDebug("strip-debug", + cl::desc("Strip debugger symbol info from translation unit")); + +static cl::opt + StripNamedMetadata("strip-named-metadata", + cl::desc("Strip module-level named metadata")); + +static cl::opt DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass")); + +static cl::opt + DisableOptimizations("disable-opt", + cl::desc("Do not run any optimization passes")); + +static cl::opt + StandardLinkOpts("std-link-opts", + cl::desc("Include the standard link time optimizations")); + +static cl::opt + OptLevelO0("O0", cl::desc("Optimization level 0. Similar to clang -O0")); + +static cl::opt + OptLevelO1("O1", cl::desc("Optimization level 1. Similar to clang -O1")); + +static cl::opt + OptLevelO2("O2", cl::desc("Optimization level 2. Similar to clang -O2")); + +static cl::opt OptLevelOs( + "Os", + cl::desc( + "Like -O2 with extra optimizations for size. Similar to clang -Os")); + +static cl::opt OptLevelOz( + "Oz", + cl::desc("Like -Os but reduces code size further. Similar to clang -Oz")); + +static cl::opt + OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3")); + +static cl::opt + CodeGenOptLevel("codegen-opt-level", + cl::desc("Override optimization level for codegen hooks")); + +static cl::opt + TargetTriple("mtriple", cl::desc("Override target triple for module")); + +static cl::opt DisableLoopUnrolling( + "disable-loop-unrolling", + cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false)); + +static cl::opt EmitSummaryIndex("module-summary", + cl::desc("Emit module summary index"), + cl::init(false)); + +static cl::opt EmitModuleHash("module-hash", cl::desc("Emit module hash"), + cl::init(false)); + +static cl::opt + DisableSimplifyLibCalls("disable-simplify-libcalls", + cl::desc("Disable simplify-libcalls")); + +static cl::list DisableBuiltins( + "disable-builtin", + cl::desc("Disable specific target library builtin function"), + cl::ZeroOrMore); + +static cl::opt Quiet("q", cl::desc("Obsolete option"), cl::Hidden); + +static cl::alias QuietA("quiet", cl::desc("Alias for -q"), cl::aliasopt(Quiet)); + +// static cl::opt +// AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); + +static cl::opt EnableDebugify( + "enable-debugify", + cl::desc( + "Start the pipeline with debugify and end it with check-debugify")); + +static cl::opt DebugifyEach( + "debugify-each", + cl::desc("Start each pass with debugify and end it with check-debugify")); + +static cl::opt + DebugifyExport("debugify-export", + cl::desc("Export per-pass debugify statistics to this file"), + cl::value_desc("filename"), cl::init("")); + +static cl::opt + PrintBreakpoints("print-breakpoints-for-testing", + cl::desc("Print select breakpoints location for testing")); + +static cl::opt ClDataLayout("data-layout", + cl::desc("data layout string to use"), + cl::value_desc("layout-string"), + cl::init("")); + +static cl::opt PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); + +static cl::opt PreserveAssemblyUseListOrder( + "preserve-ll-uselistorder", + cl::desc("Preserve use-list order when writing LLVM assembly."), + cl::init(false), cl::Hidden); + +static cl::opt + RunTwice("run-twice", + cl::desc("Run all passes twice, re-using the same pass manager."), + cl::init(false), cl::Hidden); + +static cl::opt DiscardValueNames( + "discard-value-names", + cl::desc("Discard names from Value (other than GlobalValue)."), + cl::init(false), cl::Hidden); + +static cl::opt Coroutines("enable-coroutines", + cl::desc("Enable coroutine passes."), + cl::init(false), cl::Hidden); + +static cl::opt TimeTrace("time-trace", cl::desc("Record time trace")); + +static cl::opt TimeTraceGranularity( + "time-trace-granularity", + cl::desc( + "Minimum time granularity (in microseconds) traced by time profiler"), + cl::init(500), cl::Hidden); + +static cl::opt + TimeTraceFile("time-trace-file", + cl::desc("Specify time trace file destination"), + cl::value_desc("filename")); + +static cl::opt RemarksWithHotness( + "pass-remarks-with-hotness", + cl::desc("With PGO, include profile count in optimization remarks"), + cl::Hidden); + +static cl::opt + RemarksHotnessThreshold("pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for " + "an optimization remark to be output"), + cl::Hidden); + +static cl::opt + RemarksFilename("pass-remarks-output", + cl::desc("Output filename for pass remarks"), + cl::value_desc("filename")); + +static cl::opt + RemarksPasses("pass-remarks-filter", + cl::desc("Only record optimization remarks from passes whose " + "names match the given regular expression"), + cl::value_desc("regex")); + +static cl::opt RemarksFormat( + "pass-remarks-format", + cl::desc("The format used for serializing remarks (default: YAML)"), + cl::value_desc("format"), cl::init("yaml")); + +cl::opt + PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden, + cl::desc("The kind of profile guided optimization"), + cl::values(clEnumValN(NoPGO, "nopgo", "Do not use PGO."), + clEnumValN(InstrGen, "pgo-instr-gen-pipeline", + "Instrument the IR to generate profile."), + clEnumValN(InstrUse, "pgo-instr-use-pipeline", + "Use instrumented profile to guide PGO."), + clEnumValN(SampleUse, "pgo-sample-use-pipeline", + "Use sampled profile to guide PGO."))); +cl::opt ProfileFile("profile-file", + cl::desc("Path to the profile."), cl::Hidden); + +cl::opt CSPGOKindFlag( + "cspgo-kind", cl::init(NoCSPGO), cl::Hidden, + cl::desc("The kind of context sensitive profile guided optimization"), + cl::values( + clEnumValN(NoCSPGO, "nocspgo", "Do not use CSPGO."), + clEnumValN( + CSInstrGen, "cspgo-instr-gen-pipeline", + "Instrument (context sensitive) the IR to generate profile."), + clEnumValN( + CSInstrUse, "cspgo-instr-use-pipeline", + "Use instrumented (context sensitive) profile to guide PGO."))); +cl::opt CSProfileGenFile( + "cs-profilegen-file", + cl::desc("Path to the instrumented context sensitive profile."), + cl::Hidden); + +static std::unique_ptr +parseIntegratedIRFile(StringRef Filename, StringRef Def, SMDiagnostic &Err, + LLVMContext &Context, + DataLayoutCallbackTy DataLayoutCallback) { + ErrorOr> FileOrErr = + MemoryBuffer::getFile(Filename); + if (std::error_code EC = FileOrErr.getError()) { + Err = SMDiagnostic(Filename, SourceMgr::DK_Error, + "Could not open input file: " + EC.message()); + return nullptr; + } + + // auto MB = FileOrErr.get()->getBuffer(); + // auto BufRef = FileOrErr.get()->getMemBufferRef(); + auto Buf = FileOrErr.get()->getBuffer(); + + auto ifdef = (Twine("#ifdef ") + Def + "\n").str(); + StringRef Haystack = Buf; + + auto ifdefpos = Haystack.find(ifdef); + assert(ifdefpos != StringRef::npos); + auto elsepos = Haystack.find("\n#else", ifdefpos + 1); + assert(elsepos != StringRef::npos); + auto start = ifdefpos + ifdef.size(); + + // .str() because the parser requires the end to be null-terminated. + auto ModuleStr = Haystack.substr(start, elsepos - start).str(); + + MemoryBufferRef F( + ModuleStr, + (Twine() + FileOrErr.get()->getBufferIdentifier() + ":" + Def).str()); + return parseAssembly(F, Err, Context, nullptr, DataLayoutCallback); +} + +class OptCustomPassManager : public legacy::PassManager { + DebugifyStatsMap DIStatsMap; + +public: + using super = legacy::PassManager; + + void add(Pass *P) override { + // Wrap each pass with (-check)-debugify passes if requested, making + // exceptions for passes which shouldn't see -debugify instrumentation. + bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() && + !isIRPrintingPass(P) && !isBitcodeWriterPass(P); + if (!WrapWithDebugify) { + super::add(P); + return; + } + + // Apply -debugify/-check-debugify before/after each pass and collect + // debug info loss statistics. + PassKind Kind = P->getPassKind(); + StringRef Name = P->getPassName(); + + // TODO: Implement Debugify for LoopPass. + switch (Kind) { + case PT_Function: + super::add(createDebugifyFunctionPass()); + super::add(P); + super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap)); + break; + case PT_Module: + super::add(createDebugifyModulePass()); + super::add(P); + super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap)); + break; + default: + super::add(P); + break; + } + } + + const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; } +}; + +static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { + // Add the pass to the pass manager... + PM.add(P); + + // If we are verifying all of the intermediate steps, add the verifier... + if (VerifyEach) + PM.add(createVerifierPass()); +} + +/// This routine adds optimization passes based on selected optimization level, +/// OptLevel. +/// +/// OptLevel - Optimization Level +static void AddOptimizationPasses(legacy::PassManagerBase &MPM, + legacy::FunctionPassManager &FPM, + TargetMachine *TM, unsigned OptLevel, + unsigned SizeLevel) { + if (!NoVerify || VerifyEach) + FPM.add(createVerifierPass()); // Verify that input is correct + + PassManagerBuilder Builder; + Builder.OptLevel = OptLevel; + Builder.SizeLevel = SizeLevel; + + if (DisableInline) { + // No inlining pass + } else if (OptLevel > 1) { + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); + } else { + Builder.Inliner = createAlwaysInlinerLegacyPass(); + } + Builder.DisableUnrollLoops = (DisableLoopUnrolling.getNumOccurrences() > 0) + ? DisableLoopUnrolling + : OptLevel == 0; + + Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2; + + Builder.SLPVectorize = OptLevel > 1 && SizeLevel < 2; + + if (TM) + TM->adjustPassManager(Builder); + + if (Coroutines) + addCoroutinePassesToExtensionPoints(Builder); + + switch (PGOKindFlag) { + case InstrGen: + Builder.EnablePGOInstrGen = true; + Builder.PGOInstrGen = ProfileFile; + break; + case InstrUse: + Builder.PGOInstrUse = ProfileFile; + break; + case SampleUse: + Builder.PGOSampleUse = ProfileFile; + break; + default: + break; + } + + switch (CSPGOKindFlag) { + case CSInstrGen: + Builder.EnablePGOCSInstrGen = true; + break; + case CSInstrUse: + Builder.EnablePGOCSInstrUse = true; + break; + default: + break; + } + + Builder.populateFunctionPassManager(FPM); + Builder.populateModulePassManager(MPM); +} + +static void AddStandardLinkPasses(legacy::PassManagerBase &PM) { + PassManagerBuilder Builder; + Builder.VerifyInput = true; + if (DisableOptimizations) + Builder.OptLevel = 0; + + if (!DisableInline) + Builder.Inliner = createFunctionInliningPass(); + Builder.populateLTOPassManager(PM); +} + +//===----------------------------------------------------------------------===// +// CodeGen-related helper functions. +// + +static CodeGenOpt::Level GetCodeGenOptLevel() { + if (CodeGenOptLevel.getNumOccurrences()) + return static_cast(unsigned(CodeGenOptLevel)); + if (OptLevelO1) + return CodeGenOpt::Less; + if (OptLevelO2) + return CodeGenOpt::Default; + if (OptLevelO3) + return CodeGenOpt::Aggressive; + return CodeGenOpt::None; +} + +// Returns the TargetMachine instance or zero if no triple is provided. +static TargetMachine *GetTargetMachine(Triple TheTriple, StringRef CPUStr, + StringRef FeaturesStr, + const TargetOptions &Options) { + std::string Error; + const Target *TheTarget = + TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error); + // Some modules don't specify a triple, and this is okay. + if (!TheTarget) { + return nullptr; + } + + return TheTarget->createTargetMachine( + TheTriple.getTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(), + Options, codegen::getExplicitRelocModel(), + codegen::getExplicitCodeModel(), GetCodeGenOptLevel()); +} + +#ifdef BUILD_EXAMPLES +void initializeExampleIRTransforms(llvm::PassRegistry &Registry); +#endif + +void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) { + std::error_code EC; + raw_fd_ostream OS{Path, EC}; + if (EC) { + errs() << "Could not open file: " << EC.message() << ", " << Path << '\n'; + return; + } + + OS << "Pass Name" << ',' << "# of missing debug values" << ',' + << "# of missing locations" << ',' << "Missing/Expected value ratio" << ',' + << "Missing/Expected location ratio" << '\n'; + for (const auto &Entry : Map) { + StringRef Pass = Entry.first; + DebugifyStatistics Stats = Entry.second; + + OS << Pass << ',' << Stats.NumDbgValuesMissing << ',' + << Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ',' + << Stats.getEmptyLocationRatio() << '\n'; + } +} + +#if 0 +struct TimeTracerRAII { + TimeTracerRAII(StringRef ProgramName) { + if (TimeTrace) + timeTraceProfilerInitialize(TimeTraceGranularity, ProgramName); + } + ~TimeTracerRAII() { + if (TimeTrace) { + if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) { + handleAllErrors(std::move(E), [&](const StringError &SE) { + errs() << SE.getMessage() << "\n"; + }); + return; + } + timeTraceProfilerCleanup(); + } + } +}; +#endif + +std::unique_ptr run_opt(StringRef FileName, StringRef Def, + ArrayRef Args) { + bool AnalyzeOnly = false; + bool NoOutput = true; + bool Force = false; + bool OutputAssembly = false; + bool OutputThinLTOBC = false; + bool SplitLTOUnit = false; + bool NoUpgradeDebugInfo = false; + std::string OutputFilename; + std::string ThinLinkBitcodeFile; + bool PrintEachXForm = false; + // InitLLVM X(argc, argv); + + // Enable debug stream buffering. + EnableDebugBuffering = true; + + auto Result = std::make_unique(); + LLVMContext &Context = Result->Context; + + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + // Initialize passes + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeCoroutines(Registry); + initializeScalarOpts(Registry); + initializeObjCARCOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeAggressiveInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); + + // For codegen passes, only passes that do IR to IR transformation are + // supported. + initializeExpandMemCmpPassPass(Registry); + initializeScalarizeMaskedMemIntrinPass(Registry); + initializeCodeGenPreparePass(Registry); + initializeAtomicExpandPass(Registry); + initializeRewriteSymbolsLegacyPassPass(Registry); + initializeWinEHPreparePass(Registry); + initializeDwarfEHPreparePass(Registry); + initializeSafeStackLegacyPassPass(Registry); + initializeSjLjEHPreparePass(Registry); + initializePreISelIntrinsicLoweringLegacyPassPass(Registry); + initializeGlobalMergePass(Registry); + initializeIndirectBrExpandPassPass(Registry); + initializeInterleavedLoadCombinePass(Registry); + initializeInterleavedAccessPass(Registry); + initializeEntryExitInstrumenterPass(Registry); + initializePostInlineEntryExitInstrumenterPass(Registry); + initializeUnreachableBlockElimLegacyPassPass(Registry); + initializeExpandReductionsPass(Registry); + initializeWasmEHPreparePass(Registry); + initializeWriteBitcodePassPass(Registry); + initializeHardwareLoopsPass(Registry); + initializeTypePromotionPass(Registry); + +#ifdef BUILD_EXAMPLES + initializeExampleIRTransforms(Registry); +#endif + + SmallVector argv; + argv.push_back("opt"); + argv.append(Args.begin(), Args.end()); + + // TODO: reset previous command line options + cl::ParseCommandLineOptions(argv.size(), argv.data(), + "compiled regression test\n"); + + if (AnalyzeOnly && NoOutput) { + errs() << argv[0] << ": analyze mode conflicts with no-output mode.\n"; + return {}; + } + + // TimeTracerRAII TimeTracer(argv[0]); + + SMDiagnostic Err; + + Context.setDiscardValueNames(DiscardValueNames); + if (!DisableDITypeMap) + Context.enableDebugTypeODRUniquing(); + + Expected> RemarksFileOrErr = + setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, + RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); + if (Error E = RemarksFileOrErr.takeError()) { + errs() << toString(std::move(E)) << '\n'; + return {}; + } + std::unique_ptr RemarksFile = std::move(*RemarksFileOrErr); + + // Load the input module... + auto SetDataLayout = [](StringRef) -> Optional { + if (ClDataLayout.empty()) + return None; + return ClDataLayout; + }; + std::unique_ptr &M = Result->M; + + if (NoUpgradeDebugInfo) { + llvm_unreachable( + "There is no version of parseAssemblyFileWithIndexNoUpgradeDebugInfo " + "taking a raw string"); + // M = parseAssemblyFileWithIndexNoUpgradeDebugInfo(InputFilename, Err, + // Context, nullptr, SetDataLayout).Mod; + } else + M = parseIntegratedIRFile(FileName, Def, Err, Context, SetDataLayout); + + if (!M) { + Err.print(argv[0], errs()); + return {}; + } + + // Strip debug info before running the verifier. + if (StripDebug) + StripDebugInfo(*M); + + // Erase module-level named metadata, if requested. + if (StripNamedMetadata) { + while (!M->named_metadata_empty()) { + NamedMDNode *NMD = &*M->named_metadata_begin(); + M->eraseNamedMetadata(NMD); + } + } + + // If we are supposed to override the target triple or data layout, do so now. + if (!TargetTriple.empty()) + M->setTargetTriple(Triple::normalize(TargetTriple)); + + // Immediately run the verifier to catch any problems before starting up the + // pass pipelines. Otherwise we can crash on broken code during + // doInitialization(). + if (!NoVerify && verifyModule(*M, &errs())) { + errs() << argv[0] // << ": " << InputFilename + << ": error: input module is broken!\n"; + return {}; + } + + // Enable testing of whole program devirtualization on this module by invoking + // the facility for updating public visibility to linkage unit visibility when + // specified by an internal option. This is normally done during LTO which is + // not performed via opt. + updateVCallVisibilityInModule(*M, + /* WholeProgramVisibilityEnabledInLTO */ false); + + // Figure out what stream we are supposed to write to... + std::unique_ptr Out; + std::unique_ptr ThinLinkOut; + if (NoOutput) { + if (!OutputFilename.empty()) + errs() << "WARNING: The -o (output filename) option is ignored when\n" + "the --disable-output option is used.\n"; + } else { + // Default to standard output. + if (OutputFilename.empty()) + OutputFilename = "-"; + + std::error_code EC; + sys::fs::OpenFlags Flags = + OutputAssembly ? sys::fs::OF_Text : sys::fs::OF_None; + Out.reset(new ToolOutputFile(OutputFilename, EC, Flags)); + if (EC) { + errs() << EC.message() << '\n'; + return {}; + } + + if (!ThinLinkBitcodeFile.empty()) { + ThinLinkOut.reset( + new ToolOutputFile(ThinLinkBitcodeFile, EC, sys::fs::OF_None)); + if (EC) { + errs() << EC.message() << '\n'; + return {}; + } + } + } + + Triple ModuleTriple(M->getTargetTriple()); + std::string CPUStr, FeaturesStr; + TargetMachine *Machine = nullptr; + const TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); + + if (ModuleTriple.getArch()) { + CPUStr = codegen::getCPUStr(); + FeaturesStr = codegen::getFeaturesStr(); + Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options); + } else if (ModuleTriple.getArchName() != "unknown" && + ModuleTriple.getArchName() != "") { + errs() << argv[0] << ": unrecognized architecture '" + << ModuleTriple.getArchName() << "' provided.\n"; + return {}; + } + + std::unique_ptr TM(Machine); + + // Override function attributes based on CPUStr, FeaturesStr, and command line + // flags. + codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M); + + // If the output is set to be emitted to standard out, and standard out is a + // console, print out a warning message and refuse to do it. We don't + // impress anyone by spewing tons of binary goo to a terminal. + if (!Force && !NoOutput && !AnalyzeOnly && !OutputAssembly) + if (CheckBitcodeOutputToConsole(Out->os(), !Quiet)) + NoOutput = true; + + if (OutputThinLTOBC) + M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit); + + if (PassPipeline.getNumOccurrences() > 0) { + OutputKind OK = OK_NoOutput; + if (!NoOutput) + OK = OutputAssembly + ? OK_OutputAssembly + : (OutputThinLTOBC ? OK_OutputThinLTOBitcode : OK_OutputBitcode); + + VerifierKind VK = VK_VerifyInAndOut; + if (NoVerify) + VK = VK_NoVerifier; + else if (VerifyEach) + VK = VK_VerifyEachPass; + + // The user has asked to use the new pass manager and provided a pipeline + // string. Hand off the rest of the functionality to the new code for that + // layer. + runPassPipeline(argv[0], *M.get(), TM.get(), Out.get(), ThinLinkOut.get(), + RemarksFile.get(), PassPipeline, OK, VK, + PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, + EmitSummaryIndex, EmitModuleHash, EnableDebugify, + Coroutines); + return Result; + } + + // Create a PassManager to hold and optimize the collection of passes we are + // about to build. + OptCustomPassManager Passes; + bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; + + // Add an appropriate TargetLibraryInfo pass for the module's triple. + TargetLibraryInfoImpl TLII(ModuleTriple); + + // The -disable-simplify-libcalls flag actually disables all builtin optzns. + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + else { + // Disable individual builtin functions in TargetLibraryInfo. + LibFunc F; + for (auto &FuncName : DisableBuiltins) + if (TLII.getLibFunc(FuncName, F)) + TLII.setUnavailable(F); + else { + errs() << argv[0] << ": cannot disable nonexistent builtin function " + << FuncName << '\n'; + return {}; + } + } + + Passes.add(new TargetLibraryInfoWrapperPass(TLII)); + + // Add internal analysis passes from the target machine. + Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() + : TargetIRAnalysis())); + + if (AddOneTimeDebugifyPasses) + Passes.add(createDebugifyModulePass()); + + std::unique_ptr FPasses; + if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || + OptLevelO3) { + FPasses.reset(new legacy::FunctionPassManager(M.get())); + FPasses->add(createTargetTransformInfoWrapperPass( + TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); + } + + if (PrintBreakpoints) { + // Default to standard output. + if (!Out) { + if (OutputFilename.empty()) + OutputFilename = "-"; + + std::error_code EC; + Out = std::make_unique(OutputFilename, EC, + sys::fs::OF_None); + if (EC) { + errs() << EC.message() << '\n'; + return {}; + } + } + Passes.add(createBreakpointPrinter(Out->os())); + NoOutput = true; + } + + if (TM) { + // FIXME: We should dyn_cast this when supported. + auto <M = static_cast(*TM); + Pass *TPC = LTM.createPassConfig(Passes); + Passes.add(TPC); + } + + // Create a new optimization pass for each one specified on the command line + for (unsigned i = 0; i < PassList.size(); ++i) { + if (StandardLinkOpts && + StandardLinkOpts.getPosition() < PassList.getPosition(i)) { + AddStandardLinkPasses(Passes); + StandardLinkOpts = false; + } + + if (OptLevelO0 && OptLevelO0.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 0, 0); + OptLevelO0 = false; + } + + if (OptLevelO1 && OptLevelO1.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0); + OptLevelO1 = false; + } + + if (OptLevelO2 && OptLevelO2.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0); + OptLevelO2 = false; + } + + if (OptLevelOs && OptLevelOs.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1); + OptLevelOs = false; + } + + if (OptLevelOz && OptLevelOz.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2); + OptLevelOz = false; + } + + if (OptLevelO3 && OptLevelO3.getPosition() < PassList.getPosition(i)) { + AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0); + OptLevelO3 = false; + } + + const PassInfo *PassInf = PassList[i]; + Pass *P = nullptr; + if (PassInf->getNormalCtor()) + P = PassInf->getNormalCtor()(); + else + errs() << argv[0] << ": cannot create pass: " << PassInf->getPassName() + << "\n"; + if (P) { + PassKind Kind = P->getPassKind(); + addPass(Passes, P); + + if (AnalyzeOnly) { + switch (Kind) { + case PT_Region: + Passes.add(createRegionPassPrinter(PassInf, Out->os(), Quiet)); + break; + case PT_Loop: + Passes.add(createLoopPassPrinter(PassInf, Out->os(), Quiet)); + break; + case PT_Function: + Passes.add(createFunctionPassPrinter(PassInf, Out->os(), Quiet)); + break; + case PT_CallGraphSCC: + Passes.add(createCallGraphPassPrinter(PassInf, Out->os(), Quiet)); + break; + default: + Passes.add(createModulePassPrinter(PassInf, Out->os(), Quiet)); + break; + } + } + } + + if (PrintEachXForm) + Passes.add( + createPrintModulePass(errs(), "", PreserveAssemblyUseListOrder)); + } + + if (StandardLinkOpts) { + AddStandardLinkPasses(Passes); + StandardLinkOpts = false; + } + + if (OptLevelO0) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 0, 0); + + if (OptLevelO1) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0); + + if (OptLevelO2) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0); + + if (OptLevelOs) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1); + + if (OptLevelOz) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2); + + if (OptLevelO3) + AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0); + + if (FPasses) { + FPasses->doInitialization(); + for (Function &F : *M) + FPasses->run(F); + FPasses->doFinalization(); + } + + // Check that the module is well formed on completion of optimization + if (!NoVerify && !VerifyEach) + Passes.add(createVerifierPass()); + + if (AddOneTimeDebugifyPasses) + Passes.add(createCheckDebugifyModulePass(false)); + + // In run twice mode, we want to make sure the output is bit-by-bit + // equivalent if we run the pass manager again, so setup two buffers and + // a stream to write to them. Note that llc does something similar and it + // may be worth to abstract this out in the future. + SmallVector Buffer; + SmallVector FirstRunBuffer; + std::unique_ptr BOS; + raw_ostream *OS = nullptr; + + const bool ShouldEmitOutput = !NoOutput && !AnalyzeOnly; + + // Write bitcode or assembly to the output as the last step... + if (ShouldEmitOutput || RunTwice) { + assert(Out); + OS = &Out->os(); + if (RunTwice) { + BOS = std::make_unique(Buffer); + OS = BOS.get(); + } + if (OutputAssembly) { + if (EmitSummaryIndex) + report_fatal_error("Text output is incompatible with -module-summary"); + if (EmitModuleHash) + report_fatal_error("Text output is incompatible with -module-hash"); + Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder)); + } else if (OutputThinLTOBC) + Passes.add(createWriteThinLTOBitcodePass( + *OS, ThinLinkOut ? &ThinLinkOut->os() : nullptr)); + else + Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder, + EmitSummaryIndex, EmitModuleHash)); + } + + // Before executing passes, print the final values of the LLVM options. + cl::PrintOptionValues(); + + if (!RunTwice) { + // Now that we have all of the passes ready, run them. + Passes.run(*M); + } else { + // If requested, run all passes twice with the same pass manager to catch + // bugs caused by persistent state in the passes. + std::unique_ptr M2(CloneModule(*M)); + // Run all passes on the original module first, so the second run processes + // the clone to catch CloneModule bugs. + Passes.run(*M); + FirstRunBuffer = Buffer; + Buffer.clear(); + + Passes.run(*M2); + + // Compare the two outputs and make sure they're the same + assert(Out); + if (Buffer.size() != FirstRunBuffer.size() || + (memcmp(Buffer.data(), FirstRunBuffer.data(), Buffer.size()) != 0)) { + errs() + << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output.\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + if (ShouldEmitOutput) { + Out->os() << BOS->str(); + Out->keep(); + } + if (RemarksFile) + RemarksFile->keep(); + return {}; + } + if (ShouldEmitOutput) + Out->os() << BOS->str(); + } + + if (DebugifyEach && !DebugifyExport.empty()) + exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap()); + + // Declare success. + if (!NoOutput || PrintBreakpoints) + Out->keep(); + + if (RemarksFile) + RemarksFile->keep(); + + if (ThinLinkOut) + ThinLinkOut->keep(); + + return 0; +}