muxc
is a compiler pass pipeline testing tool, similar to opt and veczc.
It takes in a pipeline of passes in textual form, creates a pipeline and runs
those passes on a LLVM IR
module.
It can either work per target or use the default set of utility passes.
Target-specific execution requires the use of --device
/--device-idx
.
Targets can support additional pass parsing and debug names as explained in
Supporting muxc passes in targets.
The tool assumes that all targets provide a compiler interface which creates a
derived class from compiler::BaseModule
when creating the Module
from the
Target
interface.
The LLVM IR
module may passed directly to the tool or may be compiled from
OpenCL C using compiler::BaseModule
’s APIs; this requires a target to work.
Executing muxc
Its most common usage requires the following arguments:
<input_filename>
. Default is'-'
(stdin).--passes <pipeline_text>
- pass pipeline. Optional. Default is""
.-o <output_filename>
. Default is'-'
(stdout).--device <device_name>
or--device-idx <device_idx>
- target-specific device to run on. Optional.
The input can be an LLVM IR module or an OpenCL C kernel (see below). The file
type is automatically worked out by the filename extension, but LLVM IR is
assumed. The input file type can be specifically controlled by passing -x
ir
for IR or -x cl
for OpenCL C.
The --passes <pipeline_text>
option allows passes separated by a comma
(,
). These passes include standard LLVM passes, such as instcombine
.
Parameters for such passes are included inside <>
and generally have
default values. For example, link-builtins
or link-builtins<early>
.
Additionally, several standard LLVM command line arguments for tools such
opt
can be used.
The --device
option is only required if target-specific testing is needed,
either to set the LLVM target triple or to register pass information. This is
also required in order for the tool to accept OpenCL C inputs.
Examples of running on LLVM IR modules:
> # Run instcombine on a textual LLVM module.
> muxc --passes instcombine test.ll
> # Run instcombine,simplifycfg on a bitcode LLVM module,
> # saving the result to a new textual file.
> muxc --passes instcombine,simplifycfg test.bc -o output.ll
> # Run instcombine,simplifycfg on a bitcode LLVM module,
> # saving the result to a new bitcode file.
> muxc --passes instcombine,simplifycfg test.bc --filetype obj -o output.bc
> # Pipe a program from stdin and run no passes on it (pass-through)
> echo 'define i32 @foo() { ret i32 0 }' | muxc
> # Run a refsi-specific compiler pass
> muxc --device "RefSi G1 RV64" --passes ir-to-builtins < test.ll
> # Run a compiler pass with options on the first known device
> muxc --device-idx 0 --passes link-builtins < test.ll
> muxc --device-idx 0 --passes "link-builtins<early>" < test.ll
> # Run instcombine, logging its debug output to stderr (requires a debug
> # build of LLVM).
> muxc --passes instcombine --debug test.ll
Examples of compiling OpenCL C (requires a known device):
> # Compile an OpenCL C kernel to IR, printing it to stdout.
> muxc --device-idx 0 kernel.cl
> # Compile an OpenCL C kernel to IR, providing compiler options
> muxc --device-idx 0 -cl-options '-cl-fast-relaxed-math' kernel.cl
> # Compile an OpenCL C kernel from stdin to IR
> echo 'kernel void foo() { }' | muxc --device-idx 0 -x cl
> # Compile an OpenCL C kernel to IR and run instcombine on it
> muxc --device-idx 0 --passes instcombine kernel.cl
Other useful options include:
--list-devices
- list all known devices.--print-passes
- print available passes that can be specified in--passes=foo
.
All of the passes available to opt
are available as well as those shown
using --print-passes
.
A full list of options can be found by passing --help
to muxc
.
Supporting muxc passes in targets
ComputeMux
targets provide an interface to muxc
through the
PassMachinery
class. Specifically, the interface needed is based off the
virtual function in BaseModule
, createPassMachinery()
. The default for
this in BaseModule
creates a BaseModulePassMachinery
. This implements
the parse and debug name support for the majority of utility passes that the
oneAPI Construction Kit provides. This method should always be overridden even
if a BaseModulePassMachinery
is used so that a TargetMachine
appropriate to the target can be attached to the PassMachinery
during
creation.
In order to extend for target specific passes, the virtual function
createPassMachinery()
should create a PassMachinery
derived from
BaseModulePassMachinery
. The PassMachinery
created should have the
methods registerPassCallbacks
and addClassToPassName
overridden to
support parsing and debug names for the target passes. Note that these methods
should still call the BaseModulePassMachinery
methods in order to ensure that
the util passes are available.