Skip to contentSkip to conversation

powerpc/lib/sstep: Add cmpb instruction emulation

PATCH Superseded Matt Brown 3 participants 8 messages Jul 24, 2017 – Jul 25, 2017 PowerPC

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Matt Brown

author
#0 lore raw

PATCH v2 1/5 powerpc/lib/sstep: Add cmpb instruction emulation

This patch adds emulation of the cmpb instruction, enabling xmon to emulate this instruction.
Tested for correctness against the cmpb asm instruction on ppc64le.

---
v2:
- fixed opcode
- fixed mask typecasting

---
 arch/powerpc/lib/sstep.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/powerpc/lib/sstep.c +20 -0
index 33117f8..87d277f 100644
@@ -596,6 +596,22 @@ static nokprobe_inline void do_cmp_unsigned(struct pt_regs *regs, unsigned long
 	regs->ccr = (regs->ccr & ~(0xf << shift)) | (crval << shift);
 }
 
+static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1,
+				unsigned long v2, int rd)
+{
+	unsigned long long out_val, mask;
+	int i;
+
+	out_val = 0;
+	for (i = 0; i < 8; i++) {
+		mask = 0xffUL << (i * 8);
+		if ((v1 & mask) == (v2 & mask))
+			out_val |= mask;
+	}
+
+	regs->gpr[rd] = out_val;
+}
+
 static nokprobe_inline int trap_compare(long v1, long v2)
 {
 	int ret = 0;
@@ -1049,6 +1065,10 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
 			do_cmp_unsigned(regs, val, val2, rd >> 2);
 			goto instr_done;
 
+		case 508: /* cmpb */
+			do_cmpb(regs, regs->gpr[rd], regs->gpr[rb], ra);
+			goto instr_done;
+
 /*
  * Arithmetic instructions
  */
Signature · 1 line
2.9.3
author
#1 lore raw

PATCH v2 2/5 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.

---
v2:
- fixed opcodes
- fixed typecasting
- fixed bitshifting error for both 32 and 64bit arch

---
 arch/powerpc/lib/sstep.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
arch/powerpc/lib/sstep.c +42 -1
index 87d277f..e6a16a3 100644
@@ -612,6 +612,35 @@ 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 high, low, mask;
+	unsigned int n;
+	int i, j;
+
+	high = 0;
+	low = 0;
+
+	for (i = 0; i < (64 / size); i++) {
+		n = 0;
+		for (j = 0; j < size; j++) {
+			mask = 1UL << (j + (i * size));
+			if (v1 & mask)
+				n++;
+		}
+		if ((i * size) < 32)
+			low |= n << (i * size);
+		else
+			high |= n << ((i * size) - 32);
+	}
+	regs->gpr[ra] = (high << 32) | low;
+}
+
 static nokprobe_inline int trap_compare(long v1, long v2)
 {
 	int ret = 0;
@@ -1194,6 +1223,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;
@@ -1206,6 +1239,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;
@@ -1217,7 +1254,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 __powerpc64__
+		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;
Signature · 1 line
2.9.3
#2 lore raw

Re: [PATCH v2 2/5] powerpc/lib/sstep: Add popcnt instruction emulation

Hi Matt,

arch/powerpc/lib/sstep.c static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1, · line 629

Quoted 1 line · Matt Brown
arch/powerpc/lib/sstep.c +1 -0
+	for (i = 0; i < (64 / size); i++) {

If you do

for (i = 0; i < 64; i += size)

things are slightly nicer.

arch/powerpc/lib/sstep.c static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1, · line 641

Quoted 6 lines
arch/powerpc/lib/sstep.c +6 -0
+		if ((i * size) < 32)
+			low |= n << (i * size);
+		else
+			high |= n << ((i * size) - 32);
+	}
+	regs->gpr[ra] = (high << 32) | low;

Why have separate high and low vars?

And there are much better ways to calculate popcount, of course.

Segher

#3 lore raw

Re: [PATCH v2 2/5] powerpc/lib/sstep: Add popcnt instruction emulation

In reply to Matt Brown
Quoted 39 lines · Matt Brown

This adds emulations for the popcntb, popcntw, and popcntd instructions. Tested for correctness against the popcnt{b,w,d} instructions on ppc64le.

---
v2:
- fixed opcodes
- fixed typecasting
- fixed bitshifting error for both 32 and 64bit arch

---
 arch/powerpc/lib/sstep.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
arch/powerpc/lib/sstep.c +18 -0
index 87d277f..e6a16a3 100644
@@ -612,6 +612,35 @@ 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 high, low, mask;
+       unsigned int n;
+       int i, j;
+
+       high = 0;
+       low = 0;
+
+       for (i = 0; i < (64 / size); i++) {
+               n = 0;
+               for (j = 0; j < size; j++) {
+                       mask = 1UL << (j + (i * size));

arch/powerpc/lib/sstep.c static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1, · line 642

Quoted 10 lines
arch/powerpc/lib/sstep.c +10 -0
+                       if (v1 & mask)
+                               n++;
+               }
+               if ((i * size) < 32)
+                       low |= n << (i * size);
+               else
+                       high |= n << ((i * size) - 32);
+       }
+       regs->gpr[ra] = (high << 32) | low;
+}

There's a way to do it in very efficient way via the Giles-Miller method of side-ways addition

Please see

http://opensourceforu.com/2012/06/power-programming-bitwise-tips-tricks/
and lib/hweight.c, you can reuse the code from lib/hweight.c

Balbir Singh

author
#4 lore raw

Re: [PATCH v2 2/5] powerpc/lib/sstep: Add popcnt instruction emulation

Quoted 55 lines · Balbir Singh
Quoted 39 lines · Matt Brown

This adds emulations for the popcntb, popcntw, and popcntd instructions. Tested for correctness against the popcnt{b,w,d} instructions on ppc64le.

---
v2:
- fixed opcodes
- fixed typecasting
- fixed bitshifting error for both 32 and 64bit arch

---
 arch/powerpc/lib/sstep.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)
arch/powerpc/lib/sstep.c +18 -0
index 87d277f..e6a16a3 100644
@@ -612,6 +612,35 @@ 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 high, low, mask;
+       unsigned int n;
+       int i, j;
+
+       high = 0;
+       low = 0;
+
+       for (i = 0; i < (64 / size); i++) {
+               n = 0;
+               for (j = 0; j < size; j++) {
+                       mask = 1UL << (j + (i * size));

arch/powerpc/lib/sstep.c static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1, · line 642

Quoted 10 lines
arch/powerpc/lib/sstep.c +10 -0
+                       if (v1 & mask)
+                               n++;
+               }
+               if ((i * size) < 32)
+                       low |= n << (i * size);
+               else
+                       high |= n << ((i * size) - 32);
+       }
+       regs->gpr[ra] = (high << 32) | low;
+}

There's a way to do it in very efficient way via the Giles-Miller method of side-ways addition

Quoted 4 lines

Please see

http://opensourceforu.com/2012/06/power-programming-bitwise-tips-tricks/
and lib/hweight.c, you can reuse the code from lib/hweight.c

Oh that's a really cool technique.
We could use that for the parity instructions too.

Quoted 2 lines

Balbir Singh

author
#5 lore raw

PATCH v2 3/5 powerpc/lib/sstep: Add bpermd instruction emulation

In reply to Matt Brown

This adds emulation for the bpermd instruction.
Tested for correctness against the bpermd instruction on ppc64le.

---
v2:
- fixed opcode
- added ifdef tags to do_bpermd func
- fixed bitshifting errors

---
 arch/powerpc/lib/sstep.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)
arch/powerpc/lib/sstep.c +23 -1
index e6a16a3..a756f44 100644
@@ -641,6 +641,24 @@ static nokprobe_inline void do_popcnt(struct pt_regs *regs, unsigned long v1,
 	regs->gpr[ra] = (high << 32) | low;
 }
 
+#ifdef __powerpc64__
+static nokprobe_inline void do_bpermd(struct pt_regs *regs, unsigned long v1,
+				unsigned long v2, int ra)
+{
+	unsigned char perm, idx;
+	unsigned int i;
+
+	perm = 0;
+	for (i = 0; i < 8; i++) {
+		idx = (v1 >> (i * 8)) & 0xff;
+		if (idx < 64)
+			if (v2 & PPC_BIT(idx))
+				perm |= 1 << i;
+	}
+	regs->gpr[ra] = perm;
+}
+#endif /* __powerpc64__ */
+
 static nokprobe_inline int trap_compare(long v1, long v2)
 {
 	int ret = 0;
@@ -1230,7 +1248,11 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
 		case 124:	/* nor */
 			regs->gpr[ra] = ~(regs->gpr[rd] | regs->gpr[rb]);
 			goto logical_done;
-
+#ifdef __powerpc64__
+		case 252:	/* bpermd */
+			do_bpermd(regs, regs->gpr[rd], regs->gpr[rb], ra);
+			goto logical_done;
+#endif
 		case 284:	/* xor */
 			regs->gpr[ra] = ~(regs->gpr[rd] ^ regs->gpr[rb]);
 			goto logical_done;
Signature · 1 line
2.9.3
author
#6 lore raw

PATCH 4/5 powerpc/lib/sstep: Add prty instruction emulation

In reply to Matt Brown

This add emulation for the prtyw and prtyd instructions. Tested for logical correctness against the prtyw and prtyd instructions on ppc64le.

---
v2:
- fixed opcodes
- fixed bitshifting and typecast errors
- merged do_prtyw and do_prtyd into single function

---
 arch/powerpc/lib/sstep.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
arch/powerpc/lib/sstep.c +31 -0
index a756f44..1820fd6 100644
@@ -658,6 +658,29 @@ static nokprobe_inline void do_bpermd(struct pt_regs *regs, unsigned long v1,
 	regs->gpr[ra] = perm;
 }
 #endif /* __powerpc64__ */
+/*
+ * The size parameter adjusts the equivalent prty instruction.
+ * prtyw = 32, prtyd = 64
+ */
+static nokprobe_inline void do_prty(struct pt_regs *regs, unsigned long v,
+				int size, int ra)
+{
+	unsigned long long high, low;
+	unsigned int i;
+
+	high = 0;
+	low = 0;
+
+	for (i = 0; i < 8; i++) {
+		if (v & (1UL << (i * 8)))
+			(i < 4) ? (low++) : (high++);
+	}
+
+	if (size == 32)
+		regs->gpr[ra] = ((high & 1) << 32) | (low & 1);
+	else
+		regs->gpr[ra] = (high + low) & 1;
+}
 
 static nokprobe_inline int trap_compare(long v1, long v2)
 {
@@ -1248,6 +1271,14 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
 		case 124:	/* nor */
 			regs->gpr[ra] = ~(regs->gpr[rd] | regs->gpr[rb]);
 			goto logical_done;
+
+		case 154:	/* prtyw */
+			do_prty(regs, regs->gpr[rd], 32, ra);
+			goto logical_done;
+
+		case 186:	/* prtyd */
+			do_prty(regs, regs->gpr[rd], 64, ra);
+			goto logical_done;
 #ifdef __powerpc64__
 		case 252:	/* bpermd */
 			do_bpermd(regs, regs->gpr[rd], regs->gpr[rb], ra);
Signature · 1 line
2.9.3
author
#7 lore raw

PATCH v2 5/5 powerpc/lib/sstep: Add isel instruction emulation

In reply to Matt Brown

This adds emulation for the isel instruction.
Tested for correctness against the isel instruction and its extended mnemonics (lt, gt, eq) on ppc64le.

---
v2:
- fixed opcode
- fixed definition to include the 'if RA=0, a=0' clause
- fixed ccr bitshifting error

---
 arch/powerpc/lib/sstep.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
arch/powerpc/lib/sstep.c +11 -0
index b08eb96..e1f4ec6 100644
@@ -1246,6 +1246,17 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
 /*
  * Logical instructions
  */
+		case 15:	/* isel */
+			mb = (instr >> 6) & 0x1f; /* bc */
+			val = (regs->ccr >> (31 - mb)) & 1;
+			val2 = (ra) ? regs->gpr[ra] : 0;
+
+			if (val)
+				regs->gpr[rd] = val2;
+			else
+				regs->gpr[rd] = regs->gpr[rb];
+			goto logical_done;
+
 		case 26:	/* cntlzw */
 			asm("cntlzw %0,%1" : "=r" (regs->gpr[ra]) :
 			    "r" (regs->gpr[rd]));
Signature · 1 line
2.9.3