Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compiler flags -Os and -Oz to optimize binary size #14463

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/compiler/crystal/codegen/target.cr
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,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)
Expand Down
17 changes: 12 additions & 5 deletions src/compiler/crystal/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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("Unknown optimization mode #{level}")
end
end
end

Expand Down Expand Up @@ -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("Unknown optimization mode #{level}")
end
end
opts.on("--single-module", "Generate a single LLVM module") do
compiler.single_module = true
Expand Down
52 changes: 45 additions & 7 deletions src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -712,6 +730,12 @@ module Crystal
builder.use_inliner_with_threshold = 150
in .o0?
# default behaviour, no optimizations
in .os?
builder.opt_level = 2
builder.use_inliner_with_threshold = 50
in .oz?
builder.opt_level = 2
builder.use_inliner_with_threshold = 5
end

builder.size_level = 0
ysbaddaden marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -722,13 +746,27 @@ module Crystal
{% else %}
protected def optimize(llvm_mod)
LLVM::PassBuilderOptions.new do |options|
mode = case @optimization_mode
in .o3? then "default<O3>"
in .o2? then "default<O2>"
in .o1? then "default<O1>"
in .o0? then "default<O0>"
end
LLVM.run_passes(llvm_mod, mode, target_machine, options)
case @optimization_mode
in .o0?, .o1?, .o2?, .o3?
passes = "default<#{@optimization_mode}>"
in .os?
{% if LibLLVM::IS_LT_170 %}
passes = "default<O2>"
options.set_inliner_threshold(50)
{% else %}
passes = "default<Os>"
{% end %}
in .oz?
{% if LibLLVM::IS_LT_170 %}
passes = "default<O2>"
options.set_inliner_threshold(5)
options.set_loop_vectorization(false)
options.set_slp_vectorization(false) # NOTE: clang keeps SLP vectorization enabled
{% else %}
passes = "default<Oz>"
{% end %}
end
LLVM.run_passes(llvm_mod, passes, target_machine, options)
end
end
{% end %}
Expand Down
4 changes: 4 additions & 0 deletions src/llvm/lib_llvm/transforms/pass_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ lib LibLLVM

fun create_pass_builder_options = LLVMCreatePassBuilderOptions : PassBuilderOptionsRef
fun dispose_pass_builder_options = LLVMDisposePassBuilderOptions(options : PassBuilderOptionsRef)
fun pass_builder_options_set_inliner_threshold = LLVMPassBuilderOptionsSetInlinerThreshold(PassBuilderOptionsRef, Int)
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
16 changes: 16 additions & 0 deletions src/llvm/pass_builder_options.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,20 @@ class LLVM::PassBuilderOptions

LibLLVM.dispose_pass_builder_options(self)
end

def set_inliner_threshold(threshold : Int)
LibLLVM.pass_builder_options_set_inliner_threshold(self, threshold)
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
Loading