diff options
author | Matt Brown | 2017-07-31 02:58:23 +0200 |
---|---|---|
committer | Michael Ellerman | 2017-08-10 14:30:27 +0200 |
commit | dcbd19b48d315a8bbf6b2b3dbdc643c747c8a7ee (patch) | |
tree | 05311ea34c7382dc99e10a10f9aea4d3d3d37985 /arch/powerpc/lib/sstep.c | |
parent | powerpc/lib/sstep: Add cmpb instruction emulation (diff) | |
download | kernel-qcow2-linux-dcbd19b48d315a8bbf6b2b3dbdc643c747c8a7ee.tar.gz kernel-qcow2-linux-dcbd19b48d315a8bbf6b2b3dbdc643c747c8a7ee.tar.xz kernel-qcow2-linux-dcbd19b48d315a8bbf6b2b3dbdc643c747c8a7ee.zip |
powerpc/lib/sstep: Add popcnt instruction emulation
This adds emulations for the popcntb, popcntw, and popcntd instructions.
Tested for correctness against the popcnt{b,w,d} instructions on ppc64le.
Signed-off-by: Matt Brown <matthew.brown.dev@gmail.com>
Reviewed-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/lib/sstep.c')
-rw-r--r-- | arch/powerpc/lib/sstep.c | 42 |
1 files changed, 41 insertions, 1 deletions
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index cc6327b8f589..d172de3d2b37 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -612,6 +612,34 @@ static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1, regs->gpr[rd] = out_val; } +/* + * The size parameter is used to adjust the equivalent popcnt instruction. + * popcntb = 8, popcntw = 32, popcntd = 64 + */ +static nokprobe_inline void do_popcnt(struct pt_regs *regs, unsigned long v1, + int size, int ra) +{ + unsigned long long out = v1; + + out -= (out >> 1) & 0x5555555555555555; + out = (0x3333333333333333 & out) + (0x3333333333333333 & (out >> 2)); + out = (out + (out >> 4)) & 0x0f0f0f0f0f0f0f0f; + + if (size == 8) { /* popcntb */ + regs->gpr[ra] = out; + return; + } + out += out >> 8; + out += out >> 16; + if (size == 32) { /* popcntw */ + regs->gpr[ra] = out & 0x0000003f0000003f; + return; + } + + out = (out + (out >> 32)) & 0x7f; + regs->gpr[ra] = out; /* popcntd */ +} + static nokprobe_inline int trap_compare(long v1, long v2) { int ret = 0; @@ -1209,6 +1237,10 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs, regs->gpr[ra] = regs->gpr[rd] & ~regs->gpr[rb]; goto logical_done; + case 122: /* popcntb */ + do_popcnt(regs, regs->gpr[rd], 8, ra); + goto logical_done; + case 124: /* nor */ regs->gpr[ra] = ~(regs->gpr[rd] | regs->gpr[rb]); goto logical_done; @@ -1221,6 +1253,10 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs, regs->gpr[ra] = regs->gpr[rd] ^ regs->gpr[rb]; goto logical_done; + case 378: /* popcntw */ + do_popcnt(regs, regs->gpr[rd], 32, ra); + goto logical_done; + case 412: /* orc */ regs->gpr[ra] = regs->gpr[rd] | ~regs->gpr[rb]; goto logical_done; @@ -1232,7 +1268,11 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs, case 476: /* nand */ regs->gpr[ra] = ~(regs->gpr[rd] & regs->gpr[rb]); goto logical_done; - +#ifdef CONFIG_PPC64 + case 506: /* popcntd */ + do_popcnt(regs, regs->gpr[rd], 64, ra); + goto logical_done; +#endif case 922: /* extsh */ regs->gpr[ra] = (signed short) regs->gpr[rd]; goto logical_done; |