riscv64: add disassembly support and tests for zacas

Add disassembly support and tests for Zacas extension on RISCV64.

Change-Id: Iea42ead99f5eba68f88c7a64e58cebba43d89a85
GitHub-Last-Rev: d6398d2ffb46f506af06d68b73126346f3795bcc
GitHub-Pull-Request: golang/arch#17
Reviewed-on: https://go-review.googlesource.com/c/arch/+/735520
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Mark Ryan <markdryan@meta.com>
Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/riscv64/riscv64asm/decode.go b/riscv64/riscv64asm/decode.go
index 390edfa..c0223df 100644
--- a/riscv64/riscv64asm/decode.go
+++ b/riscv64/riscv64asm/decode.go
@@ -61,6 +61,10 @@
 			continue
 		}
 
+		if !validEncoding(f.op, x) {
+			continue Search
+		}
+
 		// Decode args.
 		var args Args
 		k := 0
@@ -594,3 +598,17 @@
 	}
 	return newargs
 }
+
+func validEncoding(op Op, x uint32) bool {
+	switch op {
+	// The AMOCAS_Q requires the first register in the pair to be even numbered;
+	// encodings with odd numbered registers specified in rs2 and rd are reserved.
+	case AMOCAS_Q, AMOCAS_Q_AQ, AMOCAS_Q_AQRL, AMOCAS_Q_RL:
+		rd := (x >> 7) & 0x1f
+		rs2 := (x >> 20) & 0x1f
+		if (rd&1) != 0 || (rs2&1) != 0 {
+			return false
+		}
+	}
+	return true
+}
diff --git a/riscv64/riscv64asm/objdump_test.go b/riscv64/riscv64asm/objdump_test.go
index ddaa28c..f47fbe7 100644
--- a/riscv64/riscv64asm/objdump_test.go
+++ b/riscv64/riscv64asm/objdump_test.go
@@ -88,6 +88,12 @@
 		return true
 	}
 
+	// Zacas instructions are supported by objdump 2.43 and later.
+	// Note: amocas.q requires rd and rs2 to be even-numbered registers.
+	if strings.HasPrefix(dec.text, ".insn") && isZacasOp(inst.Op) && !objdumpVersionAtLeast(version, "2.43") {
+		return true
+	}
+
 	return false
 }
 
@@ -128,3 +134,13 @@
 	}
 	return false
 }
+
+func isZacasOp(op Op) bool {
+	switch op {
+	case AMOCAS_D, AMOCAS_D_AQ, AMOCAS_D_AQRL, AMOCAS_D_RL,
+		AMOCAS_Q, AMOCAS_Q_AQ, AMOCAS_Q_AQRL, AMOCAS_Q_RL,
+		AMOCAS_W, AMOCAS_W_AQ, AMOCAS_W_AQRL, AMOCAS_W_RL:
+		return true
+	}
+	return false
+}
diff --git a/riscv64/riscv64asm/objdumpext_test.go b/riscv64/riscv64asm/objdumpext_test.go
index acbd925..4ab234d 100644
--- a/riscv64/riscv64asm/objdumpext_test.go
+++ b/riscv64/riscv64asm/objdumpext_test.go
@@ -289,7 +289,7 @@
 	strtabsize := len("\x00.text\x00.riscv.attributes\x00.shstrtab\x00")
 	// RISC-V objdump needs the .riscv.attributes section to identify extensions.
 	exts := "rv64i2p0_m2p0_a2p0_f2p0_d2p0_q2p0_c2p0_v1p0_" +
-		"zicbom1p0_zicbop1p0_zicboz1p0_zicond1p0_zmmul1p0_" +
+		"zacas1p0_zicbom1p0_zicbop1p0_zicboz1p0_zicond1p0_zmmul1p0_" +
 		"zfh1p0_zfhmin1p0_zba1p0_zbb1p0_zbc1p0_zbs1p0_" +
 		"zvkg1p0_zvkned1p0_zvknha1p0_zvknhb1p0_zvksed1p0_zvksh1p0"
 	b := buildRISCVAttributes(exts)
diff --git a/riscv64/riscv64asm/plan9x.go b/riscv64/riscv64asm/plan9x.go
index 59f2a5b..e74621a 100644
--- a/riscv64/riscv64asm/plan9x.go
+++ b/riscv64/riscv64asm/plan9x.go
@@ -49,7 +49,9 @@
 
 	case AMOADD_D, AMOADD_D_AQ, AMOADD_D_RL, AMOADD_D_AQRL, AMOADD_W, AMOADD_W_AQ,
 		AMOADD_W_RL, AMOADD_W_AQRL, AMOAND_D, AMOAND_D_AQ, AMOAND_D_RL, AMOAND_D_AQRL,
-		AMOAND_W, AMOAND_W_AQ, AMOAND_W_RL, AMOAND_W_AQRL, AMOMAXU_D, AMOMAXU_D_AQ,
+		AMOAND_W, AMOAND_W_AQ, AMOAND_W_RL, AMOAND_W_AQRL, AMOCAS_D, AMOCAS_D_AQ,
+		AMOCAS_D_AQRL, AMOCAS_D_RL, AMOCAS_Q, AMOCAS_Q_AQ, AMOCAS_Q_AQRL, AMOCAS_Q_RL,
+		AMOCAS_W, AMOCAS_W_AQ, AMOCAS_W_AQRL, AMOCAS_W_RL, AMOMAXU_D, AMOMAXU_D_AQ,
 		AMOMAXU_D_RL, AMOMAXU_D_AQRL, AMOMAXU_W, AMOMAXU_W_AQ, AMOMAXU_W_RL, AMOMAXU_W_AQRL,
 		AMOMAX_D, AMOMAX_D_AQ, AMOMAX_D_RL, AMOMAX_D_AQRL, AMOMAX_W, AMOMAX_W_AQ, AMOMAX_W_RL,
 		AMOMAX_W_AQRL, AMOMINU_D, AMOMINU_D_AQ, AMOMINU_D_RL, AMOMINU_D_AQRL, AMOMINU_W,
diff --git a/riscv64/riscv64asm/tables.go b/riscv64/riscv64asm/tables.go
index f96c307..e08b749 100644
--- a/riscv64/riscv64asm/tables.go
+++ b/riscv64/riscv64asm/tables.go
@@ -29,6 +29,18 @@
 	AMOAND_W_AQ
 	AMOAND_W_AQRL
 	AMOAND_W_RL
+	AMOCAS_D
+	AMOCAS_D_AQ
+	AMOCAS_D_AQRL
+	AMOCAS_D_RL
+	AMOCAS_Q
+	AMOCAS_Q_AQ
+	AMOCAS_Q_AQRL
+	AMOCAS_Q_RL
+	AMOCAS_W
+	AMOCAS_W_AQ
+	AMOCAS_W_AQRL
+	AMOCAS_W_RL
 	AMOMAXU_D
 	AMOMAXU_D_AQ
 	AMOMAXU_D_AQRL
@@ -1053,6 +1065,18 @@
 	AMOAND_W_AQ:       "AMOAND.W.AQ",
 	AMOAND_W_AQRL:     "AMOAND.W.AQRL",
 	AMOAND_W_RL:       "AMOAND.W.RL",
+	AMOCAS_D:          "AMOCAS.D",
+	AMOCAS_D_AQ:       "AMOCAS.D.AQ",
+	AMOCAS_D_AQRL:     "AMOCAS.D.AQRL",
+	AMOCAS_D_RL:       "AMOCAS.D.RL",
+	AMOCAS_Q:          "AMOCAS.Q",
+	AMOCAS_Q_AQ:       "AMOCAS.Q.AQ",
+	AMOCAS_Q_AQRL:     "AMOCAS.Q.AQRL",
+	AMOCAS_Q_RL:       "AMOCAS.Q.RL",
+	AMOCAS_W:          "AMOCAS.W",
+	AMOCAS_W_AQ:       "AMOCAS.W.AQ",
+	AMOCAS_W_AQRL:     "AMOCAS.W.AQRL",
+	AMOCAS_W_RL:       "AMOCAS.W.RL",
 	AMOMAXU_D:         "AMOMAXU.D",
 	AMOMAXU_D_AQ:      "AMOMAXU.D.AQ",
 	AMOMAXU_D_AQRL:    "AMOMAXU.D.AQRL",
@@ -2098,6 +2122,30 @@
 	{mask: 0xfe00707f, value: 0x6600202f, op: AMOAND_W_AQRL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
 	// AMOAND.W.RL rd, rs2, rs1_ptr
 	{mask: 0xfe00707f, value: 0x6200202f, op: AMOAND_W_RL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.D rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2800302f, op: AMOCAS_D, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.D.AQ rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2c00302f, op: AMOCAS_D_AQ, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.D.AQRL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2e00302f, op: AMOCAS_D_AQRL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.D.RL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2a00302f, op: AMOCAS_D_RL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.Q rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2800402f, op: AMOCAS_Q, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.Q.AQ rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2c00402f, op: AMOCAS_Q_AQ, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.Q.AQRL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2e00402f, op: AMOCAS_Q_AQRL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.Q.RL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2a00402f, op: AMOCAS_Q_RL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.W rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2800202f, op: AMOCAS_W, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.W.AQ rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2c00202f, op: AMOCAS_W_AQ, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.W.AQRL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2e00202f, op: AMOCAS_W_AQRL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
+	// AMOCAS.W.RL rd, rs2, rs1_ptr
+	{mask: 0xfe00707f, value: 0x2a00202f, op: AMOCAS_W_RL, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
 	// AMOMAXU.D rd, rs2, rs1_ptr
 	{mask: 0xfe00707f, value: 0xe000302f, op: AMOMAXU_D, args: argTypeList{arg_rd, arg_rs2, arg_rs1_ptr}},
 	// AMOMAXU.D.AQ rd, rs2, rs1_ptr
diff --git a/riscv64/riscv64asm/testdata/gnucases.txt b/riscv64/riscv64asm/testdata/gnucases.txt
index db1fc8f..e28d4a5 100644
--- a/riscv64/riscv64asm/testdata/gnucases.txt
+++ b/riscv64/riscv64asm/testdata/gnucases.txt
@@ -19,6 +19,18 @@
 afa26360|	amoand.w x5,x6,(x7)
 afa26364|	amoand.w.aq x5,x6,(x7)
 afa26362|	amoand.w.rl x5,x6,(x7)
+afa2632e|	amocas.w.aqrl x5,x6,(x7)
+afa2632c|	amocas.w.aq x5,x6,(x7)
+afa2632a|	amocas.w.rl x5,x6,(x7)
+afa26328|	amocas.w x5,x6,(x7)
+afb2632e|	amocas.d.aqrl x5,x6,(x7)
+afb2632c|	amocas.d.aq x5,x6,(x7)
+afb2632a|	amocas.d.rl x5,x6,(x7)
+afb26328|	amocas.d x5,x6,(x7)
+2fc2632e|	amocas.q.aqrl x4,x6,(x7)
+2fc2632c|	amocas.q.aq x4,x6,(x7)
+2fc2632a|	amocas.q.rl x4,x6,(x7)
+2fc26328|	amocas.q x4,x6,(x7)
 afb263e0|	amomaxu.d x5,x6,(x7)
 afb263e4|	amomaxu.d.aq x5,x6,(x7)
 afb263e2|	amomaxu.d.rl x5,x6,(x7)
diff --git a/riscv64/riscv64asm/testdata/plan9cases.txt b/riscv64/riscv64asm/testdata/plan9cases.txt
index a361d65..554834b 100644
--- a/riscv64/riscv64asm/testdata/plan9cases.txt
+++ b/riscv64/riscv64asm/testdata/plan9cases.txt
@@ -19,6 +19,18 @@
 afa26360|	AMOANDW X6, (X7), X5
 afa26364|	AMOANDW X6, (X7), X5
 afa26362|	AMOANDW X6, (X7), X5
+afa2632e|	AMOCASW	X6, (X7), X5
+afa2632c|	AMOCASW	X6, (X7), X5
+afa2632a|	AMOCASW	X6, (X7), X5
+afa26328|	AMOCASW	X6, (X7), X5
+afb2632e|	AMOCASD	X6, (X7), X5
+afb2632c|	AMOCASD	X6, (X7), X5
+afb2632a|	AMOCASD	X6, (X7), X5
+afb26328|	AMOCASD	X6, (X7), X5
+2fc2632e|	AMOCASQ	X6, (X7), X4
+2fc2632c|	AMOCASQ	X6, (X7), X4
+2fc2632a|	AMOCASQ	X6, (X7), X4
+2fc26328|	AMOCASQ	X6, (X7), X4
 afb263e0|	AMOMAXUD X6, (X7), X5
 afb263e4|	AMOMAXUD X6, (X7), X5
 afb263e2|	AMOMAXUD X6, (X7), X5
diff --git a/riscv64/riscv64spec/spec.go b/riscv64/riscv64spec/spec.go
index 887dd14..77d61de 100644
--- a/riscv64/riscv64spec/spec.go
+++ b/riscv64/riscv64spec/spec.go
@@ -32,6 +32,7 @@
 	"rv_m",
 	"rv_q",
 	"rv_v",
+	"rv_zacas",
 	"rv_zba",
 	"rv_zbb",
 	"rv_zbc",
@@ -53,6 +54,7 @@
 	"rv64_i",
 	"rv64_m",
 	"rv64_q",
+	"rv64_zacas",
 	"rv64_zba",
 	"rv64_zbb",
 	"rv64_zbs",