PB.registerPipeline ParsingCallback only works for ModulePassManager (if FunctionPassManager is registered as well)

My code:
What works:

  • MPM alone
  • FPM alone
  • MPM if MPM and FPM are given
  • MPM if FPM and MPM are given (reverse order)

What does not work: FPM if MPM is also registered

    return {
        LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
        [](PassBuilder &PB) {
            PB.registerPipelineParsingCallback(
                [](StringRef Name, FunctionPassManager &FPM,ArrayRef<PassBuilder::PipelineElement>) {
                    //NOT EXECUTED
                    if(Name == "hello-new-pm-pass"){
                        FPM.addPass(fupass());
                        return true;
                    }
                    return false;
                }
            );
            PB.registerPipelineParsingCallback(
                [](StringRef Name,ModulePassManager &MPM,ArrayRef<PassBuilder::PipelineElement>) {
                    if(Name == "hello-new-pm-pass"){
                        MPM.addPass(modpass());
                        return true;
                    }
                    return false;
                }
            );
        }
    };

Why?
here happens basically the same: llvm-project/AMDGPUTargetMachine.cpp at 99e8e17313e76c50a0d6606394fed98832fd8fec · llvm/llvm-project · GitHub

here someone suggests the same method: c++ - How to register mutli LLVM Pass in one file? - Stack Overflow

I created a mwe

newpasstest.cpp

#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
using namespace llvm;
#define DEBUG_TYPE "newpass"

extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
    LLVM_DEBUG(dbgs() << "llvmGetPassPluginInfo\n");
    return {
        LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
        [](PassBuilder &PB) {
            LLVM_DEBUG(dbgs() << "PassBuilder\n");
            PB.registerPipelineParsingCallback(
                [](StringRef Name, FunctionPassManager &FPM,ArrayRef<PassBuilder::PipelineElement>) {
                    LLVM_DEBUG(dbgs() << "FunctionPassManager\n");
                    if(Name == "hello-new-pm-pass"){
                        LLVM_DEBUG(dbgs() << "add function pass\n");
                        return true;
                    }
                    LLVM_DEBUG(dbgs() << "FunctionPassManager false\n");
                    return false;
                }
            );
            PB.registerPipelineParsingCallback(
                [](StringRef Name,ModulePassManager &MPM,ArrayRef<PassBuilder::PipelineElement>) {
                    LLVM_DEBUG(dbgs() << "ModulePassManager\n");
                    if(Name == "hello-new-pm-pass"){
                        LLVM_DEBUG(dbgs() << "add module pass\n");
                        return true;
                    }
                    LLVM_DEBUG(dbgs() << "ModulePassManager false\n");
                    return false;
                }
            );
        }
    };
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.4)
find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(HandleLLVMOptions)
include(AddLLVM)
add_llvm_library( LLVMHello MODULE
    newpasstest.cpp
    PLUGIN_TOOL
    opt
)

build and execute

cmake .
make
opt -disable-output -load-pass-plugin=./LLVMHello.so  -passes="hello-new-pm-pass" -debug <()

output

Args: opt -disable-output -load-pass-plugin=./LLVMHello.so -passes=hello-new-pm-pass -debug /proc/self/fd/11 
llvmGetPassPluginInfo
PassBuilder
ModulePassManager
add module pass
ModulePassManager
add module pass

When I edit the source file

                        LLVM_DEBUG(dbgs() << "add module pass\n");
//                         return true;

I get:

llvmGetPassPluginInfo
PassBuilder
ModulePassManager
add module pass
ModulePassManager false
FunctionPassManager
add function pass
FunctionPassManager
add function pass

So the module pass is added and the function pass as well (twice)

Can anybody explain whats going on here? How can I add a Module and a Function pass in the same module?

IIRC you can try --passes="module(hello-new-pm-pass),function(hello-new-pm-pass)" where the first hello-new-pm-pass will trigger the parsing callback w/ ModulePassManager and second one will trigger the FunctionPassManager parsing callback.