ppc64/ppc64asm: add ISA 3.1B support

The new ISA fixes a couple typos, and adds special hashing
instructions to support ROP exploitation.

The hash instructions encode a negative offset in a novel
way which requires a bit of special handling.

Change-Id: I9491e10ac87efe37d93b6efaf7f108ae3a4402fd
Reviewed-on: https://go-review.googlesource.com/c/arch/+/418859
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
Reviewed-by: Joedian Reid <joedian@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Paul Murphy <murp@ibm.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
diff --git a/ppc64/pp64.csv b/ppc64/pp64.csv
index 3150cad..b2aa6b3 100644
--- a/ppc64/pp64.csv
+++ b/ppc64/pp64.csv
@@ -1,4 +1,4 @@
-# POWER ISA 3.1 instruction description.
+# POWER ISA 3.1B instruction description.
 #
 # This file contains comment lines, each beginning with #,
 # followed by entries in CSV format.
@@ -13,8 +13,12 @@
 #    a list of sequences of the form (,sequence)+. A leading comma is used to signify an
 #    instruction encoding requiring multiple instruction words.
 # The fourth field represents the ISA version where the instruction was introduced as
-# stated in Appendix F. of ISA 3.1
+# stated in Appendix F. of ISA 3.1B
 #
+"Hash Check X-form","hashchk RB,offset(RA)","31@0|D@6|RA@11|RB@16|754@21|DX@31|","v3.1B"
+"Hash Check Privileged X-form","hashchkp RB,offset(RA)","31@0|D@6|RA@11|RB@16|690@21|DX@31|","v3.1B"
+"Hash Store X-form","hashst RB,offset(RA)","31@0|D@6|RA@11|RB@16|722@21|DX@31|","v3.1B"
+"Hash Store Privileged X-form","hashstp RB,offset(RA)","31@0|D@6|RA@11|RB@16|658@21|DX@31|","v3.1B"
 "Byte-Reverse Doubleword X-form","brd RA,RS","31@0|RS@6|RA@11|///@16|187@21|/@31|","v3.1"
 "Byte-Reverse Halfword X-form","brh RA,RS","31@0|RS@6|RA@11|///@16|219@21|/@31|","v3.1"
 "Byte-Reverse Word X-form","brw RA,RS","31@0|RS@6|RA@11|///@16|155@21|/@31|","v3.1"
@@ -209,7 +213,7 @@
 "VSX Vector bfloat16 GER (Rank-2 Update) Negative multiply, Positive accumulate XX3-form","xvbf16ger2np AT,XA,XB","59@0|AT@6|//@9|A@11|B@16|114@21|AX@29|BX@30|/@31|","v3.1"
 "VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Negative accumulate XX3-form","xvbf16ger2pn AT,XA,XB","59@0|AT@6|//@9|A@11|B@16|178@21|AX@29|BX@30|/@31|","v3.1"
 "VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Positive accumulate XX3-form","xvbf16ger2pp AT,XA,XB","59@0|AT@6|//@9|A@11|B@16|50@21|AX@29|BX@30|/@31|","v3.1"
-"VSX Vector Convert bfloat16 to Single-Precision format XX2-form","xvcvbf16spn XT,XB","60@0|T@6|16@11|B@16|475@21|BX@30|TX@31|","v3.1"
+"VSX Vector Convert bfloat16 to Single-Precision format Non-signaling XX2-form","xvcvbf16spn XT,XB","60@0|T@6|16@11|B@16|475@21|BX@30|TX@31|","v3.1"
 "VSX Vector Convert with round Single-Precision to bfloat16 format XX2-form","xvcvspbf16 XT,XB","60@0|T@6|17@11|B@16|475@21|BX@30|TX@31|","v3.1"
 "VSX Vector 16-bit Floating-Point GER (rank-2 update) XX3-form","xvf16ger2 AT,XA,XB","59@0|AT@6|//@9|A@11|B@16|19@21|AX@29|BX@30|/@31|","v3.1"
 "VSX Vector 16-bit Floating-Point GER (rank-2 update) Negative multiply, Negative accumulate XX3-form","xvf16ger2nn AT,XA,XB","59@0|AT@6|//@9|A@11|B@16|210@21|AX@29|BX@30|/@31|","v3.1"
@@ -1034,7 +1038,7 @@
 "Add Carrying XO-form","addc RT,RA,RB (OE=0 Rc=0)|addc. RT,RA,RB (OE=0 Rc=1)|addco RT,RA,RB (OE=1 Rc=0)|addco. RT,RA,RB (OE=1 Rc=1)","31@0|RT@6|RA@11|RB@16|OE@21|10@22|Rc@31|","P1"
 "Add Extended XO-form","adde RT,RA,RB (OE=0 Rc=0)|adde. RT,RA,RB (OE=0 Rc=1)|addeo RT,RA,RB (OE=1 Rc=0)|addeo. RT,RA,RB (OE=1 Rc=1)","31@0|RT@6|RA@11|RB@16|OE@21|138@22|Rc@31|","P1"
 "Add Immediate D-form","addi RT,RA,SI|li RT,SI (RA=0)","14@0|RT@6|RA@11|SI@16|","P1"
-"Add Immediate Carrying D-formy","addic RT,RA,SI","12@0|RT@6|RA@11|SI@16|","P1"
+"Add Immediate Carrying D-form","addic RT,RA,SI","12@0|RT@6|RA@11|SI@16|","P1"
 "Add Immediate Carrying and Record D-form","addic. RT,RA,SI","13@0|RT@6|RA@11|SI@16|","P1"
 "Add Immediate Shifted D-form","addis RT,RA,SI|lis RT,SI (RA=0)","15@0|RT@6|RA@11|SI@16|","P1"
 "Add to Minus One Extended XO-form","addme RT,RA (OE=0 Rc=0)|addme. RT,RA (OE=0 Rc=1)|addmeo RT,RA (OE=1 Rc=0)|addmeo. RT,RA (OE=1 Rc=1)","31@0|RT@6|RA@11|///@16|OE@21|234@22|Rc@31|","P1"
diff --git a/ppc64/ppc64asm/decode.go b/ppc64/ppc64asm/decode.go
index 59bd325..b8d857c 100644
--- a/ppc64/ppc64asm/decode.go
+++ b/ppc64/ppc64asm/decode.go
@@ -22,9 +22,12 @@
 // The Args are stored in the same order as the instruction manual.
 //
 // Prefixed instructions are stored as:
-//   prefix << 32 | suffix,
+//
+//	prefix << 32 | suffix,
+//
 // Regular instructions are:
-//   inst << 32
+//
+//	inst << 32
 type instFormat struct {
 	Op       Op
 	Mask     uint64
@@ -77,6 +80,12 @@
 		return Label(a.BitFields.ParseSigned(i) << a.Shift)
 	case TypeOffset:
 		return Offset(a.BitFields.ParseSigned(i) << a.Shift)
+	case TypeNegOffset:
+		// An oddball encoding of offset for hashchk and similar.
+		// e.g hashchk offset is 0b1111111000000000 | DX << 8 | D << 3
+		off := a.BitFields.ParseSigned(i) << a.Shift
+		neg := int64(-1) << (int(a.Shift) + a.BitFields.NumBits())
+		return Offset(neg | off)
 	}
 }
 
@@ -98,6 +107,7 @@
 	TypeImmSigned            // signed immediate
 	TypeImmUnsigned          // unsigned immediate/flag/mask, this is the catch-all type
 	TypeOffset               // signed offset in load/store
+	TypeNegOffset            // A negative 16 bit value 0b1111111xxxxx000 encoded as 0bxxxxx (e.g in the hashchk instruction)
 	TypeLast                 // must be the last one
 )
 
@@ -135,6 +145,8 @@
 		return "Label"
 	case TypeOffset:
 		return "Offset"
+	case TypeNegOffset:
+		return "NegOffset"
 	}
 }
 
diff --git a/ppc64/ppc64asm/field.go b/ppc64/ppc64asm/field.go
index 13df063..3779446 100644
--- a/ppc64/ppc64asm/field.go
+++ b/ppc64/ppc64asm/field.go
@@ -86,3 +86,12 @@
 	u, l := bs.parse(i)
 	return int64(u) << (64 - l) >> (64 - l)
 }
+
+// Count the number of bits in the aggregate BitFields
+func (bs BitFields) NumBits() int {
+	num := 0
+	for _, b := range bs {
+		num += int(b.Bits)
+	}
+	return num
+}
diff --git a/ppc64/ppc64asm/field_test.go b/ppc64/ppc64asm/field_test.go
index 01402b5..ce18ad5 100644
--- a/ppc64/ppc64asm/field_test.go
+++ b/ppc64/ppc64asm/field_test.go
@@ -65,26 +65,29 @@
 		i    [2]uint32 // input
 		u    uint64    // unsigned output
 		s    int64     // signed output
+		nb   int       // Total number of bits in BitField
 		fail bool      // if the check should panic
 	}{
-		{BitFields{{0, 0, 1}}, [2]uint32{0, 0}, 0, 0, true},
-		{BitFields{{31, 2, 1}}, [2]uint32{0, 0}, 0, 0, true},
-		{BitFields{{31, 1, 1}}, [2]uint32{0, 1}, 1, -1, false},
-		{BitFields{{29, 2, 1}}, [2]uint32{0, 0 << 1}, 0, 0, false},
-		{BitFields{{29, 2, 1}}, [2]uint32{0, 1 << 1}, 1, 1, false},
-		{BitFields{{29, 2, 1}}, [2]uint32{0, 2 << 1}, 2, -2, false},
-		{BitFields{{29, 2, 1}}, [2]uint32{0, 3 << 1}, 3, -1, false},
-		{BitFields{{0, 32, 1}}, [2]uint32{0, 1<<32 - 1}, 1<<32 - 1, -1, false},
-		{BitFields{{16, 3, 1}}, [2]uint32{0, 1 << 15}, 4, -4, false},
-		{BitFields{{16, 16, 0}, {16, 16, 1}}, [2]uint32{0x8016, 0x32}, 0x80160032, -0x7FE9FFCE, false},
-		{BitFields{{14, 18, 0}, {16, 16, 1}}, [2]uint32{0x38016, 0x32}, 0x380160032, -0x07FE9FFCE, false},
+		{BitFields{{0, 0, 1}}, [2]uint32{0, 0}, 0, 0, 0, true},
+		{BitFields{{31, 2, 1}}, [2]uint32{0, 0}, 0, 0, 2, true},
+		{BitFields{{31, 1, 1}}, [2]uint32{0, 1}, 1, -1, 1, false},
+		{BitFields{{29, 2, 1}}, [2]uint32{0, 0 << 1}, 0, 0, 2, false},
+		{BitFields{{29, 2, 1}}, [2]uint32{0, 1 << 1}, 1, 1, 2, false},
+		{BitFields{{29, 2, 1}}, [2]uint32{0, 2 << 1}, 2, -2, 2, false},
+		{BitFields{{29, 2, 1}}, [2]uint32{0, 3 << 1}, 3, -1, 2, false},
+		{BitFields{{0, 32, 1}}, [2]uint32{0, 1<<32 - 1}, 1<<32 - 1, -1, 32, false},
+		{BitFields{{16, 3, 1}}, [2]uint32{0, 1 << 15}, 4, -4, 3, false},
+		{BitFields{{16, 16, 0}, {16, 16, 1}}, [2]uint32{0x8016, 0x32}, 0x80160032, -0x7FE9FFCE, 32, false},
+		{BitFields{{14, 18, 0}, {16, 16, 1}}, [2]uint32{0x38016, 0x32}, 0x380160032, -0x07FE9FFCE, 34, false},
 	}
 	for i, tst := range tests {
 		var (
-			ou uint64
-			os int64
+			ou  uint64
+			os  int64
+			onb int
 		)
 		failed := panicOrNot(func() {
+			onb = tst.b.NumBits()
 			ou = tst.b.Parse(tst.i)
 			os = tst.b.ParseSigned(tst.i)
 		})
@@ -99,5 +102,8 @@
 		if os != tst.s {
 			t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
 		}
+		if onb != tst.nb {
+			t.Errorf("case %d: %v.NumBits() returned %d, expected %d", i, tst.b, onb, tst.nb)
+		}
 	}
 }
diff --git a/ppc64/ppc64asm/plan9.go b/ppc64/ppc64asm/plan9.go
index 4bd1c7f..fcb2a12 100644
--- a/ppc64/ppc64asm/plan9.go
+++ b/ppc64/ppc64asm/plan9.go
@@ -83,7 +83,9 @@
 		STH, STHU,
 		STW, STWU,
 		STD, STDU,
-		STQ, STFD, STFDU, STFS, STFSU:
+		STFD, STFDU,
+		STFS, STFSU,
+		STQ, HASHST, HASHSTP:
 		return op + " " + strings.Join(args, ",")
 
 	case FCMPU, FCMPO, CMPD, CMPDI, CMPLD, CMPLDI, CMPW, CMPWI, CMPLW, CMPLWI:
diff --git a/ppc64/ppc64asm/tables.go b/ppc64/ppc64asm/tables.go
index 8d0a243..8705077 100644
--- a/ppc64/ppc64asm/tables.go
+++ b/ppc64/ppc64asm/tables.go
@@ -1,9 +1,13 @@
-// Code generated by ppc64map -fmt=decoder pp64.csv DO NOT EDIT.
+// Code generated by ppc64map -fmt=decoder ../pp64.csv DO NOT EDIT.
 
 package ppc64asm
 
 const (
 	_ Op = iota
+	HASHCHK
+	HASHCHKP
+	HASHST
+	HASHSTP
 	BRD
 	BRH
 	BRW
@@ -1420,6 +1424,10 @@
 )
 
 var opstr = [...]string{
+	HASHCHK:        "hashchk",
+	HASHCHKP:       "hashchkp",
+	HASHST:         "hashst",
+	HASHSTP:        "hashstp",
 	BRD:            "brd",
 	BRH:            "brh",
 	BRW:            "brw",
@@ -2836,9 +2844,10 @@
 }
 
 var (
+	ap_Reg_16_20                     = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{16, 5, 0}}}
+	ap_NegOffset_31_31_6_10_shift3   = &argField{Type: TypeNegOffset, Shift: 3, BitFields: BitFields{{31, 1, 0}, {6, 5, 0}}}
 	ap_Reg_11_15                     = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{11, 5, 0}}}
 	ap_Reg_6_10                      = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{6, 5, 0}}}
-	ap_Reg_16_20                     = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{16, 5, 0}}}
 	ap_FPReg_6_10                    = &argField{Type: TypeFPReg, Shift: 0, BitFields: BitFields{{6, 5, 0}}}
 	ap_VecReg_16_20                  = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{16, 5, 0}}}
 	ap_VecReg_6_10                   = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{6, 5, 0}}}
@@ -2942,6 +2951,14 @@
 )
 
 var instFormats = [...]instFormat{
+	{HASHCHK, 0xfc0007fe00000000, 0x7c0005e400000000, 0x0, // Hash Check X-form (hashchk RB,offset(RA))
+		[6]*argField{ap_Reg_16_20, ap_NegOffset_31_31_6_10_shift3, ap_Reg_11_15}},
+	{HASHCHKP, 0xfc0007fe00000000, 0x7c00056400000000, 0x0, // Hash Check Privileged X-form (hashchkp RB,offset(RA))
+		[6]*argField{ap_Reg_16_20, ap_NegOffset_31_31_6_10_shift3, ap_Reg_11_15}},
+	{HASHST, 0xfc0007fe00000000, 0x7c0005a400000000, 0x0, // Hash Store X-form (hashst RB,offset(RA))
+		[6]*argField{ap_Reg_16_20, ap_NegOffset_31_31_6_10_shift3, ap_Reg_11_15}},
+	{HASHSTP, 0xfc0007fe00000000, 0x7c00052400000000, 0x0, // Hash Store Privileged X-form (hashstp RB,offset(RA))
+		[6]*argField{ap_Reg_16_20, ap_NegOffset_31_31_6_10_shift3, ap_Reg_11_15}},
 	{BRD, 0xfc0007fe00000000, 0x7c00017600000000, 0xf80100000000, // Byte-Reverse Doubleword X-form (brd RA,RS)
 		[6]*argField{ap_Reg_11_15, ap_Reg_6_10}},
 	{BRH, 0xfc0007fe00000000, 0x7c0001b600000000, 0xf80100000000, // Byte-Reverse Halfword X-form (brh RA,RS)
@@ -3344,7 +3361,7 @@
 		[6]*argField{ap_MMAReg_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
 	{XVBF16GER2PP, 0xfc0007f800000000, 0xec00019000000000, 0x60000100000000, // VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Positive accumulate XX3-form (xvbf16ger2pp AT,XA,XB)
 		[6]*argField{ap_MMAReg_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
-	{XVCVBF16SPN, 0xfc1f07fc00000000, 0xf010076c00000000, 0x0, // VSX Vector Convert bfloat16 to Single-Precision format XX2-form (xvcvbf16spn XT,XB)
+	{XVCVBF16SPN, 0xfc1f07fc00000000, 0xf010076c00000000, 0x0, // VSX Vector Convert bfloat16 to Single-Precision format Non-signaling XX2-form (xvcvbf16spn XT,XB)
 		[6]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
 	{XVCVSPBF16, 0xfc1f07fc00000000, 0xf011076c00000000, 0x0, // VSX Vector Convert with round Single-Precision to bfloat16 format XX2-form (xvcvspbf16 XT,XB)
 		[6]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
@@ -5334,7 +5351,7 @@
 		[6]*argField{ap_Reg_6_10, ap_ImmSigned_16_31}},
 	{ADDI, 0xfc00000000000000, 0x3800000000000000, 0x0, // Add Immediate D-form (addi RT,RA,SI)
 		[6]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
-	{ADDIC, 0xfc00000000000000, 0x3000000000000000, 0x0, // Add Immediate Carrying D-formy (addic RT,RA,SI)
+	{ADDIC, 0xfc00000000000000, 0x3000000000000000, 0x0, // Add Immediate Carrying D-form (addic RT,RA,SI)
 		[6]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
 	{ADDICCC, 0xfc00000000000000, 0x3400000000000000, 0x0, // Add Immediate Carrying and Record D-form (addic. RT,RA,SI)
 		[6]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
diff --git a/ppc64/ppc64asm/testdata/decode.txt b/ppc64/ppc64asm/testdata/decode.txt
index 7bf4355..ef5c90e 100644
--- a/ppc64/ppc64asm/testdata/decode.txt
+++ b/ppc64/ppc64asm/testdata/decode.txt
@@ -873,3 +873,13 @@
 7c2311b8|	plan9	CFUGED R1,R2,R3
 04100016e4820032|	gnu	.quad 0x4100016e4820032
 0612000138820007|	gnu	.quad 0x612000138820007
+7fe20de5|	plan9	HASHCHK -8(R2),R1
+7fe20da5|	plan9	HASHST R1,-8(R2)
+7c020de4|	plan9	HASHCHK -512(R2),R1
+7c020da4|	plan9	HASHST R1,-512(R2)
+7c020de5|	plan9	HASHCHK -256(R2),R1
+7c020da5|	plan9	HASHST R1,-256(R2)
+7fe115a5|	plan9	HASHST R2,-8(R1)
+7fe11525|	plan9	HASHSTP R2,-8(R1)
+7fe115e5|	plan9	HASHCHK -8(R1),R2
+7fe11565|	plan9	HASHCHKP -8(R1),R2
diff --git a/ppc64/ppc64asm/testdata/decode_generated.txt b/ppc64/ppc64asm/testdata/decode_generated.txt
index d8619d7..13345e9 100644
--- a/ppc64/ppc64asm/testdata/decode_generated.txt
+++ b/ppc64/ppc64asm/testdata/decode_generated.txt
@@ -1,3 +1,7 @@
+7e0115e5|	gnu	hashchk r2,-128(r1)
+7e011565|	gnu	hashchkp r2,-128(r1)
+7e0115a5|	gnu	hashst r2,-128(r1)
+7e011525|	gnu	hashstp r2,-128(r1)
 7c610176|	gnu	brd r1,r3
 7c6101b6|	gnu	brh r1,r3
 7c610136|	gnu	brw r1,r3
@@ -23,9 +27,9 @@
 7c611138|	gnu	pdepd r1,r3,r2
 7c611178|	gnu	pextd r1,r3,r2
 0610001688800032|	gnu	plbz r4,1441842
+60000000|	gnu	nop
 04100016e4800032|	gnu	pld r4,1441842
 06100016c8600032|	gnu	plfd f3,1441842
-60000000|	gnu	nop
 06100016c0600032|	gnu	plfs f3,1441842
 06100016a8800032|	gnu	plha r4,1441842
 06100016a0800032|	gnu	plhz r4,1441842
@@ -240,7 +244,6 @@
 7f810162|	gnu	xxmtacc a7
 0500000188232a4f|	gnu	xxpermx vs33,vs35,vs37,vs41,1
 7f830162|	gnu	xxsetaccz a7
-60000000|	gnu	nop
 0500012380234567|	gnu	xxsplti32dx vs33,1,19088743
 0500012380254567|	gnu	xxspltidp vs33,19088743
 0500012380274567|	gnu	xxspltiw vs33,19088743
@@ -1214,13 +1217,13 @@
 7c611079|	gnu	andc. r1,r3,r2
 70610000|	gnu	andi. r1,r3,0
 74610000|	gnu	andis. r1,r3,0
-48000690|	gnu	b 0x1a90
+48000690|	gnu	b 0x1a9c
 48000692|	gnu	ba 0x690
-48000691|	gnu	bl 0x1a98
+48000691|	gnu	bl 0x1aa4
 48000693|	gnu	bla 0x690
-40860690|	gnu	bne cr1,0x1aa0
+40860690|	gnu	bne cr1,0x1aac
 40860692|	gnu	bnea cr1,0x690
-40860691|	gnu	bnel cr1,0x1aa8
+40860691|	gnu	bnel cr1,0x1ab4
 40860693|	gnu	bnela cr1,0x690
 4c860420|	gnu	bnectr cr1
 4c860421|	gnu	bnectrl cr1
diff --git a/ppc64/ppc64map/map.go b/ppc64/ppc64map/map.go
index 31d692d..1e3b1b6 100644
--- a/ppc64/ppc64map/map.go
+++ b/ppc64/ppc64map/map.go
@@ -60,6 +60,7 @@
 	ISA_V30B
 	ISA_V30C
 	ISA_V31
+	ISA_V31B
 )
 
 var isaToISA = map[string]isaversion{
@@ -77,6 +78,7 @@
 	"v3.0B": ISA_V30B,
 	"v3.0C": ISA_V30C,
 	"v3.1":  ISA_V31,
+	"v3.1B": ISA_V31B,
 }
 
 func usage() {
@@ -482,6 +484,18 @@
 					opr = "BD"
 				}
 
+			case "offset":
+				switch inst.Op {
+				// These encode a 6 bit displacement in the format of an X-form opcode.
+				// Allowable displaments are -8 to -8*64 in 8B increments.
+				case "hashchk", "hashchkp", "hashst", "hashstp":
+					typ = asm.TypeNegOffset
+					opr = "DX"
+					opr2 = "D"
+					shift = 3
+
+				}
+
 			case "XMSK", "YMSK", "PMSK", "IX", "BHRBE":
 				typ = asm.TypeImmUnsigned
 
@@ -737,7 +751,12 @@
 }
 
 // Some ISA instructions are memops, but are not described like "Load ..." or "Store ..."
-var isMemopMap = map[string]bool{}
+var isMemopMap = map[string]bool{
+	"hashst":   true,
+	"hashstp":  true,
+	"hashchk":  true,
+	"hashchkp": true,
+}
 
 // Does this instruction contain a memory argument (e.g x-form load or d-form store)
 func hasMemoryArg(insn *Inst) bool {
@@ -767,7 +786,7 @@
 
 	// Does this field require an obj.Addr.Offset?
 	isImmediate := func(t asm.ArgType) bool {
-		return t == asm.TypeImmUnsigned || t == asm.TypeSpReg || t == asm.TypeImmSigned || t == asm.TypeOffset
+		return t == asm.TypeImmUnsigned || t == asm.TypeSpReg || t == asm.TypeImmSigned || t == asm.TypeOffset || t == asm.TypeNegOffset
 	}
 
 	if insn.memOp {
@@ -827,13 +846,26 @@
 
 		// Generate a check to verify shifted inputs satisfy their constraints.
 		// For historical reasons this is not needed for 16 bit values shifted by 16. (i.e SI/UI constants in addis/xoris)
-		if atype.Shift != 0 && atype.Shift != 16 && bits != 32 {
+		if atype.Type != asm.TypeNegOffset && atype.Shift != 0 && atype.Shift != 16 && bits != 32 {
 			arg := argOrder[j] + itype
 			mod := (1 << atype.Shift) - 1
 			errCheck += fmt.Sprintf("if %s & 0x%x != 0 {\n", arg, mod)
 			errCheck += fmt.Sprintf("c.ctxt.Diag(\"Constant 0x%%x (%%d) is not a multiple of %d\\n%%v\",%s,%s,p)\n", mod+1, arg, arg)
 			errCheck += fmt.Sprintf("}\n")
 		}
+		// NegOffset requires a stronger offset check
+		if atype.Type == asm.TypeNegOffset {
+			arg := argOrder[j] + itype
+			mask := -1 << (atype.BitFields.NumBits() + int(atype.Shift))
+			maskl := mask // Sign bits are implied in this type.
+			mask |= (1 << atype.Shift) - 1
+			min := maskl
+			max := maskl | (^mask)
+			step := 1 << atype.Shift
+			errCheck += fmt.Sprintf("if %s & 0x%x != 0x%x {\n", arg, uint32(mask), uint32(maskl))
+			errCheck += fmt.Sprintf("c.ctxt.Diag(\"Constant(%%d) must within the range of [%d,%d] in steps of %d\\n%%v\",%s,p)\n", min, max, step, arg)
+			errCheck += fmt.Sprintf("}\n")
+		}
 		j++
 	}
 	buf.WriteString(errCheck)
@@ -895,6 +927,8 @@
 			if atype.Shift != 0 {
 				ret += fmt.Sprintf("%d", atype.Shift)
 			}
+		case asm.TypeNegOffset: // e.g offset in hashst rb, offset(ra)
+			ret += "N"
 		default:
 			log.Fatalf("Unhandled type in insnTypeStr: %v\n", atype)
 		}
@@ -953,6 +987,14 @@
 				shift = ""
 			}
 		}
+		if f.Type == asm.TypeNegOffset {
+			// This is a hack, but allows hashchk and like to correctly
+			// merge there argument into a C_SOREG memory location type
+			// argument a little later.
+			sign = "S"
+			bits = 16
+			shift = ""
+		}
 		return fmt.Sprintf("C_%s%d%sCON", sign, bits, shift)
 	}
 	insn := ta.Insns[0]
diff --git a/ppc64/ppc64util/hack.h b/ppc64/ppc64util/hack.h
index e7dada2..3fd9f31 100644
--- a/ppc64/ppc64util/hack.h
+++ b/ppc64/ppc64util/hack.h
@@ -129,6 +129,9 @@
 #define Rpfx 1
 #define SIpfx 0xFFFFFFFE00010007
 
+// A valid displacement value for the hash check and hash store instructions.
+#define offset -128
+
 // These decode as m.fpr* or m.vr*.  This is a matter of preference.  We
 // don't support these mnemonics, and I don't think they improve reading
 // disassembled code in most cases. so ignore.