[PATCH 21/26] target/s390x: Remove PER check from use_goto_tb

Richard Henderson posted 26 patches 3 years, 4 months ago
Maintainers: Richard Henderson <richard.henderson@linaro.org>, Cornelia Huck <cohuck@redhat.com>, Thomas Huth <thuth@redhat.com>, David Hildenbrand <david@redhat.com>
There is a newer version of this series
[PATCH 21/26] target/s390x: Remove PER check from use_goto_tb
Posted by Richard Henderson 3 years, 4 months ago
While it is common for the PC update to happen in the
shadow of a goto_tb, it is not required to be there.
By moving it before the goto_tb, we can also place the
call to helper_per_branch there, and then afterward
chain to the next tb.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/s390x/tcg/translate.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index a2315ac73e..e6c7c2a6ae 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -654,9 +654,6 @@ static void gen_op_calc_cc(DisasContext *s)
 
 static bool use_goto_tb(DisasContext *s, uint64_t dest)
 {
-    if (per_enabled(s)) {
-        return false;
-    }
     return translator_use_goto_tb(&s->base, dest);
 }
 
@@ -1157,15 +1154,16 @@ static DisasJumpType help_goto_direct(DisasContext *s, int64_t disp)
         per_branch_disp(s, disp);
         return DISAS_NEXT;
     }
+
+    update_psw_addr_disp(s, disp);
+    per_branch_dest(s, psw_addr);
+
     if (use_goto_tb(s, s->base.pc_next + disp)) {
         update_cc_op(s);
         tcg_gen_goto_tb(0);
-        update_psw_addr_disp(s, disp);
         tcg_gen_exit_tb(s->base.tb, 0);
         return DISAS_NORETURN;
     } else {
-        update_psw_addr_disp(s, disp);
-        per_branch_dest(s, psw_addr);
         return DISAS_PC_UPDATED;
     }
 }
-- 
2.34.1
Re: [PATCH 21/26] target/s390x: Remove PER check from use_goto_tb
Posted by Ilya Leoshkevich 3 years, 2 months ago
On Wed, Oct 05, 2022 at 08:44:16PM -0700, Richard Henderson wrote:
> While it is common for the PC update to happen in the
> shadow of a goto_tb, it is not required to be there.
> By moving it before the goto_tb, we can also place the
> call to helper_per_branch there, and then afterward
> chain to the next tb.
> 
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  target/s390x/tcg/translate.c | 10 ++++------
>  1 file changed, 4 insertions(+), 6 deletions(-)
> 
> diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
> index a2315ac73e..e6c7c2a6ae 100644
> --- a/target/s390x/tcg/translate.c
> +++ b/target/s390x/tcg/translate.c
> @@ -654,9 +654,6 @@ static void gen_op_calc_cc(DisasContext *s)
>  
>  static bool use_goto_tb(DisasContext *s, uint64_t dest)
>  {
> -    if (per_enabled(s)) {
> -        return false;
> -    }
>      return translator_use_goto_tb(&s->base, dest);
>  }
>  
> @@ -1157,15 +1154,16 @@ static DisasJumpType help_goto_direct(DisasContext *s, int64_t disp)
>          per_branch_disp(s, disp);
>          return DISAS_NEXT;
>      }
> +
> +    update_psw_addr_disp(s, disp);
> +    per_branch_dest(s, psw_addr);
> +
>      if (use_goto_tb(s, s->base.pc_next + disp)) {
>          update_cc_op(s);
>          tcg_gen_goto_tb(0);
> -        update_psw_addr_disp(s, disp);
>          tcg_gen_exit_tb(s->base.tb, 0);
>          return DISAS_NORETURN;
>      } else {
> -        update_psw_addr_disp(s, disp);
> -        per_branch_dest(s, psw_addr);
>          return DISAS_PC_UPDATED;
>      }
>  }
> -- 
> 2.34.1

While looking at this, I had a hard time convincing myself that
successful-branch PER events work at all. The code does set
per_perc_atmid, but afterwards it does goto_tb/exit_tb, and does
not reach per_check_exception() added by translate_one().

I wrote a small test for this theory by turning on PER for
successful-branch events and looping 10 times. It passes in KVM, but
fails in TCG. Here is the relevant IR fragment:

IN: 
0x00000212:  a706 0000       brct     %r0, 0x212

OP:
 ld_i32 tmp0,env,$0xfffffffffffffff0
 brcond_i32 tmp0,$0x0,lt,$L0

 ---- 0000000000000212 0000000000000004 0000000000000004
 mov_i64 tmp2,psw_addr
 call per_ifetch,$0x1,$0,env,tmp2
 sub_i64 tmp2,r0,$0x1
 extract2_i64 r0,r0,tmp2,$0x20
 rotl_i64 r0,r0,$0x20
 mov_i32 tmp0,tmp2
 brcond_i32 tmp0,$0x0,eq,$L1
 /* branch taken: set per_perc_atmid and exit */
 mov_i64 gbea,psw_addr
 call per_branch,$0x1,$0,env,gbea,psw_addr
 goto_tb $0x0
 exit_tb $0x7f73fc000400
 set_label $L1
 add_i64 psw_addr,psw_addr,$0x4
 goto_tb $0x1
 exit_tb $0x7f73fc000401
 /* check per_perc_atmid */
 call per_check_exception,$0x0,$0,env
 set_label $L0
 exit_tb $0x7f73fc000403

I will post the proposed fix and the test itself shortly.

That said, the patch makes sense to me and does not make things worse,
so:

Reviewed-by: Ilya Leoshkevich <iii@linux.ibm.com>
[PATCH 1/2] target/s390x: Fix successful-branch PER events
Posted by Ilya Leoshkevich 3 years, 2 months ago
The branching code sets per_perc_atmid, but afterwards it does
goto_tb/exit_tb, so per_check_exception() added by translate_one() is
not reached.

Fix by raising PER exception in per_branch().

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
 target/s390x/tcg/misc_helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c
index 71388a71197..b7220cef44b 100644
--- a/target/s390x/tcg/misc_helper.c
+++ b/target/s390x/tcg/misc_helper.c
@@ -619,6 +619,7 @@ void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to)
             || get_per_in_range(env, to)) {
             env->per_address = from;
             env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env);
+            tcg_s390_program_interrupt(env, PGM_PER, GETPC());
         }
     }
 }
-- 
2.38.1
[PATCH 2/2] tests/tcg/s390x: Add per.S
Posted by Ilya Leoshkevich 3 years, 2 months ago
Add a small test to avoid regressions.

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
 tests/tcg/s390x/Makefile.softmmu-target |  1 +
 tests/tcg/s390x/per.S                   | 55 +++++++++++++++++++++++++
 2 files changed, 56 insertions(+)
 create mode 100644 tests/tcg/s390x/per.S

diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target
index a34fa68473e..1d649753f4e 100644
--- a/tests/tcg/s390x/Makefile.softmmu-target
+++ b/tests/tcg/s390x/Makefile.softmmu-target
@@ -7,3 +7,4 @@ QEMU_OPTS=-action panic=exit-failure -kernel
 		-Wl,--build-id=none $< -o $@
 
 TESTS += unaligned-lowcore
+TESTS += per
diff --git a/tests/tcg/s390x/per.S b/tests/tcg/s390x/per.S
new file mode 100644
index 00000000000..02f8422c44d
--- /dev/null
+++ b/tests/tcg/s390x/per.S
@@ -0,0 +1,55 @@
+#define N_ITERATIONS 10
+
+    .org 0x8d
+ilc:
+    .org 0x8e
+program_interruption_code:
+    .org 0x96
+per_code:
+    .org 0x150
+program_old_psw:
+    .org 0x1d0                         /* program new PSW */
+    .quad 0,pgm_handler
+    .org 0x200                         /* lowcore padding */
+
+    .globl _start
+_start:
+    lpswe per_on_psw
+start_per:
+    lghi %r0,N_ITERATIONS
+    xgr %r1,%r1
+    lctlg %c9,%c11,per_on_regs
+loop:
+    brct %r0,loop
+    lctlg %c9,%c11,per_off_regs
+    cgijne %r1,N_ITERATIONS-1,fail     /* expected number of events? */
+    lpswe success_psw
+fail:
+    lpswe failure_psw
+
+pgm_handler:
+    chhsi program_interruption_code,0x80         /* PER event? */
+    jne fail
+    cli per_code,0x80                  /* successful-branching event? */
+    jne fail
+    la %r1,1(%r1)                      /* increment event counter */
+    mvc return_psw(8),program_old_psw
+    lg %r3,program_old_psw+8
+    llgc %r2,ilc
+    sgr %r3,%r2                        /* rewind PSW */
+    stg %r3,return_psw+8
+    lpswe return_psw
+
+    .align 8
+per_on_psw:
+    .quad 0x4000000000000000,start_per
+per_on_regs:
+    .quad 0x80000000,0,-1              /* successful-branching everywhere */
+per_off_regs:
+    .quad 0,0,0
+return_psw:
+    .quad 0,0
+success_psw:
+    .quad 0x2000000000000,0xfff        /* see is_special_wait_psw() */
+failure_psw:
+    .quad 0x2000000000000,0            /* disabled wait */
-- 
2.38.1