Beginner help for llvm passes

opt -load lib/LLVMHello.so -p=hello < ~/Prog/PE/hello.bc > /dev/null

i am getting an error opt: unknown pass name 'hello'

But this is a default pass.

Followed the instructions from llvm org.

//===- Hello.cpp - Example code from “Writing an LLVM Pass” ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements two versions of the LLVM “Hello World” pass described
// in docs/WritingAnLLVMPass.html
//
//===----------------------------------------------------------------------===//

#include “llvm/ADT/Statistic.h”
#include “llvm/IR/Function.h”
#include “llvm/Pass.h”
#include “llvm/Support/raw_ostream.h”
using namespace llvm;

#define DEBUG_TYPE “hello”

STATISTIC(HelloCounter, “Counts number of functions greeted”);

namespace {
// Hello - The first implementation, without getAnalysisUsage.
struct Hello : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
Hello() : FunctionPass(ID) {}

bool runOnFunction(Function &F) override {
  ++HelloCounter;
  errs() << "Hello: ";
  errs().write_escaped(F.getName()) << '\n';
  return false;
}

};
}

char Hello::ID = 0;
static RegisterPass X(“hello”, “Hello World Pass”);

namespace {
// Hello2 - The second implementation with getAnalysisUsage implemented.
struct Hello2 : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
Hello2() : FunctionPass(ID) {}

bool runOnFunction(Function &F) override {
  ++HelloCounter;
  errs() << "Hello: ";
  errs().write_escaped(F.getName()) << '\n';
  return false;
}

// We don't modify the program, so we preserve all analyses.
void getAnalysisUsage(AnalysisUsage &AU) const override {
  AU.setPreservesAll();
}

};
}

char Hello2::ID = 0;
static RegisterPass
Y(“hello2”, “Hello World Pass (with getAnalysisUsage implemented)”);

i feel like pass isn’t being registered, Please help me in finding a solution

Try: opt -p=helloworld < ~/Prog/PE/hello.bc > /dev/null
LLVMHello.so contains only the legacy pass implementation.

I have the same problem… that one works for now

opt -disable-output a.ll -passes=helloworld

but wondering how you can run with clang

Ok i get it but why helloworld, where is it defined as such?
Can you also help me with how to create and use my own pass?

Look at this

// LineLogger.cpp

#include “llvm/Pass.h”
#include “llvm/IR/Function.h”
#include “llvm/IR/Module.h”
#include “llvm/IR/Instructions.h”
#include “llvm/IR/DebugInfo.h”
#include “llvm/Support/raw_ostream.h”

using namespace llvm;

namespace {
struct LineLogger : public FunctionPass {
static char ID;
LineLogger() : FunctionPass(ID) {}

    virtual bool runOnFunction(Function &F) override {
        // Iterate over basic blocks in the function
        for (auto &BB : F) {
            // Iterate over instructions in the basic block
            for (auto &I : BB) {
                // Get the source location information for the instruction
                DebugLoc debugLoc = I.getDebugLoc();
                if (debugLoc) {
                    // Print the source file, line, and column information
                    StringRef fileName = debugLoc->getFilename();
                    int line = debugLoc->getLine();
                    int col = debugLoc->getColumn();
                    errs() << "Executing: " << fileName << ":" << line << ":" << col << "\n";
                }
            }
        }
        return false; // We didn't modify the function
    }
};

}

char LineLogger::ID = 0;

static RegisterPass X(“line-logger”, “Print lines during execution”, false, false);

This above is my pass

I went to the place where hello.cpp is went to its parent and Created folder My_Logger

llvm/lib/Transforms/CMakeLists.txt
add_llvm_library(LLVMLineLogger
LineLogger.cpp
)

My_Logger/CMakeLists.txt

add_llvm_library(LLVMMyLogger
LineLogger.cpp
)
The pass .cpp file i put inside the My_Logger folder

I ran

mkdir /path/to/llvm-project/build
cd /path/to/llvm-project/build
cmake …/llvm
make

opt -load /path/to/llvm-project/build/lib/LLVMMyLogger.so -passes=line-logger < input.bc -o output.bc
Above line however fails

Says no long called line-logger (which is what i have registerd it as)

However

opt -load ~/llvm-project/build/lib/MyLogger.so --help | grep line-logger

Is telling me line-logger is there, but opt says its not ie same type of error

Requesting assistance currently am trying to make a pass to print all lines the code runs on Like if a loop is there on line 1 to 4 runs 2 it should output

Numbers till main
1
2
3
4
1
2
3
4
Return 0 line number

I have recently started to explore llvm-pass… I am a beginner, too.

but my understanding is that check that example…

‘/llvm-project/llvm/lib/Transforms/Utils/HelloWorld.cpp’

and please make sure that you add it here ‘/llvm-project/llvm/lib/Passes/PassRegistry.def’

The -passes=line-logger syntax is for the new pass manager (which is the default pass manager nowadays). But it kind of looks like you have implemented your pass as a legacy pass for the legacy pass manager.

You might wanna take a look at Writing an LLVM Pass — LLVM 19.0.0git documentation for creating a pass that works with the new pass manager instead.

Thanks i will check it out

Can someone pls assist me in writing a pass for llvm to print all lines that it executes in the new method. i cannot find any tutorials to assist me…

Ill look at it but i think my version is llvm 18? At least when i run clang --version i get 18.0.0git

will my same pass work? or is there a newer syntax?

See banach-space/llvm-tutor: A collection of out-of-tree LLVM passes for teaching and learning (github.com)

Any other useful resources?

#include "llvm/Transforms/Utils/LineLogger.h"
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

PreservedAnalyses LineLoggerPass::run(Function &F,
                                      FunctionAnalysisManager &AM) {
  // Iterate over basic blocks in the function
    for (auto &BB : F) {
        // Iterate over instructions in the basic block
        for (auto &I : BB) {
            // Get the source location information for the instruction
            DebugLoc debugLoc = I.getDebugLoc();
            if (debugLoc) {
                // Print the source file, line, and column information
                StringRef fileName = debugLoc->getFilename();
                int line = debugLoc->getLine();
                int col = debugLoc->getColumn();
                errs() << "Executing: " << fileName << ":" << line << ":" << col << "\n";
            }
        }
    }
            
  return PreservedAnalyses::all();
}

I made this piece of code and registered it as send in this link. I assumed it would print all lines it executes in order.

Writing an LLVM Pass — LLVM 19.0.0git documentation

But this doesnt work, as it it runs with this… ie it is not giving errors

opt -disable-output ~/Prog/PE/X.ll -passes=linelogger

But prints nothing at all

#include <bits/stdc++.h>
// #include "llvm/Pass.h"
// #include "llvm/IR/Function.h"
// #include "llvm/IR/Module.h"
// #include "llvm/IR/Instructions.h"
// #include "llvm/Support/raw_ostream.h"

int ret(int no,int no2)
{
    int sum = 0;
    for(int i = 1; i <= no ; i = i + no2)
    {
        sum += i;
    }

    return sum;
}

int main() 
{
    std::cout << ret(10,2) << '\n';
    return 0;
}

Above is my cpp code

mkdir /path/to/llvm-project/build
cd /path/to/llvm-project/build
cmake ../llvm
make

if necessary after altering my llvm build by adding into utils etc (adding pass) above is what i use in order to build the whole thing…

I’d recomment you to try things like adding -debug-pass-manager=verbose to your opt command line to see if your pass is executed.

And you could try running in a debugger, or add debug printouts in your runOnFunction implementation to see if any part of it is executing. For example like this:

 virtual bool runOnFunction(Function &F) override {
        dbgs() << "FOO\n";
        // Iterate over basic blocks in the function
        for (auto &BB : F) {
            dbgs() << "BAR\n";
            // Iterate over instructions in the basic block
            for (auto &I : BB) {
                dbgs() << "GAZONK\n";
                // Get the source location information for the instruction

Without knowing what ~/Prog/PE/X.ll looks like (I don’t think you have shown that), and nor how it is produced, it is hard to tell what the problem is.
Is the function for example marked as “optnone” (maybe produce by “clang -O0 …”), then your pass won’t run unless it is implemented by overriding the isRequired() method.
Or if debug info (including line numbers) have been stripped, then obviously your printouts won’t occur as they are guarded by “if (debugLoc)”.

I guess first step would be to ensure that your pass is running at (i.e. to find out if the runOnFunction method is executed at all).

TXT.txt (5.3 KB)

If it is needed this is the .ll file
(Have put file in .txt form in here as i cannot copy paste, the site sees the “@” as me tagging someone and doesn’t allow more than 2).

computer:~/Prog/PE: opt -disable-output ~/Prog/PE/X.ll -passes=linelogger -debug-pass-manager=verbose
Running pass: ModuleToFunctionPassAdaptor on [module]
Running analysis: InnerAnalysisManagerProxy<llvm::AnalysisManager<llvm::Function>, llvm::Module> on [module]
Running pass: PassManager<llvm::Function> on __cxx_global_var_init (3 instructions)
Running pass: LineLoggerPass on __cxx_global_var_init (3 instructions)
Running pass: PassManager<llvm::Function> on _Z3retii (25 instructions)
Skipping pass LineLoggerPass on _Z3retii due to optnone attribute
Skipping pass: LineLoggerPass on _Z3retii
Running pass: PassManager<llvm::Function> on main (6 instructions)
Skipping pass LineLoggerPass on main due to optnone attribute
Skipping pass: LineLoggerPass on main
Running pass: PassManager<llvm::Function> on _GLOBAL__sub_I_X.cpp (2 instructions)
Running pass: LineLoggerPass on _GLOBAL__sub_I_X.cpp (2 instructions)
Running pass: VerifierPass on [module]
Running analysis: VerifierAnalysis on [module]

Right, so as those printouts say your pass is skipped due to the functions having the optnone attribute.

You got several options depending on what you want to do:

  • Do not use O0 as optimization level.
  • Use -disable-O0-optnone when running clang to prevent the frontend from adding the optnone attribute at O0 optimization level.
  • Make sure that your pass isn’t skipped when for example functions are marked as optnone. See Writing an LLVM Pass — LLVM 19.0.0git documentation for more info.

thanks will try

#ifndef LLVM_TRANSFORMS_UTILS_LINELOGGER_H
#define LLVM_TRANSFORMS_UTILS_LINELOGGER_H

#include "llvm/IR/PassManager.h"


namespace llvm{

    class LineLoggerPass : public PassInfoMixin<LineLoggerPass> {
        public:
        PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
    };

    static bool isRequired() { return true; }

}


#endif

Above is LineLogger.h i added isrequired to True

clang++ -S -emit-llvm -Xclang -disabl
e-O0-optnone X.cpp -o X.ll

Above are the commands i am running to compile X.cpp

X.cpp (395 Bytes)

; ModuleID = 'X.cpp'
source_filename = "X.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%"class.std::ios_base::Init" = type { i8 }
%"class.std::basic_ostream" = type { ptr, %"class.std::basic_ios" }
%"class.std::basic_ios" = type { %"class.std::ios_base", ptr, i8, i8, ptr, ptr, ptr, ptr }
%"class.std::ios_base" = type { ptr, i64, i64, i32, i32, i32, ptr, %"struct.std::ios_base::_Words", [8 x %"struct.std::ios_base::_Words"], i32, ptr, %"class.std::locale" }
%"struct.std::ios_base::_Words" = type { ptr, i64 }
%"class.std::locale" = type { ptr }

@_ZStL8__ioinit = internal global %"class.std::ios_base::Init" zeroinitializer, align 1
@__dso_handle = external hidden global i8
@_ZSt4cout = external global %"class.std::basic_ostream", align 8
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_X.cpp, ptr null }]

; Function Attrs: noinline uwtable
define internal void @__cxx_global_var_init() #0 section ".text.startup" {
  call void @_ZNSt8ios_base4InitC1Ev(ptr noundef nonnull align 1 dereferenceable(1) @_ZStL8__ioinit)
  %1 = call i32 @__cxa_atexit(ptr @_ZNSt8ios_base4InitD1Ev, ptr @_ZStL8__ioinit, ptr @__dso_handle) #3
  ret void
}

declare void @_ZNSt8ios_base4InitC1Ev(ptr noundef nonnull align 1 dereferenceable(1)) unnamed_addr #1

; Function Attrs: nounwind
declare void @_ZNSt8ios_base4InitD1Ev(ptr noundef nonnull align 1 dereferenceable(1)) unnamed_addr #2

; Function Attrs: nounwind
declare i32 @__cxa_atexit(ptr, ptr, ptr) #3

; Function Attrs: mustprogress noinline nounwind uwtable
define dso_local noundef i32 @_Z3retii(i32 noundef %0, i32 noundef %1) #4 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i32, align 4
  %6 = alloca i32, align 4
  store i32 %0, ptr %3, align 4
  store i32 %1, ptr %4, align 4
  store i32 0, ptr %5, align 4
  store i32 1, ptr %6, align 4
  br label %7

7:                                                ; preds = %15, %2
  %8 = load i32, ptr %6, align 4
  %9 = load i32, ptr %3, align 4
  %10 = icmp sle i32 %8, %9
  br i1 %10, label %11, label %19

11:                                               ; preds = %7
  %12 = load i32, ptr %6, align 4
  %13 = load i32, ptr %5, align 4
  %14 = add nsw i32 %13, %12
  store i32 %14, ptr %5, align 4
  br label %15

15:                                               ; preds = %11
  %16 = load i32, ptr %6, align 4
  %17 = load i32, ptr %4, align 4
  %18 = add nsw i32 %16, %17
  store i32 %18, ptr %6, align 4
  br label %7, !llvm.loop !6

19:                                               ; preds = %7
  %20 = load i32, ptr %5, align 4
  ret i32 %20
}

; Function Attrs: mustprogress noinline norecurse uwtable
define dso_local noundef i32 @main() #5 {
  %1 = alloca i32, align 4
  store i32 0, ptr %1, align 4
  %2 = call noundef i32 @_Z3retii(i32 noundef 10, i32 noundef 2)
  %3 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i32 noundef %2)
  %4 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8) %3, i8 noundef signext 10)
  ret i32 0
}

declare noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8), i8 noundef signext) #1

declare noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8), i32 noundef) #1

; Function Attrs: noinline uwtable
define internal void @_GLOBAL__sub_I_X.cpp() #0 section ".text.startup" {
  call void @__cxx_global_var_init()
  ret void
}

attributes #0 = { noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #3 = { nounwind }
attributes #4 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #5 = { mustprogress noinline norecurse uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

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

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 18.0.0git (https://github.com/llvm/llvm-project.git 595d780b492d3de3f5653851e7d64c122472d71a)"}
!6 = distinct !{!6, !7}
!7 = !{!"llvm.loop.mustprogress"}

This is the .ll file i obtain

opt -disable-output ~/Prog/PE/X.ll -passes=linelogger

I am running above to call pass on X.ll

Even after changes i see no output

Running pass: ModuleToFunctionPassAdaptor on [module]
Running analysis: InnerAnalysisManagerProxy<llvm::AnalysisManager<llvm::Function>, llvm::Module> on [module]
Running pass: PassManager<llvm::Function> on __cxx_global_var_init (3 instructions)
Running pass: LineLoggerPass on __cxx_global_var_init (3 instructions)
Running pass: PassManager<llvm::Function> on _Z3retii (25 instructions)
Running pass: LineLoggerPass on _Z3retii (25 instructions)
Running pass: PassManager<llvm::Function> on main (6 instructions)
Running pass: LineLoggerPass on main (6 instructions)
Running pass: PassManager<llvm::Function> on _GLOBAL__sub_I_X.cpp (2 instructions)
Running pass: LineLoggerPass on _GLOBAL__sub_I_X.cpp (2 instructions)
Running pass: VerifierPass on [module]
Running analysis: VerifierAnalysis on [module]

This is on running opt with verbose option, pls help.