diff --git a/man/crystal.1 b/man/crystal.1 index 15fed461c26c..04f183dd11e3 100644 --- a/man/crystal.1 +++ b/man/crystal.1 @@ -496,6 +496,10 @@ Low optimization Middle optimization .It Fl O3 High optimization +.It Fl \!Os +Middle optimization with focus on file size +.It Fl Oz +Middle optimization aggressively focused on file size . .Sh ENVIRONMENT\ VARIABLES .Bl -tag -width "12345678" -compact diff --git a/src/compiler/crystal/codegen/target.cr b/src/compiler/crystal/codegen/target.cr index 59c6b267c118..d487005b77ba 100644 --- a/src/compiler/crystal/codegen/target.cr +++ b/src/compiler/crystal/codegen/target.cr @@ -204,10 +204,10 @@ class Crystal::Codegen::Target end opt_level = case optimization_mode - in .o3? then LLVM::CodeGenOptLevel::Aggressive - in .o2? then LLVM::CodeGenOptLevel::Default - in .o1? then LLVM::CodeGenOptLevel::Less - in .o0? then LLVM::CodeGenOptLevel::None + in .o3? then LLVM::CodeGenOptLevel::Aggressive + in .o2?, .os?, .oz? then LLVM::CodeGenOptLevel::Default + in .o1? then LLVM::CodeGenOptLevel::Less + in .o0? then LLVM::CodeGenOptLevel::None end target = LLVM::Target.from_triple(self.to_s) diff --git a/src/compiler/crystal/command.cr b/src/compiler/crystal/command.cr index 6ae970c04fbf..7644f412bb8e 100644 --- a/src/compiler/crystal/command.cr +++ b/src/compiler/crystal/command.cr @@ -521,9 +521,12 @@ class Crystal::Command opts.on("--release", "Compile in release mode (-O3 --single-module)") do compiler.release! end - opts.on("-O LEVEL", "Optimization mode: 0 (default), 1, 2, 3") do |level| - optimization_mode = level.to_i?.try { |v| Compiler::OptimizationMode.from_value?(v) } - compiler.optimization_mode = optimization_mode || raise Error.new("Invalid optimization mode: #{level}") + opts.on("-O LEVEL", "Optimization mode: 0 (default), 1, 2, 3, s, z") do |level| + if mode = Compiler::OptimizationMode.from_level?(level) + compiler.optimization_mode = mode + else + raise Error.new("Invalid optimization mode: O#{level}") + end end end @@ -661,8 +664,12 @@ class Crystal::Command opts.on("--release", "Compile in release mode (-O3 --single-module)") do compiler.release! end - opts.on("-O LEVEL", "Optimization mode: 0 (default), 1, 2, 3") do |level| - compiler.optimization_mode = Compiler::OptimizationMode.from_value?(level.to_i) || raise Error.new("Unknown optimization mode #{level}") + opts.on("-O LEVEL", "Optimization mode: 0 (default), 1, 2, 3, s, z") do |level| + if mode = Compiler::OptimizationMode.from_level?(level) + compiler.optimization_mode = mode + else + raise Error.new("Invalid optimization mode: O#{level}") + end end opts.on("--single-module", "Generate a single LLVM module") do compiler.single_module = true diff --git a/src/compiler/crystal/compiler.cr b/src/compiler/crystal/compiler.cr index 16dc0a0e61a9..46bce1db1896 100644 --- a/src/compiler/crystal/compiler.cr +++ b/src/compiler/crystal/compiler.cr @@ -102,9 +102,27 @@ module Crystal # enables with --release flag O3 = 3 + # optimize for size, enables most O2 optimizations but aims for smaller + # code size + Os + + # optimize aggressively for size rather than speed + Oz + def suffix ".#{to_s.downcase}" end + + def self.from_level?(level : String) : self? + case level + when "0" then O0 + when "1" then O1 + when "2" then O2 + when "3" then O3 + when "s" then Os + when "z" then Oz + end + end end # Sets the Optimization mode. @@ -674,8 +692,8 @@ module Crystal exit 1 end - {% if LibLLVM::IS_LT_130 %} - protected def optimize(llvm_mod) + {% if LibLLVM::IS_LT_170 %} + private def optimize_with_pass_manager(llvm_mod) fun_pass_manager = llvm_mod.new_function_pass_manager pass_manager_builder.populate fun_pass_manager fun_pass_manager.run llvm_mod @@ -700,6 +718,8 @@ module Crystal registry.initialize_all builder = LLVM::PassManagerBuilder.new + builder.size_level = 0 + case optimization_mode in .o3? builder.opt_level = 3 @@ -712,26 +732,37 @@ module Crystal builder.use_inliner_with_threshold = 150 in .o0? # default behaviour, no optimizations + in .os? + builder.opt_level = 2 + builder.size_level = 1 + builder.use_inliner_with_threshold = 50 + in .oz? + builder.opt_level = 2 + builder.size_level = 2 + builder.use_inliner_with_threshold = 5 end - builder.size_level = 0 - builder end end - {% else %} - protected def optimize(llvm_mod) + {% end %} + + protected def optimize(llvm_mod) + {% if LibLLVM::IS_LT_130 %} + optimize_with_pass_manager(llvm_mod) + {% else %} + {% if LibLLVM::IS_LT_170 %} + # PassBuilder doesn't support Os and Oz before LLVM 17 + if @optimization_mode.os? || @optimization_mode.oz? + return optimize_with_pass_manager(llvm_mod) + end + {% end %} + LLVM::PassBuilderOptions.new do |options| - mode = case @optimization_mode - in .o3? then "default" - in .o2? then "default" - in .o1? then "default" - in .o0? then "default" - end - LLVM.run_passes(llvm_mod, mode, target_machine, options) + LLVM.run_passes(llvm_mod, "default<#{@optimization_mode}>", target_machine, options) end - end - {% end %} + {% end %} + end private def run_linker(linker_name, command, args) print_command(command, args) if verbose? diff --git a/src/llvm/function_pass_manager.cr b/src/llvm/function_pass_manager.cr index 620f24420a2e..d16e549427e8 100644 --- a/src/llvm/function_pass_manager.cr +++ b/src/llvm/function_pass_manager.cr @@ -1,4 +1,4 @@ -{% unless LibLLVM::IS_LT_130 %} +{% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} class LLVM::FunctionPassManager @@ -34,7 +34,7 @@ class LLVM::FunctionPassManager LibLLVM.dispose_pass_manager(@unwrap) end - {% unless LibLLVM::IS_LT_130 %} + {% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} struct Runner diff --git a/src/llvm/lib_llvm/initialization.cr b/src/llvm/lib_llvm/initialization.cr index fee9bf1aea4a..916ab6d9a16a 100644 --- a/src/llvm/lib_llvm/initialization.cr +++ b/src/llvm/lib_llvm/initialization.cr @@ -6,11 +6,11 @@ lib LibLLVM fun initialize_core = LLVMInitializeCore(r : PassRegistryRef) fun initialize_transform_utils = LLVMInitializeTransformUtils(r : PassRegistryRef) fun initialize_scalar_opts = LLVMInitializeScalarOpts(r : PassRegistryRef) - fun initialize_obj_c_arc_opts = LLVMInitializeObjCARCOpts(r : PassRegistryRef) + {% if LibLLVM::IS_LT_160 %} fun initialize_obj_c_arc_opts = LLVMInitializeObjCARCOpts(r : PassRegistryRef) {% end %} fun initialize_vectorization = LLVMInitializeVectorization(r : PassRegistryRef) fun initialize_inst_combine = LLVMInitializeInstCombine(r : PassRegistryRef) fun initialize_ipo = LLVMInitializeIPO(r : PassRegistryRef) - fun initialize_instrumentation = LLVMInitializeInstrumentation(r : PassRegistryRef) + {% if LibLLVM::IS_LT_160 %} fun initialize_instrumentation = LLVMInitializeInstrumentation(r : PassRegistryRef) {% end %} fun initialize_analysis = LLVMInitializeAnalysis(r : PassRegistryRef) fun initialize_ipa = LLVMInitializeIPA(r : PassRegistryRef) fun initialize_code_gen = LLVMInitializeCodeGen(r : PassRegistryRef) diff --git a/src/llvm/lib_llvm/transforms/pass_builder.cr b/src/llvm/lib_llvm/transforms/pass_builder.cr index 41a569c4f0a2..38a952c28f3c 100644 --- a/src/llvm/lib_llvm/transforms/pass_builder.cr +++ b/src/llvm/lib_llvm/transforms/pass_builder.cr @@ -10,4 +10,10 @@ lib LibLLVM fun create_pass_builder_options = LLVMCreatePassBuilderOptions : PassBuilderOptionsRef fun dispose_pass_builder_options = LLVMDisposePassBuilderOptions(options : PassBuilderOptionsRef) + {% unless LibLLVM::IS_LT_170 %} + fun pass_builder_options_set_inliner_threshold = LLVMPassBuilderOptionsSetInlinerThreshold(PassBuilderOptionsRef, Int) + {% end %} + fun pass_builder_options_set_loop_unrolling = LLVMPassBuilderOptionsSetLoopUnrolling(PassBuilderOptionsRef, Bool) + fun pass_builder_options_set_loop_vectorization = LLVMPassBuilderOptionsSetLoopVectorization(PassBuilderOptionsRef, Bool) + fun pass_builder_options_set_slp_vectorization = LLVMPassBuilderOptionsSetSLPVectorization(PassBuilderOptionsRef, Bool) end diff --git a/src/llvm/module.cr b/src/llvm/module.cr index e4d9dc110231..f216d485055c 100644 --- a/src/llvm/module.cr +++ b/src/llvm/module.cr @@ -84,7 +84,7 @@ class LLVM::Module self end - {% unless LibLLVM::IS_LT_130 %} + {% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} def new_function_pass_manager diff --git a/src/llvm/module_pass_manager.cr b/src/llvm/module_pass_manager.cr index c0bcad4e6198..b02fdb21f930 100644 --- a/src/llvm/module_pass_manager.cr +++ b/src/llvm/module_pass_manager.cr @@ -1,4 +1,4 @@ -{% unless LibLLVM::IS_LT_130 %} +{% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} class LLVM::ModulePassManager diff --git a/src/llvm/pass_builder_options.cr b/src/llvm/pass_builder_options.cr index 3134acb0b93a..82104b03bd8a 100644 --- a/src/llvm/pass_builder_options.cr +++ b/src/llvm/pass_builder_options.cr @@ -25,4 +25,22 @@ class LLVM::PassBuilderOptions LibLLVM.dispose_pass_builder_options(self) end + + {% unless LibLLVM::IS_LT_170 %} + def set_inliner_threshold(threshold : Int) + LibLLVM.pass_builder_options_set_inliner_threshold(self, threshold) + end + {% end %} + + def set_loop_unrolling(enabled : Bool) + LibLLVM.pass_builder_options_set_loop_unrolling(self, enabled) + end + + def set_loop_vectorization(enabled : Bool) + LibLLVM.pass_builder_options_set_loop_vectorization(self, enabled) + end + + def set_slp_vectorization(enabled : Bool) + LibLLVM.pass_builder_options_set_slp_vectorization(self, enabled) + end end diff --git a/src/llvm/pass_manager_builder.cr b/src/llvm/pass_manager_builder.cr index 148cd19fca35..46e716e89b2f 100644 --- a/src/llvm/pass_manager_builder.cr +++ b/src/llvm/pass_manager_builder.cr @@ -1,4 +1,4 @@ -{% unless LibLLVM::IS_LT_130 %} +{% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} class LLVM::PassManagerBuilder diff --git a/src/llvm/pass_registry.cr b/src/llvm/pass_registry.cr index 7a1377c35716..652cfaed0442 100644 --- a/src/llvm/pass_registry.cr +++ b/src/llvm/pass_registry.cr @@ -1,4 +1,4 @@ -{% unless LibLLVM::IS_LT_130 %} +{% unless LibLLVM::IS_LT_170 %} @[Deprecated("The legacy pass manager was removed in LLVM 17. Use `LLVM::PassBuilderOptions` instead")] {% end %} struct LLVM::PassRegistry @@ -9,17 +9,32 @@ struct LLVM::PassRegistry def initialize(@unwrap : LibLLVM::PassRegistryRef) end - Inits = %w(core transform_utils scalar_opts obj_c_arc_opts vectorization inst_combine ipo instrumentation analysis ipa code_gen target) + {% begin %} + Inits = %w[ + initialize_core + initialize_transform_utils + initialize_scalar_opts + {% if LibLLVM::IS_LT_160 %} initialize_obj_c_arc_opts {% end %} + initialize_vectorization + initialize_inst_combine + initialize_ipo + {% if LibLLVM::IS_LT_160 %} initialize_instrumentation {% end %} + initialize_analysis + initialize_ipa + initialize_code_gen + initialize_target + ] + {% end %} {% for name in Inits %} - def initialize_{{name.id}} - LibLLVM.initialize_{{name.id}} self + def {{name.id}} + LibLLVM.{{name.id}} self end {% end %} def initialize_all {% for name in Inits %} - initialize_{{name.id}} + {{name.id}} {% end %} end