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? 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; +}