Giter Club home page Giter Club logo

llvm-tutor's Introduction

llvm-tutor

Apple Silicon x86-Ubuntu

Example LLVM passes - based on LLVM 18

llvm-tutor is a collection of self-contained reference LLVM passes. It's a tutorial that targets novice and aspiring LLVM developers. Key features:

  • Out-of-tree - builds against a binary LLVM installation (no need to build LLVM from sources)
  • Complete - includes CMake build scripts, LIT tests, CI set-up and documentation
  • Modern - based on the latest version of LLVM (and updated with every release)

Overview

LLVM implements a very rich, powerful and popular API. However, like many complex technologies, it can be quite daunting and overwhelming to learn and master. The goal of this LLVM tutorial is to showcase that LLVM can in fact be easy and fun to work with. This is demonstrated through a range self-contained, testable LLVM passes, which are implemented using idiomatic LLVM.

This document explains how to set-up your environment, build and run the examples, and go about debugging. It contains a high-level overview of the implemented examples and contains some background information on writing LLVM passes. The source files, apart from the code itself, contain comments that will guide you through the implementation. All examples are complemented with LIT tests and reference input files.

Visit clang-tutor if you are internested in similar tutorial for Clang.

Table of Contents

HelloWorld: Your First Pass

The HelloWorld pass from HelloWorld.cpp is a self-contained reference example. The corresponding CMakeLists.txt implements the minimum set-up for an out-of-source pass.

For every function defined in the input module, HelloWorld prints its name and the number of arguments that it takes. You can build it like this:

export LLVM_DIR=<installation/dir/of/llvm/18>
mkdir build
cd build
cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR <source/dir/llvm/tutor>/HelloWorld/
make

Before you can test it, you need to prepare an input file:

# Generate an LLVM test file
$LLVM_DIR/bin/clang -O1 -S -emit-llvm <source/dir/llvm/tutor>/inputs/input_for_hello.c -o input_for_hello.ll

Finally, run HelloWorld with opt (use libHelloWorld.so on Linux and libHelloWorld.dylib on Mac OS):

# Run the pass
$LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.{so|dylib} -passes=hello-world -disable-output input_for_hello.ll
# Expected output
(llvm-tutor) Hello from: foo
(llvm-tutor)   number of arguments: 1
(llvm-tutor) Hello from: bar
(llvm-tutor)   number of arguments: 2
(llvm-tutor) Hello from: fez
(llvm-tutor)   number of arguments: 3
(llvm-tutor) Hello from: main
(llvm-tutor)   number of arguments: 2

The HelloWorld pass doesn't modify the input module. The -disable-output flag is used to prevent opt from printing the output bitcode file.

Development Environment

Platform Support And Requirements

This project has been tested on Ubuntu 22.04 and Mac OS X 11.7. In order to build llvm-tutor you will need:

  • LLVM 18
  • C++ compiler that supports C++17
  • CMake 3.20 or higher

In order to run the passes, you will need:

  • clang-18 (to generate input LLVM files)
  • opt (to run the passes)

There are additional requirements for tests (these will be satisfied by installing LLVM 18):

  • lit (aka llvm-lit, LLVM tool for executing the tests)
  • FileCheck (LIT requirement, it's used to check whether tests generate the expected output)

Installing LLVM 18 on Mac OS X

On Darwin you can install LLVM 18 with Homebrew:

brew install llvm@18

If you already have an older version of LLVM installed, you can upgrade it to LLVM 18 like this:

brew upgrade llvm

Once the installation (or upgrade) is complete, all the required header files, libraries and tools will be located in /opt/homebrew/opt/llvm/.

Installing LLVM 18 on Ubuntu

On Ubuntu Jammy Jellyfish, you can install modern LLVM from the official repository:

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main"
sudo apt-get update
sudo apt-get install -y llvm-18 llvm-18-dev llvm-18-tools clang-18

This will install all the required header files, libraries and tools in /usr/lib/llvm-18/.

Building LLVM 18 From Sources

Building from sources can be slow and tricky to debug. It is not necessary, but might be your preferred way of obtaining LLVM 18. The following steps will work on Linux and Mac OS X:

git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout release/18.x
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS=clang <llvm-project/root/dir>/llvm/
cmake --build .

For more details read the official documentation.

Building & Testing

Building

You can build llvm-tutor (and all the provided pass plugins) as follows:

cd <build/dir>
cmake -DLT_LLVM_INSTALL_DIR=<installation/dir/of/llvm/18> <source/dir/llvm/tutor>
make

The LT_LLVM_INSTALL_DIR variable should be set to the root of either the installation or build directory of LLVM 18. It is used to locate the corresponding LLVMConfig.cmake script that is used to set the include and library paths.

Testing

In order to run llvm-tutor tests, you need to install llvm-lit (aka lit). It's not bundled with LLVM 18 packages, but you can install it with pip:

# Install lit - note that this installs lit globally
pip install lit

Running the tests is as simple as:

$ lit <build_dir>/test

Voilà! You should see all tests passing.

LLVM Plugins as shared objects

In llvm-tutor every LLVM pass is implemented in a separate shared object (you can learn more about shared objects here). These shared objects are essentially dynamically loadable plugins for opt. All plugins are built in the <build/dir>/lib directory.

Note that the extension of dynamically loaded shared objects differs between Linux and Mac OS. For example, for the HelloWorld pass you will get:

  • libHelloWorld.so on Linux
  • libHelloWorld.dylib on MacOS.

For the sake of consistency, in this README.md file all examples use the *.so extension. When working on Mac OS, use *.dylib instead.

Overview of The Passes

The available passes are categorised as either Analysis, Transformation or CFG. The difference between Analysis and Transformation passes is rather self-explanatory (here is a more technical breakdown). A CFG pass is simply a Transformation pass that modifies the Control Flow Graph. This is frequently a bit more complex and requires some extra bookkeeping, hence a dedicated category.

In the following table the passes are grouped thematically and ordered by the level of complexity.

Name Description Category
HelloWorld visits all functions and prints their names Analysis
OpcodeCounter prints a summary of LLVM IR opcodes in the input module Analysis
InjectFuncCall instruments the input module by inserting calls to printf Transformation
StaticCallCounter counts direct function calls at compile-time (static analysis) Analysis
DynamicCallCounter counts direct function calls at run-time (dynamic analysis) Transformation
MBASub obfuscate integer sub instructions Transformation
MBAAdd obfuscate 8-bit integer add instructions Transformation
FindFCmpEq finds floating-point equality comparisons Analysis
ConvertFCmpEq converts direct floating-point equality comparisons to difference comparisons Transformation
RIV finds reachable integer values for each basic block Analysis
DuplicateBB duplicates basic blocks, requires RIV analysis results CFG
MergeBB merges duplicated basic blocks CFG

Once you've built this project, you can experiment with every pass separately. All passes, except for HelloWorld, are described in more details below.

LLVM passes work with LLVM IR files. You can generate one like this:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Textual form
$LLVM_DIR/bin/clang -O1 -emit-llvm input.c -S -o out.ll
# Binary/bit-code form
$LLVM_DIR/bin/clang -O1 -emit-llvm input.c -c -o out.bc

It doesn't matter whether you choose the binary, *.bc (default), or textual/LLVM assembly form (.ll, requires the -S flag). Obviously, the latter is more human-readable. Similar logic applies to opt - by default it generates *.bc files. You can use -S to have the output written as *.ll files instead.

Note that clang adds the optnone function attribute if either

  • no optimization level is specified, or
  • -O0 is specified.

If you want to compile at -O0, you need to specify -O0 -Xclang -disable-O0-optnone or define a static isRequired method in your pass. Alternatively, you can specify -O1 or higher. Otherwise the new pass manager will register the pass but your pass will not be executed.

As noted earlier, all examples in this file use the *.so extension for pass plugins. When working on Mac OS, use *.dylib instead.

OpcodeCounter

OpcodeCounter is an Analysis pass that prints a summary of the LLVM IR opcodes encountered in every function in the input module. This pass can be run automatically with one of the pre-defined optimisation pipelines. However, let's use our tried and tested method first.

Run the pass

We will use input_for_cc.c to test OpcodeCounter. Since OpcodeCounter is an Analysis pass, we want opt to print its results. To this end, we will use a printing pass that corresponds to OpcodeCounter. This pass is called print<opcode-counter>. No extra arguments are needed, but it's a good idea to add -disable-output to prevent opt from printing the output LLVM IR module - we are only interested in the results of the analysis rather than the module itself. In fact, as this pass does not modify the input IR, the output module would be identical to the input anyway.

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c <source_dir>/inputs/input_for_cc.c -o input_for_cc.bc
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libOpcodeCounter.so --passes="print<opcode-counter>" -disable-output input_for_cc.bc

For main, OpcodeCounter prints the following summary (note that when running the pass, a summary for other functions defined in input_for_cc.bc is also printed):

=================================================
LLVM-TUTOR: OpcodeCounter results for `main`
=================================================
OPCODE               #N TIMES USED
-------------------------------------------------
load                 2
br                   4
icmp                 1
add                  1
ret                  1
alloca               2
store                4
call                 4
-------------------------------------------------

Auto-registration with optimisation pipelines

You can run OpcodeCounter by simply specifying an optimisation level (e.g. -O{1|2|3|s}). This is achieved through auto-registration with the existing optimisation pass pipelines. Note that you still have to specify the plugin file to be loaded:

$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libOpcodeCounter.so --passes='default<O1>' input_for_cc.bc

This is implemented in OpcodeCounter.cpp, on line 106.

InjectFuncCall

This pass is a HelloWorld example for code instrumentation. For every function defined in the input module, InjectFuncCall will add (inject) the following call to printf:

printf("(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n", FuncName, FuncNumArgs)

This call is added at the beginning of each function (i.e. before any other instruction). FuncName is the name of the function and FuncNumArgs is the number of arguments that the function takes.

Run the pass

We will use input_for_hello.c to test InjectFuncCall:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -O0 -emit-llvm -c <source_dir>/inputs/input_for_hello.c -o input_for_hello.bc
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libInjectFuncCall.so --passes="inject-func-call" input_for_hello.bc -o instrumented.bin

This generates instrumented.bin, which is the instrumented version of input_for_hello.bc. In order to verify that InjectFuncCall worked as expected, you can either check the output file (and verify that it contains extra calls to printf) or run it:

$LLVM_DIR/bin/lli instrumented.bin
(llvm-tutor) Hello from: main
(llvm-tutor)   number of arguments: 2
(llvm-tutor) Hello from: foo
(llvm-tutor)   number of arguments: 1
(llvm-tutor) Hello from: bar
(llvm-tutor)   number of arguments: 2
(llvm-tutor) Hello from: foo
(llvm-tutor)   number of arguments: 1
(llvm-tutor) Hello from: fez
(llvm-tutor)   number of arguments: 3
(llvm-tutor) Hello from: bar
(llvm-tutor)   number of arguments: 2
(llvm-tutor) Hello from: foo
(llvm-tutor)   number of arguments: 1

InjectFuncCall vs HelloWorld

You might have noticed that InjectFuncCall is somewhat similar to HelloWorld. In both cases the pass visits all functions, prints their names and the number of arguments. The difference between the two passes becomes quite apparent when you compare the output generated for the same input file, e.g. input_for_hello.c. The number of times Hello from is printed is either:

  • once per every function call in the case of InjectFuncCall, or
  • once per function definition in the case of HelloWorld.

This makes perfect sense and hints how different the two passes are. Whether to print Hello from is determined at either:

  • run-time for InjectFuncCall, or
  • compile-time for HelloWorld.

Also, note that in the case of InjectFuncCall we had to first run the pass with opt and then execute the instrumented IR module in order to see the output. For HelloWorld it was sufficient to run the pass with opt.

StaticCallCounter

The StaticCallCounter pass counts the number of static function calls in the input LLVM module. Static refers to the fact that these function calls are compile-time calls (i.e. visible during the compilation). This is in contrast to dynamic function calls, i.e. function calls encountered at run-time (when the compiled module is run). The distinction becomes apparent when analysing functions calls within loops, e.g.:

  for (i = 0; i < 10; i++)
    foo();

Although at run-time foo will be executed 10 times, StaticCallCounter will report only 1 function call.

This pass will only consider direct functions calls. Functions calls via function pointers are not taken into account.

Run the pass through opt

We will use input_for_cc.c to test StaticCallCounter:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c <source_dir>/inputs/input_for_cc.c -o input_for_cc.bc
# Run the pass through opt
$LLVM_DIR/bin/opt opt -load-pass-plugin <build_dir>/lib/libStaticCallCounter.so -passes="print<static-cc>" -disable-output input_for_cc.bc

You should see the following output:

=================================================
LLVM-TUTOR: static analysis results
=================================================
NAME                 #N DIRECT CALLS
-------------------------------------------------
foo                  3
bar                  2
fez                  1
-------------------------------------------------

Note that in order to print the output, you will have to use the printing pass that corresponds to StaticCallCounter (by passing -passes="print<static-cc>" to opt). We discussed printing passes in more detail here.

Run the pass through static

You can run StaticCallCounter through a standalone tool called static. static is an LLVM based tool implemented in StaticMain.cpp. It is a command line wrapper that allows you to run StaticCallCounter without the need for opt:

<build_dir>/bin/static input_for_cc.bc

It is an example of a relatively basic static analysis tool. Its implementation demonstrates how basic pass management in LLVM works (i.e. it handles that for itself instead of relying on opt).

DynamicCallCounter

The DynamicCallCounter pass counts the number of run-time (i.e. encountered during the execution) function calls. It does so by inserting call-counting instructions that are executed every time a function is called. Only calls to functions that are defined in the input module are counted. This pass builds on top of ideas presented in InjectFuncCall. You may want to experiment with that example first.

Run the pass

We will use input_for_cc.c to test DynamicCallCounter:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c <source_dir>/inputs/input_for_cc.c -o input_for_cc.bc
# Instrument the input file
$LLVM_DIR/bin/opt -load-pass-plugin=<build_dir>/lib/libDynamicCallCounter.so -passes="dynamic-cc" input_for_cc.bc -o instrumented_bin

This generates instrumented.bin, which is the instrumented version of input_for_cc.bc. In order to verify that DynamicCallCounter worked as expected, you can either check the output file (and verify that it contains new call-counting instructions) or run it:

# Run the instrumented binary
$LLVM_DIR/bin/lli  ./instrumented_bin

You will see the following output:

=================================================
LLVM-TUTOR: dynamic analysis results
=================================================
NAME                 #N DIRECT CALLS
-------------------------------------------------
foo                  13
bar                  2
fez                  1
main                 1

DynamicCallCounter vs StaticCallCounter

The number of function calls reported by DynamicCallCounter and StaticCallCounter are different, but both results are correct. They correspond to run-time and compile-time function calls respectively. Note also that for StaticCallCounter it was sufficient to run the pass through opt to have the summary printed. For DynamicCallCounter we had to run the instrumented binary to see the output. This is similar to what we observed when comparing HelloWorld and InjectFuncCall.

Mixed Boolean Arithmetic Transformations

These passes implement mixed boolean arithmetic transformations. Similar transformation are often used in code obfuscation (you may also know them from Hacker's Delight) and are a great illustration of what and how LLVM passes can be used for.

Similar transformations are possible at the source-code level. The relevant Clang plugins are available in clang-tutor.

MBASub

The MBASub pass implements this rather basic expression:

a - b == (a + ~b) + 1

Basically, it replaces all instances of integer sub according to the above formula. The corresponding LIT tests verify that both the formula and that the implementation are correct.

Run the pass

We will use input_for_mba_sub.c to test MBASub:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S <source_dir>/inputs/input_for_mba_sub.c -o input_for_sub.ll
$LLVM_DIR/bin/opt -load-pass-plugin=<build_dir>/lib/libMBASub.so -passes="mba-sub" -S input_for_sub.ll -o out.ll

MBAAdd

The MBAAdd pass implements a slightly more involved formula that is only valid for 8 bit integers:

a + b == (((a ^ b) + 2 * (a & b)) * 39 + 23) * 151 + 111

Similarly to MBASub, it replaces all instances of integer add according to the above identity, but only for 8-bit integers. The LIT tests verify that both the formula and the implementation are correct.

Run the pass

We will use input_for_add.c to test MBAAdd:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -O1 -emit-llvm -S <source_dir>/inputs/input_for_mba.c -o input_for_mba.ll
$LLVM_DIR/bin/opt -load-pass-plugin=<build_dir>/lib/libMBAAdd.so -passes="mba-add" -S input_for_mba.ll -o out.ll

RIV

RIV is an analysis pass that for each basic block BB in the input function computes the set reachable integer values, i.e. the integer values that are visible (i.e. can be used) in BB. Since the pass operates on the LLVM IR representation of the input file, it takes into account all values that have integer type in the LLVM IR sense. In particular, since at the LLVM IR level booleans are represented as 1-bit wide integers (i.e. i1), you will notice that booleans are also included in the result.

This pass demonstrates how to request results from other analysis passes in LLVM. In particular, it relies on the Dominator Tree analysis pass from LLVM, which is used to obtain the dominance tree for the basic blocks in the input function.

Run the pass

We will use input_for_riv.c to test RIV:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_riv.c -o input_for_riv.ll
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libRIV.so -passes="print<riv>" -disable-output input_for_riv.ll

You will see the following output:

=================================================
LLVM-TUTOR: RIV analysis results
=================================================
BB id      Reachable Ineger Values
-------------------------------------------------
BB %entry
             i32 %a
             i32 %b
             i32 %c
BB %if.then
               %add = add nsw i32 %a, 123
               %cmp = icmp sgt i32 %a, 0
             i32 %a
             i32 %b
             i32 %c
BB %if.end8
               %add = add nsw i32 %a, 123
               %cmp = icmp sgt i32 %a, 0
             i32 %a
             i32 %b
             i32 %c
BB %if.then2
               %mul = mul nsw i32 %b, %a
               %div = sdiv i32 %b, %c
               %cmp1 = icmp eq i32 %mul, %div
               %add = add nsw i32 %a, 123
               %cmp = icmp sgt i32 %a, 0
             i32 %a
             i32 %b
             i32 %c
BB %if.else
               %mul = mul nsw i32 %b, %a
               %div = sdiv i32 %b, %c
               %cmp1 = icmp eq i32 %mul, %div
               %add = add nsw i32 %a, 123
               %cmp = icmp sgt i32 %a, 0
             i32 %a
             i32 %b
             i32 %c

Note that in order to print the output, you will have to use the printing pass that corresponds to RIV (by passing -passes="print<riv>" to opt). We discussed printing passes in more detail here.

DuplicateBB

This pass will duplicate all basic blocks in a module, with the exception of basic blocks for which there are no reachable integer values (identified through the RIV pass). An example of such a basic block is the entry block in a function that:

  • takes no arguments and
  • is embedded in a module that defines no global values.

Basic blocks are duplicated by first inserting an if-then-else construct and then cloning all the instructions from the original basic block (with the exception of PHI nodes) into two new basic blocks (clones of the original basic block). The if-then-else construct is introduced as a non-trivial mechanism that decides which of the cloned basic blocks to branch to. This condition is equivalent to:

if (var == 0)
  goto clone 1
else
  goto clone 2

in which:

  • var is a randomly picked variable from the RIV set for the current basic block
  • clone 1 and clone 2 are labels for the cloned basic blocks.

The complete transformation looks like this:

BEFORE:                     AFTER:
-------                     ------
                              [ if-then-else ]
             DuplicateBB           /  \
[ BB ]      ------------>   [clone 1] [clone 2]
                                   \  /
                                 [ tail ]

LEGEND:
-------
[BB]           - the original basic block
[if-then-else] - a new basic block that contains the if-then-else statement (inserted by DuplicateBB)
[clone 1|2]    - two new basic blocks that are clones of BB (inserted by DuplicateBB)
[tail]         - the new basic block that merges [clone 1] and [clone 2] (inserted by DuplicateBB)

As depicted above, DuplicateBB replaces qualifying basic blocks with 4 new basic blocks. This is implemented through LLVM's SplitBlockAndInsertIfThenElse. DuplicateBB does all the necessary preparation and clean-up. In other words, it's an elaborate wrapper for LLVM's SplitBlockAndInsertIfThenElse.

Run the pass

This pass depends on the RIV pass, which also needs be loaded in order for DuplicateBB to work. Let's use input_for_duplicate_bb.c as our sample input. First, generate the LLVM file:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_duplicate_bb.c -o input_for_duplicate_bb.ll

Function foo in input_for_duplicate_bb.ll should look like this (all metadata has been stripped):

define i32 @foo(i32) {
  ret i32 1
}

Note that there's only one basic block (the entry block) and that foo takes one argument (this means that the result from RIV will be a non-empty set). We will now apply DuplicateBB to foo:

$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libRIV.so -load-pass-plugin <build_dir>/lib/libDuplicateBB.so -passes=duplicate-bb -S input_for_duplicate_bb.ll -o duplicate.ll

After the instrumentation foo will look like this (all metadata has been stripped):

define i32 @foo(i32) {
lt-if-then-else-0:
  %2 = icmp eq i32 %0, 0
  br i1 %2, label %lt-if-then-0, label %lt-else-0

clone-1-0:
  br label %lt-tail-0

clone-2-0:
  br label %lt-tail-0

lt-tail-0:
  ret i32 1
}

There are four basic blocks instead of one. All new basic blocks end with a numeric id of the original basic block (0 in this case). lt-if-then-else-0 contains the new if-then-else condition. clone-1-0 and clone-2-0 are clones of the original basic block in foo. lt-tail-0 is the extra basic block that's required to merge clone-1-0 and clone-2-0.

MergeBB

MergeBB will merge qualifying basic blocks that are identical. To some extent, this pass reverts the transformations introduced by DuplicateBB. This is illustrated below:

BEFORE:                     AFTER DuplicateBB:                 AFTER MergeBB:
-------                     ------------------                 --------------
                              [ if-then-else ]                 [ if-then-else* ]
             DuplicateBB           /  \               MergeBB         |
[ BB ]      ------------>   [clone 1] [clone 2]      -------->    [ clone ]
                                   \  /                               |
                                 [ tail ]                         [ tail* ]

LEGEND:
-------
[BB]           - the original basic block
[if-then-else] - a new basic block that contains the if-then-else statement (**DuplicateBB**)
[clone 1|2]    - two new basic blocks that are clones of BB (**DuplicateBB**)
[tail]         - the new basic block that merges [clone 1] and [clone 2] (**DuplicateBB**)
[clone]        - [clone 1] and [clone 2] after merging, this block should be very similar to [BB] (**MergeBB**)
[label*]       - [label] after being updated by **MergeBB**

Recall that DuplicateBB replaces all qualifying basic block with four new basic blocks, two of which are clones of the original block. MergeBB will merge those two clones back together, but it will not remove the remaining two blocks added by DuplicateBB (it will update them though).

Run the pass

Let's use the following IR implementation of foo as input. Note that basic blocks 3 and 5 are identical and can safely be merged:

define i32 @foo(i32) {
  %2 = icmp eq i32 %0, 19
  br i1 %2, label %3, label %5

; <label>:3:
  %4 = add i32 %0,  13
  br label %7

; <label>:5:
  %6 = add i32 %0,  13
  br label %7

; <label>:7:
  %8 = phi i32 [ %4, %3 ], [ %6, %5 ]
  ret i32 %8
}

We will now apply MergeBB to foo:

$LLVM_DIR/bin/opt -load <build_dir>/lib/libMergeBB.so -legacy-merge-bb -S foo.ll -o merge.ll

After the instrumentation foo will look like this (all metadata has been stripped):

define i32 @foo(i32) {
  %2 = icmp eq i32 %0, 19
  br i1 %2, label %3, label %3

3:
  %4 = add i32 %0, 13
  br label %5

5:
  ret i32 %4
}

As you can see, basic blocks 3 and 5 from the input module have been merged into one basic block.

Run MergeBB on the output from DuplicateBB

It is really interesting to see the effect of MergeBB on the output from DuplicateBB. Let's start with the same input as we used for DuplicateBB:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_duplicate_bb.c -o input_for_duplicate_bb.ll

Now we will apply DuplicateBB and MergeBB (in this order) to foo. Recall that DuplicateBB requires RIV, which means that in total we have to load three plugins:

$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libRIV.so -load-pass-plugin <build_dir>/lib/libMergeBB.so -load-pass-plugin <build-dir>/lib/libDuplicateBB.so -passes=duplicate-bb,merge-bb -S input_for_duplicate_bb.ll -o merge_after_duplicate.ll

And here's the output:

define i32 @foo(i32) {
lt-if-then-else-0:
  %1 = icmp eq i32 %0, 0
  br i1 %1, label %lt-clone-2-0, label %lt-clone-2-0

lt-clone-2-0:
  br label %lt-tail-0

lt-tail-0:
  ret i32 1
}

Compare this with the output generated by DuplicateBB. Only one of the clones, lt-clone-2-0, has been preserved, and lt-if-then-else-0 has been updated accordingly. Regardless of the value of of the if condition (more precisely, variable %1), the control flow jumps to lt-clone-2-0.

FindFCmpEq

The FindFCmpEq pass finds all floating-point comparison operations that directly check for equality between two values. This is important because these sorts of comparisons can sometimes be indicators of logical issues due to rounding errors inherent in floating-point arithmetic.

FindFCmpEq is implemented as two passes: an analysis pass (FindFCmpEq) and a printing pass (FindFCmpEqPrinter). The legacy implementation (FindFCmpEqWrapper) makes use of both of these passes.

Run the pass

We will use input_for_fcmp_eq.ll to test FindFCmpEq:

export LLVM_DIR=<installation/dir/of/llvm/18>
# Generate the input file
$LLVM_DIR/bin/clang -emit-llvm -S -Xclang -disable-O0-optnone -c <source_dir>/inputs/input_for_fcmp_eq.c -o input_for_fcmp_eq.ll
# Run the pass
$LLVM_DIR/bin/opt --load-pass-plugin <build_dir>/lib/libFindFCmpEq.so --passes="print<find-fcmp-eq>" -disable-output input_for_fcmp_eq.ll

You should see the following output which lists the direct floating-point equality comparison instructions found:

Floating-point equality comparisons in "sqrt_impl":
  %11 = fcmp oeq double %9, %10
Floating-point equality comparisons in "main":
  %9 = fcmp oeq double %8, 1.000000e+00
  %13 = fcmp oeq double %11, %12
  %19 = fcmp oeq double %17, %18

ConvertFCmpEq

The ConvertFCmpEq pass is a transformation that uses the analysis results of FindFCmpEq to convert direct floating-point equality comparison instructions into logically equivalent ones that use a pre-calculated rounding threshold.

Run the pass

As with FindFCmpEq, we will use input_for_fcmp_eq.ll to test ConvertFCmpEq:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -Xclang -disable-O0-optnone \
  -c <source_dir>/inputs/input_for_fcmp_eq.c -o input_for_fcmp_eq.ll
$LLVM_DIR/bin/opt --load-pass-plugin <build_dir>/lib/libFindFCmpEq.so \
  --load-pass-plugin <build_dir>/lib/libConvertFCmpEq.so \
  --passes=convert-fcmp-eq -S input_for_fcmp_eq.ll -o fcmp_eq_after_conversion.ll

For the legacy implementation, the opt command would be changed to the following:

$LLVM_DIR/bin/opt -load <build_dir>/lib/libFindFCmpEq.so \
  <build_dir>/lib/libConvertFCmpEq.so -convert-fcmp-eq \
  -S input_for_fcmp_eq.ll -o fcmp_eq_after_conversion.ll

Notice that both libFindFCmpEq.so and libConvertFCmpEq.so must be loaded -- and the load order matters. Since ConvertFCmpEq requires FindFCmpEq, its library must be loaded before ConvertFCmpEq. If both passes were built as part of the same library, this would not be required.

After transformation, both fcmp oeq instructions will have been converted to difference based fcmp olt instructions using the IEEE 754 double-precision machine epsilon constant as the round-off threshold:

  %cmp = fcmp oeq double %0, %1

... has now become

  %3 = fsub double %0, %1
  %4 = bitcast double %3 to i64
  %5 = and i64 %4, 9223372036854775807
  %6 = bitcast i64 %5 to double
  %cmp = fcmp olt double %6, 0x3CB0000000000000

The values are subtracted from each other and the absolute value of their difference is calculated. If this absolute difference is less than the value of the machine epsilon, the original two floating-point values are considered to be equal.

Debugging

Before running a debugger, you may want to analyze the output from LLVM_DEBUG and STATISTIC macros. For example, for MBAAdd:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_mba.c -o input_for_mba.ll
$LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll -debug-only=mba-add -stats -o out.ll

Note the -debug-only=mba-add and -stats flags in the command line - that's what enables the following output:

  %12 = add i8 %1, %0 ->   <badref> = add i8 111, %11
  %20 = add i8 %12, %2 ->   <badref> = add i8 111, %19
  %28 = add i8 %20, %3 ->   <badref> = add i8 111, %27
===-------------------------------------------------------------------------===
                          ... Statistics Collected ...
===-------------------------------------------------------------------------===

3 mba-add - The # of substituted instructions

As you can see, you get a nice summary from MBAAdd. In many cases this will be sufficient to understand what might be going wrong. Note that for these macros to work you need a debug build of LLVM (i.e. opt) and llvm-tutor (i.e. use -DCMAKE_BUILD_TYPE=Debug instead of -DCMAKE_BUILD_TYPE=Release).

For tricker issues just use a debugger. Below I demonstrate how to debug MBAAdd. More specifically, how to set up a breakpoint on entry to MBAAdd::run. Hopefully that will be sufficient for you to start.

Mac OS X

The default debugger on OS X is LLDB. You will normally use it like this:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_mba.c -o input_for_mba.ll
lldb -- $LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.dylib -passes=mba-add input_for_mba.ll -o out.ll
(lldb) breakpoint set --name MBAAdd::run
(lldb) process launch

or, equivalently, by using LLDBs aliases:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_mba.c -o input_for_mba.ll
lldb -- $LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.dylib -passes=mba-add input_for_mba.ll -o out.ll
(lldb) b MBAAdd::run
(lldb) r

At this point, LLDB should break at the entry to MBAAdd::run.

Ubuntu

On most Linux systems, GDB is the most popular debugger. A typical session will look like this:

export LLVM_DIR=<installation/dir/of/llvm/18>
$LLVM_DIR/bin/clang -emit-llvm -S -O1 <source_dir>/inputs/input_for_mba.c -o input_for_mba.ll
gdb --args $LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll -o out.ll
(gdb) b MBAAdd.cpp:MBAAdd::run
(gdb) r

At this point, GDB should break at the entry to MBAAdd::run.

Analysis vs Transformation Pass

The implementation of a pass depends on whether it is an Analysis or a Transformation pass:

This is one of the key characteristics of the New Pass Managers - it makes the split into Analysis and Transformation passes very explicit. An Analysis pass requires a bit more bookkeeping and hence a bit more code. For example, you need to add an instance of AnalysisKey so that it can be identified by the New Pass Manager.

Note that for small standalone examples, the difference between Analysis and Transformation passes becomes less relevant. HelloWorld is a good example. It does not transform the input module, so in practice it is an Analysis pass. However, in order to keep the implementation as simple as possible, I used the API for Transformation passes.

Within llvm-tutor the following passes can be used as reference Analysis and Transformation examples:

Other examples also adhere to LLVM's convention, but may contain other complexities. However, only in the case of HelloWorld simplicity was favoured over strictness (i.e. it is neither a transformation nor analysis pass).

Printing passes for the new pass manager

A printing pass for an Analysis pass is basically a Transformation pass that:

  • requests the results of the analysis from the original pass, and
  • prints these results.

In other words, it's just a wrapper pass. There's a convention to register such passes under the print<analysis-pass-name> command line option.

Dynamic vs Static Plugins

By default, all examples in llvm-tutor are built as dynamic plugins. However, LLVM provides infrastructure for both dynamic and static plugins (documentation). Static plugins are simply libraries linked into your executable (e.g. opt) statically. This way, unlike dynamic plugins, they don't require to be loaded at runtime with -load-pass-plugin.

Static plugins are normally developed in-tree, i.e. within llvm-project/llvm, and all examples in llvm-tutor can be adapted to work this way. You can use static_registation.sh to see it can be done for MBASub. This script will:

  • copy the required source and test files into llvm-project/llvm
  • adapt in-tree CMake scripts so that the in-tree version of MBASub is actually built
  • remove -load and -load-pass-plugin from the in-tree tests for MBASub

Note that this script will modify llvm-project/llvm, but leave llvm-tutor intact. After running the script you will have to re-build opt. Two additional CMake flags have to be set: LLVM_BUILD_EXAMPLES and LLVM_MBASUB_LINK_INTO_TOOLS:

# LLVM_TUTOR_DIR: directory in which you cloned llvm-tutor
cd $LLVM_TUTOR_DIR
# LLVM_PROJECT_DIR: directory in which you cloned llvm-project
bash utils/static_registration.sh --llvm_project_dir $LLVM_PROJECT_DIR
# LLVM_BUILD_DIR: directory in which you previously built opt
cd $LLVM_BUILD_DIR
cmake -DLLVM_BUILD_EXAMPLES=On -DLLVM_MBASUB_LINK_INTO_TOOLS=On .
cmake --build . --target opt

Once opt is re-built, MBASub will be statically linked into opt. Now you can run it like this:

$LLVM_BUILD_DIR/bin/opt --passes=mba-sub -S $LLVM_TUTOR_DIR/test/MBA_sub.ll

Note that this time we didn't have to use -load-pass-plugin to load MBASub. If you want to dive deeper into the required steps for static registration, you can scan static_registation.sh or run:

cd $LLVM_PROJECT_DIR
git diff
git status

This will print all the changes within llvm-project/llvm introduced by the script.

Optimisation Passes Inside LLVM

Apart from writing your own transformations an analyses, you may want to familiarize yourself with the passes available within LLVM. It is a great resource for learning how LLVM works and what makes it so powerful and successful. It is also a great resource for discovering how compilers work in general. Indeed, many of the passes implement general concepts known from the theory of compiler development.

The list of the available passes in LLVM can be a bit daunting. Below is a list of the selected few that are a good starting point. Each entry contains a link to the implementation in LLVM, a short description and a link to test files available within llvm-tutor. These test files contain a collection of annotated test cases for the corresponding pass. The goal of these tests is to demonstrate the functionality of the tested pass through relatively simple examples.

Name Description Test files in llvm-tutor
dce Dead Code Elimination dce.ll
memcpyopt Optimise calls to memcpy (e.g. replace them with memset) memcpyopt.ll
reassociate Reassociate (e.g. 4 + (x + 5) -> x + (4 + 5)). This enables further optimisations, e.g. LICM. reassociate.ll
always-inline Always inlines functions decorated with alwaysinline always-inline.ll
loop-deletion Delete unused loops loop-deletion.ll
licm Loop-Invariant Code Motion (a.k.a. LICM) licm.ll
slp Superword-level parallelism vectorisation slp_x86.ll, slp_aarch64.ll

This list focuses on LLVM's transform passes that are relatively easy to demonstrate through small, standalone examples. You can ran an individual test like this:

lit <source/dir/llvm/tutor>/test/llvm/always-inline.ll

To run an individual pass, extract one RUN line from the test file and run it:

$LLVM_DIR/bin/opt -inline-threshold=0 -passes=always-inline -S <source/dir/llvm/tutor>/test/llvm/always-inline.ll

References

Below is a list of LLVM resources available outside the official online documentation that I have found very helpful. Where possible, the items are sorted by date.

  • LLVM IR
    • ”LLVM IR Tutorial-Phis,GEPs and other things, ohmy!”, V.Bridgers, F. Piovezan, EuroLLVM, (slides, video)
    • "Mapping High Level Constructs to LLVM IR", M. Rodler (link)
  • Examples in LLVM
  • LLVM Pass Development
    • "Writing an LLVM Optimization", Jonathan Smith video
    • "Getting Started With LLVM: Basics ", J. Paquette, F. Hahn, LLVM Dev Meeting 2019 video
    • "Writing an LLVM Pass: 101", A. Warzyński, LLVM Dev Meeting 2019 video
    • "Writing LLVM Pass in 2018", Min-Yih Hsu blog
    • "Building, Testing and Debugging a Simple out-of-tree LLVM Pass" Serge Guelton, Adrien Guinet, LLVM Dev Meeting 2015 (slides, video)
  • LLVM Based Tools Development
    • "Introduction to LLVM", M. Shah, Fosdem 2018, link
    • "Building an LLVM-based tool. Lessons learned", A. Denisov, blog, video

Credits

This is first and foremost a community effort. This project wouldn't be possible without the amazing LLVM online documentation, the plethora of great comments in the source code, and the llvm-dev mailing list. Thank you!

It goes without saying that there's plenty of great presentations on YouTube, blog posts and GitHub projects that cover similar subjects. I've learnt a great deal from them - thank you all for sharing! There's one presentation/tutorial that has been particularly important in my journey as an aspiring LLVM developer and that helped to democratise out-of-source pass development:

  • "Building, Testing and Debugging a Simple out-of-tree LLVM Pass" Serge Guelton, Adrien Guinet (slides, video)

Adrien and Serge came up with some great, illustrative and self-contained examples that are great for learning and tutoring LLVM pass development. You'll notice that there are similar transformation and analysis passes available in this project. The implementations available here reflect what I found most challenging while studying them.

License

The MIT License (MIT)

Copyright (c) 2019 Andrzej Warzyński

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

llvm-tutor's People

Contributors

asuka39 avatar banach-space avatar cabreraam avatar caojoshua avatar diffeo-christopher avatar ekilmer avatar fantasquex avatar finleytang avatar gannimo avatar hftrader avatar jvstech avatar khei4 avatar kippesp avatar lakshyaaagrawal avatar lanza avatar miguelraz avatar orestisfl avatar proydakov avatar qbojj avatar randoruf avatar rj-jesus avatar smallkirby avatar tannewt avatar v4kst1z avatar wyjw avatar x14ngch3n avatar xgupta avatar yuuuuuuuuy avatar zeroc0077 avatar zyfeng-go avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

llvm-tutor's Issues

Please add more detail about how to use gdb to debug pass

I follow your guide about how to debug using gdb on ubuntu. But my gdb cannot see the source file.
I using following command:

clang -emit-llvm -S -O1 input_for_mba.c -o input_for_mba.ll
gdb --args opt -S -load-pass-plugin ../build/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll
b ../lib/MBAAdd.cpp:MBAAdd::run

gdb said: No source file named ../lib/MBAAdd.cpp.

(gdb) info source
No current source file.

Please help me. thanks a lot

opt: Unknown command line argument '-debug-only=mba-add'.

I'm using macOS, and I install LLVM with brew.

$ llvm-config --version
10.0.0

In debugging:

Without -debug-only=mba-add -stats:

opt -load-pass-plugin build/lib/libMBAAdd.dylib -passes=mba-add input_for_mba.ll -S -o input_for_mba.transformed.ll

It generates input_for_mba.transformed.ll with no error/warning.

However, with -debug-only=mba-add -stats:

opt -load-pass-plugin build/lib/libMBAAdd.dylib -passes=mba-add -debug-only=mba-add -stats input_for_mba.ll -S -o input_for_mba.transformed.ll 

Output:

opt: Unknown command line argument '-debug-only=mba-add'.  Try: 'opt --help'
opt: Did you mean '--debug-pass=mba-add'?

When I used --debug-pass=mba-add:

opt -load-pass-plugin build/lib/libMBAAdd.dylib -passes=mba-add input_for_mba.ll --debug-pass=mba-add -stats -S -o input_for_mba.transformed.ll 

Output:

opt: for the --debug-pass option: Cannot find option named 'mba-add'!

Building HelloWorld pass on windows under visual studio 2019

Hello, i build LLVM on windows under Visual Studio 2019 with the config like this:

cmake -S llvm\llvm -B build -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=On -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=X86 -Thost=x64

C:\passes\december\HelloWorld>llvm-config --version
14.0.0git

now i download this tutor and want to compile it, i do:

C:\passes\december\HelloWorld>cmake -Bbuild -DLT_LLVM_INSTALL_DIR="C:\Program Files (x86)\LLVM"
-- Building for: Visual Studio 16 2019
-- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19043.
-- The C compiler identification is MSVC 19.29.30137.0
-- The CXX compiler identification is MSVC 19.29.30137.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
CMake Error in CMakeLists.txt:
  IMPORTED_IMPLIB not set for imported target "opt" configuration "Debug".


CMake Error in CMakeLists.txt:
  IMPORTED_IMPLIB not set for imported target "opt" configuration "Release".


CMake Error in CMakeLists.txt:
  IMPORTED_IMPLIB not set for imported target "opt" configuration
  "MinSizeRel".


CMake Error in CMakeLists.txt:
  IMPORTED_IMPLIB not set for imported target "opt" configuration
  "RelWithDebInfo".


-- Generating done
CMake Generate step failed.  Build files cannot be regenerated correctly.

As you can see it produces weird result, i looked solution in this repository and found one suggestion to change CMakeLists.txt:

add_llvm_library( HelloWorld SHARED BUILDTREE_ONLY
  HelloWorld.cpp

  DEPENDS
  intrinsics_gen
  PLUGIN_TOOL
  opt
  )

Changed MODULE to SHARED works it produced build files

cd build
cmake --build . --config Release --target ALL_BUILD

this produced HelloWorld.dll.

Now i want to use this and do:
opt -load-pass-plugin="C:\passes\december\HelloWorld\build\Release\HelloWorld.dll" -passes="hello-world" -disable-output Source.bc
And get output

C:\passes\hello>opt -load-pass-plugin="C:\passes\december\HelloWorld\build\Release\HelloWorld.dll" -passes="hello-world" -disable-output Source.bc
Failed to load passes from 'C:\passes\december\HelloWorld\build\Release\HelloWorld.dll'. Request ignored.
Expected<T> must be checked before access or destruction.
Unchecked Expected<T> contained error:
Plugin entry point not found in 'C:\passes\december\HelloWorld\build\Release\HelloWorld.dll'. Is this a legacy plugin?PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Stack dump:
0.      Program arguments: opt -load-pass-plugin=C:\\passes\\december\\HelloWorld\\build\\Release\\HelloWorld.dll -passes=hello-world -disable-output Source.bc
#0 0x00007ff71ae82935 C:\Program Files (x86)\LLVM\bin\opt.exe 0x1442935 (C:\Program Files (x86)\LLVM\bin\opt.exe+0x1442935)
#1 0x00007ff71ae82935
#2 0x00007ff71ae82935 (C:\Program Files (x86)\LLVM\bin\opt.exe+0x1442935)
#3 0x00007ff884fc1881 C:\Program Files (x86)\LLVM\bin\opt.exe 0x78b21 C:\Program Files (x86)\LLVM\bin\opt.exe 0x7dd5c
#4 0x00007ff884fc1881 C:\Program Files (x86)\LLVM\bin\opt.exe 0x8a7f0 C:\Program Files (x86)\LLVM\bin\opt.exe 0x1e0a600
#5 0x00007ff884fc1881 (C:\Windows\System32\ucrtbase.dll+0x71881)
#6 0x00007ff884fc2851 (C:\Windows\System32\ucrtbase.dll+0x72851)
0x00007FF71AE82935 (0x00007974991A16CF 0x0000000000000076 0x0000000000000016 0x00007FF71AE82930), HandleAbort() + 0x5 bytes(s)
0x00007FF884FC1881 (0x000001C9DC584301 0x0000000000000000 0x0000000000000000 0x000000500C58CEC0), raise() + 0x1E1 bytes(s)
0x00007FF884FC2851 (0x0000005000000003 0x0000005000000003 0x0000000000000000 0x000001C9DC5A2DB0), abort() + 0x31 bytes(s)
0x00007FF719AB8B21 (0x000001C9DC5A2DB0 0x000000500C58CFF0 0x0000000000000000 0x000001C9DC5A2DB0), ?erase@?$SmallPtrSetImpl@PEAVLiveInterval@llvm@@@llvm@@QEAA_NPEAVLiveInterval@2@@Z() + 0x111 bytes(s)
0x00007FF719ABDD5C (0x0000000000000000 0x0000000000000000 0x000000500C58E920 0x0000000000000000), ?run@?$AnalysisPassModel@VFunction@llvm@@VAAManager@2@VPreservedAnalyses@2@VInvalidator@?$AnalysisManager@VFunction@llvm@@$$V@2@$$V@detail@llvm@@UEAA?AV?$unique_ptr@U?$AnalysisResultConcept@VFunction@llvm@@VPreservedAnalyses@2@VInvalidator@?$AnalysisManager@VFunction@llvm@@$$V@2@@detail@llvm@@U?$default_delete@U?$AnalysisResultConcept@VFunction@llvm@@VPreservedAnalyses@2@VInvalidator@?$AnalysisManager@VFunction@llvm@@$$V@2@@detail@llvm@@@std@@@std@@AEAVFunction@3@AEAV?$Analys() + 0x28FC bytes(s)
0x00007FF719ACA7F0 (0x0000000000000000 0x0000000000000000 0x000001C9DC554DA0 0x0000000000000000), ?assign@?$SmallVectorImpl@PEBVValue@llvm@@@llvm@@QEAAX_KPEBVValue@2@@Z() + 0x4780 bytes(s)
0x00007FF71B84A600 (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), ?identify_magic@llvm@@YA?AVerror_code@std@@AEBVTwine@1@AEAUfile_magic@1@@Z() + 0x1360 bytes(s)
0x00007FF887597034 (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), BaseThreadInitThunk() + 0x14 bytes(s)
0x00007FF887882651 (0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000), RtlUserThreadStart() + 0x21 bytes(s)

I checked HelloWord.dll and main entry point is DllEntryPoint i assume i need llvmGetPassPluginInfo as exported function?? How do you get this to work

AttributeError: 'NoneType' object has no attribute 'use_lit_shell'

image

@banach-space I followed the instructions in the README and proceeded with the operation.
First,

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main"
sudo apt-get update
sudo apt-get install -y llvm-16 llvm-16-dev llvm-16-tools clang-16

Second,

cd /root/llvm-tutor/build/
cmake -DLT_LLVM_INSTALL_DIR=/usr/lib/llvm-16 /root/llvm-tutor
make

Third

root@LAPTOP-OJLO0M82:~/llvm-tutor/build# ls
CMakeCache.txt  CMakeFiles  Makefile  cmake_install.cmake  input_for_hello.ll
root@LAPTOP-OJLO0M82:~/llvm-tutor/build# lit --version
lit 16.0.6
root@LAPTOP-OJLO0M82:~/llvm-tutor/build# lit ../test
lit: /usr/local/lib/python3.10/dist-packages/lit/TestingConfig.py:140: fatal: unable to parse config file '/root/llvm-tutor/test/lit.cfg.py', traceback: Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/lit/TestingConfig.py", line 129, in load_from_path
    exec(compile(data, path, 'exec'), cfg_globals, None)
  File "/root/llvm-tutor/test/lit.cfg.py", line 22, in <module>
    config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
AttributeError: 'NoneType' object has no attribute 'use_lit_shell'

how can I run pass on specific functions?

hello,
I have a question.

  1. is there any way to run the function pass(runOnFunction) on a specific function that gets the function name from the user? I mean can I pass some input to runOnFunction?

Thanks a lot

【HELP】Linking with Multiple Modules/Files

This may not be a bug, but I am curious about how to link multiple modules.

I found the linker reports duplicate symbols (like some global variables ResultFormatStrIR, printf_wrapper) when I was trying to link two c files together.

C Code

The file foo.c

#include <stdio.h>
void bar(); 
void foo(){
    bar(); 
    printf("foo\n");
}
int main(){
    foo(); 
}

The file bar.c

#include <stdio.h>
void bar(){
    printf("bar\n");
}

and generate the LLVM IR

clang -emit-llvm -S foo.c -o foo.ll
clang -emit-llvm -S bar.c -o bar.ll

Using Opt to Load Pass

opt -enable-new-pm=0 -S -load libDynamicCallCounter.dylib -legacy-dynamic-cc foo.ll -o foo_opt.ll
opt -enable-new-pm=0 -S -load libDynamicCallCounter.dylib -legacy-dynamic-cc bar.ll -o bar_opt.ll

Compiling and Linking

llc foo_opt.ll 
llc bar_opt.ll 
clang bar_opt.s foo_opt.s -o foo

and the error

duplicate symbol '_printf_wrapper' in:
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/bar_opt-eb2385.o
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/foo_opt-8688fa.o
duplicate symbol '_ResultFormatStrIR' in:
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/bar_opt-eb2385.o
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/foo_opt-8688fa.o
duplicate symbol '_ResultHeaderStrIR' in:
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/bar_opt-eb2385.o
    /var/folders/c8/q41g17kj78j1t6_4wrf0py2w0000gn/T/foo_opt-8688fa.o
ld: 3 duplicate symbols for architecture x86_64
clang-13: error: linker command failed with exit code 1 (use -v to see invocation)

How to add a plugin when compiling an entire codebase

Related

I am trying to instrument all function calls in an existing codebase.

I don't want to have to modify existing makefile scripts too extensively.


The solution is to use the legacy pass manager.

clang++ -flegacy-pass-manager -Xclang -load -Xclang ./libmypass.so input.cpp

It seems that the new pass manager doesn't allow this yet.

You must take care to choose the correct extension point. See here are the extension points. See [here] (https://github.com/rdadolf/clangtool/blob/353b80061ce30c3062bbe4752dbaa2a1c84cc9c8/clangtool.cpp#L42-L49) for an explanation of which ones to use.


This information should be added to the readme.md.

dynamic-cc instrumented prints nothing

I'm using llvm 13.0.1
After being instrumented by dynamic-cc, running the program in lli prints nothing.
But when I run the same program in lli 15.0.0, it prints as expected.

Anybody experienced same issue?

how to generate LLVM IR files for a whole project?

Hello
Thank you for your useful repository.
I have a question about generating .ll files, you used this command:

$LLVM_DIR/bin/clang -S -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll

The question is how to generate this for all source files of a project, also each file has some included header files.
for example how to generate LLVM IR for all of the source files in Libgd?
I have tried this way but the result was almost empty:

$LLVM_DIR/clang -emit-llvm -S -O0 src/gd_tiff.c -o src/gd_tiff.ll

Thank you very much.

How to instrument call to function that needs library

I want to instrument a call to a function every time a program calls a function.

I've started from the lib/InjectFuncCall.cpp example, but my custom function has type int32 func (int32).

I've managed to make it all compile, but I'm getting a segfault. This is my patch:

diff --git a/lib/InjectFuncCall.cpp b/lib/InjectFuncCall.cpp
index 72dde08..8d6c0b7 100644
--- a/lib/InjectFuncCall.cpp
+++ b/lib/InjectFuncCall.cpp
@@ -47,39 +47,27 @@ bool InjectFuncCall::runOnModule(Module &M) {
   bool InsertedAtLeastOnePrintf = false;
 
   auto &CTX = M.getContext();
-  PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX));
 
   // STEP 1: Inject the declaration of printf
   // ----------------------------------------
   // Create (or _get_ in cases where it's already available) the following
   // declaration in the IR module:
-  //    declare i32 @printf(i8*, ...)
+  //    declare i32 @func(i32)
   // It corresponds to the following C declaration:
-  //    int printf(char *, ...)
+  //    int func(int)
   FunctionType *PrintfTy = FunctionType::get(
       IntegerType::getInt32Ty(CTX),
-      PrintfArgTy,
-      /*IsVarArgs=*/true);
+      IntegerType::getInt32Ty(CTX),
+      /*IsVarArgs=*/false);
 
-  FunctionCallee Printf = M.getOrInsertFunction("printf", PrintfTy);
+  FunctionCallee Printf = M.getOrInsertFunction("func", PrintfTy);
 
   // Set attributes as per inferLibFuncAttributes in BuildLibCalls.cpp
   Function *PrintfF = dyn_cast<Function>(Printf.getCallee());
   PrintfF->setDoesNotThrow();
-  PrintfF->addParamAttr(0, Attribute::NoCapture);
-  PrintfF->addParamAttr(0, Attribute::ReadOnly);
-
-
-  // STEP 2: Inject a global variable that will hold the printf format string
-  // ------------------------------------------------------------------------
-  llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString(
-      CTX, "(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n");
 
-  Constant *PrintfFormatStrVar =
-      M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType());
-  dyn_cast<GlobalVariable>(PrintfFormatStrVar)->setInitializer(PrintfFormatStr);
 
-  // STEP 3: For each function in the module, inject a call to printf
+  // STEP 2: For each function in the module, inject a call to func
   // ----------------------------------------------------------------
   for (auto &F : M) {
     if (F.isDeclaration())
@@ -88,22 +76,13 @@ bool InjectFuncCall::runOnModule(Module &M) {
     // Get an IR builder. Sets the insertion point to the top of the function
     IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt());
 
-    // Inject a global variable that contains the function name
-    auto FuncName = Builder.CreateGlobalStringPtr(F.getName());
-
-    // Printf requires i8*, but PrintfFormatStrVar is an array: [n x i8]. Add
-    // a cast: [n x i8] -> i8*
-    llvm::Value *FormatStrPtr =
-        Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr");
-
     // The following is visible only if you pass -debug on the command line
     // *and* you have an assert build.
-    LLVM_DEBUG(dbgs() << " Injecting call to printf inside " << F.getName()
+    LLVM_DEBUG(dbgs() << " Injecting call to func inside " << F.getName()
                       << "\n");
 
-    // Finally, inject a call to printf
-    Builder.CreateCall(
-        Printf, {FormatStrPtr, FuncName, Builder.getInt32(F.arg_size())});
+    // Finally, inject a call to func
+    Builder.CreateCall(Printf, {Builder.getInt32(F.arg_size())});
 
     InsertedAtLeastOnePrintf = true;
   }

At present, func just does a printf, but, ideally, I'd want it to call a function defined in an auxiliary library linked with some flag -laux.

Calling pass from Clang

When you call opt directly you use --passes and the list of desired passes, but in some older code I've seen you could call from clang with -mllvm followed by some additional options added directly to PassManagerBuilder in the form of cl::opt items. This was also how you could then pass along additional options to the pass. This code was using the legacy pass manager. What is the current way to specify the passes and options when using clang?

The RIV pass doesn't show anything

Hello Andrzej,

I tried HelloWorld on macOS, and it works fine. But RIV doesn't show anything.

The commands I used:

export LLVM_DIR=/usr/local/opt/llvm
git clone https://github.com/banach-space/llvm-tutor
cd llvm-tutor
export TUTOR_DIR=$(pwd)

mkdir build
cd build
cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../
make

$LLVM_DIR/bin/clang -emit-llvm -S -O1 $TUTOR_DIR/inputs/input_for_riv.c -o input_for_riv.ll
$LLVM_DIR/bin/opt -load ./lib/libRIV.dylib -legacy-riv input_for_riv.ll

I got this:

WARNING: You're attempting to print out a bitcode file.
This is inadvisable as it may cause display problems. If
you REALLY want to taste LLVM bitcode first-hand, you
can force output with the `-f' option.

Then I used -disable-output, but got nothing on stdout.

$LLVM_DIR/bin/opt -load ./lib/libRIV.dylib -legacy-riv -disable-output input_for_riv.ll

I guess there are some problems with pass registration because when I changed the code (using scaffolding codes for pass registration in HelloWorld) I was able to make it work.

Thanks.

MBASub doesn't seem to change the ll files

input_for_mba_sub.ll

; ModuleID = '../inputs/input_for_mba_sub.c'
source_filename = "../inputs/input_for_mba_sub.c"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx13.0.0"

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @main(i32 %0, i8** %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca i32, align 4
  %7 = alloca i32, align 4
  %8 = alloca i32, align 4
  %9 = alloca i32, align 4
  %10 = alloca i32, align 4
  %11 = alloca i32, align 4
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %12 = load i8**, i8*** %5, align 8
  %13 = getelementptr inbounds i8*, i8** %12, i64 1
  %14 = load i8*, i8** %13, align 8
  %15 = call i32 @atoi(i8* %14)
  store i32 %15, i32* %6, align 4
  %16 = load i8**, i8*** %5, align 8
  %17 = getelementptr inbounds i8*, i8** %16, i64 2
  %18 = load i8*, i8** %17, align 8
  %19 = call i32 @atoi(i8* %18)
  store i32 %19, i32* %7, align 4
  %20 = load i8**, i8*** %5, align 8
  %21 = getelementptr inbounds i8*, i8** %20, i64 3
  %22 = load i8*, i8** %21, align 8
  %23 = call i32 @atoi(i8* %22)
  store i32 %23, i32* %8, align 4
  %24 = load i8**, i8*** %5, align 8
  %25 = getelementptr inbounds i8*, i8** %24, i64 4
  %26 = load i8*, i8** %25, align 8
  %27 = call i32 @atoi(i8* %26)
  store i32 %27, i32* %9, align 4
  %28 = load i32, i32* %6, align 4
  %29 = load i32, i32* %7, align 4
  %30 = sub nsw i32 %28, %29
  store i32 %30, i32* %10, align 4
  %31 = load i32, i32* %8, align 4
  %32 = load i32, i32* %9, align 4
  %33 = sub nsw i32 %31, %32
  store i32 %33, i32* %11, align 4
  %34 = load i32, i32* %10, align 4
  %35 = load i32, i32* %11, align 4
  %36 = sub nsw i32 %34, %35
  ret i32 %36
}

declare i32 @atoi(i8*) #1

attributes #0 = { noinline nounwind optnone ssp uwtable "darwin-stkchk-strong-link" "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }
attributes #1 = { "darwin-stkchk-strong-link" "frame-pointer"="all" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 1]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Apple clang version 14.0.0 (clang-1400.0.29.202)"}

out.ll

; ModuleID = 'input_for_mba_sub.ll'
source_filename = "../inputs/input_for_mba_sub.c"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx13.0.0"

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @main(i32 %0, i8** %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca i32, align 4
  %7 = alloca i32, align 4
  %8 = alloca i32, align 4
  %9 = alloca i32, align 4
  %10 = alloca i32, align 4
  %11 = alloca i32, align 4
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %12 = load i8**, i8*** %5, align 8
  %13 = getelementptr inbounds i8*, i8** %12, i64 1
  %14 = load i8*, i8** %13, align 8
  %15 = call i32 @atoi(i8* %14)
  store i32 %15, i32* %6, align 4
  %16 = load i8**, i8*** %5, align 8
  %17 = getelementptr inbounds i8*, i8** %16, i64 2
  %18 = load i8*, i8** %17, align 8
  %19 = call i32 @atoi(i8* %18)
  store i32 %19, i32* %7, align 4
  %20 = load i8**, i8*** %5, align 8
  %21 = getelementptr inbounds i8*, i8** %20, i64 3
  %22 = load i8*, i8** %21, align 8
  %23 = call i32 @atoi(i8* %22)
  store i32 %23, i32* %8, align 4
  %24 = load i8**, i8*** %5, align 8
  %25 = getelementptr inbounds i8*, i8** %24, i64 4
  %26 = load i8*, i8** %25, align 8
  %27 = call i32 @atoi(i8* %26)
  store i32 %27, i32* %9, align 4
  %28 = load i32, i32* %6, align 4
  %29 = load i32, i32* %7, align 4
  %30 = sub nsw i32 %28, %29
  store i32 %30, i32* %10, align 4
  %31 = load i32, i32* %8, align 4
  %32 = load i32, i32* %9, align 4
  %33 = sub nsw i32 %31, %32
  store i32 %33, i32* %11, align 4
  %34 = load i32, i32* %10, align 4
  %35 = load i32, i32* %11, align 4
  %36 = sub nsw i32 %34, %35
  ret i32 %36
}

declare i32 @atoi(i8*) #1

attributes #0 = { noinline nounwind optnone ssp uwtable "darwin-stkchk-strong-link" "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }
attributes #1 = { "darwin-stkchk-strong-link" "frame-pointer"="all" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 1]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Apple clang version 14.0.0 (clang-1400.0.29.202)"}

Identical!

Failed to load passes from 'libHelloWorld.so'. Request ignored.

Hello Andrzej,

I built HelloWorld pass on macOS, and it worked fine, but I had a problem on Linux.

The command I used:

git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout release/10.x
mkdir build
cd build

cmake -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 ../llvm
cmake --build .

export LLVM_DIR=$(pwd)

git clone https://github.com/banach-space/llvm-tutor
cd llvm-tutor
export TUTOR_DIR=$(pwd)
cd HelloWorld/
mkdir build
cd build

cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../
make

$LLVM_DIR/bin/clang -S -emit-llvm $TUTOR_DIR/inputs/input_for_hello.c -o input_for_hello.ll

$LLVM_DIR/bin/opt -load-pass-plugin libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll

The error from opt:

Failed to load passes from 'libHelloWorld.so'. Request ignored.
/home/amir/llvm-project/build/bin/opt: unknown pass name 'hello-world'

Thanks,
Amir

No -debug-only option in clang-12

When I tried

$LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll -debug-only=mba-add -stats -o out.ll

It gives

opt-12: Unknown command line argument '-debug-only=mba-add'.  Try: 'opt-12 --help'
opt-12: Did you mean '--debug-pass=mba-add'?

But both

$LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll --debug-pass=Structure -stats -o out1.ll

and

$LLVM_DIR/bin/opt -S -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll -o out2.ll

generate the exactly same output files when comparing them by diff out1.ll out2.ll. I have no idea what goes wrong here.

$opt-12 --version
LLVM (http://llvm.org/):
  LLVM version 12.0.1
  
  Optimized build.
  Default target: x86_64-pc-linux-gnu
  Host CPU: broadwell

How to print all passes that are used during compilation in order? (LLVM 10.0.0)

Hi there!

I'm trying to write a legacy pass which can be used like:
clang -O3 -Xclang -load -Xclang libMyPass.so test.c -o test

And I also noticed that by setting the parameter llvm::PassManagerBuilder::ExtensionPointTy I can control the loading order of the pass at a degree.

However, I have been wondering how I can explore the exact execution order of the passes used in the whole compilation process? (Including those are used by O3 or other operations)

HelloWorld example doesn't show anything

Hi, sir, follow the instructions you mentioned in the document, I got nothing in the screen:

 2314  echo "export LLVM_DIR=/usr/local/opt/llvm@12" >>  ~/.zshrc
...
 2321  cd build
 2322  cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../HelloWorld/
 2323  make
 2324  $LLVM_DIR/bin/clang -S -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll
 2325  $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.dylib  -disable-output input_for_hello.ll 

[OpCodeCounter] Wrong result for functions

It is expected :

=================================================
LLVM-TUTOR: OpcodeCounter results for main

OPCODE #N TIMES USED

load 2
br 4
icmp 1
add 1
ret 1
alloca 2
store 4
call 4

What is happening:

Printing analysis 'OpcodeCounter Pass' for function 'main':

LLVM-TUTOR: OpcodeCounter results

OPCODE #TIMES USED

ret 1

I think main should print all instructions opcodes.

$LLVM_DIR/bin/opt --version

LLVM (http://llvm.org/):
LLVM version 12.0.0
Optimized build.
Default target: x86_64-pc-linux-gnu
Host CPU: bdver4

Missing "Run the pass through opt - New PM" for InjectFuncCall

I noticed that the instruction for "Run the pass through opt - New PM" in section InjectFuncCall is missing. And I tried as following, it worked. So I suggested to add this instruction to README.md

$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libInjectFuncCall.so --passes="inject-func-call" input_for_hello.bc -o instrumented.bin

Questions about StaticMain.cpp

Hello Andrzej,

I have some questions about StaticMain.cpp:

  1. In function countStaticCalls, you added an instance of StaticWrapper to the ModulePassManager. It seems that the tool is an analysis tool (in the sense that it doesn't transform any units of IR), so why we are wrapping our analysis pass (i.e., StaticCallCounter) inside StaticWrapper (which is derived from PassInfoMixin)?
  2. Why just registering StaticCallCounter (which is derived from AnalysisInfoMixin) to ModuleAnalysisManager is not enough?
  3. If we have to explicitly call MAM.getResult<StaticCallCounter>(M), why we didn't call it inside of a pass derived from AnalysisInfoMixin (instead we are calling it from StaticWrapper which is derived from PassInfoMixin)
  4. How can I write my own transformation pass as a standalone tool? Which parts of the StaticMain.cpp should be changed?

Thank you very much.

Cheers,
Amir

Undefined symbol

Hello !

To begin, thanks for your super repo !
I have some troubles with running the HelloWorld example pass... By following your tutorial I am doing this :

(orc) root[0]/r/testzone > cd llvm-tutor/
(orc) root[0]/r/t/llvm-tutor > export LLVM_DIR=/root/orchestra/root/lib64/llvm/llvm
(orc) root[0]/r/t/llvm-tutor > mkdir build
                               cd build
                               cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../HelloWorld/
                               make
-- The C compiler identification is GNU 11.2.0
-- The CXX compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /root/orchestra/root/link-only/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /root/orchestra/root/link-only/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found ZLIB: /root/orchestra/root/lib64/libz.so (found version "1.2.11")
-- Configuring done
-- Generating done
-- Build files have been written to: /root/testzone/llvm-tutor/build
[ 50%] Building CXX object CMakeFiles/HelloWorld.dir/HelloWorld.cpp.o
[100%] Linking CXX shared library libHelloWorld.so
[100%] Built target HelloWorld
(orc) root[0]/r/t/l/build > $LLVM_DIR/bin/clang -O1 -S -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll
(orc) root[0]/r/t/l/build >
(orc) root[134]/r/t/l/build > $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output
 input_for_hello.ll
Failed to load passes from './libHelloWorld.so'. Request ignored.
Expected<T> must be checked before access or destruction.
Unchecked Expected<T> contained error:
Could not load library './libHelloWorld.so': ./libHelloWorld.so: undefined symbol: _ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /root/orchestra/root/lib64/llvm/llvm/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll
 #0 0x00007f42457c4a5e llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Unix/Signals.inc:570:13
 #1 0x00007f42457c2dc4 llvm::sys::RunSignalHandlers() /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Signals.cpp:105:18
 #2 0x00007f42457c4fcd SignalHandler(int) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Unix/Signals.inc:415:1
 #3 0x00007f4245205520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007f4245259a7c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007f4245259a7c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x00007f4245259a7c pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x00007f4245205476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x00007f42451eb7f3 abort ./stdlib/abort.c:81:7
 #9 0x000055c33f5d4c11 llvm::raw_ostream::operator<<(llvm::StringRef) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/raw_ostream.h:220:7
#10 0x000055c33f5d4c11 llvm::raw_ostream::operator<<(char const*) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/raw_ostream.h:244:18
#11 0x000055c33f5d4c11 llvm::Expected<llvm::PassPlugin>::fatalUncheckedExpected() const /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/Error.h:704:14
#12 0x000055c33f5d0d98 (/root/orchestra/root/lib64/llvm/llvm/bin/opt+0x2ad98)
#13 0x000055c33f5d2bfb std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__is_long[abi:v160001]() const /builds/gitlab/revng/orchestra/orchestra/root/lib64/llvm/clang-release/bin/../include/c++/v1/string:1682:16
#14 0x000055c33f5d2bfb std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::~basic_string() /builds/gitlab/revng/orchestra/orchestra/root/lib64/llvm/clang-release/bin/../include/c++/v1/string:2361:9
#15 0x000055c33f5d2bfb llvm::cl::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, llvm::cl::parser<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::handleOccurrence(unsigned int, llvm::StringRef, llvm::StringRef) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/CommandLine.h:1673:3
#16 0x00007f42456da45d ProvideOption(llvm::cl::Option*, llvm::StringRef, llvm::StringRef, int, char const* const*, int&) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:708:1
#17 0x00007f42456de4ef (anonymous namespace)::CommandLineParser::ParseCommandLineOptions(int, char const* const*, llvm::StringRef, llvm::raw_ostream*, bool) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:1714:23
#18 0x00007f42456de4ef llvm::cl::ParseCommandLineOptions(int, char const* const*, llvm::StringRef, llvm::raw_ostream*, char const*, bool) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:1470:24
#19 0x000055c33f5ce47a main /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/tools/opt/opt.cpp:0:3
#20 0x00007f42451ecd90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#21 0x00007f42451ece40 call_init ./csu/../csu/libc-start.c:128:20
#22 0x00007f42451ece40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#23 0x000055c33f5bf379 _start (/root/orchestra/root/lib64/llvm/llvm/bin/opt+0x19379)
fish: Job 1, '$LLVM_DIR/bin/opt -load-pass-pl…' terminated by signal SIGABRT (Abort)

I saw this problem in a stackoverflow post. It was an ABI match problem. The user had solve it by inserting the _GLIBCXX_USE_CXX11_ABI=0 compilation option...In my case doing that let me fall in another symbol problem :

(orc) root[0]/r/t/l/build > $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output i
nput_for_hello.ll
Failed to load passes from './libHelloWorld.so'. Request ignored.
Expected<T> must be checked before access or destruction.
Unchecked Expected<T> contained error:
Could not load library './libHelloWorld.so': ./libHelloWorld.so: undefined symbol: _ZNK4llvm12FunctionPass17createPrinterPassERNS_11raw_ostreamERKSsPLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /root/orchestra/root/lib64/llvm/llvm/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll
 #0 0x00007fd15505ea5e llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Unix/Signals.inc:570:13
 #1 0x00007fd15505cdc4 llvm::sys::RunSignalHandlers() /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Signals.cpp:105:18
 #2 0x00007fd15505efcd SignalHandler(int) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/Unix/Signals.inc:415:1
 #3 0x00007fd154a9f520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007fd154af3a7c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007fd154af3a7c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x00007fd154af3a7c pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x00007fd154a9f476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x00007fd154a857f3 abort ./stdlib/abort.c:81:7
 #9 0x000055e011ed3c11 llvm::raw_ostream::operator<<(llvm::StringRef) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/raw_ostream.h:220:7
#10 0x000055e011ed3c11 llvm::raw_ostream::operator<<(char const*) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/raw_ostream.h:244:18
#11 0x000055e011ed3c11 llvm::Expected<llvm::PassPlugin>::fatalUncheckedExpected() const /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/Error.h:704:14
#12 0x000055e011ecfd98 (/root/orchestra/root/lib64/llvm/llvm/bin/opt+0x2ad98)
#13 0x000055e011ed1bfb std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__is_long[abi:v160001]() const /builds/gitlab/revng/orchestra/orchestra/root/lib64/llvm/clang-release/bin/../include/c++/v1/string:1682:16
#14 0x000055e011ed1bfb std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::~basic_string() /builds/gitlab/revng/orchestra/orchestra/root/lib64/llvm/clang-release/bin/../include/c++/v1/string:2361:9
#15 0x000055e011ed1bfb llvm::cl::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, llvm::cl::parser<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::handleOccurrence(unsigned int, llvm::StringRef, llvm::StringRef) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/include/llvm/Support/CommandLine.h:1673:3
#16 0x00007fd154f7445d ProvideOption(llvm::cl::Option*, llvm::StringRef, llvm::StringRef, int, char const* const*, int&) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:708:1
#17 0x00007fd154f784ef (anonymous namespace)::CommandLineParser::ParseCommandLineOptions(int, char const* const*, llvm::StringRef, llvm::raw_ostream*, bool) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:1714:23
#18 0x00007fd154f784ef llvm::cl::ParseCommandLineOptions(int, char const* const*, llvm::StringRef, llvm::raw_ostream*, char const*, bool) /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/lib/Support/CommandLine.cpp:1470:24
#19 0x000055e011ecd47a main /builds/gitlab/revng/orchestra/orchestra/sources/llvm-project/llvm/tools/opt/opt.cpp:0:3
#20 0x00007fd154a86d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#21 0x00007fd154a86e40 call_init ./csu/../csu/libc-start.c:128:20
#22 0x00007fd154a86e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#23 0x000055e011ebe379 _start (/root/orchestra/root/lib64/llvm/llvm/bin/opt+0x19379)
fish: Job 1, '$LLVM_DIR/bin/opt -load-pass-pl…' terminated by signal SIGABRT (Abort)

Some other issues in other projects (sampsyo/llvm-pass-skeleton#12) give some solutions...which are not working for me.

My setup is :

(orc) root[0]/r/t/l/build > $LLVM_DIR/bin/opt --version
LLVM (http://llvm.org/):
  LLVM version 16.0.1
  Optimized build with assertions.
  Default target: x86_64-unknown-linux-gnu
  Host CPU: skylake

If you have any suggestions, I take 😄

Have a nice day.

Hello World pass doesn't print anything

Hi Andrzej,
I'm on macOS Big Sur (Intel, not M1). I've worked through the "HelloWorld: Your First Pass" section, up to running the pass.

When I run $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.dylib -passes=hello-world -disable-output input_for_hello.ll, nothing appears and it immediately returns to a prompt. No error, nothing at all. Obviously I'm expecting messages showing the numbers of arguments, like in your readme.

I've tried using my own build of LLVM, LLVM 12 installed via brew, and with the prebuilt LLVM 12 release from GitHub.

My own build was done today, directly from the main branch of the llvm-project repository on GitHub. It claims to be version 13.0.0.

I'm new to both LLVM and macOS, so very possible this is user error.

Finally, a big thank you for creating this project. I found this repo from one of your talks on YouTube. The talk helped me a lot in understanding what's going on.
It's rare to find resources specifically meant to guide beginners through learning about open-source projects. As a noob accustomed to being told to rtfm, your work here is hugely appreciated.

Add CI configuration for Fedora

Currently all testing is run on either Ubuntu or MacOS. It would be great to cover more OSes! Fedora should be relatively easy to set-up.

This is a direct follow-up from #31.

MBAAdd Example with new PM

The MBAAdd pass has a custom command line option specified (-mba-ratio), which you show how to use with the legacy pass manager, but you don't show an example of how this is done with the new pass manager. I haven't been able to figure out how to use custom command line options with the new pass manager. Can you provide an example of this?

New examples

Hi! Thank you for creating such a useful resource to learn about LLVM!

I have some questions about LLVM out-of-source development that you can probably answer

  1. Can I change a pipeline of optimizations passes from my own pass / plugin? If yes, can you please share some some docs about it or perhaps make an example?
  2. Can I forbid default LLVM passes to run on specific functions? If yes, how?

Thank you in advance!

opt segmentation fault in macOS Catalina

I use the macOS Catalina (10.15.2)

After running:

$LLVM_DIR/bin/opt -load-pass-plugin libHelloWorld.dylib -passes=hello-world -disable-output input_for_hello.ll 

I got this:

(llvm-tutor) Hello from: foo
(llvm-tutor)   number of arguments: 1
(llvm-tutor) Hello from: bar
(llvm-tutor)   number of arguments: 2
(llvm-tutor) Hello from: fez
(llvm-tutor)   number of arguments: 3
(llvm-tutor) Hello from: main
(llvm-tutor)   number of arguments: 2
Stack dump:
0.	Program arguments: /usr/local/opt/llvm/bin/opt -load-pass-plugin libHelloWorld.dylib -passes=hello-world -disable-output input_for_hello.ll 
0  opt                      0x000000010d2629c6 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  opt                      0x000000010d262db8 SignalHandler(int) + 180
2  libsystem_platform.dylib 0x00007fff63a9b42d _sigtramp + 29
3  libsystem_platform.dylib 000000000000000000 _sigtramp + 18446603338844097520
4  opt                      0x000000010cf31d05 llvm::object_deleter<llvm::SmallVector<std::__1::pair<llvm::PassManagerBuilder::ExtensionPointTy, std::__1::function<void (llvm::PassManagerBuilder const&, llvm::legacy::PassManagerBase&)> >, 8u> >::call(void*) + 19
5  opt                      0x000000010d223805 llvm::llvm_shutdown() + 53
6  opt                      0x000000010d20bb41 llvm::InitLLVM::~InitLLVM() + 15
7  opt                      0x000000010c0afc5a main + 10025
8  libdyld.dylib            0x00007fff638a27fd start + 1
Segmentation fault: 11

Do you know why I get the segfault?

BTW, thanks a lot for this great tutorial!

Possible issue with optimization for the InjectFuncCall part

I'm using LLVM compiled from branch release/13.x with the new Pass Manager.

After following the steps at the InjectFuncCall section when i run
$LLVM_DIR/bin/lli instrumented_hello.bin
I only get the following output

(llvm-tutor) Hello from: main
(llvm-tutor) number of arguments: 2

I tried by removing -O1 option when compiling with clang-13 and it shows the correct output.

I'd open a pr but since I'm a beginner with compilers and llvm I'm not sure if there could be other reasons I got this behavior.

Output for test in README fails with expected top-level entity

After building the project on Ubuntu with LLVM-10, updated yesterday, I see the following:

cades@kharazi-cades-dev:~/llvm-tutor$ $LLVM_DIR/bin/opt -load-pass-plugin libHelloWorld.dylib -passes=hello-world -disable-output input_for_hello.ll
/home/cades/.llvm/bin/opt: input_for_hello.ll:1:2: error: expected top-level entity
        .text
        ^ 

Handling LLVM builds with LTOed objects

Shall we continue our discussion from Homebrew/discussions#3666 here?

Here's a summary of alternatives discussed so far:

  1. Revert the change in Homebrew. This may be essentially equivalent to not supporting any LLVM builds configured with LLVM_ENABLE_LTO=ON on macOS.
  2. Link with the libLLVM shared library instead.
  3. Require extra configuration from Homebrew users (e.g. setting C[XX]FLAGS)
  4. Somehow detecting an LTOed build and configuring it accordingly.

2 and 3 seem like the most attractive alternatives now, but it may still be possible to do 1 (depending on how other users receive the changes wrt LTO), or even 4 if there is a neat enough way to do it.

Is it possible to obtain and print the specified local variable value by llvm pass?

Hello, I just started to learn llvm pass. You must be very professional in llvm pass, so I want to ask a practical question, as the title says.
For example, this is a very simple program.

#include <stdlib.h>

int main(){
	int a=0;
	for(int i=0;i<5;i++){
		a++;
	}
	return 0;
}

Can I dynamically obtain the value of local variable a in llvm pass? My purpose is to observe its value change When it changes. So I have to obtain the value of the local variable a firstly.

If it's a global variable, it should be very simple to obtain its value, but I don't have any ideas about local variables.

I have checked the official documents and don't know whether this can be realized. If so, can you provide some ideas?

Unable to load passes from libHelloWorld.so

Hi

I have tried the recommended command line from #12 but I am still not able to load the .so file. The command line I used was

$LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_helloworld.ll

I am running on Ubuntu 2004 LTS in VirtualBox.

$LLVM_DIR/bin/opt --version
LLVM version 12.0.0-test
Optimized build
Default target: x86_64-unknown-linux-gnu
Host CPU: haswell

replace dataype

I want to write a pass which replaces float datatype with int.To achieve this ,How can I replace a datatype in .cpp file.

Thank You

Option 'load' registered more than once!

Description:
I'm running into an error when trying to use the opt command. All the passes in the repo work fine. But when I wrote and ran my own pass code using MBASub.cpp as a template and this error produced.

I've checked my command line arguments to make sure I'm not specifying -load multiple times and my code only change the name and runOnBasicBlock function in MBASub.cpp , so I'm not sure what's causing this error. Can anyone help me troubleshoot this issue?

Steps to reproduce:

Run the opt command with opt-14 -load-pass-plugin ./lib/libIndirectJmp.so -passes=indirect-jmp -S ./inputs/input_for_mba_sub.ll -o out.ll
Observe the "Option 'load' registered more than once!" error message
Expected result:
The opt command should run successfully with the specified options.

Actual result:

opt-14: CommandLine Error: Option 'load' registered more than once!
LLVM ERROR: inconsistency in registered CommandLine options
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0. Program arguments: opt-14 -load-pass-plugin ./lib/libIndirectJmp.so -passes=indirect-jmp -S ./inputs/input_for_mba_sub.ll -o out.ll
#0 0x00007fae6c81aa71 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xea5a71)
#1 0x00007fae6c8187be llvm::sys::RunSignalHandlers() (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xea37be)
#2 0x00007fae6c81afa6 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xea5fa6)
#3 0x00007fae6b4aff90 (/lib/x86_64-linux-gnu/libc.so.6+0x3bf90)
#4 0x00007fae6b4feccc __pthread_kill_implementation ./nptl/./nptl/pthread_kill.c:44:76
#5 0x00007fae6b4afef2 raise ./signal/../sysdeps/posix/raise.c:27:6
#6 0x00007fae6b49a472 abort ./stdlib/./stdlib/abort.c:81:7
#7 0x00007fae6c74f668 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xdda668)
#8 0x00007fae6c74f486 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xdda486)
#9 0x00007fae6c73a4e0 (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xdc54e0)
#10 0x00007fae6c72be5b llvm::cl::Option::addArgument() (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xdb6e5b)
#11 0x00007fae67bc0ec0 llvm::cl::opt<llvm::PluginLoader, false, llvm::cl::parser<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >::done() /usr/include/llvm-14/llvm/Support/CommandLine.h:1489:22
#12 0x00007fae67bc0046 llvm::cl::opt<llvm::PluginLoader, false, llvm::cl::parser<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >::opt<char [5], llvm::cl::NumOccurrencesFlag, llvm::cl::value_desc, llvm::cl::desc>(char const (&) [5], llvm::cl::NumOccurrencesFlag const&, llvm::cl::value_desc const&, llvm::cl::desc const&) /usr/include/llvm-14/llvm/Support/CommandLine.h:1513:3
#13 0x00007fae67bbd188 __static_initialization_and_destruction_0(int, int) /usr/include/llvm-14/llvm/Support/PluginLoader.h:35:5
#14 0x00007fae67bbd248 _GLOBAL__sub_I_IndirectJmp.cpp /home/kali/learnllvm/my-pass/lib/IndirectJmp.cpp:145:59
#15 0x00007fae722f4abe call_init ./elf/./elf/dl-init.c:69:21
#16 0x00007fae722f4abe call_init ./elf/./elf/dl-init.c:26:1
#17 0x00007fae722f4ba4 _dl_init ./elf/./elf/dl-init.c:116:14
#18 0x00007fae6b5c2e44 _dl_catch_exception ./elf/./elf/dl-error-skeleton.c:184:18
#19 0x00007fae722fb30e dl_open_worker ./elf/./elf/dl-open.c:812:6
#20 0x00007fae6b5c2dea _dl_catch_exception ./elf/./elf/dl-error-skeleton.c:209:18
#21 0x00007fae722fb6a8 _dl_open ./elf/./elf/dl-open.c:884:17
#22 0x00007fae6b4f92d8 dlopen_doit ./dlfcn/./dlfcn/dlopen.c:56:13
#23 0x00007fae6b5c2dea _dl_catch_exception ./elf/./elf/dl-error-skeleton.c:209:18
#24 0x00007fae6b5c2e9f _dl_catch_error ./elf/./elf/dl-error-skeleton.c:228:12
#25 0x00007fae6b4f8dc7 _dlerror_run ./dlfcn/./dlfcn/dlerror.c:145:17
#26 0x00007fae6b4f9389 dlopen_implementation ./dlfcn/./dlfcn/dlopen.c:71:51
#27 0x00007fae6b4f9389 dlopen ./dlfcn/./dlfcn/dlopen.c:81:12
#28 0x00007fae6c8052f9 llvm::sys::DynamicLibrary::getPermanentLibrary(char const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0xe902f9)
#29 0x00007fae6f39326f llvm::PassPlugin::Load(std::__cxx11::basic_string<char, std::char_traits, std::allocator > const&) (/lib/x86_64-linux-gnu/libLLVM-14.so.1+0x3a1e26f)
#30 0x0000000000420088 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine
, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRefllvm::StringRef, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool) (/usr/lib/llvm-14/bin/opt+0x420088)
#31 0x0000000000435fb7 main (/usr/lib/llvm-14/bin/opt+0x435fb7)
#32 0x00007fae6b49b18a __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#33 0x00007fae6b49b245 call_init ./csu/../csu/libc-start.c:128:20
#34 0x00007fae6b49b245 __libc_start_main ./csu/../csu/libc-start.c:368:5
#35 0x000000000041bd8a _start (/usr/lib/llvm-14/bin/opt+0x41bd8a)
zsh: IOT instruction opt-14 -load-pass-plugin ./lib/libIndirectJmp.so -passes="indirect-jmp" -S -

Environment details:

Operating system: kali
LLVM/Clang version: 14.0.6-2
here is my source code
indirectjmp.txt

Thank you for your help!

about DuplicateBB pass , the return value of the run method is not right

In DuplicateBB.cpp, the run method of the DuplicateBB struct return none() when Targets is empty and return all() when Targets is not empty.
Should the return value be reversed?
Because when Targets is empty the pass dose not modify the LLVM program, the run method should return all()

Ubuntu: Could not find a configuration file for package "LLVM"

Recently I've started seeing this in my CI jobs on Ubuntu Bionic:

Could not find a configuration file for package "LLVM" that is compatible
  with requested version "11.0.0"

It turns out that the package for LLVM 11 for Ubuntu Bionic started claiming to be version 11.1.0 rather than 11.0.1 or 11.0.0.

As LLVM 11.1.0 has not been released yet (rc1 has just been tagged: llvm-dev thread), I assume that this is a bug in the packaging. I reported it here.

Although LLVM 11.1.0 and LLVM 11.0.0 will be ABI incompatible, I doubt that this will affect llvm-tutor. But it does confuse the CMake scripts.

can't build HelloWorld on windows (llvm 12.0.1)

when I try to build HelloWorld project with VS2019, I got LINK error( my cmake command is: cmake -G "Visual Studio 16 2019" -A x64 -Thost=x64 ../):
image

if I modify add_llvm_library MODULE to SHARED, VS2019 can successfully compile the HelloWolrd
image2

but got the following warning:
image3

and then opt.exe failed to load passes
image4

In addition, the cmake invocation for llvm is:
cmake -DLLVM_ENABLE_PROJECTS="clang;lld;libcxx;llvm" -DLLVM_EXPORT_SYMOBLS_FOR_PLUGINS=On -G "Visual Studio 16 2019" -A x64 -Thost=x64 ..\

opt command line

my opt version is

opt --version
LLVM (http://llvm.org/):
  LLVM version 9.0.0
  Optimized build.
  Default target: x86_64-apple-darwin19.0.0
  Host CPU: broadwell

when i run opt, it seem the command line flags is not right

opt -load-pass-plugin libHelloWorld.dylib -hello-world -disable-output ../input_for_hello.ll
OVERVIEW: llvm .bc -> .bc modular optimizer and analysis printer

USAGE: opt [options] <input bitcode file>

OPTIONS:

Color Options:

  --color                                            - Use colors in output (default=autodetect)

General options:

  --O0                                               - Optimization level 0. Similar to clang -O0
  --O1                                               - Optimization level 1. Similar to clang -O1
  --O2                                               - Optimization level 2. Similar to clang -O2
  --O3                                               - Optimization level 3. Similar to clang -O3
  --Os                                               - Like -O2 with extra optimizations for size. Similar to clang -Os
  --Oz                                               - Like -Os but reduces code size further. Similar to clang -Oz
  -S                                                 - Write output as LLVM assembly
  --aarch64-neon-syntax=<value>                      - Choose style of NEON code to emit from AArch64 backend:
......

DynamicCounterCall.cpp fails if indrect call is used (function pointer)

Hi @banach-space, this is my question, I think it is not really a bug.

I would like to know why GlobalValue::CommonLinkage is used insead of GlobalValue::ExternalLinkage when creating a global variable. It seems work well if I use GlobalValue::ExternalLinkage.

I made some changes to the input_for_cc.c and here is my code. It has an indirect function call.

void foo() { }
void bar() {foo(); }
void fez() {bar(); }

int main() {
  foo();
  bar();
  fez();

  void (*p) () = &foo; 
  p(); 

  int ii = 0;
  for (ii = 0; ii < 10; ii++)
    foo();

  return 0;
}

I found that the pass DynamicCounterCall.cpp would fail in this scenario.
And here is the report generated by LLVM

Assertion failed: (!isZeroFill() && "Adding edge to zero-fill block?"), function addEdge, file JITLink.h, line 301.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: lli instrument_bin
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  lli                      0x000000010a1151dd llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 61
1  lli                      0x000000010a11571b PrintStackTraceSignalHandler(void*) + 27
2  lli                      0x000000010a11357f llvm::sys::RunSignalHandlers() + 127
3  lli                      0x000000010a11729f SignalHandler(int) + 223
4  libsystem_platform.dylib 0x00007ff81be79e2d _sigtramp + 29
5  libsystem_platform.dylib 0x00007fc0c605f830 _sigtramp + 18446743836045498912
6  libsystem_c.dylib        0x00007ff81bdb0d10 abort + 123
7  libsystem_c.dylib        0x00007ff81bdb00be err + 0
8  lli                      0x0000000109804325 llvm::jitlink::Block::addEdge(unsigned char, unsigned int, llvm::jitlink::Symbol&, long long) + 117
9  lli                      0x00000001092c89da llvm::jitlink::EHFrameEdgeFixer::processFDE(llvm::jitlink::EHFrameEdgeFixer::ParseContext&, llvm::jitlink::Block&, unsigned long, unsigned long, unsigned long, unsigned int, llvm::DenseMap<unsigned int, llvm::jitlink::EHFrameEdgeFixer::EdgeTarget, llvm::DenseMapInfo<unsigned int, void>, llvm::detail::DenseMapPair<unsigned int, llvm::jitlink::EHFrameEdgeFixer::EdgeTarget> >&) + 3882
10 lli                      0x00000001092c6876 llvm::jitlink::EHFrameEdgeFixer::processBlock(llvm::jitlink::EHFrameEdgeFixer::ParseContext&, llvm::jitlink::Block&) + 2550
11 lli                      0x00000001092c5953 llvm::jitlink::EHFrameEdgeFixer::operator()(llvm::jitlink::LinkGraph&) + 1171
12 lli                      0x00000001093ad365 decltype(std::__1::forward<llvm::jitlink::EHFrameEdgeFixer&>(fp)(std::__1::forward<llvm::jitlink::LinkGraph&>(fp0))) std::__1::__invoke<llvm::jitlink::EHFrameEdgeFixer&, llvm::jitlink::LinkGraph&>(llvm::jitlink::EHFrameEdgeFixer&, llvm::jitlink::LinkGraph&) + 69
13 lli                      0x00000001093ad2f5 llvm::Error std::__1::__invoke_void_return_wrapper<llvm::Error, false>::__call<llvm::jitlink::EHFrameEdgeFixer&, llvm::jitlink::LinkGraph&>(llvm::jitlink::EHFrameEdgeFixer&, llvm::jitlink::LinkGraph&) + 69
14 lli                      0x00000001093ad2a5 std::__1::__function::__alloc_func<llvm::jitlink::EHFrameEdgeFixer, std::__1::allocator<llvm::jitlink::EHFrameEdgeFixer>, llvm::Error (llvm::jitlink::LinkGraph&)>::operator()(llvm::jitlink::LinkGraph&) + 69
15 lli                      0x00000001093ac084 std::__1::__function::__func<llvm::jitlink::EHFrameEdgeFixer, std::__1::allocator<llvm::jitlink::EHFrameEdgeFixer>, llvm::Error (llvm::jitlink::LinkGraph&)>::operator()(llvm::jitlink::LinkGraph&) + 68
16 lli                      0x00000001092fc93d std::__1::__function::__value_func<llvm::Error (llvm::jitlink::LinkGraph&)>::operator()(llvm::jitlink::LinkGraph&) const + 93
17 lli                      0x00000001092f7510 std::__1::function<llvm::Error (llvm::jitlink::LinkGraph&)>::operator()(llvm::jitlink::LinkGraph&) const + 64
18 lli                      0x00000001092f4f49 llvm::jitlink::JITLinkerBase::runPasses(std::__1::vector<std::__1::function<llvm::Error (llvm::jitlink::LinkGraph&)>, std::__1::allocator<std::__1::function<llvm::Error (llvm::jitlink::LinkGraph&)> > >&) + 153
19 lli                      0x00000001092f4b45 llvm::jitlink::JITLinkerBase::linkPhase1(std::__1::unique_ptr<llvm::jitlink::JITLinkerBase, std::__1::default_delete<llvm::jitlink::JITLinkerBase> >) + 165
20 lli                      0x0000000109337842 void llvm::jitlink::JITLinker<llvm::jitlink::MachOJITLinker_x86_64>::link<std::__1::unique_ptr<llvm::jitlink::JITLinkContext, std::__1::default_delete<llvm::jitlink::JITLinkContext> >, std::__1::unique_ptr<llvm::jitlink::LinkGraph, std::__1::default_delete<llvm::jitlink::LinkGraph> >, llvm::jitlink::PassConfiguration>(std::__1::unique_ptr<llvm::jitlink::JITLinkContext, std::__1::default_delete<llvm::jitlink::JITLinkContext> >&&, std::__1::unique_ptr<llvm::jitlink::LinkGraph, std::__1::default_delete<llvm::jitlink::LinkGraph> >&&, llvm::jitlink::PassConfiguration&&) + 130
21 lli                      0x00000001093374f8 llvm::jitlink::link_MachO_x86_64(std::__1::unique_ptr<llvm::jitlink::LinkGraph, std::__1::default_delete<llvm::jitlink::LinkGraph> >, std::__1::unique_ptr<llvm::jitlink::JITLinkContext, std::__1::default_delete<llvm::jitlink::JITLinkContext> >) + 968
22 lli                      0x000000010931c10e llvm::jitlink::link_MachO(std::__1::unique_ptr<llvm::jitlink::LinkGraph, std::__1::default_delete<llvm::jitlink::LinkGraph> >, std::__1::unique_ptr<llvm::jitlink::JITLinkContext, std::__1::default_delete<llvm::jitlink::JITLinkContext> >) + 206
23 lli                      0x00000001092e3ff0 llvm::jitlink::link(std::__1::unique_ptr<llvm::jitlink::LinkGraph, std::__1::default_delete<llvm::jitlink::LinkGraph> >, std::__1::unique_ptr<llvm::jitlink::JITLinkContext, std::__1::default_delete<llvm::jitlink::JITLinkContext> >) + 128
24 lli                      0x000000010991794f llvm::orc::ObjectLinkingLayer::emit(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer> >) + 463
25 lli                      0x000000010994fed4 llvm::orc::ObjectTransformLayer::emit(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >, std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer> >) + 484
26 lli                      0x00000001098447d7 llvm::orc::IRCompileLayer::emit(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >, llvm::orc::ThreadSafeModule) + 503
27 lli                      0x0000000109847120 llvm::orc::IRTransformLayer::emit(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >, llvm::orc::ThreadSafeModule) + 352
28 lli                      0x0000000109847120 llvm::orc::IRTransformLayer::emit(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >, llvm::orc::ThreadSafeModule) + 352
29 lli                      0x000000010986b6e7 llvm::orc::BasicIRLayerMaterializationUnit::materialize(std::__1::unique_ptr<llvm::orc::MaterializationResponsibility, std::__1::default_delete<llvm::orc::MaterializationResponsibility> >) + 471
30 lli                      0x0000000109719c88 llvm::orc::MaterializationTask::run() + 72
31 lli                      0x0000000109719ee2 llvm::orc::ExecutionSession::runOnCurrentThread(std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >) + 18
32 lli                      0x0000000109757be2 void llvm::detail::UniqueFunctionBase<void, std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> > >::CallImpl<void (*)(std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >)>(void*, std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >&) + 66
33 lli                      0x00000001097222a7 llvm::unique_function<void (std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >)>::operator()(std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >) + 55
34 lli                      0x0000000109709ee5 llvm::orc::ExecutionSession::dispatchTask(std::__1::unique_ptr<llvm::orc::Task, std::__1::default_delete<llvm::orc::Task> >) + 245
35 lli                      0x000000010971b576 llvm::orc::ExecutionSession::dispatchOutstandingMUs() + 566
36 lli                      0x000000010971ea9c llvm::orc::ExecutionSession::OL_completeLookup(std::__1::unique_ptr<llvm::orc::InProgressLookupState, std::__1::default_delete<llvm::orc::InProgressLookupState> >, std::__1::shared_ptr<llvm::orc::AsynchronousSymbolQuery>, std::__1::function<void (llvm::DenseMap<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib*, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > const&)>) + 956
37 lli                      0x0000000109779e74 llvm::orc::InProgressFullLookupState::complete(std::__1::unique_ptr<llvm::orc::InProgressLookupState, std::__1::default_delete<llvm::orc::InProgressLookupState> >) + 228
38 lli                      0x000000010971090d llvm::orc::ExecutionSession::OL_applyQueryPhase1(std::__1::unique_ptr<llvm::orc::InProgressLookupState, std::__1::default_delete<llvm::orc::InProgressLookupState> >, llvm::Error) + 3901
39 lli                      0x000000010970da5d llvm::orc::ExecutionSession::lookup(llvm::orc::LookupKind, std::__1::vector<std::__1::pair<llvm::orc::JITDylib*, llvm::orc::JITDylibLookupFlags>, std::__1::allocator<std::__1::pair<llvm::orc::JITDylib*, llvm::orc::JITDylibLookupFlags> > > const&, llvm::orc::SymbolLookupSet, llvm::orc::SymbolState, llvm::unique_function<void (llvm::Expected<llvm::DenseMap<llvm::orc::SymbolStringPtr, llvm::JITEvaluatedSymbol, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void>, llvm::detail::DenseMapPair<llvm::orc::SymbolStringPtr, llvm::JITEvaluatedSymbol> > >)>, std::__1::function<void (llvm::DenseMap<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib*, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > const&)>) + 381
40 lli                      0x0000000109718fb8 llvm::orc::Platform::lookupInitSymbols(llvm::orc::ExecutionSession&, llvm::DenseMap<llvm::orc::JITDylib*, llvm::orc::SymbolLookupSet, llvm::DenseMapInfo<llvm::orc::JITDylib*, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib*, llvm::orc::SymbolLookupSet> > const&) + 952
41 lli                      0x00000001098a2821 (anonymous namespace)::GenericLLVMIRPlatformSupport::issueInitLookups(llvm::orc::JITDylib&) + 305
42 lli                      0x00000001098a1eae (anonymous namespace)::GenericLLVMIRPlatformSupport::getInitializers(llvm::orc::JITDylib&) + 78
43 lli                      0x0000000109895bc7 (anonymous namespace)::GenericLLVMIRPlatformSupport::initialize(llvm::orc::JITDylib&) + 151
44 lli                      0x00000001082fd965 llvm::orc::LLJIT::initialize(llvm::orc::JITDylib&) + 277
45 lli                      0x00000001082f849f runOrcJIT(char const*) + 5359
46 lli                      0x00000001082f4d96 main + 438
47 dyld                     0x0000000118b5a4fe start + 462
[1]    44361 abort      lli instrument_bin

If I replace the GlobalValue::CommonLinkage with GlobalValue::ExternalLinkage, then DynamicCounterCall works well.
Here is the result returned by the program.

=================================================
LLVM-TUTOR: dynamic analysis results
=================================================
NAME                 #N DIRECT CALLS
-------------------------------------------------
foo                  14
bar                  2
fez                  1
main                 1

Cannot compile with LLVM 17

Currently the version is set to 16 (required).

Tried to compile with fresh llvm 17 and failed (can't match).

I'm working on a patch, if acceptable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.