On Jan 15, 2018, at 3:46 AM, Richard Biener <richard.guent...@gmail.com> wrote: > > On Sun, Jan 14, 2018 at 5:53 AM, Bill Schmidt > <wschm...@linux.vnet.ibm.com> wrote: >> Hi, >> >> [This patch supercedes and extends >> https://gcc.gnu.org/ml/gcc-patches/2018-01/msg01135.html. >> There was a small error in the assembly code produced by that patch (bad >> memory on my account of how to spell "crset eq"). I've also increased the >> function provided; see below.] >> >> This patch adds a new option for the compiler to produce only "safe" indirect >> jumps, in the sense that these jumps are deliberately mispredicted to inhibit >> speculative execution. For now, this option is undocumented; this may change >> at some future date. It is intended eventually for the linker to also honor >> this flag when creating PLT stubs, for example. >> >> In addition to the new option, I've included changes to indirect calls for >> the ELFv2 ABI when the option is specified. In place of bctrl, we generate >> a "crset eq" followed by a beqctrl-. Using the CR0.eq bit is safe since CR0 >> is volatile over the call. >> >> I've also added code to replace uses of bctr when the new option is >> specified, >> with the sequence >> >> crset 4x[CRb]+2 >> beqctr- CRb >> b . >> >> where CRb is an available condition register field. This applies to all >> subtargets, and in particular is not restricted to ELFv2. The use cases >> covered here are computed gotos and switch statements. >> >> NOT yet covered by this patch: indirect calls for ELFv1. That will come >> later. >> >> Please let me know if there is a better way to represent the crset without >> an unspec. For the indirect jump, I don't see a way around it due to the >> expected form of indirect jumps in cfganal.c. >> >> Bootstrapped and tested on powerpc64-linux-gnu and powerpc64le-linux-gnu with >> no regressions. Is this okay for trunk? > > As this sounds Spectre related feel free to backport this to the GCC 7 branch > as well (even if you don't hit the Wednesday deadline for RC1 of GCC 7.3).
Thanks, Richard -- I hadn't seen this deadline announced, but I will do my best to get this completed/committed by then. Thanks for the heads-up! Bill > > Thanks, > Richard. > >> Thanks, >> Bill >> >> >> [gcc] >> >> 2018-01-13 Bill Schmidt <wschm...@linux.vnet.ibm.com> >> >> * config/rs6000/rs6000.c (rs6000_opt_vars): Add entry for >> -msafe-indirect-jumps. >> * config/rs6000/rs6000.md (UNSPEC_CRSET_EQ): New UNSPEC enum. >> (UNSPEC_COMP_GOTO_CR): Likewise. >> (*call_indirect_elfv2<mode>): Disable for -msafe-indirect-jumps. >> (*call_indirect_elfv2<mode>_safe): New define_insn. >> (*call_value_indirect_elfv2<mode>): Disable for >> -msafe-indirect-jumps. >> (*call_value_indirect_elfv2<mode>_safe): New define_insn. >> (indirect_jump): Emit different RTL for -msafe-indirect-jumps. >> (*indirect_jump<mode>): Disable for -msafe-indirect-jumps. >> (*indirect_jump<mode>_safe): New define_insn. >> (*set_cr_eq): New define_insn. >> (tablejump): Emit different RTL for -msafe-indirect-jumps. >> (tablejumpsi): Disable for -msafe-indirect-jumps. >> (tablejumpsi_safe): New define_expand. >> (tablejumpdi): Disable for -msafe-indirect-jumps. >> (tablejumpdi_safe): New define_expand. >> (*tablejump<mode>_internal1): Disable for -msafe-indirect-jumps. >> (*tablejump<mode>_internal1_safe): New define_insn. >> * config/rs6000/rs6000.opt (msafe-indirect-jumps): New option. >> >> [gcc/testsuite] >> >> 2018-01-13 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. >> >> >> 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), }, >> + { "safe-indirect-jumps", >> + offsetof (struct gcc_options, x_rs6000_safe_indirect_jumps), >> + offsetof (struct cl_target_option, x_rs6000_safe_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) >> @@ -150,6 +150,8 @@ >> UNSPEC_SIGNBIT >> UNSPEC_SF_FROM_SI >> UNSPEC_SI_FROM_SF >> + UNSPEC_CRSET_EQ >> + UNSPEC_COMP_GOTO_CR >> ]) >> >> ;; >> @@ -11222,11 +11224,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_safe_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>_safe" >> + [(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_safe_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 +11246,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_safe_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>_safe" >> + [(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_safe_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 +12941,50 @@ >> [(set_attr "type" "jmpreg")]) >> >> (define_expand "indirect_jump" >> - [(set (pc) (match_operand 0 "register_operand"))]) >> + [(set (pc) (match_operand 0 "register_operand"))] >> + "" >> +{ >> + /* We need to reserve a CR when forcing a mispredicted jump. */ >> + if (rs6000_safe_indirect_jumps) { >> + rtx ccreg = gen_reg_rtx (CCmode); >> + emit_insn (gen_rtx_SET (ccreg, >> + gen_rtx_UNSPEC (CCmode, >> + gen_rtvec (1, const0_rtx), >> + UNSPEC_CRSET_EQ))); >> + rtvec v = rtvec_alloc (2); >> + RTVEC_ELT (v, 0) = operands[0]; >> + RTVEC_ELT (v, 1) = ccreg; >> + emit_jump_insn (gen_rtx_SET (pc_rtx, >> + gen_rtx_UNSPEC (Pmode, v, >> + UNSPEC_COMP_GOTO_CR))); >> + DONE; >> + } >> +}) >> >> (define_insn "*indirect_jump<mode>" >> [(set (pc) >> (match_operand:P 0 "register_operand" "c,*l"))] >> - "" >> + "!rs6000_safe_indirect_jumps" >> "b%T0" >> [(set_attr "type" "jmpreg")]) >> >> +(define_insn "*indirect_jump<mode>_safe" >> + [(set (pc) >> + (unspec:P [(match_operand:P 0 "register_operand" "c,*l") >> + (match_operand:CC 1 "cc_reg_operand" "y,y")] >> + UNSPEC_COMP_GOTO_CR))] >> + "rs6000_safe_indirect_jumps" >> + "beq%T0- %1\;b ." >> + [(set_attr "type" "jmpreg") >> + (set_attr "length" "8")]) >> + >> +(define_insn "*set_cr_eq" >> + [(set (match_operand:CC 0 "cc_reg_operand" "=y") >> + (unspec:CC [(const_int 0)] UNSPEC_CRSET_EQ))] >> + "rs6000_safe_indirect_jumps" >> + "crset %E0" >> + [(set_attr "type" "cr_logical")]) >> + >> ;; Table jump for switch statements: >> (define_expand "tablejump" >> [(use (match_operand 0)) >> @@ -12933,9 +12992,19 @@ >> "" >> { >> if (TARGET_32BIT) >> - emit_jump_insn (gen_tablejumpsi (operands[0], operands[1])); >> + { >> + if (rs6000_safe_indirect_jumps) >> + emit_jump_insn (gen_tablejumpsi_safe (operands[0], operands[1])); >> + else >> + emit_jump_insn (gen_tablejumpsi (operands[0], operands[1])); >> + } >> else >> - emit_jump_insn (gen_tablejumpdi (operands[0], operands[1])); >> + { >> + if (rs6000_safe_indirect_jumps) >> + emit_jump_insn (gen_tablejumpdi_safe (operands[0], operands[1])); >> + else >> + emit_jump_insn (gen_tablejumpdi (operands[0], operands[1])); >> + } >> DONE; >> }) >> >> @@ -12946,7 +13015,7 @@ >> (parallel [(set (pc) >> (match_dup 3)) >> (use (label_ref (match_operand 1)))])] >> - "TARGET_32BIT" >> + "TARGET_32BIT && !rs6000_safe_indirect_jumps" >> { >> operands[0] = force_reg (SImode, operands[0]); >> operands[2] = force_reg (SImode, gen_rtx_LABEL_REF (SImode, operands[1])); >> @@ -12953,6 +13022,23 @@ >> operands[3] = gen_reg_rtx (SImode); >> }) >> >> +(define_expand "tablejumpsi_safe" >> + [(set (match_dup 3) >> + (plus:SI (match_operand:SI 0) >> + (match_dup 2))) >> + (set (match_dup 4) (unspec:CC [(const_int 0)] UNSPEC_CRSET_EQ)) >> + (parallel [(set (pc) >> + (match_dup 3)) >> + (use (label_ref (match_operand 1))) >> + (use (match_dup 4))])] >> + "TARGET_32BIT && rs6000_safe_indirect_jumps" >> +{ >> + operands[0] = force_reg (SImode, operands[0]); >> + operands[2] = force_reg (SImode, gen_rtx_LABEL_REF (SImode, operands[1])); >> + operands[3] = gen_reg_rtx (SImode); >> + operands[4] = gen_reg_rtx (CCmode); >> +}) >> + >> (define_expand "tablejumpdi" >> [(set (match_dup 4) >> (sign_extend:DI (match_operand:SI 0 "lwa_operand"))) >> @@ -12962,7 +13048,7 @@ >> (parallel [(set (pc) >> (match_dup 3)) >> (use (label_ref (match_operand 1)))])] >> - "TARGET_64BIT" >> + "TARGET_64BIT && !rs6000_safe_indirect_jumps" >> { >> operands[2] = force_reg (DImode, gen_rtx_LABEL_REF (DImode, operands[1])); >> operands[3] = gen_reg_rtx (DImode); >> @@ -12969,14 +13055,43 @@ >> operands[4] = gen_reg_rtx (DImode); >> }) >> >> +(define_expand "tablejumpdi_safe" >> + [(set (match_dup 4) >> + (sign_extend:DI (match_operand:SI 0 "lwa_operand"))) >> + (set (match_dup 5) (unspec:CC [(const_int 0)] UNSPEC_CRSET_EQ)) >> + (set (match_dup 3) >> + (plus:DI (match_dup 4) >> + (match_dup 2))) >> + (parallel [(set (pc) >> + (match_dup 3)) >> + (use (label_ref (match_operand 1))) >> + (use (match_dup 5))])] >> + "TARGET_64BIT && rs6000_safe_indirect_jumps" >> +{ >> + operands[2] = force_reg (DImode, gen_rtx_LABEL_REF (DImode, operands[1])); >> + operands[3] = gen_reg_rtx (DImode); >> + operands[4] = gen_reg_rtx (DImode); >> + operands[5] = gen_reg_rtx (CCmode); >> +}) >> + >> (define_insn "*tablejump<mode>_internal1" >> [(set (pc) >> (match_operand:P 0 "register_operand" "c,*l")) >> (use (label_ref (match_operand 1)))] >> - "" >> + "!rs6000_safe_indirect_jumps" >> "b%T0" >> [(set_attr "type" "jmpreg")]) >> >> +(define_insn "*tablejump<mode>_internal1_safe" >> + [(set (pc) >> + (match_operand:P 0 "register_operand" "c,*l")) >> + (use (label_ref (match_operand 1))) >> + (use (match_operand:CC 2 "cc_reg_operand" "y,y"))] >> + "rs6000_safe_indirect_jumps" >> + "beq%T0- %2\;b ." >> + [(set_attr "type" "jmpreg") >> + (set_attr "length" "8")]) >> + >> (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 >> + >> +;; -msafe-indirect-jumps adds deliberate misprediction to indirect >> +;; branches via the CTR. >> +msafe-indirect-jumps >> +Target Undocumented Var(rs6000_safe_indirect_jumps) Init(0) 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-options "-msafe-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 { target { powerpc*-*-* } } } */ >> +/* { dg-options "-msafe-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 { target { powerpc*-*-* } } } */ >> +/* { dg-options "-msafe-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 ." } } */