llvm: Delete bugpoint (#182320)

For crash reduction, I don't think it does anything that llvm-reduce
can't. Pass pipeline reduction also has a separate reduction script.
The main thing there isn't a replacement tool is the miscompilation
reducer, but I'm not sure that's actually functioned for years.

There are still some references to bugpoint in various comments
and pieces of documentation that don't all necessarily make sense
to replace or remove. In particular there are a few passes documented
as "only for bugpoint", but I've left those alone in case they are
useful for manual reductions.
This commit is contained in:
Matt Arsenault 2026-02-25 08:47:28 +01:00 committed by GitHub
parent 550c65b3dd
commit 9d5574dda6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 17 additions and 6706 deletions

View File

@ -1,227 +0,0 @@
====================================
LLVM bugpoint tool: design and usage
====================================
.. contents::
:local:
Description
===========
``bugpoint`` narrows down the source of problems in LLVM tools and passes. It
can be used to debug three types of failures: optimizer crashes, miscompilations
by optimizers, or bad native code generation (including problems in the static
and JIT compilers). It aims to reduce large test cases to small, useful ones.
For example, if ``opt`` crashes while optimizing a file, it will identify the
optimization (or combination of optimizations) that causes the crash, and reduce
the file down to a small example which triggers the crash.
For detailed case scenarios, such as debugging ``opt``, or one of the LLVM code
generators, see :doc:`HowToSubmitABug`.
Design Philosophy
=================
``bugpoint`` is designed to be a useful tool without requiring any hooks into
the LLVM infrastructure at all. It works with any and all LLVM passes and code
generators, and does not need to "know" how they work. Because of this, it may
appear to do stupid things or miss obvious simplifications. ``bugpoint`` is
also designed to trade off programmer time for computer time in the
compiler-debugging process; consequently, it may take a long period of
(unattended) time to reduce a test case, but we feel it is still worth it. Note
that ``bugpoint`` is generally very quick unless debugging a miscompilation
where each test of the program (which requires executing it) takes a long time.
Automatic Debugger Selection
----------------------------
``bugpoint`` reads each ``.bc`` or ``.ll`` file specified on the command line
and links them together into a single module, called the test program. If any
LLVM passes are specified on the command line, it runs these passes on the test
program. If any of the passes crash, or if they produce malformed output (which
causes the verifier to abort), ``bugpoint`` starts the `crash debugger`_.
Otherwise, if the ``-output`` option was not specified, ``bugpoint`` runs the
test program with the "safe" backend (which is assumed to generate good code) to
generate a reference output. Once ``bugpoint`` has a reference output for the
test program, it tries executing it with the selected code generator. If the
selected code generator crashes, ``bugpoint`` starts the `crash debugger`_ on
the code generator. Otherwise, if the resulting output differs from the
reference output, it assumes the difference resulted from a code generator
failure, and starts the `code generator debugger`_.
Finally, if the output of the selected code generator matches the reference
output, ``bugpoint`` runs the test program after all of the LLVM passes have
been applied to it. If its output differs from the reference output, it assumes
the difference resulted from a failure in one of the LLVM passes, and enters the
`miscompilation debugger`_. Otherwise, there is no problem ``bugpoint`` can
debug.
.. _crash debugger:
Crash debugger
--------------
If an optimizer or code generator crashes, ``bugpoint`` will try as hard as it
can to reduce the list of passes (for optimizer crashes) and the size of the
test program. First, ``bugpoint`` figures out which combination of optimizer
passes triggers the bug. This is useful when debugging a problem exposed by
``opt``, for example, because it runs over 38 passes.
Next, ``bugpoint`` tries removing functions from the test program, to reduce its
size. Usually it is able to reduce a test program to a single function, when
debugging intraprocedural optimizations. Once the number of functions has been
reduced, it attempts to delete various edges in the control flow graph, to
reduce the size of the function as much as possible. Finally, ``bugpoint``
deletes any individual LLVM instructions whose absence does not eliminate the
failure. At the end, ``bugpoint`` should tell you what passes crash, give you a
bitcode file, and give you instructions on how to reproduce the failure with
``opt`` or ``llc``.
.. _code generator debugger:
Code generator debugger
-----------------------
The code generator debugger attempts to narrow down the amount of code that is
being miscompiled by the selected code generator. To do this, it takes the test
program and partitions it into two pieces: one piece which it compiles with the
"safe" backend (into a shared object), and one piece which it runs with either
the JIT or the static LLC compiler. It uses several techniques to reduce the
amount of code pushed through the LLVM code generator, to reduce the potential
scope of the problem. After it is finished, it emits two bitcode files (called
"test" [to be compiled with the code generator] and "safe" [to be compiled with
the "safe" backend], respectively), and instructions for reproducing the
problem. The code generator debugger assumes that the "safe" backend produces
good code.
.. _miscompilation debugger:
Miscompilation debugger
-----------------------
The miscompilation debugger works similarly to the code generator debugger. It
works by splitting the test program into two pieces, running the optimizations
specified on one piece, linking the two pieces back together, and then executing
the result. It attempts to narrow down the list of passes to the one (or few)
which are causing the miscompilation, then reduce the portion of the test
program which is being miscompiled. The miscompilation debugger assumes that
the selected code generator is working properly.
Advice for using bugpoint
=========================
``bugpoint`` can be a remarkably useful tool, but it sometimes works in
non-obvious ways. Here are some hints and tips:
* In the code generator and miscompilation debuggers, ``bugpoint`` only works
with programs that have deterministic output. Thus, if the program outputs
``argv[0]``, the date, time, or any other "random" data, ``bugpoint`` may
misinterpret differences in these data, when output, as the result of a
miscompilation. Programs should be temporarily modified to disable outputs
that are likely to vary from run to run.
* In the `crash debugger`_, ``bugpoint`` does not distinguish different crashes
during reduction. Thus, if new crash or miscompilation happens, ``bugpoint``
will continue with the new crash instead. If you would like to stick to
particular crash, you should write check scripts to validate the error
message, see ``-compile-command`` in :doc:`CommandGuide/bugpoint`.
* In the code generator and miscompilation debuggers, debugging will go faster
if you manually modify the program or its inputs to reduce the runtime, but
still exhibit the problem.
* ``bugpoint`` is extremely useful when working on a new optimization: it helps
track down regressions quickly. To avoid having to relink ``bugpoint`` every
time you change your optimization however, have ``bugpoint`` dynamically load
your optimization with the ``-load`` option.
* ``bugpoint`` can generate a lot of output and run for a long period of time.
It is often useful to capture the output of the program to file. For example,
in the C shell, you can run:
.. code-block:: console
$ bugpoint ... |& tee bugpoint.log
to get a copy of ``bugpoint``'s output in the file ``bugpoint.log``, as well
as on your terminal.
* ``bugpoint`` cannot debug problems with the LLVM linker. If ``bugpoint``
crashes before you see its "All input ok" message, you might try ``llvm-link
-v`` on the same set of input files. If that also crashes, you may be
experiencing a linker bug.
* ``bugpoint`` is useful for proactively finding bugs in LLVM. Invoking
``bugpoint`` with the ``-find-bugs`` option will cause the list of specified
optimizations to be randomized and applied to the program. This process will
repeat until a bug is found or the user kills ``bugpoint``.
* ``bugpoint`` can produce IR which contains long names. Run ``opt
-passes=metarenamer`` over the IR to rename everything using easy-to-read,
metasyntactic names. Alternatively, run ``opt -passes=strip,instnamer`` to
rename everything with very short (often purely numeric) names.
What to do when bugpoint isn't enough
=====================================
Sometimes, ``bugpoint`` is not enough. In particular, InstCombine and
TargetLowering both have visitor structured code with lots of potential
transformations. If the process of using bugpoint has left you with still too
much code to figure out and the problem seems to be in instcombine, the
following steps may help. These same techniques are useful with TargetLowering
as well.
Turn on ``-debug-only=instcombine`` and see which transformations within
instcombine are firing by selecting out lines with "``IC``" in them.
At this point, you have a decision to make. Is the number of transformations
small enough to step through them using a debugger? If so, then try that.
If there are too many transformations, then a source modification approach may
be helpful. In this approach, you can modify the source code of instcombine to
disable just those transformations that are being performed on your test input
and perform a binary search over the set of transformations. One set of places
to modify are the "``visit*``" methods of ``InstCombiner`` (*e.g.*
``visitICmpInst``) by adding a "``return false``" as the first line of the
method.
If that still doesn't remove enough, then change the caller of
``InstCombiner::DoOneIteration``, ``InstCombiner::runOnFunction`` to limit the
number of iterations.
You may also find it useful to use "``-stats``" now to see what parts of
instcombine are firing. This can guide where to put additional reporting code.
At this point, if the amount of transformations is still too large, then
inserting code to limit whether or not to execute the body of the code in the
visit function can be helpful. Add a static counter which is incremented on
every invocation of the function. Then add code which simply returns false on
desired ranges. For example:
.. code-block:: c++
static int calledCount = 0;
calledCount++;
LLVM_DEBUG(if (calledCount < 212) return false);
LLVM_DEBUG(if (calledCount > 217) return false);
LLVM_DEBUG(if (calledCount == 213) return false);
LLVM_DEBUG(if (calledCount == 214) return false);
LLVM_DEBUG(if (calledCount == 215) return false);
LLVM_DEBUG(if (calledCount == 216) return false);
LLVM_DEBUG(dbgs() << "visitXOR calledCount: " << calledCount << "\n");
LLVM_DEBUG(dbgs() << "I: "; I->dump());
could be added to ``visitXOR`` to limit ``visitXor`` to being applied only to
calls 212 and 217. This is from an actual test case and raises an important
point---a simple binary search may not be sufficient, as transformations that
interact may require isolating more than one call. In TargetLowering, use
``return SDNode();`` instead of ``return false;``.
Now that the number of transformations is down to a manageable number, try
examining the output to see if you can figure out which transformations are
being done. If that can be figured out, then do the usual debugging. If which
code corresponds to the transformation being performed isn't obvious, set a
breakpoint after the call count based disabling and step through the code.
Alternatively, you can use "``printf``" style debugging to report waypoints.

View File

@ -1,197 +0,0 @@
bugpoint - automatic test case reduction tool
=============================================
.. program:: bugpoint
SYNOPSIS
--------
**bugpoint** [*options*] [*input LLVM ll/bc files*] [*LLVM passes*] **--args**
*program arguments*
DESCRIPTION
-----------
**bugpoint** narrows down the source of problems in LLVM tools and passes. It
can be used to debug three types of failures: optimizer crashes, miscompilations
by optimizers, or bad native code generation (including problems in the static
and JIT compilers). It aims to reduce large test cases to small, useful ones.
For more information on the design and inner workings of **bugpoint**, as well as
advice for using bugpoint, see :doc:`/Bugpoint` in the LLVM
distribution.
OPTIONS
-------
**--additional-so** *library*
Load the dynamic shared object *library* into the test program whenever it is
run. This is useful if you are debugging programs which depend on non-LLVM
libraries (such as the X or curses libraries) to run.
**--append-exit-code**\ =\ *{true,false}*
Append the test programs exit code to the output file so that a change in exit
code is considered a test failure. Defaults to false.
**--args** *program args*
Pass all arguments specified after **--args** to the test program whenever it runs.
Note that if any of the *program args* start with a "``-``", you should use:
.. code-block:: bash
bugpoint [bugpoint args] --args -- [program args]
The "``--``" right after the **--args** option tells **bugpoint** to consider
any options starting with "``-``" to be part of the **--args** option, not as
options to **bugpoint** itself.
**--tool-args** *tool args*
Pass all arguments specified after **--tool-args** to the LLVM tool under test
(**llc**, **lli**, etc.) whenever it runs. You should use this option in the
following way:
.. code-block:: bash
bugpoint [bugpoint args] --tool-args -- [tool args]
The "``--``" right after the **--tool-args** option tells **bugpoint** to
consider any options starting with "``-``" to be part of the **--tool-args**
option, not as options to **bugpoint** itself. (See **--args**, above.)
**--safe-tool-args** *tool args*
Pass all arguments specified after **--safe-tool-args** to the "safe" execution
tool.
**--gcc-tool-args** *gcc tool args*
Pass all arguments specified after **--gcc-tool-args** to the invocation of
**gcc**.
**--opt-args** *opt args*
Pass all arguments specified after **--opt-args** to the invocation of **opt**.
**--disable-{dce,simplifycfg}**
Do not run the specified passes to clean up and reduce the size of the test
program. By default, **bugpoint** uses these passes internally when attempting to
reduce test programs. If you're trying to find a bug in one of these passes,
**bugpoint** may crash.
**--enable-valgrind**
Use valgrind to find faults in the optimization phase. This will allow
bugpoint to find otherwise asymptomatic problems caused by memory
mis-management.
**-find-bugs**
Continually randomize the specified passes and run them on the test program
until a bug is found or the user kills **bugpoint**.
**-help**
Print a summary of command line options.
**--input** *filename*
Open *filename* and redirect the standard input of the test program, whenever
it runs, to come from that file.
**--load** *plugin*
Load the dynamic object *plugin* into **bugpoint** itself. This object should
register new optimization passes. Once loaded, the object will add new command
line options to enable various optimizations. To see the new complete list of
optimizations, use the **-help** and **--load** options together; for example:
.. code-block:: bash
bugpoint --load myNewPass.so -help
**--mlimit** *megabytes*
Specifies an upper limit on memory usage of the optimization and codegen. Set
to zero to disable the limit.
**--output** *filename*
Whenever the test program produces output on its standard output stream, it
should match the contents of *filename* (the "reference output"). If you
do not use this option, **bugpoint** will attempt to generate a reference output
by compiling the program with the "safe" backend and running it.
**--run-{int,jit,llc,custom}**
Whenever the test program is compiled, **bugpoint** should generate code for it
using the specified code generator. These options allow you to choose the
interpreter, the JIT compiler, the static native code compiler, or a
custom command (see **--exec-command**) respectively.
**--safe-{llc,custom}**
When debugging a code generator, **bugpoint** should use the specified code
generator as the "safe" code generator. This is a known-good code generator
used to generate the "reference output" if it has not been provided, and to
compile portions of the program that as they are excluded from the testcase.
These options allow you to choose the
static native code compiler, or a custom command, (see **--exec-command**)
respectively. The interpreter and the JIT backends cannot currently
be used as the "safe" backends.
**--exec-command** *command*
This option defines the command to use with the **--run-custom** and
**--safe-custom** options to execute the bitcode testcase. This can
be useful for cross-compilation.
**--compile-command** *command*
This option defines the command to use with the **--compile-custom**
option to compile the bitcode testcase. The command should exit with a
failure exit code if the file is "interesting" and should exit with a
success exit code (i.e. 0) otherwise (this is the same as if it crashed on
"interesting" inputs).
This can be useful for
testing compiler output without running any link or execute stages. To
generate a reduced unit test, you may add CHECK directives to the
testcase and pass the name of an executable compile-command script in this form:
.. code-block:: sh
#!/bin/sh
llc "$@"
not FileCheck [bugpoint input file].ll < bugpoint-test-program.s
This script will "fail" as long as FileCheck passes. So the result
will be the minimum bitcode that passes FileCheck.
**--safe-path** *path*
This option defines the path to the command to execute with the
**--safe-{int,jit,llc,custom}**
option.
**--verbose-errors**\ =\ *{true,false}*
The default behavior of bugpoint is to print "<crash>" when it finds a reduced
test that crashes compilation. This flag prints the output of the crashing
program to stderr. This is useful to make sure it is the same error being
tracked down and not a different error that happens to crash the compiler as
well. Defaults to false.
EXIT STATUS
-----------
If **bugpoint** succeeds in finding a problem, it will exit with 0. Otherwise,
if an error occurs, it will exit with a non-zero value.
SEE ALSO
--------
:manpage:`opt(1)`

View File

@ -69,9 +69,9 @@ Debugging Tools
.. toctree::
:maxdepth: 1
bugpoint
llvm-extract
llvm-bcanalyzer
llvm-reduce
Developer Tools
~~~~~~~~~~~~~~~

View File

@ -107,4 +107,4 @@ occurs, it will exit with a non-zero value.
SEE ALSO
--------
:manpage:`bugpoint(1)`
:manpage:`llvm-reduce(1)`

View File

@ -998,7 +998,7 @@ This section describes the LLVM Utility Passes.
-----------------------------------------------------------------------
Same as dead argument elimination, but deletes arguments to functions which are
external. This is only for use by :doc:`bugpoint <Bugpoint>`.
external. This is only for use by `bugpoint`.
``extract-blocks``: Extract Basic Blocks From Module (for bugpoint use)
-----------------------------------------------------------------------

View File

@ -14,7 +14,6 @@ LLVM and API reference documentation.
BitCodeFormat
BlockFrequencyTerminology
BranchWeightMetadata
Bugpoint
CalleeTypeMetadata
CallGraphSection
CIBestPractices
@ -86,7 +85,7 @@ Command Line Utilities
A reference manual for the LLVM command line utilities ("man" pages for LLVM
tools).
:doc:`Bugpoint`
:doc:`llvm-reduce <CommandGuide/llvm-reduce>`
Automatic bug finder and test-case reducer description and usage
information.

View File

@ -80,6 +80,9 @@ Changes to LLVM infrastructure
* Removed TypePromoteFloat legalization from SelectionDAG
* Removed `bugpoint`. Usage has been replaced by `llvm-reduce` and
`llvm/utils/reduce_pipeline.py`.
Changes to building LLVM
------------------------

View File

@ -132,9 +132,9 @@ void Process::GetTimeUsage(TimePoint<> &elapsed,
#include <mach/mach.h>
#endif
// Some LLVM programs such as bugpoint produce core files as a normal part of
// their operation. To prevent the disk from filling up, this function
// does what's necessary to prevent their generation.
// Some LLVM programs such as llvm-reduce produce core files as a normal part of
// their operation. To prevent the disk from filling up, this function does
// what's necessary to prevent their generation.
void Process::PreventCoreFiles() {
struct rlimit rlim;
getrlimit(RLIMIT_CORE, &rlim);

View File

@ -90,9 +90,9 @@ void Process::GetTimeUsage(TimePoint<> &elapsed,
sys_time = toDuration(KernelTime);
}
// Some LLVM programs such as bugpoint produce core files as a normal part of
// their operation. To prevent the disk from filling up, this configuration
// item does what's necessary to prevent their generation.
// Some LLVM programs such as llvm-reduce produce core files as a normal part of
// their operation. To prevent the disk from filling up, this configuration item
// does what's necessary to prevent their generation.
void Process::PreventCoreFiles() {
// Windows does have the concept of core files, called minidumps. However,
// disabling minidumps for a particular application extends past the lifetime
@ -103,7 +103,7 @@ void Process::PreventCoreFiles() {
// later).
//
// Windows also has modal pop-up message boxes. As this method is used by
// bugpoint, preventing these pop-ups is additionally important.
// llvm-reduce, preventing these pop-ups is additionally important.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX);

View File

@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This pass renames everything with metasyntatic names. The intent is to use
// this pass after bugpoint reduction to conceal the nature of the original
// this pass after llvm-reduce reduction to conceal the nature of the original
// program.
//
//===----------------------------------------------------------------------===//

View File

@ -1,16 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashfuncattr 2>&1 | FileCheck %s
; REQUIRES: plugins
;
; ModuleID = 'attr-crash.ll'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main(i32 %argc, ptr %argv) local_unnamed_addr #0 {
ret i32 0
}
; CHECK-NOT: Attribute 'optnone' requires 'noinline'!
attributes #0 = { noinline nounwind optnone uwtable "bugpoint-crash" }

View File

@ -1,12 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext --compile-custom --compile-command="%python %/s.py arg1 arg2" --output-prefix %t %s | FileCheck %s
; REQUIRES: plugins
; Test that arguments are correctly passed in --compile-command. The output
; of bugpoint includes the output of the custom tool, so we just echo the args
; in the tool and check here.
; CHECK: Error: arg1 arg2
define void @noop() {
ret void
}

View File

@ -1,12 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
# Currently any print-out from the custom tool is interpreted as a crash
# (i.e. test is still interesting)
print("Error: " + " ".join(sys.argv[1:]))
sys.exit(1)

View File

@ -1,13 +0,0 @@
; Test that bugpoint can narrow down the testcase to the important function
;
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashcalls -silence-passes > /dev/null
; REQUIRES: plugins
define i32 @foo() { ret i32 1 }
define i32 @test() {
call i32 @test()
ret i32 %1
}
define i32 @bar() { ret i32 2 }

View File

@ -1,11 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashfuncattr -silence-passes
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s
; REQUIRES: plugins
; CHECK: f() #[[ATTRS:[0-9]+]]
define void @f() #0 {
ret void
}
; CHECK: attributes #[[ATTRS]] = { "bugpoint-crash"="sure" }
attributes #0 = { "bugpoint-crash"="sure" noreturn "frame-pointer"="non-leaf" }

View File

@ -1,15 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashfuncattr -silence-passes
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck -check-prefixes=ALL,ENABLED %s
; RUN: bugpoint -disable-attribute-remove -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashfuncattr -silence-passes
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck -check-prefixes=ALL,DISABLED %s
; REQUIRES: plugins
; ALL: f() #[[ATTRS:[0-9]+]]
define void @f() #0 {
ret void
}
; ENABLED: attributes #[[ATTRS]] = { "bugpoint-crash" }
; DISABLED: attributes #[[ATTRS]] = { noinline "bugpoint-crash" "frame-pointer"="non-leaf" }
attributes #0 = { noinline "bugpoint-crash" "frame-pointer"="non-leaf" }

View File

@ -1,22 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes 2>&1 | FileCheck %s
; REQUIRES: plugins
; CHECK: DICompileUnit not listed in llvm.dbg.cu
; When bugpoint hacks at this testcase it will at one point create illegal IR
; that won't even pass the Verifier. A bugpoint *driver* built with assertions
; should not assert on it, but reject the malformed intermediate step.
define void @f() !dbg !9 { ret void }
!llvm.dbg.cu = !{!0, !1, !2, !3, !4, !5}
!0 = distinct !DICompileUnit(language: 12, file: !6)
!1 = distinct !DICompileUnit(language: 12, file: !6)
!2 = distinct !DICompileUnit(language: 12, file: !6)
!3 = distinct !DICompileUnit(language: 12, file: !6)
!4 = distinct !DICompileUnit(language: 12, file: !6)
!5 = distinct !DICompileUnit(language: 12, file: !6)
!6 = !DIFile(filename: "path/to/file", directory: "/path/to/dir")
!llvm.module.flags = !{!7, !8}
!7 = !{i32 2, !"Dwarf Version", i32 4}
!8 = !{i32 2, !"Debug Info Version", i32 3}
!9 = distinct !DISubprogram(unit: !0)

View File

@ -1,54 +0,0 @@
; REQUIRES: plugins
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashcalls -silence-passes -disable-namedmd-remove -disable-strip-debuginfo -disable-strip-debug-types > /dev/null
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s
;
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t-nodebug -bugpoint-crashcalls -silence-passes -disable-namedmd-remove > /dev/null
; RUN: llvm-dis %t-nodebug-reduced-simplified.bc -o - | FileCheck %s --check-prefix=NODEBUG
;
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t-notype -bugpoint-crashcalls -silence-passes -disable-namedmd-remove -disable-strip-debuginfo > /dev/null
; RUN: llvm-dis %t-notype-reduced-simplified.bc -o - | FileCheck %s --check-prefix=NOTYPE
;
; Bugpoint can drop the metadata on the call, as it does not contrinute to the crash.
; CHECK: call void @foo()
; NODEBUG: call void @foo()
; NOTYPE: call void @foo()
; NODEBUG-NOT: call void @foo()
; NOTYPE-NOT: !DIBasicType
; NOTYPE: !DICompileUnit
; NOTYPE-NOT: !DIBasicType
%rust_task = type {}
define void @test(ptr %a, ptr %b) !dbg !9 {
%s = mul i8 22, 9, !attach !0, !dbg !10
store i8 %s, ptr %b, !attach !1, !dbg !11
call void @foo(), !attach !2, !dbg !12
store i32 7, ptr %a, !attach !3, !dbg !13
%t = add i32 0, 5, !attach !4, !dbg !14
ret void
}
declare void @foo()
!llvm.module.flags = !{!17}
!llvm.dbg.cu = !{!8}
!0 = !{!"boring"}
!1 = !{!"uninteresting"}
!2 = !{!"the call to foo"}
!3 = !{!"noise"}
!4 = !{!"filler"}
!8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !15)
!9 = distinct !DISubprogram(name: "test", file: !15, type: !18, unit: !8)
!10 = !DILocation(line: 100, column: 101, scope: !9)
!11 = !DILocation(line: 102, column: 103, scope: !9)
!12 = !DILocation(line: 104, column: 105, scope: !9)
!13 = !DILocation(line: 106, column: 107, scope: !9)
!14 = !DILocation(line: 108, column: 109, scope: !9)
!15 = !DIFile(filename: "source.c", directory: "/dir")
!16 = !{}
!17 = !{i32 1, !"Debug Info Version", i32 3}
!18 = !DISubroutineType(types: !19)
!19 = !{!20, !20}
!20 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)

View File

@ -1,39 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes -disable-strip-debuginfo > /dev/null
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s
; RUN-DISABLE: bugpoint -disable-namedmd-remove -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crash-too-many-cus -silence-passes > /dev/null
; RUN-DISABLE: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s
; REQUIRES: plugins
; CHECK: !llvm.dbg.cu = !{![[FIRST:[0-9]+]], ![[SECOND:[0-9]+]]}
; CHECK-DISABLE: !llvm.dbg.cu = !{![[FIRST:[0-9]+]], ![[SECOND:[0-9]+]],
; CHECK-DISABLE-SAME: ![[THIRD:[0-9]+]], ![[FOURTH:[0-9]+]], ![[FIFTH:[0-9]+]]}
!llvm.dbg.cu = !{!0, !1, !2, !3, !4, !5}
; CHECK-NOT: !named
; CHECK-DISABLE: !named
!named = !{!0, !1, !2, !3, !4, !5}
; CHECK: !llvm.module.flags = !{![[DIVERSION:[0-9]+]]}
!llvm.module.flags = !{!6, !7}
; CHECK-DAG: ![[FIRST]] = distinct !DICompileUnit(language: DW_LANG_Julia,
; CHECK-DAG: ![[SECOND]] = distinct !DICompileUnit(language: DW_LANG_Julia,
; CHECK-DAG: ![[DIVERSION]] = !{i32 2, !"Debug Info Version", i32 3}
; CHECK-DAG: !DIFile(filename: "a", directory: "b")
; 4 nodes survive. Due to renumbering !4 should not exist
; CHECK-NOT: !4
!0 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!1 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!2 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!3 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!4 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!5 = distinct !DICompileUnit(language: DW_LANG_Julia,
file: !8)
!6 = !{i32 2, !"Dwarf Version", i32 2}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !DIFile(filename: "a", directory: "b")

View File

@ -1,18 +0,0 @@
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashcalls -silence-passes
; RUN: llvm-dis %t-reduced-simplified.bc -o - | FileCheck %s
; REQUIRES: plugins
; Test to make sure that arguments are removed from the function if they are
; unnecessary. And clean up any types that frees up too.
; CHECK: ModuleID
; CHECK-NOT: struct.anon
%struct.anon = type { i32 }
declare i32 @test2()
; CHECK: define void @test() {
define i32 @test(i32 %A, ptr %B, float %C) {
call i32 @test2()
ret i32 %1
}

View File

@ -1,17 +0,0 @@
; Test that bugpoint can reduce the set of functions by replacing them with null.
;
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -replace-funcs-with-null -bugpoint-crash-decl-funcs -silence-passes -safe-run-llc
; REQUIRES: plugins
@foo2 = alias i32 (), ptr @foo
define i32 @foo() { ret i32 1 }
define i32 @test() {
call i32 @test()
ret i32 %1
}
define i32 @bar() { ret i32 2 }
@llvm.used = appending global [1 x ptr] [ptr @foo], section "llvm.metadata"

View File

@ -1,22 +0,0 @@
; REQUIRES: plugins
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t-notype -bugpoint-crashmetadata -silence-passes > /dev/null
; RUN: llvm-dis %t-notype-reduced-simplified.bc -o - | FileCheck %s
;
; Make sure BugPoint retains metadata contributing to a crash.
; CHECK-LABEL: define void @test2(float %f) {
; CHECK-NEXT: %arg = fadd float %f, 1.000000e+01
; CHECK-NOT: !fpmath
; CHECK-NEXT: %x = call float @llvm.fabs.f32(float %arg), !fpmath [[FPMATH:![0-9]+]]
; CHECK-NEXT: ret void
; CHECK: [[FPMATH]] = !{float 2.500000e+00}
define void @test2(float %f) {
%arg = fadd float %f, 1.000000e+01, !fpmath !0
%x = call float @llvm.fabs.f32(float %arg), !fpmath !0
ret void
}
declare float @llvm.fabs.f32(float)
!0 = !{float 2.500000e+00}

View File

@ -1,23 +0,0 @@
; REQUIRES: plugins
; RUN: echo "import sys" > %t.py
; RUN: echo "print('args = ' + str(sys.argv))" >> %t.py
; RUN: echo "exit(1)" >> %t.py
; RUN: not bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashcalls -opt-command=%python -opt-args %t.py | FileCheck %s
; RUN: not --crash opt -bugpoint-enable-legacy-pm -load %llvmshlibdir/BugpointPasses%pluginext %s -bugpoint-crashcalls -disable-symbolication 2>&1 | FileCheck --check-prefix=CRASH %s
; RUN: not bugpoint -load %llvmshlibdir/BugpointPasses%pluginext %s -output-prefix %t -bugpoint-crashcalls -opt-command=%t.non.existent.opt.binary -opt-args %t.py 2>&1 | FileCheck %s --check-prefix=BAD-OPT
; Test that bugpoint disables symbolication on the opt tool to reduce runtime overhead when opt crashes
; CHECK: args = {{.*}}'-disable-symbolication'
; Test that opt, when it crashes & is passed -disable-symbolication, doesn't symbolicate.
; In theory this test should maybe be in test/tools/opt or
; test/Transforms, but since there doesn't seem to be another convenient way to
; crash opt, apart from the BugpointPasses dynamic plugin, this is the spot for
; now.
; CRASH-NOT: Signals.inc
; BAD-OPT: Specified `opt' binary does not exist: {{.*}}non.existent.opt.binary
define void @f() {
call void @f()
ret void
}

View File

@ -70,9 +70,7 @@ set(LLVM_TEST_DEPENDS_COMMON
set(LLVM_TEST_DEPENDS
${LLVM_TEST_DEPENDS_COMMON}
BugpointPasses
LLVMWindowsDriver
bugpoint
llc
lli
lli-child-target

View File

@ -317,7 +317,6 @@ tools.extend(
"obj2yaml",
"yaml-bench",
"verify-uselistorder",
"bugpoint",
"llc",
"llvm-symbolizer",
"opt",

View File

@ -1,18 +0,0 @@
if( NOT LLVM_BUILD_TOOLS )
set(EXCLUDE_FROM_ALL ON)
endif()
# There's no reason to export anything from the plugin.
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/bugpoint.exports)
if(WIN32 OR CYGWIN OR ZOS)
set(LLVM_LINK_COMPONENTS Core Support)
endif()
add_llvm_library( BugpointPasses MODULE BUILDTREE_ONLY
TestPasses.cpp
DEPENDS
intrinsics_gen
bugpoint
)

View File

@ -1,187 +0,0 @@
//===- TestPasses.cpp - "buggy" passes used to test bugpoint --------------===//
//
//
// 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 contains "buggy" passes that are used to test bugpoint, to check
// that it is narrowing down testcases correctly.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Type.h"
#include "llvm/Pass.h"
#include "llvm/IR/PatternMatch.h"
using namespace llvm::PatternMatch;
using namespace llvm;
namespace {
/// CrashOnCalls - This pass is used to test bugpoint. It intentionally
/// crashes on any call instructions.
class CrashOnCalls : public FunctionPass {
public:
static char ID; // Pass ID, replacement for typeid
CrashOnCalls() : FunctionPass(ID) {}
private:
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
bool runOnFunction(Function &F) override {
for (auto &BB : F)
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
if (isa<CallInst>(*I))
abort();
return false;
}
};
}
char CrashOnCalls::ID = 0;
static RegisterPass<CrashOnCalls>
X("bugpoint-crashcalls",
"BugPoint Test Pass - Intentionally crash on CallInsts");
namespace {
/// DeleteCalls - This pass is used to test bugpoint. It intentionally
/// deletes some call instructions, "misoptimizing" the program.
class DeleteCalls : public FunctionPass {
public:
static char ID; // Pass ID, replacement for typeid
DeleteCalls() : FunctionPass(ID) {}
private:
bool runOnFunction(Function &F) override {
for (auto &BB : F)
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I)
if (CallInst *CI = dyn_cast<CallInst>(I)) {
if (!CI->use_empty())
CI->replaceAllUsesWith(Constant::getNullValue(CI->getType()));
CI->eraseFromParent();
break;
}
return false;
}
};
}
char DeleteCalls::ID = 0;
static RegisterPass<DeleteCalls>
Y("bugpoint-deletecalls",
"BugPoint Test Pass - Intentionally 'misoptimize' CallInsts");
namespace {
/// CrashOnDeclFunc - This pass is used to test bugpoint. It intentionally
/// crashes if the module has an undefined function (ie a function that is
/// defined in an external module).
class CrashOnDeclFunc : public ModulePass {
public:
static char ID; // Pass ID, replacement for typeid
CrashOnDeclFunc() : ModulePass(ID) {}
private:
bool runOnModule(Module &M) override {
for (auto &F : M.functions()) {
if (F.isDeclaration())
abort();
}
return false;
}
};
}
char CrashOnDeclFunc::ID = 0;
static RegisterPass<CrashOnDeclFunc>
Z("bugpoint-crash-decl-funcs",
"BugPoint Test Pass - Intentionally crash on declared functions");
namespace {
/// CrashOnOneCU - This pass is used to test bugpoint. It intentionally
/// crashes if the Module has two or more compile units
class CrashOnTooManyCUs : public ModulePass {
public:
static char ID;
CrashOnTooManyCUs() : ModulePass(ID) {}
private:
bool runOnModule(Module &M) override {
NamedMDNode *CU_Nodes = M.getNamedMetadata("llvm.dbg.cu");
if (!CU_Nodes)
return false;
if (CU_Nodes->getNumOperands() >= 2)
abort();
return false;
}
};
}
char CrashOnTooManyCUs::ID = 0;
static RegisterPass<CrashOnTooManyCUs>
A("bugpoint-crash-too-many-cus",
"BugPoint Test Pass - Intentionally crash on too many CUs");
namespace {
class CrashOnFunctionAttribute : public FunctionPass {
public:
static char ID; // Pass ID, replacement for typeid
CrashOnFunctionAttribute() : FunctionPass(ID) {}
private:
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
bool runOnFunction(Function &F) override {
AttributeSet A = F.getAttributes().getFnAttrs();
if (A.hasAttribute("bugpoint-crash"))
abort();
return false;
}
};
} // namespace
char CrashOnFunctionAttribute::ID = 0;
static RegisterPass<CrashOnFunctionAttribute>
B("bugpoint-crashfuncattr", "BugPoint Test Pass - Intentionally crash on "
"function attribute 'bugpoint-crash'");
namespace {
class CrashOnMetadata : public FunctionPass {
public:
static char ID; // Pass ID, replacement for typeid
CrashOnMetadata() : FunctionPass(ID) {}
private:
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
// Crash on fabs calls with fpmath metdata and an fadd as argument. This
// ensures the fadd instruction sticks around and we can check that the
// metadata there is dropped correctly.
bool runOnFunction(Function &F) override {
for (Instruction &I : instructions(F))
if (match(&I, m_FAbs(m_FAdd(m_Value(), m_Value()))) &&
I.hasMetadata("fpmath"))
abort();
return false;
}
};
} // namespace
char CrashOnMetadata::ID = 0;
static RegisterPass<CrashOnMetadata>
C("bugpoint-crashmetadata",
"BugPoint Test Pass - Intentionally crash on "
"fabs calls with fpmath metadata and an fadd as argument");

View File

@ -1,255 +0,0 @@
//===- BugDriver.cpp - Top-Level BugPoint class implementation ------------===//
//
// 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 class contains all of the shared state and information that is used by
// the BugPoint tool to track down errors in optimizations. This class is the
// main driver class that invokes all sub-functionality.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "ToolRunner.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include <memory>
using namespace llvm;
Triple llvm::TargetTriple;
DiscardTemp::~DiscardTemp() {
if (SaveTemps) {
if (Error E = File.keep())
errs() << "Failed to keep temp file " << toString(std::move(E)) << '\n';
return;
}
if (Error E = File.discard())
errs() << "Failed to delete temp file " << toString(std::move(E)) << '\n';
}
// Output - The user can specify a file containing the expected output of the
// program. If this filename is set, it is used as the reference diff source,
// otherwise the raw input run through an interpreter is used as the reference
// source.
//
static cl::opt<std::string>
OutputFile("output", cl::desc("Specify a reference program output "
"(for miscompilation detection)"));
/// If we reduce or update the program somehow, call this method to update
/// bugdriver with it. This deletes the old module and sets the specified one
/// as the current program.
void BugDriver::setNewProgram(std::unique_ptr<Module> M) {
Program = std::move(M);
}
/// getPassesString - Turn a list of passes into a string which indicates the
/// command line options that must be passed to add the passes.
///
std::string llvm::getPassesString(const std::vector<std::string> &Passes) {
std::string Result;
for (unsigned i = 0, e = Passes.size(); i != e; ++i) {
if (i)
Result += " ";
Result += "-";
Result += Passes[i];
}
return Result;
}
BugDriver::BugDriver(const char *toolname, bool find_bugs, unsigned timeout,
unsigned memlimit, bool use_valgrind, LLVMContext &ctxt)
: Context(ctxt), ToolName(toolname), ReferenceOutputFile(OutputFile),
Program(nullptr), Interpreter(nullptr), SafeInterpreter(nullptr),
cc(nullptr), run_find_bugs(find_bugs), Timeout(timeout),
MemoryLimit(memlimit), UseValgrind(use_valgrind) {}
BugDriver::~BugDriver() {
if (Interpreter != SafeInterpreter)
delete Interpreter;
delete SafeInterpreter;
delete cc;
}
std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename,
LLVMContext &Ctxt) {
SMDiagnostic Err;
std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
if (!Result) {
Err.print("bugpoint", errs());
return Result;
}
if (verifyModule(*Result, &errs())) {
errs() << "bugpoint: " << Filename << ": error: input module is broken!\n";
return std::unique_ptr<Module>();
}
// If we don't have an override triple, use the first one to configure
// bugpoint, or use the host triple if none provided.
if (TargetTriple.getTriple().empty()) {
Triple TheTriple(Result->getTargetTriple());
if (TheTriple.getTriple().empty())
TheTriple.setTriple(sys::getDefaultTargetTriple());
TargetTriple.setTriple(TheTriple.getTriple());
}
// override the triple
Result->setTargetTriple(TargetTriple);
return Result;
}
std::unique_ptr<Module> BugDriver::swapProgramIn(std::unique_ptr<Module> M) {
std::unique_ptr<Module> OldProgram = std::move(Program);
Program = std::move(M);
return OldProgram;
}
// This method takes the specified list of LLVM input files, attempts to load
// them, either as assembly or bitcode, then link them together. It returns
// true on failure (if, for example, an input bitcode file could not be
// parsed), and false on success.
//
bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
assert(!Program && "Cannot call addSources multiple times!");
assert(!Filenames.empty() && "Must specify at least on input filename!");
// Load the first input file.
Program = parseInputFile(Filenames[0], Context);
if (!Program)
return true;
outs() << "Read input file : '" << Filenames[0] << "'\n";
for (unsigned i = 1, e = Filenames.size(); i != e; ++i) {
std::unique_ptr<Module> M = parseInputFile(Filenames[i], Context);
if (!M)
return true;
outs() << "Linking in input file: '" << Filenames[i] << "'\n";
if (Linker::linkModules(*Program, std::move(M)))
return true;
}
outs() << "*** All input ok\n";
// All input files read successfully!
return false;
}
/// run - The top level method that is invoked after all of the instance
/// variables are set up from command line arguments.
///
Error BugDriver::run() {
if (run_find_bugs) {
// Rearrange the passes and apply them to the program. Repeat this process
// until the user kills the program or we find a bug.
return runManyPasses(PassesToRun);
}
// If we're not running as a child, the first thing that we must do is
// determine what the problem is. Does the optimization series crash the
// compiler, or does it produce illegal code? We make the top-level
// decision by trying to run all of the passes on the input program,
// which should generate a bitcode file. If it does generate a bitcode
// file, then we know the compiler didn't crash, so try to diagnose a
// miscompilation.
if (!PassesToRun.empty()) {
outs() << "Running selected passes on program to test for crash: ";
if (runPasses(*Program, PassesToRun))
return debugOptimizerCrash();
}
// Set up the execution environment, selecting a method to run LLVM bitcode.
if (Error E = initializeExecutionEnvironment())
return E;
// Test to see if we have a code generator crash.
outs() << "Running the code generator to test for a crash: ";
if (Error E = compileProgram(*Program)) {
outs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
outs() << '\n';
// Run the raw input to see where we are coming from. If a reference output
// was specified, make sure that the raw output matches it. If not, it's a
// problem in the front-end or the code generator.
//
bool CreatedOutput = false;
if (ReferenceOutputFile.empty()) {
outs() << "Generating reference output from raw program: ";
if (Error E = createReferenceFile(*Program)) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
CreatedOutput = true;
}
// Make sure the reference output file gets deleted on exit from this
// function, if appropriate.
std::string ROF(ReferenceOutputFile);
FileRemover RemoverInstance(ROF, CreatedOutput && !SaveTemps);
// Diff the output of the raw program against the reference output. If it
// matches, then we assume there is a miscompilation bug and try to
// diagnose it.
outs() << "*** Checking the code generator...\n";
Expected<bool> Diff = diffProgram(*Program, "", "", false);
if (Error E = Diff.takeError()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
if (!*Diff) {
outs() << "\n*** Output matches: Debugging miscompilation!\n";
if (Error E = debugMiscompilation()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
return Error::success();
}
outs() << "\n*** Input program does not match reference diff!\n";
outs() << "Debugging code generator problem!\n";
if (Error E = debugCodeGenerator()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
return Error::success();
}
void llvm::printFunctionList(const std::vector<Function *> &Funcs) {
unsigned NumPrint = Funcs.size();
if (NumPrint > 10)
NumPrint = 10;
for (unsigned i = 0; i != NumPrint; ++i)
outs() << " " << Funcs[i]->getName();
if (NumPrint < Funcs.size())
outs() << "... <" << Funcs.size() << " total>";
outs().flush();
}
void llvm::printGlobalVariableList(const std::vector<GlobalVariable *> &GVs) {
unsigned NumPrint = GVs.size();
if (NumPrint > 10)
NumPrint = 10;
for (unsigned i = 0; i != NumPrint; ++i)
outs() << " " << GVs[i]->getName();
if (NumPrint < GVs.size())
outs() << "... <" << GVs.size() << " total>";
outs().flush();
}

View File

@ -1,290 +0,0 @@
//===- BugDriver.h - Top-Level BugPoint class -------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This class contains all of the shared state and information that is used by
// the BugPoint tool to track down errors in optimizations. This class is the
// main driver class that invokes all sub-functionality.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_BUGPOINT_BUGDRIVER_H
#define LLVM_TOOLS_BUGPOINT_BUGDRIVER_H
#include "llvm/IR/ValueMap.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include <memory>
#include <string>
#include <vector>
namespace llvm {
class Module;
class GlobalVariable;
class Function;
class BasicBlock;
class AbstractInterpreter;
class Instruction;
class LLVMContext;
class CC;
extern bool DisableSimplifyCFG;
/// BugpointIsInterrupted - Set to true when the user presses ctrl-c.
///
extern bool BugpointIsInterrupted;
/// Command line options used across files.
extern cl::list<std::string> InputArgv;
extern cl::opt<std::string> OutputPrefix;
class BugDriver {
LLVMContext &Context;
const char *ToolName; // argv[0] of bugpoint
std::string ReferenceOutputFile; // Name of `good' output file
std::unique_ptr<Module> Program; // The raw program, linked together
std::vector<std::string> PassesToRun;
AbstractInterpreter *Interpreter; // How to run the program
AbstractInterpreter *SafeInterpreter; // To generate reference output, etc.
CC *cc;
bool run_find_bugs;
unsigned Timeout;
unsigned MemoryLimit;
bool UseValgrind;
// FIXME: sort out public/private distinctions...
friend class ReducePassList;
public:
BugDriver(const char *toolname, bool find_bugs, unsigned timeout,
unsigned memlimit, bool use_valgrind, LLVMContext &ctxt);
~BugDriver();
const char *getToolName() const { return ToolName; }
LLVMContext &getContext() const { return Context; }
// Set up methods... these methods are used to copy information about the
// command line arguments into instance variables of BugDriver.
//
bool addSources(const std::vector<std::string> &FileNames);
void addPass(std::string p) { PassesToRun.push_back(std::move(p)); }
void setPassesToRun(const std::vector<std::string> &PTR) {
PassesToRun = PTR;
}
ArrayRef<std::string> getPassesToRun() const { return PassesToRun; }
/// run - The top level method that is invoked after all of the instance
/// variables are set up from command line arguments. The \p as_child argument
/// indicates whether the driver is to run in parent mode or child mode.
///
Error run();
/// debugOptimizerCrash - This method is called when some optimizer pass
/// crashes on input. It attempts to prune down the testcase to something
/// reasonable, and figure out exactly which pass is crashing.
///
Error debugOptimizerCrash(const std::string &ID = "passes");
/// debugCodeGeneratorCrash - This method is called when the code generator
/// crashes on an input. It attempts to reduce the input as much as possible
/// while still causing the code generator to crash.
Error debugCodeGeneratorCrash();
/// debugMiscompilation - This method is used when the passes selected are not
/// crashing, but the generated output is semantically different from the
/// input.
Error debugMiscompilation();
/// compileSharedObject - This method creates a SharedObject from a given
/// BitcodeFile for debugging a code generator.
///
Expected<std::string> compileSharedObject(const std::string &BitcodeFile);
/// debugCodeGenerator - This method narrows down a module to a function or
/// set of functions, using the CBE as a ``safe'' code generator for other
/// functions that are not under consideration.
Error debugCodeGenerator();
/// isExecutingJIT - Returns true if bugpoint is currently testing the JIT
bool isExecutingJIT();
Module &getProgram() const { return *Program; }
/// Set the current module to the specified module, returning the old one.
std::unique_ptr<Module> swapProgramIn(std::unique_ptr<Module> M);
AbstractInterpreter *switchToSafeInterpreter() {
AbstractInterpreter *Old = Interpreter;
Interpreter = (AbstractInterpreter *)SafeInterpreter;
return Old;
}
void switchToInterpreter(AbstractInterpreter *AI) { Interpreter = AI; }
/// If we reduce or update the program somehow, call this method to update
/// bugdriver with it. This deletes the old module and sets the specified one
/// as the current program.
void setNewProgram(std::unique_ptr<Module> M);
/// Try to compile the specified module. This is used for code generation
/// crash testing.
Error compileProgram(Module &M) const;
/// This method runs "Program", capturing the output of the program to a file.
/// A recommended filename may be optionally specified.
Expected<std::string> executeProgram(const Module &Program,
std::string OutputFilename,
std::string Bitcode,
const std::string &SharedObjects,
AbstractInterpreter *AI) const;
/// Used to create reference output with the "safe" backend, if reference
/// output is not provided. If there is a problem with the code generator
/// (e.g., llc crashes), this will return false and set Error.
Expected<std::string>
executeProgramSafely(const Module &Program,
const std::string &OutputFile) const;
/// Calls compileProgram and then records the output into ReferenceOutputFile.
/// Returns true if reference file created, false otherwise. Note:
/// initializeExecutionEnvironment should be called BEFORE this function.
Error createReferenceFile(Module &M, const std::string &Filename =
"bugpoint.reference.out-%%%%%%%");
/// This method executes the specified module and diffs the output against the
/// file specified by ReferenceOutputFile. If the output is different, 1 is
/// returned. If there is a problem with the code generator (e.g., llc
/// crashes), this will return -1 and set Error.
Expected<bool> diffProgram(const Module &Program,
const std::string &BitcodeFile = "",
const std::string &SharedObj = "",
bool RemoveBitcode = false) const;
/// This function is used to output M to a file named "bugpoint-ID.bc".
void emitProgressBitcode(const Module &M, const std::string &ID,
bool NoFlyer = false) const;
/// This method clones the current Program and deletes the specified
/// instruction from the cloned module. It then runs a series of cleanup
/// passes (ADCE and SimplifyCFG) to eliminate any code which depends on the
/// value. The modified module is then returned.
///
std::unique_ptr<Module> deleteInstructionFromProgram(const Instruction *I,
unsigned Simp);
/// This method clones the current Program and performs a series of cleanups
/// intended to get rid of extra cruft on the module. If the
/// MayModifySemantics argument is true, then the cleanups is allowed to
/// modify how the code behaves.
///
std::unique_ptr<Module> performFinalCleanups(std::unique_ptr<Module> M,
bool MayModifySemantics = false);
/// Given a module, extract up to one loop from it into a new function. This
/// returns null if there are no extractable loops in the program or if the
/// loop extractor crashes.
std::unique_ptr<Module> extractLoop(Module *M);
/// Extract all but the specified basic blocks into their own functions. The
/// only detail is that M is actually a module cloned from the one the BBs are
/// in, so some mapping needs to be performed. If this operation fails for
/// some reason (ie the implementation is buggy), this function should return
/// null, otherwise it returns a new Module.
std::unique_ptr<Module>
extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
Module *M);
/// Carefully run the specified set of pass on the specified/ module,
/// returning the transformed module on success, or a null pointer on failure.
std::unique_ptr<Module> runPassesOn(Module *M,
const std::vector<std::string> &Passes,
ArrayRef<std::string> ExtraArgs = {});
/// runPasses - Run the specified passes on Program, outputting a bitcode
/// file and writting the filename into OutputFile if successful. If the
/// optimizations fail for some reason (optimizer crashes), return true,
/// otherwise return false. If DeleteOutput is set to true, the bitcode is
/// deleted on success, and the filename string is undefined. This prints to
/// outs() a single line message indicating whether compilation was successful
/// or failed, unless Quiet is set. ExtraArgs specifies additional arguments
/// to pass to the child bugpoint instance.
bool runPasses(Module &Program, const std::vector<std::string> &PassesToRun,
std::string &OutputFilename, bool DeleteOutput = false,
bool Quiet = false,
ArrayRef<std::string> ExtraArgs = {}) const;
/// runPasses - Just like the method above, but this just returns true or
/// false indicating whether or not the optimizer crashed on the specified
/// input (true = crashed). Does not produce any output.
bool runPasses(Module &M, const std::vector<std::string> &PassesToRun) const {
std::string Filename;
return runPasses(M, PassesToRun, Filename, true);
}
/// Take the specified pass list and create different combinations of passes
/// to compile the program with. Compile the program with each set and mark
/// test to see if it compiled correctly. If the passes compiled correctly
/// output nothing and rearrange the passes into a new order. If the passes
/// did not compile correctly, output the command required to recreate the
/// failure.
Error runManyPasses(const std::vector<std::string> &AllPasses);
/// This writes the current "Program" to the named bitcode file. If an error
/// occurs, true is returned.
bool writeProgramToFile(const std::string &Filename, const Module &M) const;
bool writeProgramToFile(const std::string &Filename, int FD,
const Module &M) const;
bool writeProgramToFile(int FD, const Module &M) const;
private:
/// initializeExecutionEnvironment - This method is used to set up the
/// environment for executing LLVM programs.
Error initializeExecutionEnvironment();
};
struct DiscardTemp {
sys::fs::TempFile &File;
~DiscardTemp();
};
/// Given a bitcode or assembly input filename, parse and return it, or return
/// null if not possible.
std::unique_ptr<Module> parseInputFile(StringRef InputFilename,
LLVMContext &ctxt);
/// getPassesString - Turn a list of passes into a string which indicates the
/// command line options that must be passed to add the passes.
std::string getPassesString(const std::vector<std::string> &Passes);
/// Prints out list of problematic functions
void printFunctionList(const std::vector<Function *> &Funcs);
/// Prints out list of problematic global variables
void printGlobalVariableList(const std::vector<GlobalVariable *> &GVs);
/// "Remove" the global variable by deleting its initializer, making it
/// external.
void deleteGlobalInitializer(GlobalVariable *GV);
/// "Remove" the function by deleting all of it's basic blocks, making it
/// external.
void deleteFunctionBody(Function *F);
/// Given a module and a list of functions in the module, split the functions
/// OUT of the specified module, and place them in the new module.
std::unique_ptr<Module>
splitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
ValueToValueMapTy &VMap);
} // End llvm namespace
#endif

View File

@ -1,43 +0,0 @@
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsInfos
Analysis
BitWriter
CodeGen
Extensions
Core
IPO
IRReader
AggressiveInstCombine
InstCombine
Instrumentation
Linker
Plugins
ObjCARCOpts
ScalarOpts
Support
Target
TargetParser
TransformUtils
Vectorize
)
add_llvm_tool(bugpoint
BugDriver.cpp
CrashDebugger.cpp
ExecutionDriver.cpp
ExtractFunction.cpp
FindBugs.cpp
Miscompilation.cpp
OptimizerDriver.cpp
ToolRunner.cpp
bugpoint.cpp
DEPENDS
intrinsics_gen
SUPPORT_PLUGINS
EXPORT_SYMBOLS
)

File diff suppressed because it is too large Load Diff

View File

@ -1,451 +0,0 @@
//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//
//
// 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 contains code used to execute the program utilizing one of the
// various ways of running LLVM bitcode.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "ToolRunner.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
using namespace llvm;
namespace {
// OutputType - Allow the user to specify the way code should be run, to test
// for miscompilation.
//
enum OutputType {
AutoPick,
RunLLI,
RunJIT,
RunLLC,
RunLLCIA,
CompileCustom,
Custom
};
} // namespace
static cl::opt<double> AbsTolerance("abs-tolerance",
cl::desc("Absolute error tolerated"),
cl::init(0.0));
static cl::opt<double> RelTolerance("rel-tolerance",
cl::desc("Relative error tolerated"),
cl::init(0.0));
static cl::opt<OutputType> InterpreterSel(
cl::desc("Specify the \"test\" i.e. suspect back-end:"),
cl::values(clEnumValN(AutoPick, "auto", "Use best guess"),
clEnumValN(RunLLI, "run-int", "Execute with the interpreter"),
clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
clEnumValN(RunLLCIA, "run-llc-ia",
"Compile with LLC with integrated assembler"),
clEnumValN(CompileCustom, "compile-custom",
"Use -compile-command to define a command to "
"compile the bitcode. Useful to avoid linking."),
clEnumValN(Custom, "run-custom",
"Use -exec-command to define a command to execute "
"the bitcode. Useful for cross-compilation.")),
cl::init(AutoPick));
static cl::opt<OutputType> SafeInterpreterSel(
cl::desc("Specify \"safe\" i.e. known-good backend:"),
cl::values(clEnumValN(AutoPick, "safe-auto", "Use best guess"),
clEnumValN(RunLLC, "safe-run-llc", "Compile with LLC"),
clEnumValN(Custom, "safe-run-custom",
"Use -exec-command to define a command to execute "
"the bitcode. Useful for cross-compilation.")),
cl::init(AutoPick));
static cl::opt<std::string> SafeInterpreterPath(
"safe-path", cl::desc("Specify the path to the \"safe\" backend program"),
cl::init(""));
static cl::opt<bool> AppendProgramExitCode(
"append-exit-code",
cl::desc("Append the exit code to the output so it gets diff'd too"),
cl::init(false));
static cl::opt<std::string>
InputFile("input", cl::init("/dev/null"),
cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
static cl::list<std::string>
AdditionalSOs("additional-so", cl::desc("Additional shared objects to load "
"into executing programs"));
static cl::list<std::string> AdditionalLinkerArgs(
"Xlinker", cl::desc("Additional arguments to pass to the linker"));
static cl::opt<std::string> CustomCompileCommand(
"compile-command", cl::init("llc"),
cl::desc("Command to compile the bitcode (use with -compile-custom) "
"(default: llc)"));
static cl::opt<std::string> CustomExecCommand(
"exec-command", cl::init("simulate"),
cl::desc("Command to execute the bitcode (use with -run-custom) "
"(default: simulate)"));
// Anything specified after the --args option are taken as arguments to the
// program being debugged.
cl::list<std::string> llvm::InputArgv("args", cl::Positional,
cl::desc("<program arguments>..."),
cl::PositionalEatsArgs);
cl::opt<std::string> llvm::OutputPrefix(
"output-prefix", cl::init("bugpoint"),
cl::desc("Prefix to use for outputs (default: 'bugpoint')"));
static cl::list<std::string> ToolArgv("tool-args", cl::Positional,
cl::desc("<tool arguments>..."),
cl::PositionalEatsArgs);
static cl::list<std::string> SafeToolArgv("safe-tool-args", cl::Positional,
cl::desc("<safe-tool arguments>..."),
cl::PositionalEatsArgs);
static cl::opt<std::string> CCBinary("gcc", cl::init(""),
cl::desc("The gcc binary to use."));
static cl::list<std::string> CCToolArgv("gcc-tool-args", cl::Positional,
cl::desc("<gcc-tool arguments>..."),
cl::PositionalEatsArgs);
//===----------------------------------------------------------------------===//
// BugDriver method implementation
//
/// initializeExecutionEnvironment - This method is used to set up the
/// environment for executing LLVM programs.
///
Error BugDriver::initializeExecutionEnvironment() {
outs() << "Initializing execution environment: ";
// Create an instance of the AbstractInterpreter interface as specified on
// the command line
SafeInterpreter = nullptr;
std::string Message;
if (CCBinary.empty()) {
if (ErrorOr<std::string> ClangPath =
FindProgramByName("clang", getToolName(), &AbsTolerance))
CCBinary = *ClangPath;
else
CCBinary = "gcc";
}
switch (InterpreterSel) {
case AutoPick:
if (!Interpreter) {
InterpreterSel = RunJIT;
Interpreter =
AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);
}
if (!Interpreter) {
InterpreterSel = RunLLC;
Interpreter = AbstractInterpreter::createLLC(
getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv);
}
if (!Interpreter) {
InterpreterSel = RunLLI;
Interpreter =
AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);
}
if (!Interpreter) {
InterpreterSel = AutoPick;
Message = "Sorry, I can't automatically select an interpreter!\n";
}
break;
case RunLLI:
Interpreter =
AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);
break;
case RunLLC:
case RunLLCIA:
Interpreter = AbstractInterpreter::createLLC(
getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv,
InterpreterSel == RunLLCIA);
break;
case RunJIT:
Interpreter =
AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);
break;
case CompileCustom:
Interpreter = AbstractInterpreter::createCustomCompiler(
getToolName(), Message, CustomCompileCommand);
break;
case Custom:
Interpreter = AbstractInterpreter::createCustomExecutor(
getToolName(), Message, CustomExecCommand);
break;
}
if (!Interpreter)
errs() << Message;
else // Display informational messages on stdout instead of stderr
outs() << Message;
std::string Path = SafeInterpreterPath;
if (Path.empty())
Path = getToolName();
std::vector<std::string> SafeToolArgs = SafeToolArgv;
switch (SafeInterpreterSel) {
case AutoPick:
// In "llc-safe" mode, default to using LLC as the "safe" backend.
if (InterpreterSel == RunLLC) {
SafeInterpreterSel = RunLLC;
SafeToolArgs.push_back("--relocation-model=pic");
SafeInterpreter = AbstractInterpreter::createLLC(
Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv);
} else if (InterpreterSel != CompileCustom) {
SafeInterpreterSel = AutoPick;
Message = "Sorry, I can't automatically select a safe interpreter!\n";
}
break;
case RunLLC:
case RunLLCIA:
SafeToolArgs.push_back("--relocation-model=pic");
SafeInterpreter = AbstractInterpreter::createLLC(
Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv,
SafeInterpreterSel == RunLLCIA);
break;
case Custom:
SafeInterpreter = AbstractInterpreter::createCustomExecutor(
getToolName(), Message, CustomExecCommand);
break;
default:
Message = "Sorry, this back-end is not supported by bugpoint as the "
"\"safe\" backend right now!\n";
break;
}
if (!SafeInterpreter && InterpreterSel != CompileCustom) {
outs() << Message << "\nExiting.\n";
exit(1);
}
cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv);
if (!cc) {
outs() << Message << "\nExiting.\n";
exit(1);
}
// If there was an error creating the selected interpreter, quit with error.
if (Interpreter == nullptr)
return make_error<StringError>("Failed to init execution environment",
inconvertibleErrorCode());
return Error::success();
}
/// Try to compile the specified module, returning false and setting Error if an
/// error occurs. This is used for code generation crash testing.
Error BugDriver::compileProgram(Module &M) const {
// Emit the program to a bitcode file...
auto Temp =
sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");
if (!Temp) {
errs() << ToolName
<< ": Error making unique filename: " << toString(Temp.takeError())
<< "\n";
exit(1);
}
DiscardTemp Discard{*Temp};
if (writeProgramToFile(Temp->FD, M)) {
errs() << ToolName << ": Error emitting bitcode to file '" << Temp->TmpName
<< "'!\n";
exit(1);
}
// Actually compile the program!
return Interpreter->compileProgram(Temp->TmpName, Timeout, MemoryLimit);
}
/// This method runs "Program", capturing the output of the program to a file,
/// returning the filename of the file. A recommended filename may be
/// optionally specified.
Expected<std::string> BugDriver::executeProgram(const Module &Program,
std::string OutputFile,
std::string BitcodeFile,
const std::string &SharedObj,
AbstractInterpreter *AI) const {
if (!AI)
AI = Interpreter;
assert(AI && "Interpreter should have been created already!");
bool CreatedBitcode = false;
if (BitcodeFile.empty()) {
// Emit the program to a bitcode file...
SmallString<128> UniqueFilename;
int UniqueFD;
std::error_code EC = sys::fs::createUniqueFile(
OutputPrefix + "-test-program-%%%%%%%.bc", UniqueFD, UniqueFilename);
if (EC) {
errs() << ToolName << ": Error making unique filename: " << EC.message()
<< "!\n";
exit(1);
}
BitcodeFile = std::string(UniqueFilename);
if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) {
errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile
<< "'!\n";
exit(1);
}
CreatedBitcode = true;
}
// Remove the temporary bitcode file when we are done.
std::string BitcodePath(BitcodeFile);
FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode && !SaveTemps);
if (OutputFile.empty())
OutputFile = OutputPrefix + "-execution-output-%%%%%%%";
// Check to see if this is a valid output filename...
SmallString<128> UniqueFile;
std::error_code EC = sys::fs::createUniqueFile(OutputFile, UniqueFile);
if (EC) {
errs() << ToolName << ": Error making unique filename: " << EC.message()
<< "\n";
exit(1);
}
OutputFile = std::string(UniqueFile);
// Figure out which shared objects to run, if any.
std::vector<std::string> SharedObjs(AdditionalSOs);
if (!SharedObj.empty())
SharedObjs.push_back(SharedObj);
Expected<int> RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile,
OutputFile, AdditionalLinkerArgs,
SharedObjs, Timeout, MemoryLimit);
if (Error E = RetVal.takeError())
return std::move(E);
if (*RetVal == -1) {
errs() << "<timeout>";
static bool FirstTimeout = true;
if (FirstTimeout) {
outs()
<< "\n"
"*** Program execution timed out! This mechanism is designed to "
"handle\n"
" programs stuck in infinite loops gracefully. The -timeout "
"option\n"
" can be used to change the timeout threshold or disable it "
"completely\n"
" (with -timeout=0). This message is only displayed once.\n";
FirstTimeout = false;
}
}
if (AppendProgramExitCode) {
std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);
outFile << "exit " << *RetVal << '\n';
outFile.close();
}
// Return the filename we captured the output to.
return OutputFile;
}
/// Used to create reference output with the "safe" backend, if reference output
/// is not provided.
Expected<std::string>
BugDriver::executeProgramSafely(const Module &Program,
const std::string &OutputFile) const {
return executeProgram(Program, OutputFile, "", "", SafeInterpreter);
}
Expected<std::string>
BugDriver::compileSharedObject(const std::string &BitcodeFile) {
assert(Interpreter && "Interpreter should have been created already!");
std::string OutputFile;
// Using the known-good backend.
Expected<CC::FileType> FT =
SafeInterpreter->OutputCode(BitcodeFile, OutputFile);
if (Error E = FT.takeError())
return std::move(E);
std::string SharedObjectFile;
if (Error E = cc->MakeSharedObject(OutputFile, *FT, SharedObjectFile,
AdditionalLinkerArgs))
return std::move(E);
// Remove the intermediate C file
sys::fs::remove(OutputFile);
return SharedObjectFile;
}
/// Calls compileProgram and then records the output into ReferenceOutputFile.
/// Returns true if reference file created, false otherwise. Note:
/// initializeExecutionEnvironment should be called BEFORE this function.
Error BugDriver::createReferenceFile(Module &M, const std::string &Filename) {
if (Error E = compileProgram(*Program))
return E;
Expected<std::string> Result = executeProgramSafely(*Program, Filename);
if (Error E = Result.takeError()) {
if (Interpreter != SafeInterpreter) {
E = joinErrors(
std::move(E),
make_error<StringError>(
"*** There is a bug running the \"safe\" backend. Either"
" debug it (for example with the -run-jit bugpoint option,"
" if JIT is being used as the \"safe\" backend), or fix the"
" error some other way.\n",
inconvertibleErrorCode()));
}
return E;
}
ReferenceOutputFile = *Result;
outs() << "\nReference output is: " << ReferenceOutputFile << "\n\n";
return Error::success();
}
/// This method executes the specified module and diffs the output against the
/// file specified by ReferenceOutputFile. If the output is different, 1 is
/// returned. If there is a problem with the code generator (e.g., llc
/// crashes), this will set ErrMsg.
Expected<bool> BugDriver::diffProgram(const Module &Program,
const std::string &BitcodeFile,
const std::string &SharedObject,
bool RemoveBitcode) const {
// Execute the program, generating an output file...
Expected<std::string> Output =
executeProgram(Program, "", BitcodeFile, SharedObject, nullptr);
if (Error E = Output.takeError())
return std::move(E);
std::string Error;
bool FilesDifferent = false;
if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, *Output,
AbsTolerance, RelTolerance, &Error)) {
if (Diff == 2) {
errs() << "While diffing output: " << Error << '\n';
exit(1);
}
FilesDifferent = true;
} else {
// Remove the generated output if there are no differences.
sys::fs::remove(*Output);
}
// Remove the bitcode file if we are supposed to.
if (RemoveBitcode)
sys::fs::remove(BitcodeFile);
return FilesDifferent;
}
bool BugDriver::isExecutingJIT() { return InterpreterSel == RunJIT; }

View File

@ -1,410 +0,0 @@
//===- ExtractFunction.cpp - Extract a function from Program --------------===//
//
// 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 implements several methods that are used to extract functions,
// loops, or portions of a module from the rest of the module.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/CodeExtractor.h"
#include <set>
using namespace llvm;
#define DEBUG_TYPE "bugpoint"
bool llvm::DisableSimplifyCFG = false;
static cl::opt<bool>
NoDCE("disable-dce",
cl::desc("Do not use the -dce pass to reduce testcases"));
static cl::opt<bool, true>
NoSCFG("disable-simplifycfg", cl::location(DisableSimplifyCFG),
cl::desc("Do not use the -simplifycfg pass to reduce testcases"));
static Function *globalInitUsesExternalBA(GlobalVariable *GV) {
if (!GV->hasInitializer())
return nullptr;
Constant *I = GV->getInitializer();
// walk the values used by the initializer
// (and recurse into things like ConstantExpr)
std::vector<Constant *> Todo;
std::set<Constant *> Done;
Todo.push_back(I);
while (!Todo.empty()) {
Constant *V = Todo.back();
Todo.pop_back();
Done.insert(V);
if (BlockAddress *BA = dyn_cast<BlockAddress>(V)) {
Function *F = BA->getFunction();
if (F->isDeclaration())
return F;
}
for (User::op_iterator i = V->op_begin(), e = V->op_end(); i != e; ++i) {
Constant *C = dyn_cast<Constant>(*i);
if (C && !isa<GlobalValue>(C) && !Done.count(C))
Todo.push_back(C);
}
}
return nullptr;
}
std::unique_ptr<Module>
BugDriver::deleteInstructionFromProgram(const Instruction *I,
unsigned Simplification) {
// FIXME, use vmap?
std::unique_ptr<Module> Clone = CloneModule(*Program);
const BasicBlock *PBB = I->getParent();
const Function *PF = PBB->getParent();
Module::iterator RFI = Clone->begin(); // Get iterator to corresponding fn
std::advance(
RFI, std::distance(PF->getParent()->begin(), Module::const_iterator(PF)));
Function::iterator RBI = RFI->begin(); // Get iterator to corresponding BB
std::advance(RBI, std::distance(PF->begin(), Function::const_iterator(PBB)));
BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst
std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I)));
Instruction *TheInst = &*RI; // Got the corresponding instruction!
// If this instruction produces a value, replace any users with null values
if (!TheInst->getType()->isVoidTy())
TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType()));
// Remove the instruction from the program.
TheInst->eraseFromParent();
// Spiff up the output a little bit.
std::vector<std::string> Passes;
/// Can we get rid of the -disable-* options?
if (Simplification > 1 && !NoDCE)
Passes.push_back("dce");
if (Simplification && !DisableSimplifyCFG)
Passes.push_back("simplifycfg"); // Delete dead control flow
Passes.push_back("verify");
std::unique_ptr<Module> New = runPassesOn(Clone.get(), Passes);
if (!New) {
errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n";
exit(1);
}
return New;
}
std::unique_ptr<Module>
BugDriver::performFinalCleanups(std::unique_ptr<Module> M,
bool MayModifySemantics) {
// Make all functions external, so GlobalDCE doesn't delete them...
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
I->setLinkage(GlobalValue::ExternalLinkage);
std::vector<std::string> CleanupPasses;
if (MayModifySemantics)
CleanupPasses.push_back("deadarghaX0r");
else
CleanupPasses.push_back("deadargelim");
std::unique_ptr<Module> New = runPassesOn(M.get(), CleanupPasses);
if (!New) {
errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n";
return nullptr;
}
return New;
}
std::unique_ptr<Module> BugDriver::extractLoop(Module *M) {
std::vector<std::string> LoopExtractPasses;
LoopExtractPasses.push_back("loop-extract-single");
std::unique_ptr<Module> NewM = runPassesOn(M, LoopExtractPasses);
if (!NewM) {
outs() << "*** Loop extraction failed: ";
emitProgressBitcode(*M, "loopextraction", true);
outs() << "*** Sorry. :( Please report a bug!\n";
return nullptr;
}
// Check to see if we created any new functions. If not, no loops were
// extracted and we should return null. Limit the number of loops we extract
// to avoid taking forever.
static unsigned NumExtracted = 32;
if (M->size() == NewM->size() || --NumExtracted == 0) {
return nullptr;
} else {
assert(M->size() < NewM->size() && "Loop extract removed functions?");
Module::iterator MI = NewM->begin();
for (unsigned i = 0, e = M->size(); i != e; ++i)
++MI;
}
return NewM;
}
static void eliminateAliases(GlobalValue *GV) {
// First, check whether a GlobalAlias references this definition.
// GlobalAlias MAY NOT reference declarations.
for (;;) {
// 1. Find aliases
SmallVector<GlobalAlias *, 1> aliases;
Module *M = GV->getParent();
for (Module::alias_iterator I = M->alias_begin(), E = M->alias_end();
I != E; ++I)
if (I->getAliasee()->stripPointerCasts() == GV)
aliases.push_back(&*I);
if (aliases.empty())
break;
// 2. Resolve aliases
for (unsigned i = 0, e = aliases.size(); i < e; ++i) {
aliases[i]->replaceAllUsesWith(aliases[i]->getAliasee());
aliases[i]->eraseFromParent();
}
// 3. Repeat until no more aliases found; there might
// be an alias to an alias...
}
}
// "Remove" the global variable by deleting its initializer, making it external.
void llvm::deleteGlobalInitializer(GlobalVariable *GV) {
eliminateAliases(GV);
GV->setInitializer(nullptr);
GV->setComdat(nullptr);
}
// "Remove" the function by deleting all of its basic blocks, making it
// external.
void llvm::deleteFunctionBody(Function *F) {
eliminateAliases(F);
// Function declarations can't have comdats.
F->setComdat(nullptr);
// delete the body of the function...
F->deleteBody();
assert(F->isDeclaration() && "This didn't make the function external!");
}
/// getTorInit - Given a list of entries for static ctors/dtors, return them
/// as a constant array.
static Constant *getTorInit(std::vector<std::pair<Function *, int>> &TorList) {
assert(!TorList.empty() && "Don't create empty tor list!");
std::vector<Constant *> ArrayElts;
Type *Int32Ty = Type::getInt32Ty(TorList[0].first->getContext());
StructType *STy = StructType::get(Int32Ty, TorList[0].first->getType());
for (unsigned i = 0, e = TorList.size(); i != e; ++i) {
Constant *Elts[] = {ConstantInt::get(Int32Ty, TorList[i].second),
TorList[i].first};
ArrayElts.push_back(ConstantStruct::get(STy, Elts));
}
return ConstantArray::get(
ArrayType::get(ArrayElts[0]->getType(), ArrayElts.size()), ArrayElts);
}
/// splitStaticCtorDtor - A module was recently split into two parts, M1/M2, and
/// M1 has all of the global variables. If M2 contains any functions that are
/// static ctors/dtors, we need to add an llvm.global_[cd]tors global to M2, and
/// prune appropriate entries out of M1s list.
static void splitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2,
ValueToValueMapTy &VMap) {
GlobalVariable *GV = M1->getNamedGlobal(GlobalName);
if (!GV || GV->isDeclaration() || GV->hasLocalLinkage() || !GV->use_empty())
return;
std::vector<std::pair<Function *, int>> M1Tors, M2Tors;
ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
if (!InitList)
return;
for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
if (ConstantStruct *CS =
dyn_cast<ConstantStruct>(InitList->getOperand(i))) {
if (CS->getNumOperands() != 2)
return; // Not array of 2-element structs.
if (CS->getOperand(1)->isNullValue())
break; // Found a null terminator, stop here.
ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));
int Priority = CI ? CI->getSExtValue() : 0;
Constant *FP = CS->getOperand(1);
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP))
if (CE->isCast())
FP = CE->getOperand(0);
if (Function *F = dyn_cast<Function>(FP)) {
if (!F->isDeclaration())
M1Tors.push_back(std::make_pair(F, Priority));
else {
// Map to M2's version of the function.
F = cast<Function>(VMap[F]);
M2Tors.push_back(std::make_pair(F, Priority));
}
}
}
}
GV->eraseFromParent();
if (!M1Tors.empty()) {
Constant *M1Init = getTorInit(M1Tors);
new GlobalVariable(*M1, M1Init->getType(), false,
GlobalValue::AppendingLinkage, M1Init, GlobalName);
}
GV = M2->getNamedGlobal(GlobalName);
assert(GV && "Not a clone of M1?");
assert(GV->use_empty() && "llvm.ctors shouldn't have uses!");
GV->eraseFromParent();
if (!M2Tors.empty()) {
Constant *M2Init = getTorInit(M2Tors);
new GlobalVariable(*M2, M2Init->getType(), false,
GlobalValue::AppendingLinkage, M2Init, GlobalName);
}
}
std::unique_ptr<Module>
llvm::splitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
ValueToValueMapTy &VMap) {
// Make sure functions & globals are all external so that linkage
// between the two modules will work.
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
I->setLinkage(GlobalValue::ExternalLinkage);
for (Module::global_iterator I = M->global_begin(), E = M->global_end();
I != E; ++I) {
if (I->hasName() && I->getName()[0] == '\01')
I->setName(I->getName().substr(1));
I->setLinkage(GlobalValue::ExternalLinkage);
}
ValueToValueMapTy NewVMap;
std::unique_ptr<Module> New = CloneModule(*M, NewVMap);
// Remove the Test functions from the Safe module
std::set<Function *> TestFunctions;
for (unsigned i = 0, e = F.size(); i != e; ++i) {
Function *TNOF = cast<Function>(VMap[F[i]]);
LLVM_DEBUG(errs() << "Removing function ");
LLVM_DEBUG(TNOF->printAsOperand(errs(), false));
LLVM_DEBUG(errs() << "\n");
TestFunctions.insert(cast<Function>(NewVMap[TNOF]));
deleteFunctionBody(TNOF); // Function is now external in this module!
}
// Remove the Safe functions from the Test module
for (Function &I : *New)
if (!TestFunctions.count(&I))
deleteFunctionBody(&I);
// Try to split the global initializers evenly
for (GlobalVariable &I : M->globals()) {
GlobalVariable *GV = cast<GlobalVariable>(NewVMap[&I]);
if (Function *TestFn = globalInitUsesExternalBA(&I)) {
if (Function *SafeFn = globalInitUsesExternalBA(GV)) {
errs() << "*** Error: when reducing functions, encountered "
"the global '";
GV->printAsOperand(errs(), false);
errs() << "' with an initializer that references blockaddresses "
"from safe function '"
<< SafeFn->getName() << "' and from test function '"
<< TestFn->getName() << "'.\n";
exit(1);
}
deleteGlobalInitializer(&I); // Delete the initializer to make it external
} else {
// If we keep it in the safe module, then delete it in the test module
deleteGlobalInitializer(GV);
}
}
// Make sure that there is a global ctor/dtor array in both halves of the
// module if they both have static ctor/dtor functions.
splitStaticCtorDtor("llvm.global_ctors", M, New.get(), NewVMap);
splitStaticCtorDtor("llvm.global_dtors", M, New.get(), NewVMap);
return New;
}
//===----------------------------------------------------------------------===//
// Basic Block Extraction Code
//===----------------------------------------------------------------------===//
std::unique_ptr<Module>
BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
Module *M) {
auto Temp = sys::fs::TempFile::create(OutputPrefix + "-extractblocks%%%%%%%");
if (!Temp) {
outs() << "*** Basic Block extraction failed!\n";
errs() << "Error creating temporary file: " << toString(Temp.takeError())
<< "\n";
emitProgressBitcode(*M, "basicblockextractfail", true);
return nullptr;
}
DiscardTemp Discard{*Temp};
// Extract all of the blocks except the ones in BBs.
SmallVector<BasicBlock *, 32> BlocksToExtract;
for (Function &F : *M)
for (BasicBlock &BB : F)
// Check if this block is going to be extracted.
if (!llvm::is_contained(BBs, &BB))
BlocksToExtract.push_back(&BB);
raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
for (BasicBlock *BB : BBs) {
// If the BB doesn't have a name, give it one so we have something to key
// off of.
if (!BB->hasName())
BB->setName("tmpbb");
OS << BB->getParent()->getName() << " " << BB->getName() << "\n";
}
OS.flush();
if (OS.has_error()) {
errs() << "Error writing list of blocks to not extract\n";
emitProgressBitcode(*M, "basicblockextractfail", true);
OS.clear_error();
return nullptr;
}
std::string uniqueFN = "--extract-blocks-file=";
uniqueFN += Temp->TmpName;
std::vector<std::string> PI;
PI.push_back("extract-blocks");
std::unique_ptr<Module> Ret = runPassesOn(M, PI, {uniqueFN});
if (!Ret) {
outs() << "*** Basic Block extraction failed, please report a bug!\n";
emitProgressBitcode(*M, "basicblockextractfail", true);
}
return Ret;
}

View File

@ -1,98 +0,0 @@
//===-- FindBugs.cpp - Run Many Different Optimizations -------------------===//
//
// 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 an interface that allows bugpoint to choose different
// combinations of optimizations to run on the selected input. Bugpoint will
// run these optimizations and record the success/failure of each. This way
// we can hopefully spot bugs in the optimizations.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <random>
using namespace llvm;
Error
BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
setPassesToRun(AllPasses);
outs() << "Starting bug finding procedure...\n\n";
// Creating a reference output if necessary
if (Error E = initializeExecutionEnvironment())
return E;
outs() << "\n";
if (ReferenceOutputFile.empty()) {
outs() << "Generating reference output from raw program: \n";
if (Error E = createReferenceFile(*Program))
return E;
}
std::mt19937 randomness(std::random_device{}());
unsigned num = 1;
while (true) {
//
// Step 1: Randomize the order of the optimizer passes.
//
llvm::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness);
//
// Step 2: Run optimizer passes on the program and check for success.
//
outs() << "Running selected passes on program to test for crash: ";
for (const std::string &Pass : PassesToRun)
outs() << "-" << Pass << " ";
std::string Filename;
if (runPasses(*Program, PassesToRun, Filename, false)) {
outs() << "\n";
outs() << "Optimizer passes caused failure!\n\n";
return debugOptimizerCrash();
} else {
outs() << "Combination " << num << " optimized successfully!\n";
}
//
// Step 3: Compile the optimized code.
//
outs() << "Running the code generator to test for a crash: ";
if (Error E = compileProgram(*Program)) {
outs() << "\n*** compileProgram threw an exception: ";
outs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
outs() << '\n';
//
// Step 4: Run the program and compare its output to the reference
// output (created above).
//
outs() << "*** Checking if passes caused miscompliation:\n";
Expected<bool> Diff = diffProgram(*Program, Filename, "", false);
if (Error E = Diff.takeError()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
if (*Diff) {
outs() << "\n*** diffProgram returned true!\n";
Error E = debugMiscompilation();
if (!E)
return Error::success();
}
outs() << "\n*** diff'd output matches!\n";
sys::fs::remove(Filename);
outs() << "\n\n";
num++;
} // end while
// Unreachable.
}

View File

@ -1,208 +0,0 @@
//===- ListReducer.h - Trim down list while retaining property --*- 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
//
//===----------------------------------------------------------------------===//
//
// This class is to be used as a base class for operations that want to zero in
// on a subset of the input which still causes the bug we are tracking.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
#define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdlib>
#include <random>
#include <vector>
namespace llvm {
extern bool BugpointIsInterrupted;
template <typename ElTy> struct ListReducer {
enum TestResult {
NoFailure, // No failure of the predicate was detected
KeepSuffix, // The suffix alone satisfies the predicate
KeepPrefix // The prefix alone satisfies the predicate
};
virtual ~ListReducer() = default;
/// This virtual function should be overriden by subclasses to implement the
/// test desired. The testcase is only required to test to see if the Kept
/// list still satisfies the property, but if it is going to check the prefix
/// anyway, it can.
virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix,
std::vector<ElTy> &Kept) = 0;
/// This function attempts to reduce the length of the specified list while
/// still maintaining the "test" property. This is the core of the "work"
/// that bugpoint does.
Expected<bool> reduceList(std::vector<ElTy> &TheList) {
std::vector<ElTy> empty;
std::mt19937 randomness(0x6e5ea738); // Seed the random number generator
Expected<TestResult> Result = doTest(TheList, empty);
if (Error E = Result.takeError())
return std::move(E);
switch (*Result) {
case KeepPrefix:
if (TheList.size() == 1) // we are done, it's the base case and it fails
return true;
else
break; // there's definitely an error, but we need to narrow it down
case KeepSuffix:
// cannot be reached!
llvm_unreachable("bugpoint ListReducer internal error: "
"selected empty set.");
case NoFailure:
return false; // there is no failure with the full set of passes/funcs!
}
// Maximal number of allowed splitting iterations,
// before the elements are randomly shuffled.
const unsigned MaxIterationsWithoutProgress = 3;
// Maximal number of allowed single-element trim iterations. We add a
// threshold here as single-element reductions may otherwise take a
// very long time to complete.
const unsigned MaxTrimIterationsWithoutBackJump = 3;
bool ShufflingEnabled = true;
Backjump:
unsigned MidTop = TheList.size();
unsigned MaxIterations = MaxIterationsWithoutProgress;
unsigned NumOfIterationsWithoutProgress = 0;
while (MidTop > 1) { // Binary split reduction loop
// Halt if the user presses ctrl-c.
if (BugpointIsInterrupted) {
errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
return true;
}
// If the loop doesn't make satisfying progress, try shuffling.
// The purpose of shuffling is to avoid the heavy tails of the
// distribution (improving the speed of convergence).
if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {
std::vector<ElTy> ShuffledList(TheList);
llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
errs() << "\n\n*** Testing shuffled set...\n\n";
// Check that random shuffle doesn't lose the bug
Expected<TestResult> Result = doTest(ShuffledList, empty);
// TODO: Previously, this error was ignored and we treated it as if
// shuffling hid the bug. This should really either be consumeError if
// that behaviour was sensible, or we should propagate the error.
assert(!Result.takeError() && "Shuffling caused internal error?");
if (*Result == KeepPrefix) {
// If the bug is still here, use the shuffled list.
TheList.swap(ShuffledList);
MidTop = TheList.size();
// Must increase the shuffling treshold to avoid the small
// probability of infinite looping without making progress.
MaxIterations += 2;
errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
} else {
ShufflingEnabled = false; // Disable shuffling further on
errs() << "\n\n*** Shuffling hides the bug...\n\n";
}
NumOfIterationsWithoutProgress = 0;
}
unsigned Mid = MidTop / 2;
std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid);
std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end());
Expected<TestResult> Result = doTest(Prefix, Suffix);
if (Error E = Result.takeError())
return std::move(E);
switch (*Result) {
case KeepSuffix:
// The property still holds. We can just drop the prefix elements, and
// shorten the list to the "kept" elements.
TheList.swap(Suffix);
MidTop = TheList.size();
// Reset progress treshold and progress counter
MaxIterations = MaxIterationsWithoutProgress;
NumOfIterationsWithoutProgress = 0;
break;
case KeepPrefix:
// The predicate still holds, shorten the list to the prefix elements.
TheList.swap(Prefix);
MidTop = TheList.size();
// Reset progress treshold and progress counter
MaxIterations = MaxIterationsWithoutProgress;
NumOfIterationsWithoutProgress = 0;
break;
case NoFailure:
// Otherwise the property doesn't hold. Some of the elements we removed
// must be necessary to maintain the property.
MidTop = Mid;
NumOfIterationsWithoutProgress++;
break;
}
}
// Probability of backjumping from the trimming loop back to the binary
// split reduction loop.
const int BackjumpProbability = 10;
// Okay, we trimmed as much off the top and the bottom of the list as we
// could. If there is more than two elements in the list, try deleting
// interior elements and testing that.
//
if (TheList.size() > 2) {
bool Changed = true;
std::vector<ElTy> EmptyList;
unsigned TrimIterations = 0;
while (Changed) { // Trimming loop.
Changed = false;
// If the binary split reduction loop made an unfortunate sequence of
// splits, the trimming loop might be left off with a huge number of
// remaining elements (large search space). Backjumping out of that
// search space and attempting a different split can significantly
// improve the convergence speed.
if (std::rand() % 100 < BackjumpProbability)
goto Backjump;
for (unsigned i = 1; i < TheList.size() - 1; ++i) {
// Check interior elts
if (BugpointIsInterrupted) {
errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
return true;
}
std::vector<ElTy> TestList(TheList);
TestList.erase(TestList.begin() + i);
Expected<TestResult> Result = doTest(EmptyList, TestList);
if (Error E = Result.takeError())
return std::move(E);
if (*Result == KeepSuffix) {
// We can trim down the list!
TheList.swap(TestList);
--i; // Don't skip an element of the list
Changed = true;
}
}
if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
break;
TrimIterations++;
}
}
return true; // there are some failure and we've narrowed them down
}
};
} // End llvm namespace
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,278 +0,0 @@
//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//
//
// 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 an interface that allows bugpoint to run various passes
// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint
// may have its own bugs, but that's another story...). It achieves this by
// forking a copy of itself and having the child process do the optimizations.
// If this client dies, we can always fork a new one. :)
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "ToolRunner.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/ToolOutputFile.h"
#define DONT_GET_PLUGIN_LOADER_OPTION
#include "llvm/Support/PluginLoader.h"
using namespace llvm;
#define DEBUG_TYPE "bugpoint"
static cl::opt<std::string>
OptCmd("opt-command", cl::init(""),
cl::desc("Path to opt. (default: search path "
"for 'opt'.)"));
/// This writes the current "Program" to the named bitcode file. If an error
/// occurs, true is returned.
static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {
WriteBitcodeToFile(M, Out.os(), /* ShouldPreserveUseListOrder */ true);
Out.os().close();
if (!Out.os().has_error()) {
Out.keep();
return false;
}
return true;
}
bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
const Module &M) const {
ToolOutputFile Out(Filename, FD);
return writeProgramToFileAux(Out, M);
}
bool BugDriver::writeProgramToFile(int FD, const Module &M) const {
raw_fd_ostream OS(FD, /*shouldClose*/ false);
WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ true);
OS.flush();
if (!OS.has_error())
return false;
OS.clear_error();
return true;
}
bool BugDriver::writeProgramToFile(const std::string &Filename,
const Module &M) const {
std::error_code EC;
ToolOutputFile Out(Filename, EC, sys::fs::OF_None);
if (!EC)
return writeProgramToFileAux(Out, M);
return true;
}
/// This function is used to output the current Program to a file named
/// "bugpoint-ID.bc".
void BugDriver::emitProgressBitcode(const Module &M, const std::string &ID,
bool NoFlyer) const {
// Output the input to the current pass to a bitcode file, emit a message
// telling the user how to reproduce it: opt -foo blah.bc
//
std::string Filename = OutputPrefix + "-" + ID + ".bc";
if (writeProgramToFile(Filename, M)) {
errs() << "Error opening file '" << Filename << "' for writing!\n";
return;
}
outs() << "Emitted bitcode to '" << Filename << "'\n";
if (NoFlyer || PassesToRun.empty())
return;
outs() << "\n*** You can reproduce the problem with: ";
if (UseValgrind)
outs() << "valgrind ";
outs() << "opt " << Filename;
for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
outs() << " -load " << PluginLoader::getPlugin(i);
}
outs() << " " << getPassesString(PassesToRun) << "\n";
}
static cl::opt<bool> SilencePasses(
"silence-passes",
cl::desc("Suppress output of running passes (both stdout and stderr)"));
static cl::list<std::string> OptArgs("opt-args", cl::Positional,
cl::desc("<opt arguments>..."),
cl::PositionalEatsArgs);
/// runPasses - Run the specified passes on Program, outputting a bitcode file
/// and writing the filename into OutputFile if successful. If the
/// optimizations fail for some reason (optimizer crashes), return true,
/// otherwise return false. If DeleteOutput is set to true, the bitcode is
/// deleted on success, and the filename string is undefined. This prints to
/// outs() a single line message indicating whether compilation was successful
/// or failed.
///
bool BugDriver::runPasses(Module &Program,
const std::vector<std::string> &Passes,
std::string &OutputFilename, bool DeleteOutput,
bool Quiet, ArrayRef<std::string> ExtraArgs) const {
// setup the output file name
outs().flush();
SmallString<128> UniqueFilename;
std::error_code EC = sys::fs::createUniqueFile(
OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);
if (EC) {
errs() << getToolName()
<< ": Error making unique filename: " << EC.message() << "\n";
return true;
}
OutputFilename = std::string(UniqueFilename);
// set up the input file name
Expected<sys::fs::TempFile> Temp =
sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");
if (!Temp) {
errs() << getToolName()
<< ": Error making unique filename: " << toString(Temp.takeError())
<< "\n";
return true;
}
DiscardTemp Discard{*Temp};
raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
WriteBitcodeToFile(Program, OS, /* ShouldPreserveUseListOrder */ true);
OS.flush();
if (OS.has_error()) {
errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";
OS.clear_error();
return true;
}
std::string tool = OptCmd;
if (OptCmd.empty()) {
if (ErrorOr<std::string> Path =
FindProgramByName("opt", getToolName(), &OutputPrefix))
tool = *Path;
else
errs() << Path.getError().message() << "\n";
}
if (tool.empty()) {
errs() << "Cannot find `opt' in PATH!\n";
return true;
}
if (!sys::fs::exists(tool)) {
errs() << "Specified `opt' binary does not exist: " << tool << "\n";
return true;
}
std::string Prog;
if (UseValgrind) {
if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))
Prog = *Path;
else
errs() << Path.getError().message() << "\n";
} else
Prog = tool;
if (Prog.empty()) {
errs() << "Cannot find `valgrind' in PATH!\n";
return true;
}
// setup the child process' arguments
SmallVector<StringRef, 8> Args;
if (UseValgrind) {
Args.push_back("valgrind");
Args.push_back("--error-exitcode=1");
Args.push_back("-q");
Args.push_back(tool);
} else
Args.push_back(tool);
llvm::append_range(Args, OptArgs);
// Pin to legacy PM since bugpoint has lots of infra and hacks revolving
// around the legacy PM.
Args.push_back("-bugpoint-enable-legacy-pm");
Args.push_back("-disable-symbolication");
Args.push_back("-o");
Args.push_back(OutputFilename);
std::vector<std::string> pass_args;
for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
pass_args.push_back(std::string("-load"));
pass_args.push_back(PluginLoader::getPlugin(i));
}
for (std::vector<std::string>::const_iterator I = Passes.begin(),
E = Passes.end();
I != E; ++I)
pass_args.push_back(std::string("-") + (*I));
for (std::vector<std::string>::const_iterator I = pass_args.begin(),
E = pass_args.end();
I != E; ++I)
Args.push_back(*I);
Args.push_back(Temp->TmpName);
Args.append(ExtraArgs.begin(), ExtraArgs.end());
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
<< " " << Args[i];
errs() << "\n";);
std::optional<StringRef> Redirects[3] = {std::nullopt, std::nullopt,
std::nullopt};
// Redirect stdout and stderr to nowhere if SilencePasses is given.
if (SilencePasses) {
Redirects[1] = "";
Redirects[2] = "";
}
std::string ErrMsg;
int result = sys::ExecuteAndWait(Prog, Args, std::nullopt, Redirects, Timeout,
MemoryLimit, &ErrMsg);
// If we are supposed to delete the bitcode file or if the passes crashed,
// remove it now. This may fail if the file was never created, but that's ok.
if (DeleteOutput || result != 0)
sys::fs::remove(OutputFilename);
if (!Quiet) {
if (result == 0)
outs() << "Success!\n";
else if (result > 0)
outs() << "Exited with error code '" << result << "'\n";
else if (result < 0) {
if (result == -1)
outs() << "Execute failed: " << ErrMsg << "\n";
else
outs() << "Crashed: " << ErrMsg << "\n";
}
if (result & 0x01000000)
outs() << "Dumped core\n";
}
// Was the child successful?
return result != 0;
}
std::unique_ptr<Module>
BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
ArrayRef<std::string> ExtraArgs) {
std::string BitcodeResult;
if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
ExtraArgs)) {
return nullptr;
}
std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);
if (!Ret) {
errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult
<< "'!\n";
exit(1);
}
sys::fs::remove(BitcodeResult);
return Ret;
}

View File

@ -1,849 +0,0 @@
//===-- ToolRunner.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the interfaces described in the ToolRunner.h file.
//
//===----------------------------------------------------------------------===//
#include "ToolRunner.h"
#include "llvm/Config/config.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <sstream>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "toolrunner"
cl::opt<bool> llvm::SaveTemps("save-temps", cl::init(false),
cl::desc("Save temporary files"));
static cl::opt<std::string>
RemoteClient("remote-client",
cl::desc("Remote execution client (rsh/ssh)"));
static cl::opt<std::string>
RemoteHost("remote-host", cl::desc("Remote execution (rsh/ssh) host"));
static cl::opt<std::string>
RemotePort("remote-port", cl::desc("Remote execution (rsh/ssh) port"));
static cl::opt<std::string>
RemoteUser("remote-user", cl::desc("Remote execution (rsh/ssh) user id"));
static cl::opt<std::string>
RemoteExtra("remote-extra-options",
cl::desc("Remote execution (rsh/ssh) extra options"));
/// RunProgramWithTimeout - This function provides an alternate interface
/// to the sys::Program::ExecuteAndWait interface.
/// @see sys::Program::ExecuteAndWait
static int RunProgramWithTimeout(StringRef ProgramPath,
ArrayRef<StringRef> Args, StringRef StdInFile,
StringRef StdOutFile, StringRef StdErrFile,
unsigned NumSeconds = 0,
unsigned MemoryLimit = 0,
std::string *ErrMsg = nullptr) {
std::optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
return sys::ExecuteAndWait(ProgramPath, Args, std::nullopt, Redirects,
NumSeconds, MemoryLimit, ErrMsg);
}
/// RunProgramRemotelyWithTimeout - This function runs the given program
/// remotely using the given remote client and the sys::Program::ExecuteAndWait.
/// Returns the remote program exit code or reports a remote client error if it
/// fails. Remote client is required to return 255 if it failed or program exit
/// code otherwise.
/// @see sys::Program::ExecuteAndWait
static int RunProgramRemotelyWithTimeout(
StringRef RemoteClientPath, ArrayRef<StringRef> Args, StringRef StdInFile,
StringRef StdOutFile, StringRef StdErrFile, unsigned NumSeconds = 0,
unsigned MemoryLimit = 0) {
std::optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
// Run the program remotely with the remote client
int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, std::nullopt,
Redirects, NumSeconds, MemoryLimit);
// Has the remote client fail?
if (255 == ReturnCode) {
std::ostringstream OS;
OS << "\nError running remote client:\n ";
for (StringRef Arg : Args)
OS << " " << Arg.str();
OS << "\n";
// The error message is in the output file, let's print it out from there.
std::string StdOutFileName = StdOutFile.str();
std::ifstream ErrorFile(StdOutFileName.c_str());
if (ErrorFile) {
std::copy(std::istreambuf_iterator<char>(ErrorFile),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(OS));
ErrorFile.close();
}
errs() << OS.str();
}
return ReturnCode;
}
static Error ProcessFailure(StringRef ProgPath, ArrayRef<StringRef> Args,
unsigned Timeout = 0, unsigned MemoryLimit = 0) {
std::ostringstream OS;
OS << "\nError running tool:\n ";
for (StringRef Arg : Args)
OS << " " << Arg.str();
OS << "\n";
// Rerun the compiler, capturing any error messages to print them.
SmallString<128> ErrorFilename;
std::error_code EC = sys::fs::createTemporaryFile(
"bugpoint.program_error_messages", "", ErrorFilename);
if (EC) {
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
RunProgramWithTimeout(ProgPath, Args, "", ErrorFilename.str(),
ErrorFilename.str(), Timeout, MemoryLimit);
// FIXME: check return code ?
// Print out the error messages generated by CC if possible...
std::ifstream ErrorFile(ErrorFilename.c_str());
if (ErrorFile) {
std::copy(std::istreambuf_iterator<char>(ErrorFile),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(OS));
ErrorFile.close();
}
sys::fs::remove(ErrorFilename.c_str());
return make_error<StringError>(OS.str(), inconvertibleErrorCode());
}
//===---------------------------------------------------------------------===//
// LLI Implementation of AbstractIntepreter interface
//
namespace {
class LLI : public AbstractInterpreter {
std::string LLIPath; // The path to the LLI executable
std::vector<std::string> ToolArgs; // Args to pass to LLI
public:
LLI(const std::string &Path, const std::vector<std::string> *Args)
: LLIPath(Path) {
ToolArgs.clear();
if (Args) {
ToolArgs = *Args;
}
}
Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
};
} // namespace
Expected<int> LLI::ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &Args,
const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout, unsigned MemoryLimit) {
std::vector<StringRef> LLIArgs;
LLIArgs.push_back(LLIPath);
LLIArgs.push_back("-force-interpreter=true");
for (std::vector<std::string>::const_iterator i = SharedLibs.begin(),
e = SharedLibs.end();
i != e; ++i) {
LLIArgs.push_back("-load");
LLIArgs.push_back(*i);
}
// Add any extra LLI args.
llvm::append_range(LLIArgs, ToolArgs);
LLIArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
llvm::append_range(LLIArgs, Args);
outs() << "<lli>";
outs().flush();
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = LLIArgs.size(); i != e; ++i) errs()
<< " " << LLIArgs[i];
errs() << "\n";);
return RunProgramWithTimeout(LLIPath, LLIArgs, InputFile, OutputFile,
OutputFile, Timeout, MemoryLimit);
}
void AbstractInterpreter::anchor() {}
ErrorOr<std::string> llvm::FindProgramByName(const std::string &ExeName,
const char *Argv0,
void *MainAddr) {
// Check the directory that the calling program is in. We can do
// this if ProgramPath contains at least one / character, indicating that it
// is a relative path to the executable itself.
std::string Main = sys::fs::getMainExecutable(Argv0, MainAddr);
StringRef Result = sys::path::parent_path(Main);
if (ErrorOr<std::string> Path = sys::findProgramByName(ExeName, Result))
return *Path;
// Check the user PATH.
return sys::findProgramByName(ExeName);
}
// LLI create method - Try to find the LLI executable
AbstractInterpreter *
AbstractInterpreter::createLLI(const char *Argv0, std::string &Message,
const std::vector<std::string> *ToolArgs) {
if (ErrorOr<std::string> LLIPath =
FindProgramByName("lli", Argv0, (void *)(intptr_t)&createLLI)) {
Message = "Found lli: " + *LLIPath + "\n";
return new LLI(*LLIPath, ToolArgs);
} else {
Message = LLIPath.getError().message() + "\n";
return nullptr;
}
}
//===---------------------------------------------------------------------===//
// Custom compiler command implementation of AbstractIntepreter interface
//
// Allows using a custom command for compiling the bitcode, thus allows, for
// example, to compile a bitcode fragment without linking or executing, then
// using a custom wrapper script to check for compiler errors.
namespace {
class CustomCompiler : public AbstractInterpreter {
std::string CompilerCommand;
std::vector<std::string> CompilerArgs;
public:
CustomCompiler(const std::string &CompilerCmd,
std::vector<std::string> CompArgs)
: CompilerCommand(CompilerCmd), CompilerArgs(std::move(CompArgs)) {}
Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
unsigned MemoryLimit = 0) override;
Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs = std::vector<std::string>(),
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) override {
return make_error<StringError>(
"Execution not supported with -compile-custom",
inconvertibleErrorCode());
}
};
} // namespace
Error CustomCompiler::compileProgram(const std::string &Bitcode,
unsigned Timeout, unsigned MemoryLimit) {
std::vector<StringRef> ProgramArgs;
ProgramArgs.push_back(CompilerCommand);
llvm::append_range(ProgramArgs, CompilerArgs);
ProgramArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
llvm::append_range(ProgramArgs, CompilerArgs);
if (RunProgramWithTimeout(CompilerCommand, ProgramArgs, "", "", "", Timeout,
MemoryLimit))
return ProcessFailure(CompilerCommand, ProgramArgs, Timeout, MemoryLimit);
return Error::success();
}
//===---------------------------------------------------------------------===//
// Custom execution command implementation of AbstractIntepreter interface
//
// Allows using a custom command for executing the bitcode, thus allows,
// for example, to invoke a cross compiler for code generation followed by
// a simulator that executes the generated binary.
namespace {
class CustomExecutor : public AbstractInterpreter {
std::string ExecutionCommand;
std::vector<std::string> ExecutorArgs;
public:
CustomExecutor(const std::string &ExecutionCmd,
std::vector<std::string> ExecArgs)
: ExecutionCommand(ExecutionCmd), ExecutorArgs(std::move(ExecArgs)) {}
Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
};
} // namespace
Expected<int> CustomExecutor::ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs, unsigned Timeout,
unsigned MemoryLimit) {
std::vector<StringRef> ProgramArgs;
ProgramArgs.push_back(ExecutionCommand);
llvm::append_range(ProgramArgs, ExecutorArgs);
ProgramArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
llvm::append_range(ProgramArgs, Args);
return RunProgramWithTimeout(ExecutionCommand, ProgramArgs, InputFile,
OutputFile, OutputFile, Timeout, MemoryLimit);
}
// Tokenize the CommandLine to the command and the args to allow
// defining a full command line as the command instead of just the
// executed program. We cannot just pass the whole string after the command
// as a single argument because then the program sees only a single
// command line argument (with spaces in it: "foo bar" instead
// of "foo" and "bar").
//
// Spaces are used as a delimiter; however repeated, leading, and trailing
// whitespace are ignored. Simple escaping is allowed via the '\'
// character, as seen below:
//
// Two consecutive '\' evaluate to a single '\'.
// A space after a '\' evaluates to a space that is not interpreted as a
// delimiter.
// Any other instances of the '\' character are removed.
//
// Example:
// '\\' -> '\'
// '\ ' -> ' '
// 'exa\mple' -> 'example'
//
static void lexCommand(const char *Argv0, std::string &Message,
const std::string &CommandLine, std::string &CmdPath,
std::vector<std::string> &Args) {
std::string Token;
std::string Command;
bool FoundPath = false;
// first argument is the PATH.
// Skip repeated whitespace, leading whitespace and trailing whitespace.
for (std::size_t Pos = 0u; Pos <= CommandLine.size(); ++Pos) {
if ('\\' == CommandLine[Pos]) {
if (Pos + 1 < CommandLine.size())
Token.push_back(CommandLine[++Pos]);
continue;
}
if (' ' == CommandLine[Pos] || CommandLine.size() == Pos) {
if (Token.empty())
continue;
if (!FoundPath) {
Command = Token;
FoundPath = true;
Token.clear();
continue;
}
Args.push_back(Token);
Token.clear();
continue;
}
Token.push_back(CommandLine[Pos]);
}
auto Path = FindProgramByName(Command, Argv0, (void *)(intptr_t)&lexCommand);
if (!Path) {
Message = std::string("Cannot find '") + Command +
"' in PATH: " + Path.getError().message() + "\n";
return;
}
CmdPath = *Path;
Message = "Found command in: " + CmdPath + "\n";
}
// Custom execution environment create method, takes the execution command
// as arguments
AbstractInterpreter *AbstractInterpreter::createCustomCompiler(
const char *Argv0, std::string &Message,
const std::string &CompileCommandLine) {
std::string CmdPath;
std::vector<std::string> Args;
lexCommand(Argv0, Message, CompileCommandLine, CmdPath, Args);
if (CmdPath.empty())
return nullptr;
return new CustomCompiler(CmdPath, Args);
}
// Custom execution environment create method, takes the execution command
// as arguments
AbstractInterpreter *
AbstractInterpreter::createCustomExecutor(const char *Argv0,
std::string &Message,
const std::string &ExecCommandLine) {
std::string CmdPath;
std::vector<std::string> Args;
lexCommand(Argv0, Message, ExecCommandLine, CmdPath, Args);
if (CmdPath.empty())
return nullptr;
return new CustomExecutor(CmdPath, Args);
}
//===----------------------------------------------------------------------===//
// LLC Implementation of AbstractIntepreter interface
//
Expected<CC::FileType> LLC::OutputCode(const std::string &Bitcode,
std::string &OutputAsmFile,
unsigned Timeout, unsigned MemoryLimit) {
const char *Suffix = (UseIntegratedAssembler ? ".llc.o" : ".llc.s");
SmallString<128> UniqueFile;
std::error_code EC =
sys::fs::createUniqueFile(Bitcode + "-%%%%%%%" + Suffix, UniqueFile);
if (EC) {
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
OutputAsmFile = std::string(UniqueFile);
std::vector<StringRef> LLCArgs;
LLCArgs.push_back(LLCPath);
// Add any extra LLC args.
llvm::append_range(LLCArgs, ToolArgs);
LLCArgs.push_back("-o");
LLCArgs.push_back(OutputAsmFile); // Output to the Asm file
LLCArgs.push_back(Bitcode); // This is the input bitcode
if (UseIntegratedAssembler)
LLCArgs.push_back("-filetype=obj");
outs() << (UseIntegratedAssembler ? "<llc-ia>" : "<llc>");
outs().flush();
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = LLCArgs.size(); i != e; ++i) errs()
<< " " << LLCArgs[i];
errs() << "\n";);
if (RunProgramWithTimeout(LLCPath, LLCArgs, "", "", "", Timeout, MemoryLimit))
return ProcessFailure(LLCPath, LLCArgs, Timeout, MemoryLimit);
return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile;
}
Error LLC::compileProgram(const std::string &Bitcode, unsigned Timeout,
unsigned MemoryLimit) {
std::string OutputAsmFile;
Expected<CC::FileType> Result =
OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit);
sys::fs::remove(OutputAsmFile);
if (Error E = Result.takeError())
return E;
return Error::success();
}
Expected<int> LLC::ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &Args,
const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &ArgsForCC,
const std::vector<std::string> &SharedLibs,
unsigned Timeout, unsigned MemoryLimit) {
std::string OutputAsmFile;
Expected<CC::FileType> FileKind =
OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit);
FileRemover OutFileRemover(OutputAsmFile, !SaveTemps);
if (Error E = FileKind.takeError())
return std::move(E);
std::vector<std::string> CCArgs(ArgsForCC);
llvm::append_range(CCArgs, SharedLibs);
// Assuming LLC worked, compile the result with CC and run it.
return cc->ExecuteProgram(OutputAsmFile, Args, *FileKind, InputFile,
OutputFile, CCArgs, Timeout, MemoryLimit);
}
/// createLLC - Try to find the LLC executable
///
LLC *AbstractInterpreter::createLLC(const char *Argv0, std::string &Message,
const std::string &CCBinary,
const std::vector<std::string> *Args,
const std::vector<std::string> *CCArgs,
bool UseIntegratedAssembler) {
ErrorOr<std::string> LLCPath =
FindProgramByName("llc", Argv0, (void *)(intptr_t)&createLLC);
if (!LLCPath) {
Message = LLCPath.getError().message() + "\n";
return nullptr;
}
CC *cc = CC::create(Argv0, Message, CCBinary, CCArgs);
if (!cc) {
errs() << Message << "\n";
exit(1);
}
Message = "Found llc: " + *LLCPath + "\n";
return new LLC(*LLCPath, cc, Args, UseIntegratedAssembler);
}
//===---------------------------------------------------------------------===//
// JIT Implementation of AbstractIntepreter interface
//
namespace {
class JIT : public AbstractInterpreter {
std::string LLIPath; // The path to the LLI executable
std::vector<std::string> ToolArgs; // Args to pass to LLI
public:
JIT(const std::string &Path, const std::vector<std::string> *Args)
: LLIPath(Path) {
ToolArgs.clear();
if (Args) {
ToolArgs = *Args;
}
}
Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs = std::vector<std::string>(),
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
};
} // namespace
Expected<int> JIT::ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &Args,
const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout, unsigned MemoryLimit) {
// Construct a vector of parameters, incorporating those from the command-line
std::vector<StringRef> JITArgs;
JITArgs.push_back(LLIPath);
JITArgs.push_back("-force-interpreter=false");
// Add any extra LLI args.
llvm::append_range(JITArgs, ToolArgs);
for (unsigned i = 0, e = SharedLibs.size(); i != e; ++i) {
JITArgs.push_back("-load");
JITArgs.push_back(SharedLibs[i]);
}
JITArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
llvm::append_range(JITArgs, Args);
outs() << "<jit>";
outs().flush();
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = JITArgs.size(); i != e; ++i) errs()
<< " " << JITArgs[i];
errs() << "\n";);
LLVM_DEBUG(errs() << "\nSending output to " << OutputFile << "\n");
return RunProgramWithTimeout(LLIPath, JITArgs, InputFile, OutputFile,
OutputFile, Timeout, MemoryLimit);
}
/// createJIT - Try to find the LLI executable
///
AbstractInterpreter *
AbstractInterpreter::createJIT(const char *Argv0, std::string &Message,
const std::vector<std::string> *Args) {
if (ErrorOr<std::string> LLIPath =
FindProgramByName("lli", Argv0, (void *)(intptr_t)&createJIT)) {
Message = "Found lli: " + *LLIPath + "\n";
return new JIT(*LLIPath, Args);
} else {
Message = LLIPath.getError().message() + "\n";
return nullptr;
}
}
//===---------------------------------------------------------------------===//
// CC abstraction
//
static bool IsARMArchitecture(std::vector<StringRef> Args) {
for (size_t I = 0; I < Args.size(); ++I) {
if (!Args[I].equals_insensitive("-arch"))
continue;
++I;
if (I == Args.size())
break;
if (Args[I].starts_with_insensitive("arm"))
return true;
}
return false;
}
Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
const std::vector<std::string> &Args,
FileType fileType,
const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &ArgsForCC,
unsigned Timeout, unsigned MemoryLimit) {
std::vector<StringRef> CCArgs;
CCArgs.push_back(CCPath);
if (TargetTriple.getArch() == Triple::x86)
CCArgs.push_back("-m32");
for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
E = ccArgs.end();
I != E; ++I)
CCArgs.push_back(*I);
// Specify -x explicitly in case the extension is wonky
if (fileType != ObjectFile) {
CCArgs.push_back("-x");
if (fileType == CFile) {
CCArgs.push_back("c");
CCArgs.push_back("-fno-strict-aliasing");
} else {
CCArgs.push_back("assembler");
// For ARM architectures we don't want this flag. bugpoint isn't
// explicitly told what architecture it is working on, so we get
// it from cc flags
if (TargetTriple.isOSDarwin() && !IsARMArchitecture(CCArgs))
CCArgs.push_back("-force_cpusubtype_ALL");
}
}
CCArgs.push_back(ProgramFile); // Specify the input filename.
CCArgs.push_back("-x");
CCArgs.push_back("none");
CCArgs.push_back("-o");
SmallString<128> OutputBinary;
std::error_code EC =
sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.cc.exe", OutputBinary);
if (EC) {
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
CCArgs.push_back(OutputBinary); // Output to the right file...
// Add any arguments intended for CC. We locate them here because this is
// most likely -L and -l options that need to come before other libraries but
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
llvm::append_range(CCArgs, ArgsForCC);
CCArgs.push_back("-lm"); // Hard-code the math library...
CCArgs.push_back("-O2"); // Optimize the program a bit...
if (TargetTriple.getArch() == Triple::sparc)
CCArgs.push_back("-mcpu=v9");
outs() << "<CC>";
outs().flush();
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = CCArgs.size(); i != e; ++i) errs()
<< " " << CCArgs[i];
errs() << "\n";);
if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
return ProcessFailure(CCPath, CCArgs);
std::vector<StringRef> ProgramArgs;
// Declared here so that the destructor only runs after
// ProgramArgs is used.
std::string Exec;
if (RemoteClientPath.empty())
ProgramArgs.push_back(OutputBinary);
else {
ProgramArgs.push_back(RemoteClientPath);
ProgramArgs.push_back(RemoteHost);
if (!RemoteUser.empty()) {
ProgramArgs.push_back("-l");
ProgramArgs.push_back(RemoteUser);
}
if (!RemotePort.empty()) {
ProgramArgs.push_back("-p");
ProgramArgs.push_back(RemotePort);
}
if (!RemoteExtra.empty()) {
ProgramArgs.push_back(RemoteExtra);
}
// Full path to the binary. We need to cd to the exec directory because
// there is a dylib there that the exec expects to find in the CWD
char *env_pwd = getenv("PWD");
Exec = "cd ";
Exec += env_pwd;
Exec += "; ./";
Exec += OutputBinary.c_str();
ProgramArgs.push_back(Exec);
}
// Add optional parameters to the running program from Argv
llvm::append_range(ProgramArgs, Args);
// Now that we have a binary, run it!
outs() << "<program>";
outs().flush();
LLVM_DEBUG(
errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = ProgramArgs.size(); i != e; ++i) errs()
<< " " << ProgramArgs[i];
errs() << "\n";);
FileRemover OutputBinaryRemover(OutputBinary.str(), !SaveTemps);
if (RemoteClientPath.empty()) {
LLVM_DEBUG(errs() << "<run locally>");
std::string Error;
int ExitCode = RunProgramWithTimeout(OutputBinary.str(), ProgramArgs,
InputFile, OutputFile, OutputFile,
Timeout, MemoryLimit, &Error);
// Treat a signal (usually SIGSEGV) or timeout as part of the program output
// so that crash-causing miscompilation is handled seamlessly.
if (ExitCode < -1) {
std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);
outFile << Error << '\n';
outFile.close();
}
return ExitCode;
} else {
outs() << "<run remotely>";
outs().flush();
return RunProgramRemotelyWithTimeout(RemoteClientPath, ProgramArgs,
InputFile, OutputFile, OutputFile,
Timeout, MemoryLimit);
}
}
Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
std::string &OutputFile,
const std::vector<std::string> &ArgsForCC) {
SmallString<128> UniqueFilename;
std::error_code EC = sys::fs::createUniqueFile(
InputFile + "-%%%%%%%" + LTDL_SHLIB_EXT, UniqueFilename);
if (EC) {
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
OutputFile = std::string(UniqueFilename);
std::vector<StringRef> CCArgs;
CCArgs.push_back(CCPath);
if (TargetTriple.getArch() == Triple::x86)
CCArgs.push_back("-m32");
for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
E = ccArgs.end();
I != E; ++I)
CCArgs.push_back(*I);
// Compile the C/asm file into a shared object
if (fileType != ObjectFile) {
CCArgs.push_back("-x");
CCArgs.push_back(fileType == AsmFile ? "assembler" : "c");
}
CCArgs.push_back("-fno-strict-aliasing");
CCArgs.push_back(InputFile); // Specify the input filename.
CCArgs.push_back("-x");
CCArgs.push_back("none");
if (TargetTriple.getArch() == Triple::sparc)
CCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc
else if (TargetTriple.isOSDarwin()) {
// link all source files into a single module in data segment, rather than
// generating blocks. dynamic_lookup requires that you set
// MACOSX_DEPLOYMENT_TARGET=10.3 in your env. FIXME: it would be better for
// bugpoint to just pass that in the environment of CC.
CCArgs.push_back("-single_module");
CCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC
CCArgs.push_back("-undefined");
CCArgs.push_back("dynamic_lookup");
} else
CCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others
if (TargetTriple.getArch() == Triple::x86_64)
CCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC
if (TargetTriple.getArch() == Triple::sparc)
CCArgs.push_back("-mcpu=v9");
CCArgs.push_back("-o");
CCArgs.push_back(OutputFile); // Output to the right filename.
CCArgs.push_back("-O2"); // Optimize the program a bit.
// Add any arguments intended for CC. We locate them here because this is
// most likely -L and -l options that need to come before other libraries but
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
llvm::append_range(CCArgs, ArgsForCC);
outs() << "<CC>";
outs().flush();
LLVM_DEBUG(errs() << "\nAbout to run:\t";
for (unsigned i = 0, e = CCArgs.size(); i != e; ++i) errs()
<< " " << CCArgs[i];
errs() << "\n";);
if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
return ProcessFailure(CCPath, CCArgs);
return Error::success();
}
/// create - Try to find the CC executable
///
CC *CC::create(const char *Argv0, std::string &Message,
const std::string &CCBinary,
const std::vector<std::string> *Args) {
auto CCPath = FindProgramByName(CCBinary, Argv0, (void *)(intptr_t)&create);
if (!CCPath) {
Message = "Cannot find `" + CCBinary + "' in PATH: " +
CCPath.getError().message() + "\n";
return nullptr;
}
std::string RemoteClientPath;
if (!RemoteClient.empty()) {
auto Path = sys::findProgramByName(RemoteClient);
if (!Path) {
Message = "Cannot find `" + RemoteClient + "' in PATH: " +
Path.getError().message() + "\n";
return nullptr;
}
RemoteClientPath = *Path;
}
Message = "Found CC: " + *CCPath + "\n";
return new CC(*CCPath, RemoteClientPath, Args);
}

View File

@ -1,190 +0,0 @@
//===-- tools/bugpoint/ToolRunner.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
//
//===----------------------------------------------------------------------===//
//
// This file exposes an abstraction around a platform C compiler, used to
// compile C and assembly code. It also exposes an "AbstractIntepreter"
// interface, which is used to execute code using one of the LLVM execution
// engines.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H
#define LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/TargetParser/Triple.h"
#include <exception>
#include <vector>
namespace llvm {
extern cl::opt<bool> SaveTemps;
extern Triple TargetTriple;
class LLC;
//===---------------------------------------------------------------------===//
// CC abstraction
//
class CC {
std::string CCPath; // The path to the cc executable.
std::string RemoteClientPath; // The path to the rsh / ssh executable.
std::vector<std::string> ccArgs; // CC-specific arguments.
CC(StringRef ccPath, StringRef RemotePath,
const std::vector<std::string> *CCArgs)
: CCPath(std::string(ccPath)), RemoteClientPath(std::string(RemotePath)) {
if (CCArgs)
ccArgs = *CCArgs;
}
public:
enum FileType { AsmFile, ObjectFile, CFile };
static CC *create(const char *Argv0, std::string &Message,
const std::string &CCBinary,
const std::vector<std::string> *Args);
/// ExecuteProgram - Execute the program specified by "ProgramFile" (which is
/// either a .s file, or a .c file, specified by FileType), with the specified
/// arguments. Standard input is specified with InputFile, and standard
/// Output is captured to the specified OutputFile location. The SharedLibs
/// option specifies optional native shared objects that can be loaded into
/// the program for execution.
///
Expected<int> ExecuteProgram(
const std::string &ProgramFile, const std::vector<std::string> &Args,
FileType fileType, const std::string &InputFile,
const std::string &OutputFile,
const std::vector<std::string> &CCArgs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0);
/// MakeSharedObject - This compiles the specified file (which is either a .c
/// file or a .s file) into a shared object.
///
Error MakeSharedObject(const std::string &InputFile, FileType fileType,
std::string &OutputFile,
const std::vector<std::string> &ArgsForCC);
};
//===---------------------------------------------------------------------===//
/// AbstractInterpreter Class - Subclasses of this class are used to execute
/// LLVM bitcode in a variety of ways. This abstract interface hides this
/// complexity behind a simple interface.
///
class AbstractInterpreter {
virtual void anchor();
public:
static LLC *createLLC(const char *Argv0, std::string &Message,
const std::string &CCBinary,
const std::vector<std::string> *Args = nullptr,
const std::vector<std::string> *CCArgs = nullptr,
bool UseIntegratedAssembler = false);
static AbstractInterpreter *
createLLI(const char *Argv0, std::string &Message,
const std::vector<std::string> *Args = nullptr);
static AbstractInterpreter *
createJIT(const char *Argv0, std::string &Message,
const std::vector<std::string> *Args = nullptr);
static AbstractInterpreter *
createCustomCompiler(const char *Argv0, std::string &Message,
const std::string &CompileCommandLine);
static AbstractInterpreter *
createCustomExecutor(const char *Argv0, std::string &Message,
const std::string &ExecCommandLine);
virtual ~AbstractInterpreter() = default;
/// compileProgram - Compile the specified program from bitcode to executable
/// code. This does not produce any output, it is only used when debugging
/// the code generator. It returns false if the code generator fails.
virtual Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
unsigned MemoryLimit = 0) {
return Error::success();
}
/// Compile the specified program from bitcode to code understood by the CC
/// driver (either C or asm). Returns an error if the code generator fails,,
/// otherwise, the type of code emitted.
virtual Expected<CC::FileType> OutputCode(const std::string &Bitcode,
std::string &OutFile,
unsigned Timeout = 0,
unsigned MemoryLimit = 0) {
return make_error<StringError>(
"OutputCode not supported by this AbstractInterpreter!",
inconvertibleErrorCode());
}
/// ExecuteProgram - Run the specified bitcode file, emitting output to the
/// specified filename. This sets RetVal to the exit code of the program or
/// returns an Error if a problem was encountered that prevented execution of
/// the program.
///
virtual Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs = std::vector<std::string>(),
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) = 0;
};
//===---------------------------------------------------------------------===//
// LLC Implementation of AbstractIntepreter interface
//
class LLC : public AbstractInterpreter {
std::string LLCPath; // The path to the LLC executable.
std::vector<std::string> ToolArgs; // Extra args to pass to LLC.
CC *cc;
bool UseIntegratedAssembler;
public:
LLC(const std::string &llcPath, CC *cc, const std::vector<std::string> *Args,
bool useIntegratedAssembler)
: LLCPath(llcPath), cc(cc),
UseIntegratedAssembler(useIntegratedAssembler) {
ToolArgs.clear();
if (Args)
ToolArgs = *Args;
}
~LLC() override { delete cc; }
/// compileProgram - Compile the specified program from bitcode to executable
/// code. This does not produce any output, it is only used when debugging
/// the code generator. Returns false if the code generator fails.
Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
unsigned MemoryLimit = 0) override;
Expected<int> ExecuteProgram(
const std::string &Bitcode, const std::vector<std::string> &Args,
const std::string &InputFile, const std::string &OutputFile,
const std::vector<std::string> &CCArgs = std::vector<std::string>(),
const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
Expected<CC::FileType> OutputCode(const std::string &Bitcode,
std::string &OutFile, unsigned Timeout = 0,
unsigned MemoryLimit = 0) override;
};
/// Find the first executable file \ExeName, either in the user's PATH or,
/// failing that, in the same directory as argv[0]. This allows us to find
/// another LLVM tool if it is built in the same directory. If no executable is
/// found, an error is returned.
ErrorOr<std::string> FindProgramByName(const std::string &ExeName,
const char *Argv0, void *MainAddr);
} // End llvm namespace
#endif

View File

@ -1,181 +0,0 @@
//===- bugpoint.cpp - The LLVM Bugpoint utility ---------------------------===//
//
// 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 program is an automated compiler debugger tool. It is used to narrow
// down miscompilations and crash problems to a specific pass in the compiler,
// and the specific Module or Function input that is causing the problem.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "ToolRunner.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassNameParser.h"
#include "llvm/InitializePasses.h"
#include "llvm/LinkAllIR.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Support/AlwaysTrue.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Valgrind.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
// Enable this macro to debug bugpoint itself.
//#define DEBUG_BUGPOINT 1
using namespace llvm;
static cl::opt<bool>
FindBugs("find-bugs", cl::desc("Run many different optimization sequences "
"on program to find bugs"),
cl::init(false));
static cl::list<std::string>
InputFilenames(cl::Positional, cl::OneOrMore,
cl::desc("<input llvm ll/bc files>"));
static cl::opt<unsigned> TimeoutValue(
"timeout", cl::init(300), cl::value_desc("seconds"),
cl::desc("Number of seconds program is allowed to run before it "
"is killed (default is 300s), 0 disables timeout"));
static cl::opt<int> MemoryLimit(
"mlimit", cl::init(-1), cl::value_desc("MBytes"),
cl::desc("Maximum amount of memory to use. 0 disables check. Defaults to "
"400MB (800MB under valgrind, 0 with sanitizers)."));
static cl::opt<bool>
UseValgrind("enable-valgrind",
cl::desc("Run optimizations through valgrind"));
// The AnalysesList is automatically populated with registered Passes by the
// PassNameParser.
//
static cl::list<const PassInfo *, bool, PassNameParser>
PassList(cl::desc("Passes available:"));
static cl::opt<std::string>
OverrideTriple("mtriple", cl::desc("Override target triple for module"));
/// BugpointIsInterrupted - Set to true when the user presses ctrl-c.
bool llvm::BugpointIsInterrupted = false;
#ifndef DEBUG_BUGPOINT
static void BugpointInterruptFunction() { BugpointIsInterrupted = true; }
#endif
// Hack to capture a pass list.
namespace {
class AddToDriver : public legacy::FunctionPassManager {
BugDriver &D;
public:
AddToDriver(BugDriver &_D) : FunctionPassManager(nullptr), D(_D) {}
void add(Pass *P) override {
const void *ID = P->getPassID();
const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(ID);
D.addPass(std::string(PI->getPassArgument()));
}
};
} // namespace
#define HANDLE_EXTENSION(Ext) \
llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
#include "llvm/Support/Extension.def"
int main(int argc, char **argv) {
#ifndef DEBUG_BUGPOINT
InitLLVM X(argc, argv);
#endif
// Initialize passes
PassRegistry &Registry = *PassRegistry::getPassRegistry();
initializeCore(Registry);
initializeScalarOpts(Registry);
initializeVectorization(Registry);
initializeIPO(Registry);
initializeAnalysis(Registry);
initializeTransformUtils(Registry);
initializeInstCombine(Registry);
initializeTarget(Registry);
if (!llvm::getNonFoldableAlwaysTrue()) {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
}
cl::ParseCommandLineOptions(argc, argv,
"LLVM automatic testcase reducer. See\nhttp://"
"llvm.org/cmds/bugpoint.html"
" for more information.\n");
#ifndef DEBUG_BUGPOINT
sys::SetInterruptFunction(BugpointInterruptFunction);
#endif
LLVMContext Context;
// If we have an override, set it and then track the triple we want Modules
// to use.
if (!OverrideTriple.empty()) {
TargetTriple.setTriple(Triple::normalize(OverrideTriple));
outs() << "Override triple set to '" << TargetTriple.getTriple() << "'\n";
}
if (MemoryLimit < 0) {
// Set the default MemoryLimit. Be sure to update the flag's description if
// you change this.
if (sys::RunningOnValgrind() || UseValgrind)
MemoryLimit = 800;
else
MemoryLimit = 400;
#if (LLVM_ADDRESS_SANITIZER_BUILD || LLVM_MEMORY_SANITIZER_BUILD || \
LLVM_THREAD_SANITIZER_BUILD)
// Starting from kernel 4.9 memory allocated with mmap is counted against
// RLIMIT_DATA. Sanitizers need to allocate tens of terabytes for shadow.
MemoryLimit = 0;
#endif
}
BugDriver D(argv[0], FindBugs, TimeoutValue, MemoryLimit, UseValgrind,
Context);
if (D.addSources(InputFilenames))
return 1;
AddToDriver PM(D);
for (const PassInfo *PI : PassList)
D.addPass(std::string(PI->getPassArgument()));
// Bugpoint has the ability of generating a plethora of core files, so to
// avoid filling up the disk, we prevent it
#ifndef DEBUG_BUGPOINT
sys::Process::PreventCoreFiles();
#endif
// Needed to pull in symbols from statically linked extensions, including static
// registration. It is unused otherwise because bugpoint has no support for
// NewPM.
#define HANDLE_EXTENSION(Ext) \
(void)get##Ext##PluginInfo();
#include "llvm/Support/Extension.def"
if (Error E = D.run()) {
errs() << toString(std::move(E));
return 1;
}
return 0;
}

View File

@ -73,13 +73,6 @@ static codegen::RegisterSaveStatsFlag SSF;
static cl::list<const PassInfo *, bool, PassNameParser> PassList(cl::desc(
"Optimizations available (use \"-passes=\" for the new pass manager)"));
static cl::opt<bool> EnableLegacyPassManager(
"bugpoint-enable-legacy-pm",
cl::desc(
"Enable the legacy pass manager. This is strictly for bugpoint "
"due to it not working with the new PM, please do not use otherwise."),
cl::init(false));
// 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
@ -469,8 +462,8 @@ optMain(int argc, char **argv,
LLVMContext Context;
// TODO: remove shouldForceLegacyPM().
const bool UseNPM = (!EnableLegacyPassManager && !shouldForceLegacyPM()) ||
PassPipeline.getNumOccurrences() > 0;
const bool UseNPM =
!shouldForceLegacyPM() || PassPipeline.getNumOccurrences() > 0;
if (UseNPM && !PassList.empty()) {
errs() << "The `opt -passname` syntax for the new pass manager is "

View File

@ -263,7 +263,6 @@ group("test") {
# lit tests run.
"//llvm/lib/LineEditor",
"//llvm/lib/Testing/Support",
"//llvm/tools/bugpoint",
"//llvm/tools/dsymutil",
"//llvm/tools/llc",
"//llvm/tools/lli",
@ -355,12 +354,6 @@ group("test") {
# its tests, but nothing in the tree depends on them.
"//third-party/unittest/UnitTestMain:gtest_main",
]
if (host_os != "win") {
# loadable_modules don't work on Windows.
# FIXME: In the CMake build, ENABLE_SHARED makes them work somehow
# (but they're off by default there too).
deps += [ "//llvm/tools/bugpoint-passes" ]
}
# FIXME: llvm_build_examples
testonly = true

View File

@ -1,26 +0,0 @@
import("//llvm/utils/gn/build/symbol_exports.gni")
assert(host_os != "win", "loadable modules not supported on win")
symbol_exports("exports") {
exports_file = "bugpoint.exports"
}
loadable_module("bugpoint-passes") {
output_name = "BugpointPasses"
deps = [
":exports",
# BugpointPasses doesn't want to link in any LLVM code, it just
# needs its headers.
"//llvm/include/llvm/IR:public_tablegen",
]
sources = [ "TestPasses.cpp" ]
if (host_os != "mac" && host_os != "win") {
# The GN build currently doesn't globally pass -fPIC, but that's
# needed for building .so files on ELF. Just pass it manually
# for loadable_modules for now.
cflags = [ "-fPIC" ]
}
}