This patch adds support for an -Oz command line option, aggressively
optimizing for size at the expense of performance. GCC's current -Os
provides a reasonable balance of size and performance, whereas -Oz is
probably only useful for code size benchmarks such as CSiBE. Or so I
thought until I read in https://news.ycombinator.com/item?id=
25408853
that clang's -Oz sometimes outperforms -O[23s]; I suspect modern instruction
decode stages can treat "pushq $1; popq %rax" as a short uop encoding.
Instead of introducing a new global variable, this patch simply abuses
the existing optimize_size by setting its value to 2. The only change
in behaviour is the tweak to the i386 backend implementing the suggestion
in PR target/32803 to use a short push/pop sequence for loading small
immediate values (-128..127) on x86, matching the behaviour of LLVM.
On x86_64, the simple function:
int foo() { return 25; }
currently generates with -Os:
foo: movl $25, %eax // 5 bytes
ret
With the proposed -Oz, it generates:
foo: pushq $25 // 2 bytes
popq %rax // 1 byte
ret
On CSiBE, this results in a 0.94% improvement (3703513 bytes total
down to 3668516 bytes).
2021-12-18 Roger Sayle <roger@nextmovesoftware.com>
gcc/ChangeLog
PR target/32803
* common.opt (Oz): New command line option.
* doc/invoke.texi: Document the new -Oz option.
* lto-wrapper.c (merge_and_complain, append_compiler_options):
Treat OPT_Oz as synonymous with OPT_Os.
* optc-save-gen.awk: Increase maximum value of optimize_size to 2.
* opts.c (default_options_optimization) [OPT_Oz]: Handle OPT_Oz
just like OPT_Os, except set opt->x_optimize_size to 2.
(common_handle_option): Skip OPT_Oz just like OPT_Os.
* config/i386/i386.md (*movdi_internal): Use a push/pop sequence
for suitable SImode TYPE_IMOV moves when optimize_size > 1.
(*movsi_internal): Likewise.
gcc/testsuite/ChangeLog
PR target/32803
* gcc.target/i386/pr32803.c: New test case.
Common Optimization
Optimize for debugging experience rather than speed or size.
+Oz
+Common Optimization
+Optimize for space aggressively rather than speed.
+
Q
Driver
case TYPE_IMOV:
gcc_assert (!flag_pic || LEGITIMATE_PIC_OPERAND_P (operands[1]));
if (get_attr_mode (insn) == MODE_SI)
- return "mov{l}\t{%k1, %k0|%k0, %k1}";
+ {
+ if (optimize_size > 1
+ && TARGET_64BIT
+ && CONST_INT_P (operands[1])
+ && IN_RANGE (INTVAL (operands[1]), -128, 127))
+ return "push{q}\t%1\n\tpop{q}\t%0";
+ return "mov{l}\t{%k1, %k0|%k0, %k1}";
+ }
else if (which_alternative == 4)
return "movabs{q}\t{%1, %0|%0, %1}";
else if (ix86_use_lea_for_mov (insn, operands))
gcc_assert (!flag_pic || LEGITIMATE_PIC_OPERAND_P (operands[1]));
if (ix86_use_lea_for_mov (insn, operands))
return "lea{l}\t{%E1, %0|%0, %E1}";
+ else if (optimize_size > 1
+ && CONST_INT_P (operands[1])
+ && IN_RANGE (INTVAL (operands[1]), -128, 127))
+ {
+ if (TARGET_64BIT)
+ return "push{q}\t%1\n\tpop{q}\t%q0";
+ return "push{l}\t%1\n\tpop{l}\t%0";
+ }
else
return "mov{l}\t{%1, %0|%0, %1}";
-fipa-ra -fvariable-expansion-in-unroller -fvect-cost-model -fvpt @gol
-fweb -fwhole-program -fwpa -fuse-linker-plugin -fzero-call-used-regs @gol
--param @var{name}=@var{value}
--O -O0 -O1 -O2 -O3 -Os -Ofast -Og}
+-O -O0 -O1 -O2 -O3 -Os -Ofast -Og -Oz}
@item Program Instrumentation Options
@xref{Instrumentation Options,,Program Instrumentation Options}.
-fmove-loop-invariants -fmove-loop-stores -fssa-phiopt @gol
-ftree-bit-ccp -ftree-dse -ftree-pta -ftree-sra}
+@item -Oz
+@opindex Oz
+Optimize aggressively for size rather than speed. This may increase
+the number of instructions executed if those instructions require
+fewer bytes to encode. @option{-Oz} behaves similarly to @option{-Os}
+including enabling most @option{-O2} optimizations.
+
@end table
If you use multiple @option{-O} options, with or without level numbers,
case OPT_Ofast:
case OPT_Og:
case OPT_Os:
+ case OPT_Oz:
existing_opt = -1;
for (j = 0; j < decoded_options.length (); ++j)
if (decoded_options[j].opt_index == OPT_O
|| decoded_options[j].opt_index == OPT_Ofast
|| decoded_options[j].opt_index == OPT_Og
- || decoded_options[j].opt_index == OPT_Os)
+ || decoded_options[j].opt_index == OPT_Os
+ || decoded_options[j].opt_index == OPT_Oz)
{
existing_opt = j;
break;
level = MAX (level, 1);
break;
case OPT_Os:
+ case OPT_Oz:
level = MAX (level, 2);
break;
default:
level = MAX (level, 1);
break;
case OPT_Os:
+ case OPT_Oz:
level = MAX (level, 2);
break;
default:
case OPT_Ofast:
case OPT_Og:
case OPT_Os:
+ case OPT_Oz:
break;
case OPT_Xassembler:
var_opt_char[2] = "optimize_debug";
var_opt_char[3] = "optimize_fast";
var_opt_range["optimize"] = "0, 255";
-var_opt_range["optimize_size"] = "0, 1";
+var_opt_range["optimize_size"] = "0, 2";
var_opt_range["optimize_debug"] = "0, 1";
var_opt_range["optimize_fast"] = "0, 1";
opts->x_optimize_debug = 0;
break;
+ case OPT_Oz:
+ opts->x_optimize_size = 2;
+
+ /* Optimizing for size forces optimize to be 2. */
+ opts->x_optimize = 2;
+ opts->x_optimize_fast = 0;
+ opts->x_optimize_debug = 0;
+ break;
+
case OPT_Ofast:
/* -Ofast only adds flags to -O3. */
opts->x_optimize_size = 0;
case OPT_Os:
case OPT_Ofast:
case OPT_Og:
+ case OPT_Oz:
/* Currently handled in a prescan. */
break;
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-Oz" } */
+
+int foo()
+{
+ return 25;
+}
+
+/* { dg-final { scan-assembler "push" } } */
+/* { dg-final { scan-assembler "pop" } } */