ruby-llvm/ruby-llvm

View on GitHub
lib/llvm/transforms/pass_builder.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# frozen_string_literal: true

module LLVM
  class PassBuilder # rubocop:disable Metrics/ClassLength
    extend FFI::Library
    ffi_lib ["libLLVM-18.so.1", "libLLVM.so.18", "LLVM-18"]

    attr_reader :passes
    attr_accessor :inliner_threshold, :merge_functions

    # rubocop:disable Layout/LineLength
    OPT_PASSES = {
      '0' => 'always-inline,coro-cond(coro-early,cgscc(coro-split),coro-cleanup,globaldce),function(annotation-remarks),verify',
      '1' => 'annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,sroa<modify-cfg>,early-cse<>),openmp-opt,ipsccp,called-value-propagation,globalopt,function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),always-inline,require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,libcalls-shrinkwrap,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<no-nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,memcpyopt,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,coro-elide,adce,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,vector-combine,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-unroll<O1>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify',
      '2' => 'annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,sroa<modify-cfg>,early-cse<>),openmp-opt,ipsccp,called-value-propagation,globalopt,function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),always-inline,require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<no-nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,slp-vectorizer,vector-combine,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-unroll<O2>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify',
      '3' => 'annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,sroa<modify-cfg>,early-cse<>,callsite-splitting),openmp-opt,ipsccp,called-value-propagation,globalopt,function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),always-inline,require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,argpromotion,openmp-opt-cgscc,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,libcalls-shrinkwrap,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,chr,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,slp-vectorizer,vector-combine,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-unroll<O3>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify',
      's' => 'annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,sroa<modify-cfg>,early-cse<>),openmp-opt,ipsccp,called-value-propagation,globalopt,function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),always-inline,require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<no-nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,slp-vectorizer,vector-combine,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-unroll<O2>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify',
      'z' => 'annotation2metadata,forceattrs,inferattrs,coro-early,function<eager-inv>(lower-expect,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,sroa<modify-cfg>,early-cse<>),openmp-opt,ipsccp,called-value-propagation,globalopt,function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),always-inline,require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline,function-attrs<skip-non-recursive-function-attrs>,function<eager-inv;no-rerun>(sroa<modify-cfg>,early-cse<memssa>,speculative-execution<only-if-divergent-target>,jump-threading,correlated-propagation,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,aggressive-instcombine,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,reassociate,constraint-elimination,loop-mssa(loop-instsimplify,loop-simplifycfg,licm<no-allowspeculation>,loop-rotate<no-header-duplication;no-prepare-for-lto>,licm<allowspeculation>,simple-loop-unswitch<no-nontrivial;trivial>),simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop(loop-idiom,indvars,loop-deletion,loop-unroll-full),sroa<modify-cfg>,vector-combine,mldst-motion<no-split-footer-bb>,gvn<>,sccp,bdce,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,jump-threading,correlated-propagation,adce,memcpyopt,dse,move-auto-init,loop-mssa(licm<allowspeculation>),coro-elide,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>),function-attrs,function(require<should-not-run-function-passes>),coro-split)),deadargelim,coro-cleanup,globalopt,globaldce,elim-avail-extern,rpo-function-attrs,recompute-globalsaa,function<eager-inv>(float2int,lower-constant-intrinsics,loop(loop-rotate<no-header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,vector-combine,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-unroll<O2>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>),globaldce,constmerge,cg-profile,rel-lookup-table-converter,function(annotation-remarks),verify',
    }.freeze
    # rubocop:enable Layout/LineLength

    def initialize
      @passes = []
      @inliner_threshold = nil
      @merge_functions = nil
    end

    def add_function_pass
      pb = PassBuilder.new
      if block_given?
        yield pb
      end

      add_pass("function(#{pb.pass_string})")
    end

    # --O0 - Optimization level 0. Similar to clang -O0. Use -passes='default<O0>' for the new PM
    # --O1 - Optimization level 1. Similar to clang -O1. Use -passes='default<O1>' for the new PM
    # --O2 - Optimization level 2. Similar to clang -O2. Use -passes='default<O2>' for the new PM
    # --O3 - Optimization level 3. Similar to clang -O3. Use -passes='default<O3>' for the new PM
    # --Os - Like -O2 but size-conscious. Similar to clang -Os. Use -passes='default<Os>' for the new PM
    # --Oz - Like -O2 but optimize for code size above all else. Similar to clang -Oz. Use -passes='default<Oz>' for the new PM
    # @return self
    def o!(opt_level = '0', options = {})
      opt_level = opt_level.to_s
      expanded_pass = OPT_PASSES[opt_level]

      if expanded_pass.nil?
        return add_pass("default<O#{opt_level}>")
      end

      if options[:disable_inline]
        expanded_pass = expanded_pass.gsub('devirt<4>(inline,', 'devirt<4>(')
      end
      if options[:disable_always_inline]
        expanded_pass = expanded_pass.gsub('always-inline,', '')
      end
      add_pass(expanded_pass)
    end

    # @return self
    def add_pass(pass)
      passes << pass
      self
    end

    # @return self
    def dce!
      add_pass('dce')
    end

    # @return self
    def licm!
      add_pass('licm')
    end

    # A pass to simplify and canonicalize the CFG of a function.
    # This pass iteratively simplifies the entire CFG of a function. It may change
    # or remove control flow to put the CFG into a canonical form expected by
    # other passes of the mid-level optimizer. Depending on the specified options,
    # it may further optimize control-flow to create non-canonical form
    # https://llvm.org/doxygen/SimplifyCFG_8h_source.html
    # TODO: takes params
    # Options: simplifycfg<no-forward-switch-cond;forward-switch-cond;no-switch-range-to-icmp;switch-range-to-icmp;no-switch-to-lookup;switch-to-lookup;no-keep-loops;keep-loops;no-hoist-common-insts;hoist-common-insts;no-sink-common-insts;sink-common-insts;bonus-inst-threshold=N>
    # @return self
    def simplifycfg!
      add_pass('simplifycfg')
    end

    # @return self
    def scalarizer!
      add_pass('scalarizer')
    end

    # Merged Load Store Motion
    # @return self
    def mldst_motion!
      add_pass('mldst-motion')
    end

    # Global Value Numbering pass
    # https://llvm.org/doxygen/GVN_8h_source.html
    # TODO: takes params
    # @return self
    def gvn!
      add_pass('gvn')
    end

    # New Global Value Numbering pass
    # https://llvm.org/doxygen/NewGVN_8cpp.html#details
    # @return self
    def newgvn!
      add_pass('newgvn')
    end

    # hoists expressions from branches to a common dominator.
    # https://llvm.org/doxygen/GVNHoist_8cpp_source.html
    # @return self
    def gvn_hoist!
      add_pass('gvn-hoist')
    end

    # sink instructions into successors
    # https://llvm.org/doxygen/GVNSink_8cpp_source.html
    # @return self
    def gvn_sink!
      add_pass('gvn-sink')
    end

    # @return self
    def jump_threading!
      add_pass('jump-threading')
    end

    # @return self
    def indvars!
      add_pass('indvars')
    end

    # @return self
    def alignment_from_assumptions!
      add_pass('alignment-from-assumptions')
    end

    # @return self
    def loop_deletion!
      add_pass('loop-deletion')
    end

    # @return self
    def loop_idiom!
      add_pass('loop-idiom')
    end

    # @return self
    def loop_rotate!
      add_pass('loop-rotate')
    end

    # @return self
    def loop_reroll!
      add_pass('loop-reroll')
    end

    # @return self
    def loop_unroll!
      add_pass('loop-unroll')
    end

    # @return self
    def loop_unroll_and_jam!
      add_pass('loop-unroll-and-jam')
    end

    # @return self
    def simple_loop_unswitch!
      add_pass('simple-loop-unswitch')
    end

    # @return self
    def loop_unswitch!
      simple_loop_unswitch!
    end

    # TODO: takes params
    # @return self
    def loop_vectorize!
      add_pass('loop-vectorize')
    end

    # @return self
    def memcpyopt!
      add_pass('memcpyopt')
    end

    # @return self
    def sccp!
      add_pass('sccp')
    end

    # Combine instructions to form fewer, simple instructions.
    # This pass does not modify the CFG.
    # This pass is where algebraic simplification happens.
    # https://llvm.org/doxygen/InstructionCombining_8cpp_source.html
    # https://llvm.org/doxygen/InstCombineInternal_8h_source.html
    # @return self
    def instcombine!
      add_pass('instcombine')
    end

    # @return self
    def instsimplify!
      add_pass('instsimplify')
    end

    # @return self
    def loweratomic!
      add_pass('loweratomic')
    end

    # @return self
    def partially_inline_libcalls!
      add_pass('partially-inline-libcalls')
    end

    # @return self
    def reassociate!
      add_pass('reassociate')
    end

    # @return self
    def tailcallelim!
      add_pass('tailcallelim')
    end

    # @return self
    def reg2mem!
      add_pass('reg2mem')
    end

    # @return self
    def mem2reg!
      add_pass('mem2reg')
    end

    # @return self
    def verify!
      add_pass('verify')
    end

    # @return self
    def module_summary!
      add_pass('require<module-summary>')
    end

    # @return self
    def no_op_module!
      add_pass('no-op-module')
    end

    # @return self
    def no_op_cgscc!
      add_pass('no-op-cgscc')
    end

    # @return self
    def no_op_function!
      add_pass('no-op-function')
    end

    # @return self
    def stack_safety!
      add_pass('require<stack-safety>')
    end

    # A simple and fast domtree-based CSE pass.
    #
    # This pass does a simple depth-first walk over the dominator tree,
    # eliminating trivially redundant instructions and using instsimplify to
    # canonicalize things as it goes. It is intended to be fast and catch obvious
    # cases so that instcombine and other passes are more effective. It is
    # expected that a later pass of GVN will catch the interesting/hard cases.
    # https://llvm.org/doxygen/EarlyCSE_8h_source.html
    # https://llvm.org/doxygen/EarlyCSE_8cpp.html
    # @return self
    def early_cse!
      add_pass('early-cse')
    end

    # A simple and fast domtree-based CSE pass.
    # https://llvm.org/doxygen/EarlyCSE_8h_source.html
    # https://llvm.org/doxygen/EarlyCSE_8cpp.html
    # @return self
    def early_cse_memssa!
      add_pass('early-cse<memssa>')
    end

    # @return self
    def lcssa!
      add_pass('lcssa')
    end

    # @return self
    def memoryssa!
      add_pass('require<memoryssa>')
    end

    # Scalar Replacement Of Aggregates
    # https://llvm.org/doxygen/SROA_8h_source.html
    # https://llvm.org/doxygen/SROA_8cpp.html
    # @return self
    def sroa!
      add_pass('sroa')
    end

    # @return self
    def lower_expect!
      add_pass('lower-expect')
    end

    # @return self
    def cvprop!
      correlated_propagation!
    end

    # @return self
    def correlated_propagation!
      add_pass('correlated-propagation')
    end

    # @return self
    def lower_constant_intrinsics!
      add_pass('lower-constant-intrinsics')
    end

    # @return self
    def slp_vectorize!
      slp_vectorizer!
    end

    # @return self
    def slp_vectorizer!
      add_pass('slp-vectorizer')
    end

    # @return self
    def add_discriminators!
      add_pass('add-discriminators')
    end

    # @return self
    def mergereturn!
      add_pass('mergereturn')
    end

    # @return self
    def mergeicmps!
      add_pass('mergeicmps')
    end

    # @return self
    def basic_aa!
      add_pass('require<basic-aa>')
    end

    alias basicaa! basic_aa!

    # @return self
    def objc_arc_aa
      add_pass('require<objc-arc-aa>')
    end

    # @return self
    def scev_aa!
      add_pass('require<scev-aa>')
    end

    # @return self
    def scoped_noalias_aa!
      add_pass('require<scoped-noalias-aa>')
    end

    # @return self
    def tbaa!
      add_pass('require<tbaa>')
    end

    # @return self
    def gobals_aa!
      add_pass('require<globals-aa>')
    end

    # @return self
    def lowerswitch!
      add_pass('lowerswitch')
    end

    # Inlines functions marked as "always_inline".
    # https://llvm.org/doxygen/AlwaysInliner_8h_source.html
    # https://llvm.org/doxygen/AlwaysInliner_8cpp_source.html
    # @return self
    def always_inline!
      add_pass('always-inline')
    end

    # This pass performs partial inlining, typically by inlining an if statement
    # that surrounds the body of the function.
    # https://llvm.org/doxygen/PartialInlining_8h_source.html
    # https://llvm.org/doxygen/PartialInlining_8cpp_source.html
    # https://llvm.org/doxygen/PartialInlining_8h.html
    # https://llvm.org/doxygen/PartialInlining_8cpp.html
    # @return self
    def partial_inliner!
      add_pass('partial-inliner')
    end

    # This pass looks for equivalent functions that are mergable and folds them.
    # https://llvm.org/docs/MergeFunctions.html
    # https://llvm.org/doxygen/MergeFunctions_8cpp_source.html
    # https://llvm.org/doxygen/MergeFunctions_8h_source.html
    # @return self
    def mergefunc!
      add_pass('mergefunc')
    end

    # Propagate called values
    # This file implements a transformation that attaches !callees metadata to
    # indirect call sites. For a given call site, the metadata, if present,
    # indicates the set of functions the call site could possibly target at
    # run-time. This metadata is added to indirect call sites when the set of
    # possible targets can be determined by analysis and is known to be small. The
    # analysis driving the transformation is similar to constant propagation and
    # makes uses of the generic sparse propagation solver.
    # https://llvm.org/doxygen/CalledValuePropagation_8h_source.html
    # @return self
    def called_value_propagation!
      add_pass('called-value-propagation')
    end

    # @return self
    def deadargelim!
      add_pass('deadargelim')
    end

    alias dae! deadargelim!

    # ConstantMerge is designed to build up a map of available constants and eliminate duplicates when it is initialized.
    # https://llvm.org/doxygen/ConstantMerge_8cpp_source.html
    # https://llvm.org/doxygen/ConstantMerge_8h_source.html
    # @return self
    def constmerge!
      add_pass('constmerge')
    end

    alias const_merge! constmerge!

    # Aggressive Dead Code Elimination
    # @return self
    def adce!
      add_pass('adce')
    end

    # @return self
    def function_attrs!
      add_pass('function-attrs')
    end

    alias fun_attrs! function_attrs!

    # @return self
    def strip!
      add_pass('strip')
    end

    # @return self
    def strip_dead_prototypes!
      add_pass('strip-dead-prototypes')
    end

    alias sdp! strip_dead_prototypes!

    # @return self
    # TODO: test this
    def internalize!(_all_but_main = true) # rubocop:disable Style/OptionalBooleanParameter
      add_pass('internalize')
    end

    # This pass implements interprocedural sparse conditional constant propagation and merging.
    # https://llvm.org/doxygen/IPO_2SCCP_8h_source.html
    # https://llvm.org/doxygen/IPO_2SCCP_8cpp_source.html
    # @return self
    # @todo accept parameters ipsccp<no-func-spec;func-spec>
    def ipsccp!
      add_pass('ipsccp')
    end

    # @return self
    def global_opt!
      add_pass('globalopt')
    end

    # Global Dead Code Elimination
    # TODO: takes params
    # @return self
    def globaldce!
      add_pass('globaldce')
    end

    alias gdce! globaldce!

    # Bit-Tracking Dead Code Elimination pass
    # @return self
    def bdce!
      add_pass('bdce')
    end

    # Dead Store Elimination
    # his file implements a trivial dead store elimination that only considers basic-block local redundant stores.
    # https://llvm.org/doxygen/DeadStoreElimination_8h_source.html
    # @return self
    def dse!
      add_pass('dse')
    end

    # @return self
    def argpromotion!
      add_pass('argpromotion')
    end

    alias arg_promote! argpromotion!

    # The inliner pass for the new pass manager.
    # https://llvm.org/doxygen/classllvm_1_1InlinerPass.html
    # https://llvm.org/doxygen/Inliner_8h_source.html
    # https://llvm.org/doxygen/Inliner_8cpp_source.html
    # @return self
    def inline!
      add_pass('inline')
    end

    # Thread Sanitizer
    # https://clang.llvm.org/docs/ThreadSanitizer.html
    # @return self
    def tsan!
      add_pass('tsan')
    end

    # Thread Sanitiver - Module
    # https://clang.llvm.org/docs/ThreadSanitizer.html
    # @return self
    def tsan_module!
      add_pass('tsan-module')
    end

    # Hardware Assisted Address Sanitiver
    # TODO: takes params
    # https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
    # https://llvm.org/doxygen/HWAddressSanitizer_8cpp_source.html
    # @return self
    def hwasan!(_options = {})
      add_pass('hwasan')
    end

    # Address Sanitizer
    # TODO: takes params
    # https://clang.llvm.org/docs/AddressSanitizer.html
    # https://llvm.org/doxygen/AddressSanitizer_8h_source.html
    # https://llvm.org/doxygen/AddressSanitizer_8cpp_source.html
    # @return self
    def asan!(options = {})
      opt_str = options[:kernel] ? '<kernel>' : ''
      add_pass("asan#{opt_str}")
    end

    # Memory Sanitizer
    # TODO: takes params
    # https://llvm.org/doxygen/MemorySanitizer_8cpp.html
    # https://llvm.org/doxygen/MemorySanitizer_8h_source.html
    # https://clang.llvm.org/docs/MemorySanitizer.html
    # KernelMemorySanitizer only supports X86_64 and SystemZ at the moment.
    # @return self
    def msan!(options = {})
      opt_str = options[:kernel] ? '<kernel>' : ''
      add_pass("msan#{opt_str}")
    end

    # DataFlow Sanitizer
    # https://clang.llvm.org/docs/DataFlowSanitizer.html
    # @return self
    def dfsan!
      add_pass('dfsan')
    end

    # https://clang.llvm.org/docs/SanitizerCoverage.html
    # @return self
    def sancov_module!
      add_pass('sancov-module')
    end

    # https://llvm.org/docs/doxygen/SanitizerBinaryMetadata_8h_source.html
    # @return self
    def sanmd_module!
      add_pass('sanmd-module')
    end

    def run(mod, target)
      return self if passes.empty?

      error = with_options { |options| C.run_passes(mod, pass_string, target, options) }
      if !error.null?
        error_msg = C.get_error_message(error)
        # TODO: clone then dispose of error_msg, currently produces "munmap_chunk(): invalid pointer"
        # save_message = error_msg.clone
        # C.dispose_error_message(error_msg)
        raise ArgumentError, error_msg
      end
      self
    end

    def pass_string
      passes.join(',')
    end

    def ipcp!
      deprecated('ipcp! / LLVMAddIPConstantPropagationPass was removed from LLVM')
    end

    def prune_eh!
      deprecated('prune_eh! / LLVMAddPruneEHPass was removed in LLVM 16')
    end

    def simplify_libcalls!
      deprecated('simplify_libcalls! / LLVMAddSimplifyLibCallsPass was removed from LLVM')
    end

    def scalarrepl!
      deprecated('TODO: scalarrepl')
    end

    def scalarrepl_ssa!
      deprecated('TODO: scalarrepl_ssa')
    end

    def scalarrepl_threshold!(_threshold = 0)
      deprecated('TODO: scalarrepl_threshold')
    end

    def bb_vectorize!
      warn('bb_vectorize! / LLVMAddBBVectorizePass was removed from LLVM - replace with slp_vectorize!')
      slp_vectorize!
    end

    def constprop!
      warn('constprop! / LLVMAddConstantPropagationPass was removed from LLVM')
    end

    private

    attr_writer :passes

    def deprecated(message)
      warn message
      self
    end

    # updates options parameter and returns it
    def build_options!(options)
      if inliner_threshold
        C.set_inliner_threshold(options, inliner_threshold)
      end

      if merge_functions
        C.set_merge_functions(options, !!merge_functions)
      end

      options
    end

    # wraps creation and disposal of options in block
    def with_options
      options = C.create_pass_builder_options
      build_options!(options)
      yield options
    ensure
      C.dispose_pass_builder_options(options)
    end
  end

  module C
    #
    # @method run_passes(pmb, opt_level)
    # @param [OpaquePassManagerBuilder] pmb
    # @param [Integer] opt_level
    # @return [nil]
    # @scope class
    # /**
    #  * Construct and run a set of passes over a module
    #  *
    #  * This function takes a string with the passes that should be used. The format
    #  * of this string is the same as opt's -passes argument for the new pass
    #  * manager. Individual passes may be specified, separated by commas. Full
    #  * pipelines may also be invoked using `default<O3>` and friends. See opt for
    #  * full reference of the Passes format.
    #  */
    # LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes,
    #                            LLVMTargetMachineRef TM,
    #                            LLVMPassBuilderOptionsRef Options);

    attach_function :run_passes, :LLVMRunPasses, [:pointer, :string, :pointer, :pointer], :pointer

    attach_function :create_pass_builder_options, :LLVMCreatePassBuilderOptions, [], :pointer

    attach_function :dispose_pass_builder_options, :LLVMDisposePassBuilderOptions, [:pointer], :void

    attach_function(:get_error_message, :LLVMGetErrorMessage, [:pointer], :string)

    attach_function(:dispose_error_message, :LLVMDisposeErrorMessage, [:string], :void)

    attach_function(:set_inliner_threshold, :LLVMPassBuilderOptionsSetInlinerThreshold, [:pointer, :int], :void)

    attach_function(:set_merge_functions, :LLVMPassBuilderOptionsSetMergeFunctions, [:pointer, :bool], :void)
  end
end