On Tue, Jan 16, 2018 at 12:09 AM, Bill Schmidt <wschm...@linux.vnet.ibm.com> wrote: > Hi, > > This patch supercedes v2: > https://gcc.gnu.org/ml/gcc-patches/2018-01/msg01204.html, > and fixes the problems noted in its review. It also adds the test cases from > https://gcc.gnu.org/ml/gcc-patches/2018-01/msg01261.html and adjusts them > according > to the results of the review. > > There is more function to be provided in a future patch: Sibling calls for > all ABIs, > and indirect calls for non-ELFv2 ABIs. I'm getting close on that, but I > think it's > better to keep that separate at this point. > > Bootstrapped and tested on powerpc64-linux-gnu and powerpc64le-linux-gnu with > no > regressions. Is this okay for trunk?
Did you consider simply removing the tablejump/casesi support so expansion always expands to a balanced tree? At least if we have any knobs to tune we should probably tweak them away from the indirect jump using variants with -mno-speculate-indirect-jumps, right? Performance optimization, so shouldn't block this patch - I just thought I should probably mention this. Richard. > Thanks, > Bill > > > [gcc] > > 2018-01-15 Bill Schmidt <wschm...@linux.vnet.ibm.com> > > * config/rs6000/rs6000.c (rs6000_opt_vars): Add entry for > -mspeculate-indirect-jumps. > * config/rs6000/rs6000.md (*call_indirect_elfv2<mode>): Disable > for -mno-speculate-indirect-jumps. > (*call_indirect_elfv2<mode>_nospec): New define_insn. > (*call_value_indirect_elfv2<mode>): Disable for > -mno-speculate-indirect-jumps. > (*call_value_indirect_elfv2<mode>_nospec): New define_insn. > (indirect_jump): Emit different RTL for > -mno-speculate-indirect-jumps. > (*indirect_jump<mode>): Disable for > -mno-speculate-indirect-jumps. > (*indirect_jump<mode>_nospec): New define_insn. > (tablejump): Emit different RTL for > -mno-speculate-indirect-jumps. > (tablejumpsi): Disable for -mno-speculate-indirect-jumps. > (tablejumpsi_nospec): New define_expand. > (tablejumpdi): Disable for -mno-speculate-indirect-jumps. > (tablejumpdi_nospec): New define_expand. > (*tablejump<mode>_internal1): Disable for > -mno-speculate-indirect-jumps. > (*tablejump<mode>_internal1_nospec): New define_insn. > * config/rs6000/rs6000.opt (mspeculate-indirect-jumps): New > option. > > [gcc/testsuite] > > 2018-01-15 Bill Schmidt <wschm...@linux.vnet.ibm.com> > > * gcc.target/powerpc/safe-indirect-jump-1.c: New file. > * gcc.target/powerpc/safe-indirect-jump-2.c: New file. > * gcc.target/powerpc/safe-indirect-jump-3.c: New file. > * gcc.target/powerpc/safe-indirect-jump-4.c: New file. > * gcc.target/powerpc/safe-indirect-jump-5.c: New file. > * gcc.target/powerpc/safe-indirect-jump-6.c: New file. > > > Index: gcc/config/rs6000/rs6000.c > =================================================================== > --- gcc/config/rs6000/rs6000.c (revision 256364) > +++ gcc/config/rs6000/rs6000.c (working copy) > @@ -36726,6 +36726,9 @@ static struct rs6000_opt_var const rs6000_opt_vars > { "sched-epilog", > offsetof (struct gcc_options, x_TARGET_SCHED_PROLOG), > offsetof (struct cl_target_option, x_TARGET_SCHED_PROLOG), }, > + { "speculate-indirect-jumps", > + offsetof (struct gcc_options, x_rs6000_speculate_indirect_jumps), > + offsetof (struct cl_target_option, x_rs6000_speculate_indirect_jumps), }, > }; > > /* Inner function to handle attribute((target("..."))) and #pragma GCC target > Index: gcc/config/rs6000/rs6000.md > =================================================================== > --- gcc/config/rs6000/rs6000.md (revision 256364) > +++ gcc/config/rs6000/rs6000.md (working copy) > @@ -11222,11 +11222,22 @@ > (match_operand 1 "" "g,g")) > (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" > "n,n")] UNSPEC_TOCSLOT)) > (clobber (reg:P LR_REGNO))] > - "DEFAULT_ABI == ABI_ELFv2" > + "DEFAULT_ABI == ABI_ELFv2 && rs6000_speculate_indirect_jumps" > "b%T0l\;<ptrload> 2,%2(1)" > [(set_attr "type" "jmpreg") > (set_attr "length" "8")]) > > +;; Variant with deliberate misprediction. > +(define_insn "*call_indirect_elfv2<mode>_nospec" > + [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l")) > + (match_operand 1 "" "g,g")) > + (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" > "n,n")] UNSPEC_TOCSLOT)) > + (clobber (reg:P LR_REGNO))] > + "DEFAULT_ABI == ABI_ELFv2 && !rs6000_speculate_indirect_jumps" > + "crset eq\;beq%T0l-\;<ptrload> 2,%2(1)" > + [(set_attr "type" "jmpreg") > + (set_attr "length" "12")]) > + > (define_insn "*call_value_indirect_elfv2<mode>" > [(set (match_operand 0 "" "") > (call (mem:SI (match_operand:P 1 "register_operand" "c,*l")) > @@ -11233,11 +11244,22 @@ > (match_operand 2 "" "g,g"))) > (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" > "n,n")] UNSPEC_TOCSLOT)) > (clobber (reg:P LR_REGNO))] > - "DEFAULT_ABI == ABI_ELFv2" > + "DEFAULT_ABI == ABI_ELFv2 && rs6000_speculate_indirect_jumps" > "b%T1l\;<ptrload> 2,%3(1)" > [(set_attr "type" "jmpreg") > (set_attr "length" "8")]) > > +; Variant with deliberate misprediction. > +(define_insn "*call_value_indirect_elfv2<mode>_nospec" > + [(set (match_operand 0 "" "") > + (call (mem:SI (match_operand:P 1 "register_operand" "c,*l")) > + (match_operand 2 "" "g,g"))) > + (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" > "n,n")] UNSPEC_TOCSLOT)) > + (clobber (reg:P LR_REGNO))] > + "DEFAULT_ABI == ABI_ELFv2 && !rs6000_speculate_indirect_jumps" > + "crset eq\;beq%T1l-\;<ptrload> 2,%3(1)" > + [(set_attr "type" "jmpreg") > + (set_attr "length" "12")]) > > ;; Call subroutine returning any type. > (define_expand "untyped_call" > @@ -12917,15 +12939,34 @@ > [(set_attr "type" "jmpreg")]) > > (define_expand "indirect_jump" > - [(set (pc) (match_operand 0 "register_operand"))]) > + [(set (pc) (match_operand 0 "register_operand"))] > + "" > +{ > + if (!rs6000_speculate_indirect_jumps) { > + rtx ccreg = gen_reg_rtx (CCmode); > + if (Pmode == DImode) > + emit_jump_insn (gen_indirect_jumpdi_nospec (operands[0], ccreg)); > + else > + emit_jump_insn (gen_indirect_jumpsi_nospec (operands[0], ccreg)); > + DONE; > + } > +}) > > (define_insn "*indirect_jump<mode>" > [(set (pc) > (match_operand:P 0 "register_operand" "c,*l"))] > - "" > + "rs6000_speculate_indirect_jumps" > "b%T0" > [(set_attr "type" "jmpreg")]) > > +(define_insn "indirect_jump<mode>_nospec" > + [(set (pc) (match_operand:P 0 "register_operand" "c,*l")) > + (clobber (match_operand:CC 1 "cc_reg_operand" "=y,y"))] > + "!rs6000_speculate_indirect_jumps" > + "crset %E1\;beq%T0- %1\;b ." > + [(set_attr "type" "jmpreg") > + (set_attr "length" "12")]) > + > ;; Table jump for switch statements: > (define_expand "tablejump" > [(use (match_operand 0)) > @@ -12933,9 +12974,27 @@ > "" > { > if (TARGET_32BIT) > - emit_jump_insn (gen_tablejumpsi (operands[0], operands[1])); > + { > + if (rs6000_speculate_indirect_jumps) > + emit_jump_insn (gen_tablejumpsi (operands[0], operands[1])); > + else > + { > + rtx ccreg = gen_reg_rtx (CCmode); > + rtx jump = gen_tablejumpsi_nospec (operands[0], operands[1], ccreg); > + emit_jump_insn (jump); > + } > + } > else > - emit_jump_insn (gen_tablejumpdi (operands[0], operands[1])); > + { > + if (rs6000_speculate_indirect_jumps) > + emit_jump_insn (gen_tablejumpdi (operands[0], operands[1])); > + else > + { > + rtx ccreg = gen_reg_rtx (CCmode); > + rtx jump = gen_tablejumpdi_nospec (operands[0], operands[1], ccreg); > + emit_jump_insn (jump); > + } > + } > DONE; > }) > > @@ -12946,7 +13005,7 @@ > (parallel [(set (pc) > (match_dup 3)) > (use (label_ref (match_operand 1)))])] > - "TARGET_32BIT" > + "TARGET_32BIT && rs6000_speculate_indirect_jumps" > { > operands[0] = force_reg (SImode, operands[0]); > operands[2] = force_reg (SImode, gen_rtx_LABEL_REF (SImode, operands[1])); > @@ -12953,6 +13012,21 @@ > operands[3] = gen_reg_rtx (SImode); > }) > > +(define_expand "tablejumpsi_nospec" > + [(set (match_dup 4) > + (plus:SI (match_operand:SI 0) > + (match_dup 3))) > + (parallel [(set (pc) > + (match_dup 4)) > + (use (label_ref (match_operand 1))) > + (clobber (match_operand 2))])] > + "TARGET_32BIT && !rs6000_speculate_indirect_jumps" > +{ > + operands[0] = force_reg (SImode, operands[0]); > + operands[3] = force_reg (SImode, gen_rtx_LABEL_REF (SImode, operands[1])); > + operands[4] = gen_reg_rtx (SImode); > +}) > + > (define_expand "tablejumpdi" > [(set (match_dup 4) > (sign_extend:DI (match_operand:SI 0 "lwa_operand"))) > @@ -12962,7 +13036,7 @@ > (parallel [(set (pc) > (match_dup 3)) > (use (label_ref (match_operand 1)))])] > - "TARGET_64BIT" > + "TARGET_64BIT && rs6000_speculate_indirect_jumps" > { > operands[2] = force_reg (DImode, gen_rtx_LABEL_REF (DImode, operands[1])); > operands[3] = gen_reg_rtx (DImode); > @@ -12969,14 +13043,41 @@ > operands[4] = gen_reg_rtx (DImode); > }) > > +(define_expand "tablejumpdi_nospec" > + [(set (match_dup 5) > + (sign_extend:DI (match_operand:SI 0 "lwa_operand"))) > + (set (match_dup 4) > + (plus:DI (match_dup 5) > + (match_dup 3))) > + (parallel [(set (pc) > + (match_dup 4)) > + (use (label_ref (match_operand 1))) > + (clobber (match_operand 2))])] > + "TARGET_64BIT && !rs6000_speculate_indirect_jumps" > +{ > + operands[3] = force_reg (DImode, gen_rtx_LABEL_REF (DImode, operands[1])); > + operands[4] = gen_reg_rtx (DImode); > + operands[5] = gen_reg_rtx (DImode); > +}) > + > (define_insn "*tablejump<mode>_internal1" > [(set (pc) > (match_operand:P 0 "register_operand" "c,*l")) > (use (label_ref (match_operand 1)))] > - "" > + "rs6000_speculate_indirect_jumps" > "b%T0" > [(set_attr "type" "jmpreg")]) > > +(define_insn "*tablejump<mode>_internal1_nospec" > + [(set (pc) > + (match_operand:P 0 "register_operand" "c,*l")) > + (use (label_ref (match_operand 1))) > + (clobber (match_operand:CC 2 "cc_reg_operand" "=y,y"))] > + "!rs6000_speculate_indirect_jumps" > + "crset %E2\;beq%T0- %2\;b ." > + [(set_attr "type" "jmpreg") > + (set_attr "length" "12")]) > + > (define_insn "nop" > [(unspec [(const_int 0)] UNSPEC_NOP)] > "" > Index: gcc/config/rs6000/rs6000.opt > =================================================================== > --- gcc/config/rs6000/rs6000.opt (revision 256364) > +++ gcc/config/rs6000/rs6000.opt (working copy) > @@ -617,3 +617,8 @@ Use the given offset for addressing the stack-prot > > TargetVariable > long rs6000_stack_protector_guard_offset = 0 > + > +;; -mno-speculate-indirect-jumps adds deliberate misprediction to indirect > +;; branches via the CTR. > +mspeculate-indirect-jumps > +Target Undocumented Var(rs6000_speculate_indirect_jumps) Init(1) Save > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-1.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-1.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-1.c (working copy) > @@ -0,0 +1,14 @@ > +/* { dg-do compile { target { powerpc64le-*-* } } } */ > +/* { dg-additional-options "-mno-speculate-indirect-jumps" } */ > + > +/* Test for deliberate misprediction of indirect calls for ELFv2. */ > + > +extern int (*f)(); > + > +int bar () > +{ > + return (*f) (); > +} > + > +/* { dg-final { scan-assembler "crset eq" } } */ > +/* { dg-final { scan-assembler "beqctrl-" } } */ > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-2.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-2.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-2.c (working copy) > @@ -0,0 +1,33 @@ > +/* { dg-do compile } */ > +/* { dg-options "-mno-speculate-indirect-jumps" } */ > + > +/* Test for deliberate misprediction of computed goto. */ > + > +int bar (int); > +int baz (int); > +int spaz (int); > + > +int foo (int x) > +{ > + static void *labptr[] = { &&lab0, &&lab1, &&lab2 }; > + > + if (x < 0 || x > 2) > + return -1; > + > + goto *labptr[x]; > + > + lab0: > + return bar (x); > + > + lab1: > + return baz (x) + 1; > + > + lab2: > + return spaz (x) / 2; > +} > + > +/* The following assumes CR7 as the first chosen volatile. */ > + > +/* { dg-final { scan-assembler "crset 30" } } */ > +/* { dg-final { scan-assembler "beqctr- 7" } } */ > +/* { dg-final { scan-assembler "b ." } } */ > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-3.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-3.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-3.c (working copy) > @@ -0,0 +1,52 @@ > +/* { dg-do compile } */ > +/* { dg-options "-mno-speculate-indirect-jumps" } */ > + > +/* Test for deliberate misprediction of jump tables. */ > + > +void bar (void); > + > +int foo (int x) > +{ > + int a; > + > + switch (x) > + { > + default: > + a = -1; > + break; > + case 0: > + a = x * x; > + break; > + case 1: > + a = x + 1; > + break; > + case 2: > + a = x + x; > + break; > + case 3: > + a = x << 3; > + break; > + case 4: > + a = x >> 1; > + break; > + case 5: > + a = x; > + break; > + case 6: > + a = 0; > + break; > + case 7: > + a = x * x + x; > + break; > + } > + > + bar(); > + > + return a; > +} > + > +/* The following assumes CR7 as the first chosen volatile. */ > + > +/* { dg-final { scan-assembler "crset 30" } } */ > +/* { dg-final { scan-assembler "beqctr- 7" } } */ > +/* { dg-final { scan-assembler "b ." } } */ > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-4.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-4.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-4.c (working copy) > @@ -0,0 +1,25 @@ > +/* { dg-do run } */ > +/* { dg-additional-options "-mno-speculate-indirect-jumps" } */ > + > +/* Test for deliberate misprediction of indirect calls for ELFv2. */ > + > +int (*f)(); > + > +int __attribute__((noinline)) bar () > +{ > + return (*f) (); > +} > + > +int g () > +{ > + return 26; > +} > + > +int main () > +{ > + f = &g; > + if (bar () != 26) > + __builtin_abort (); > + > + return 0; > +} > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-5.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-5.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-5.c (working copy) > @@ -0,0 +1,55 @@ > +/* { dg-do run } */ > +/* { dg-additional-options "-mno-speculate-indirect-jumps -Wno-pedantic" } */ > + > +/* Test for deliberate misprediction of computed goto. */ > + > +int __attribute__((noinline)) bar (int i) > +{ > + return 1960 + i; > +} > + > +int __attribute__((noinline)) baz (int i) > +{ > + return i * i; > +} > + > +int __attribute__((noinline)) spaz (int i) > +{ > + return i + 1; > +} > + > +int foo (int x) > +{ > + static void *labptr[] = { &&lab0, &&lab1, &&lab2 }; > + > + if (x < 0 || x > 2) > + return -1; > + > + goto *labptr[x]; > + > + lab0: > + return bar (x); > + > + lab1: > + return baz (x) + 1; > + > + lab2: > + return spaz (x) / 2; > +} > + > +int main () > +{ > + if (foo (0) != 1960) > + __builtin_abort (); > + > + if (foo (1) != 2) > + __builtin_abort (); > + > + if (foo (2) != 1) > + __builtin_abort (); > + > + if (foo (3) != -1) > + __builtin_abort (); > + > + return 0; > +} > Index: gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-6.c > =================================================================== > --- gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-6.c (nonexistent) > +++ gcc/testsuite/gcc.target/powerpc/safe-indirect-jump-6.c (working copy) > @@ -0,0 +1,80 @@ > +/* { dg-do run } */ > +/* { dg-additional-options "-mno-speculate-indirect-jumps" } */ > + > +/* Test for deliberate misprediction of jump tables. */ > + > +void __attribute__((noinline)) bar () > +{ > +} > + > +int foo (int x) > +{ > + int a; > + > + switch (x) > + { > + default: > + a = -1; > + break; > + case 0: > + a = x * x + 3; > + break; > + case 1: > + a = x + 1; > + break; > + case 2: > + a = x + x; > + break; > + case 3: > + a = x << 3; > + break; > + case 4: > + a = x >> 1; > + break; > + case 5: > + a = x; > + break; > + case 6: > + a = 0; > + break; > + case 7: > + a = x * x + x; > + break; > + } > + > + bar(); > + > + return a; > +} > + > +int main () > +{ > + if (foo (0) != 3) > + __builtin_abort (); > + > + if (foo (1) != 2) > + __builtin_abort (); > + > + if (foo (2) != 4) > + __builtin_abort (); > + > + if (foo (3) != 24) > + __builtin_abort (); > + > + if (foo (4) != 2) > + __builtin_abort (); > + > + if (foo (5) != 5) > + __builtin_abort (); > + > + if (foo (6) != 0) > + __builtin_abort (); > + > + if (foo (7) != 56) > + __builtin_abort (); > + > + if (foo (8) != -1) > + __builtin_abort (); > + > + return 0; > +} >