On Thu, 30 Jul 2026 05:09:49 +0200
Anton Johansson <[email protected]> wrote:

> Make an early pass over all functions in the input module and filter out
> functions with:
> 
>   1. Invalid return type, or;
>   2. No helper-to-tcg annotation and not called by a function with such
>      a annotation.
> 
> A commandline option is also added to force translation of all functions
> starting with "helper_".
> 
> Signed-off-by: Anton Johansson <[email protected]>
> ---
>  .../helper-to-tcg/include/CmdLineOptions.hpp  |  2 +
>  .../include/PrepareForOptPass.hpp             |  7 +-
>  subprojects/helper-to-tcg/src/Pipeline.cpp    |  5 ++
>  .../PrepareForOptPass/PrepareForOptPass.cpp   | 86 +++++++++++++++++++
>  4 files changed, 96 insertions(+), 4 deletions(-)
> 
> diff --git a/subprojects/helper-to-tcg/include/CmdLineOptions.hpp 
> b/subprojects/helper-to-tcg/include/CmdLineOptions.hpp
> index 93706b78c5..ca1cb59835 100644
> --- a/subprojects/helper-to-tcg/include/CmdLineOptions.hpp
> +++ b/subprojects/helper-to-tcg/include/CmdLineOptions.hpp
> @@ -21,3 +21,5 @@
>  
>  // Options for pipeline
>  extern llvm::cl::list<std::string> InputFiles;
> +// Options for PrepareForOptPass
> +extern llvm::cl::opt<bool> TranslateAllHelpers;
> diff --git a/subprojects/helper-to-tcg/include/PrepareForOptPass.hpp 
> b/subprojects/helper-to-tcg/include/PrepareForOptPass.hpp
> index e007243578..08ca9a43bb 100644
> --- a/subprojects/helper-to-tcg/include/PrepareForOptPass.hpp
> +++ b/subprojects/helper-to-tcg/include/PrepareForOptPass.hpp
> @@ -29,11 +29,10 @@
>  
>  class PrepareForOptPass : public llvm::PassInfoMixin<PrepareForOptPass> {
>      AnnotationMapTy &ResultAnnotations;
> -public:
> +
> +  public:
>      PrepareForOptPass(AnnotationMapTy &ResultAnnotations)
> -        : ResultAnnotations(ResultAnnotations)
> -    {
> -    }
> +        : ResultAnnotations(ResultAnnotations) {}
>      llvm::PreservedAnalyses run(llvm::Module &M,
>                                  llvm::ModuleAnalysisManager &MAM);
>  };
> diff --git a/subprojects/helper-to-tcg/src/Pipeline.cpp 
> b/subprojects/helper-to-tcg/src/Pipeline.cpp
> index 051611b0f3..89637eaec6 100644
> --- a/subprojects/helper-to-tcg/src/Pipeline.cpp
> +++ b/subprojects/helper-to-tcg/src/Pipeline.cpp
> @@ -65,6 +65,11 @@ static cl::opt<std::string>
>                cl::init(""), cl::cat(Cat));
>  #endif
>  
> +// Options for PrepareForOptPass
> +cl::opt<bool> TranslateAllHelpers(
> +    "translate-all-helpers", cl::init(false),
> +    cl::desc("Translate all functions starting with helper_*"), 
> cl::cat(Cat));
> +
>  // Define a TargetTransformInfo (TTI) subclass, this allows for overriding
>  // common per-llvm-target information expected by other LLVM passes, such
>  // as the width of the largest scalar/vector registers.  Needed for 
> consistent
> diff --git 
> a/subprojects/helper-to-tcg/src/PrepareForOptPass/PrepareForOptPass.cpp 
> b/subprojects/helper-to-tcg/src/PrepareForOptPass/PrepareForOptPass.cpp
> index 1228ac952f..df6d9eeec8 100644
> --- a/subprojects/helper-to-tcg/src/PrepareForOptPass/PrepareForOptPass.cpp
> +++ b/subprojects/helper-to-tcg/src/PrepareForOptPass/PrepareForOptPass.cpp
> @@ -16,17 +16,25 @@
>  //
>  
>  #include "PrepareForOptPass.hpp"
> +#include "CmdLineOptions.hpp"
>  #include "Error.hpp"
> +#include "FunctionAnnotation.hpp"
> +#include "LlvmCompat.hpp"
>  
> +#include <llvm/ADT/SmallPtrSet.h>
>  #include <llvm/ADT/StringRef.h>
>  #include <llvm/ADT/StringSet.h>
>  #include <llvm/Demangle/Demangle.h>
>  #include <llvm/IR/Constants.h>
>  #include <llvm/IR/Function.h>
>  #include <llvm/IR/Instruction.h>
> +#include <llvm/IR/Instructions.h>
>  #include <llvm/IR/Module.h>
>  #include <llvm/Support/Debug.h>
>  
> +#include <queue>
> +#include <set>
> +
>  #define DEBUG_TYPE "prepare-for-opt"
>  
>  using namespace llvm;
> @@ -156,9 +164,87 @@ static void collectAnnotations(Module &M, 
> AnnotationMapTy &ResultAnnotations) {
>      });
>  }
>  
> +inline bool hasValidReturnTy(const Module &M, const Function *F) {
> +    Type *RetTy = F->getReturnType();
> +    return RetTy->isStructTy() || RetTy == Type::getVoidTy(F->getContext()) 
> ||
> +           RetTy == Type::getInt8Ty(M.getContext()) ||
> +           RetTy == Type::getInt16Ty(M.getContext()) ||
> +           RetTy == Type::getInt32Ty(M.getContext()) ||
> +           RetTy == Type::getInt64Ty(M.getContext());
> +}
> +
> +// Functions that should be removed:
> +//   - No helper-to-tcg annotation (if TranslateAllHelpers == false);
> +//   - Invalid (non-integer/void) return type
> +static bool shouldRemoveFunction(const Module &M, const Function &F,
> +                                 const AnnotationMapTy &AnnotationMap) {
> +    if (F.isDeclaration()) {
> +        return false;
> +    }
> +
> +    if (!hasValidReturnTy(M, &F)) {

I'd do this check after the others and write something to `llvm::errs()`.
If a helper has been explicitly marked as to be translated, but it's
unsuitable, we need to notify the user.

> +        return true;
> +    }
> +
> +    std::queue<const Function *> Worklist;
> +    std::set<const Function *> Visited;
> +    Worklist.push(&F);
> +    while (!Worklist.empty()) {
> +        const Function *F = Worklist.front();
> +        Worklist.pop();
> +        if (F->isDeclaration() or Visited.find(F) != Visited.end()) {
> +            continue;
> +        }
> +        Visited.insert(F);
> +
> +        if (TranslateAllHelpers and
> +            compat::isFunctionQemuHelper(F->getName())) {
> +            // If --translate-all-helpers is provided and `F` starts with
> +            // "helper_*", then don't skip it.
> +            return false;
> +        } else if (auto It = AnnotationMap.find(F); It != 
> AnnotationMap.end()) {
> +            // Otherwise check "helper-to-tcg" annotation.
> +            const Annotations &Ann = It->second;
> +            if (Ann.isSet(FunctionAnnotation::HelperToTcg)) {
> +                return false;
> +            }
> +        }
> +
> +        // Push functions that call `F` to the worklist, this way we retain
> +        // functions that are being called by functions with the 
> "helper-to-tcg"
> +        // annotation.
> +        for (const User *U : F->users()) {
> +            auto Call = dyn_cast<CallInst>(U);

Use `CallBase` so you also catch `InvokeInst`.

> +            if (!Call) {
> +                continue;
> +            }
> +            const Function *ParentF = Call->getParent()->getParent();
> +            Worklist.push(ParentF);
> +        }
> +    }
> +
> +    return true;
> +}
> +
> +static void cullUnusedFunctions(Module &M, AnnotationMapTy &Annotations) {

I'd call it `purgeUnusedFunctions`, but this is fine as well.

> +    SmallPtrSet<Function *, 16> FunctionsToRemove;
> +    for (auto &F : M) {
> +        if (shouldRemoveFunction(M, F, Annotations)) {
> +            FunctionsToRemove.insert(&F);
> +        }
> +    }

This is not super efficient, for every function you walk the call graph.

I'd walk the list of functions and mark those we need to keep due to
name/annotation.
Then I'd walk the CallGraph (check out CallGraph.h) starting from them
(and stopping if you get to a function you already visited.

It'd also be nice to fail in case we have indirect calls, at some point.

> +
> +    for (Function *F : FunctionsToRemove) {
> +        Annotations.erase(F);
> +        F->setComdat(nullptr);
> +        F->deleteBody();
> +    }
> +}
> +
>  PreservedAnalyses PrepareForOptPass::run(Module &M,
>                                           ModuleAnalysisManager &MAM) {
>      demangleFunctionNames(M);
>      collectAnnotations(M, ResultAnnotations);
> +    cullUnusedFunctions(M, ResultAnnotations);
>      return PreservedAnalyses::none();
>  }
> -- 
> 2.52.0

Reviewed-by: Alessandro Di Federico <[email protected]>

-- 
Alessandro Di Federico
rev.ng Labs

Reply via email to