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).

This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check with no new failures.  Ok for mainline?


2021-12-10  Roger Sayle  <ro...@nextmovesoftware.com>

gcc/ChangeLog
        PR target/32803
        * common.opt (Oz): New command line 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.


Thanks in advance,
Roger
--

diff --git a/gcc/common.opt b/gcc/common.opt
index fa0a44f..bf6aed2 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -483,6 +483,10 @@ Og
 Common Optimization
 Optimize for debugging experience rather than speed or size.
 
+Oz
+Common Optimization
+Optimize for space aggressively rather than speed.
+
 Q
 Driver
 
diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c
index 54f642d..7d2b7e5 100644
--- a/gcc/lto-wrapper.c
+++ b/gcc/lto-wrapper.c
@@ -370,12 +370,14 @@ merge_and_complain (vec<cl_decoded_option> 
&decoded_options,
        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;
@@ -407,6 +409,7 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options,
                  level = MAX (level, 1);
                  break;
                case OPT_Os:
+               case OPT_Oz:
                  level = MAX (level, 2);
                  break;
                default:
@@ -428,6 +431,7 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options,
                  level = MAX (level, 1);
                  break;
                case OPT_Os:
+               case OPT_Oz:
                  level = MAX (level, 2);
                  break;
                default:
@@ -725,6 +729,7 @@ append_compiler_options (obstack *argv_obstack, 
vec<cl_decoded_option> opts)
        case OPT_Ofast:
        case OPT_Og:
        case OPT_Os:
+       case OPT_Oz:
          break;
 
        case OPT_Xassembler:
diff --git a/gcc/optc-save-gen.awk b/gcc/optc-save-gen.awk
index e363ac7..81c2db0 100644
--- a/gcc/optc-save-gen.awk
+++ b/gcc/optc-save-gen.awk
@@ -93,7 +93,7 @@ var_opt_char[1] = "optimize_size";
 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";
 
diff --git a/gcc/opts.c b/gcc/opts.c
index b16497e..7fba3d7a 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -745,6 +745,15 @@ default_options_optimization (struct gcc_options *opts,
          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;
@@ -2609,6 +2618,7 @@ common_handle_option (struct gcc_options *opts,
     case OPT_Os:
     case OPT_Ofast:
     case OPT_Og:
+    case OPT_Oz:
       /* Currently handled in a prescan.  */
       break;
 
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 4e9fae8..bde22b8 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -2213,7 +2213,14 @@
     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))
@@ -2431,6 +2438,14 @@
       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}";
 
diff --git a/gcc/testsuite/gcc.target/i386/pr32803.c 
b/gcc/testsuite/gcc.target/i386/pr32803.c
new file mode 100644
index 0000000..f26f07a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32803.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-Oz" } */
+
+int foo()
+{
+  return 25;
+}
+
+/* { dg-final { scan-assembler "push" } } */
+/* { dg-final { scan-assembler "pop" } } */

Reply via email to