The PMU is already counting cycles by calculating time elapsed in
nanoseconds. Counting instructions is a different matter and requires
another approach.
This patch adds the capability of counting completed instructions
(Perf event PM_INST_CMPL) by counting the amount of instructions
translated in each translation block right before exiting it.
A new pmu_count_insns() helper in translation.c was added to do that.
After verifying that the PMU is running (MMCR0_FC bit not set), we
call helper_insns_inc(). This is new helper from power8_pmu.c that
will add the instructions to the relevant counters.
At this moment helper_insns_inc() is just summing instructions to
counters, but it will also trigger counter negative overflow in a
later patch.
Signed-off-by: Daniel Henrique Barboza <danielhb...@gmail.com>
---
target/ppc/helper.h | 1 +
target/ppc/power8_pmu.c | 61 ++++++++++++++++++++++++++++++++++++++---
target/ppc/translate.c | 46 +++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+), 4 deletions(-)
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 5122632784..47dbbe6da1 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -21,6 +21,7 @@ DEF_HELPER_1(hrfid, void, env)
DEF_HELPER_2(store_lpcr, void, env, tl)
DEF_HELPER_2(store_pcr, void, env, tl)
DEF_HELPER_2(store_mmcr0, void, env, tl)
+DEF_HELPER_2(insns_inc, void, env, i32)
#endif
DEF_HELPER_1(check_tlb_flush_local, void, env)
DEF_HELPER_1(check_tlb_flush_global, void, env)
diff --git a/target/ppc/power8_pmu.c b/target/ppc/power8_pmu.c
index 007007824d..311eaa358f 100644
--- a/target/ppc/power8_pmu.c
+++ b/target/ppc/power8_pmu.c
@@ -31,10 +31,9 @@ static void update_PMC_PM_CYC(CPUPPCState *env, int sprn,
env->spr[sprn] += time_delta;
}
-static void update_programmable_PMC_reg(CPUPPCState *env, int sprn,
- uint64_t time_delta)
+static uint8_t get_PMC_event(CPUPPCState *env, int sprn)
{
- uint8_t event;
+ int event = 0x0;
switch (sprn) {
case SPR_POWER_PMC1:
@@ -53,9 +52,17 @@ static void update_programmable_PMC_reg(CPUPPCState *env,
int sprn,
event = MMCR1_PMC4SEL & env->spr[SPR_POWER_MMCR1];
break;
default:
- return;
+ break;
}
+ return event;
+}
+
+static void update_programmable_PMC_reg(CPUPPCState *env, int sprn,
+ uint64_t time_delta)
+{
+ uint8_t event = get_PMC_event(env, sprn);
+
/*
* MMCR0_PMC1SEL = 0xF0 is the architected PowerISA v3.1 event
* that counts cycles using PMC1.
@@ -124,4 +131,50 @@ void helper_store_mmcr0(CPUPPCState *env, target_ulong
value)
}
}
+static bool pmc_counting_insns(CPUPPCState *env, int sprn)
+{
+ bool ret = false;
+ uint8_t event;
+
+ if (sprn == SPR_POWER_PMC5) {
+ return true;
+ }
+
+ event = get_PMC_event(env, sprn);
+
+ /*
+ * Event 0x2 is an implementation-dependent event that IBM
+ * POWER chips implement (at least since POWER8) that is
+ * equivalent to PM_INST_CMPL. Let's support this event on
+ * all programmable PMCs.
+ *
+ * Event 0xFE is the PowerISA v3.1 architected event to
+ * sample PM_INST_CMPL using PMC1.
+ */
+ switch (sprn) {
+ case SPR_POWER_PMC1:
+ return event == 0x2 || event == 0xFE;
+ case SPR_POWER_PMC2:
+ case SPR_POWER_PMC3:
+ case SPR_POWER_PMC4:
+ return event == 0x2;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* This helper assumes that the PMC is running. */
+void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
+{
+ int sprn;
+
+ for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
+ if (pmc_counting_insns(env, sprn)) {
+ env->spr[sprn] += num_insns;
+ }
+ }
+}
+
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index e4f75ba380..d45ce79a3e 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -4425,6 +4425,30 @@ static inline void gen_update_cfar(DisasContext *ctx,
target_ulong nip)
#endif
}
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+static void pmu_count_insns(DisasContext *ctx)
+{
+ TCGv t_mmcr0FC = tcg_constant_i64(MMCR0_FC);
+ TCGv t0 = tcg_temp_new();
+ TCGLabel *l_exit = gen_new_label();
+
+ /* Do not bother calling the helper if the PMU is frozen */
+ gen_load_spr(t0, SPR_POWER_MMCR0);
+ tcg_gen_andi_tl(t0, t0, MMCR0_FC);
+ tcg_gen_brcond_tl(TCG_COND_EQ, t0, t_mmcr0FC, l_exit);
+
+ gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
+
+ gen_set_label(l_exit);
+ tcg_temp_free(t_mmcr0FC);
+ tcg_temp_free(t0);
+}
+#else
+static void pmu_count_insns(DisasContext *ctx)
+{
+ return;
+}
+#endif
static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
{
return translator_use_goto_tb(&ctx->base, dest);
@@ -4439,9 +4463,17 @@ static void gen_lookup_and_goto_ptr(DisasContext *ctx)
} else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) {
gen_helper_raise_exception(cpu_env,
tcg_constant_i32(gen_prep_dbgex(ctx)));
} else {
+ pmu_count_insns(ctx);
tcg_gen_exit_tb(NULL, 0);
}
} else {
+ /*
+ * tcg_gen_lookup_and_goto_ptr will exit the TB if
+ * CF_NO_GOTO_PTR is set. Count insns now.
+ */
+ if (ctx->base.tb->flags & CF_NO_GOTO_PTR) {
+ pmu_count_insns(ctx);
+ }