arm64/instgen: add support for immediates

This CL generates CL 763769.

Change-Id: If26642a49f7af847b0d2d8750bb0b81781a8bc6b
Reviewed-on: https://go-review.googlesource.com/c/arch/+/763781
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/arm64/instgen/encodings.go b/arm64/instgen/encodings.go
index fc099be..08ca665 100644
--- a/arm64/instgen/encodings.go
+++ b/arm64/instgen/encodings.go
@@ -923,7 +923,7 @@
 	`Is the name of the second source scalable vector register Z0-Z7, encoded in the "Zm" field.
 bit range mappings:
 Zm: [16:19)
-`: {"encodeZm_1619_Range0_7", `if v <= 7 {
+`: {"encodeZm_1619_Range0_7V2", `if v <= 7 {
 		return v << 16, true
 	}
 	return 0, false`, "enc_Zm"},
@@ -978,4 +978,516 @@
 		return 16 << 16, true
 	}
 	return 0, false`, "enc_tsz"},
+	`Check this is a 64-bit scalar register`: {"encodeXCheck", `return 0, true`, "enc_NIL"},
+	`Check this is immediate 0.0`: {"encodeFimm0_0_56", `if (v & 0x7FFFFFFF) != 0 {
+		return 0, false
+	}
+	return 0, true`, "enc_NIL"},
+	`For the "16-bit" variant: is the element index, in the range 0 to 3, encoded in the "i2" field.
+bit range mappings:
+i2: [19:21)
+`: {"encodeI2_1921_16bit", `if v > 3 {
+		return 0, false
+	}
+	return v << 19, true`, "enc_i2"},
+	`For the "16-bit" variant: is the name of the second source scalable vector register Z0-Z7, encoded in the "Zm" field.
+bit range mappings:
+Zm: [16:19)
+`: {"encodeZm_1619_Range0_7V1", `if v <= 7 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_Zm"},
+	`For the "32-bit" variant: is the element index, in the range 0 to 1, encoded in the "i1" field.
+bit range mappings:
+i1: [20:21)
+`: {"encodeI1_2021_32bit", `if v > 1 {
+		return 0, false
+	}
+	return v << 20, true`, "enc_i1"},
+	`For the "32-bit" variant: is the name of the second source scalable vector register Z0-Z15, encoded in the "Zm" field.
+bit range mappings:
+Zm: [16:20)
+`: {"encodeZm_1620_Range0_15", `if v <= 15 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_Zm"},
+	`For the "Equal", "Greater than or equal", "Greater than", "Less than or equal", "Less than", and "Not equal" variants: is the signed immediate operand, in the range -16 to 15, encoded in the "imm5" field.
+bit range mappings:
+imm5: [16:21)
+`: {"encodeImm5Signed_1621V2", `if int32(v) >= -16 && int32(v) <= 15 {
+		return (v & 31) << 16, true
+	}
+	return 0, false`, "enc_imm5"},
+	`For the "Half-precision" variant: is the index of a Real and Imaginary pair, in the range 0 to 3, encoded in the "i2" field.
+bit range mappings:
+i2: [19:21)
+`: {"encodeI2_1921_Half", `if v > 3 {
+		return 0, false
+	}
+	return v << 19, true`, "enc_i2"},
+	`For the "Half-precision" variant: is the name of the second source scalable vector register Z0-Z7, encoded in the "Zm" field.
+bit range mappings:
+Zm: [16:19)
+`: {"encodeZm_1619_Half", `if v <= 7 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_Zm"},
+	`For the "Higher or same", "Higher", "Lower or same", and "Lower" variants: is the unsigned immediate operand, in the range 0 to 127, encoded in the "imm7" field.
+bit range mappings:
+imm7: [14:21)
+`: {"encodeImm7Unsigned_1421", `if v <= 127 {
+		return v << 14, true
+	}
+	return 0, false`, "enc_imm7"},
+	`For the "Single-precision" variant: is the index of a Real and Imaginary pair, in the range 0 to 1, encoded in the "i1" field.
+bit range mappings:
+i1: [20:21)
+`: {"encodeI1_2021_Single", `if v > 1 {
+		return 0, false
+	}
+	return v << 20, true`, "enc_i1"},
+	`For the "Single-precision" variant: is the name of the second source scalable vector register Z0-Z15, encoded in the "Zm" field.
+bit range mappings:
+Zm: [16:20)
+`: {"encodeZm_1620_Single", `if v <= 15 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_Zm"},
+	`Is a 64, 32, 16 or 8-bit bitmask consisting of replicated 2, 4, 8, 16, 32 or 64 bit fields, each field containing a rotated run of non-zero bits, encoded in the "imm13" field.
+bit range mappings:
+imm13: [5:18)
+`: {"encodeImm13_518", `return codeLogicalImmArrEncoding, false`, "enc_imm13"},
+	`Is a floating-point immediate value expressible as ±n÷16×2^r, where n and r are integers such that 16 ≤ n ≤ 31 and -3 ≤ r ≤ 4, i.e. a normalized binary floating-point encoding with 1 sign bit, 3-bit exponent, and 4-bit fractional part, encoded in the "imm8" field.
+bit range mappings:
+imm8: [5:13)
+`: {"encodeImm8_513_Fimm", `if v <= 255 {
+		return v << 5, true
+	}
+	return 0, false`, "enc_imm8"},
+	`Is a signed immediate in the range -128 to 127, encoded in the "imm8" field.
+bit range mappings:
+imm8: [5:13)
+
+Is the optional left shift to apply to the immediate, defaulting to LSL #0 and
+sh	<shift>
+0	LSL #0
+1	LSL #8
+bit range mappings:
+sh: [13:14)
+`: {"encodeImm8SignedLsl8", `vi := int32(v)
+	if vi >= -128 && vi <= 127 {
+		imm8 := uint32(uint8(int8(vi)))
+		return (imm8 << 5), true
+	}
+	if vi&255 == 0 {
+		unshifted := vi >> 8
+		if unshifted >= -128 && unshifted <= 127 {
+			imm8 := uint32(uint8(int8(unshifted)))
+			return (imm8 << 5) | (1 << 13), true
+		}
+	}
+	return 0, false`, "enc_imm8"},
+	`Is an unsigned immediate in the range 0 to 255, encoded in the "imm8" field.
+bit range mappings:
+imm8: [5:13)
+
+Is the optional left shift to apply to the immediate, defaulting to LSL #0 and
+sh	<shift>
+0	LSL #0
+1	LSL #8
+bit range mappings:
+sh: [13:14)
+`: {"encodeImm8UnsignedLsl8", `if v <= 255 {
+		return v << 5, true
+	}
+	if v&255 == 0 {
+		unshifted := v >> 8
+		if unshifted <= 255 {
+			return (unshifted << 5) | (1 << 13), true
+		}
+	}
+	return 0, false`, "enc_imm8"},
+	`Is the 64-bit name of the destination general-purpose register or stack pointer, encoded in the "Rd" field.
+bit range mappings:
+Rd: [0:5)
+`: {"encodeRd05_SPAllowed", `if v == REG_R31 {
+		return 0, false
+	}
+	if v == REG_RSP {
+		return 31, true
+	}
+	return v & 31, true`, "enc_Rd"},
+	`Is the 64-bit name of the source general-purpose register or stack pointer, encoded in the "Rn" field.
+bit range mappings:
+Rn: [16:21)
+`: {"encodeRn1621_SPAllowed", `if v == REG_R31 {
+		return 0, false
+	}
+	if v == REG_RSP {
+		return 31 << 16, true
+	}
+	return (v & 31) << 16, true`, "enc_Rn"},
+	`Is the const specifier,
+rot	<const>
+0	#90
+1	#270
+bit range mappings:
+rot: [10:11)
+`: {"encodeRot90_270_1011", `switch v {
+	case 90:
+		return 0, true
+	case 270:
+		return 1 << 10, true
+	}
+	return 0, false`, "enc_rot"},
+	`Is the const specifier,
+rot	<const>
+0	#90
+1	#270
+bit range mappings:
+rot: [16:17)
+`: {"encodeRot90_270_1617", `switch v {
+	case 90:
+		return 0, true
+	case 270:
+		return 1 << 16, true
+	}
+	return 0, false`, "enc_rot"},
+	`Is the const specifier,
+rot	<const>
+00	#0
+01	#90
+10	#180
+11	#270
+bit range mappings:
+rot: [10:12)
+`: {"encodeRot0_90_180_270_1012", `switch v {
+	case 0:
+		return 0, true
+	case 90:
+		return 1 << 10, true
+	case 180:
+		return 2 << 10, true
+	case 270:
+		return 3 << 10, true
+	}
+	return 0, false`, "enc_rot"},
+	`Is the const specifier,
+rot	<const>
+00	#0
+01	#90
+10	#180
+11	#270
+bit range mappings:
+rot: [13:15)
+`: {"encodeRot0_90_180_270_1315", `switch v {
+	case 0:
+		return 0, true
+	case 90:
+		return 1 << 13, true
+	case 180:
+		return 2 << 13, true
+	case 270:
+		return 3 << 13, true
+	}
+	return 0, false`, "enc_rot"},
+	`Is the first signed immediate operand, in the range -16 to 15, encoded in the "imm5" field.
+bit range mappings:
+imm5: [5:10)
+`: {"encodeImm5Signed_510", `if int32(v) >= -16 && int32(v) <= 15 {
+		return (v & 31) << 5, true
+	}
+	return 0, false`, "enc_imm5"},
+	`Is the floating-point immediate value,
+i1	<const>
+0	#0.0
+1	#1.0
+bit range mappings:
+i1: [5:6)
+`: {"encodeFimm0_0_1_0_56", `switch v {
+	case 0:
+		return 0, true
+	case 0x3F800000: // 1.0
+		return 1 << 5, true
+	}
+	return 0, false`, "enc_i1"},
+	`Is the floating-point immediate value,
+i1	<const>
+0	#0.5
+1	#1.0
+bit range mappings:
+i1: [5:6)
+`: {"encodeFimm0_5_1_0_56", `switch v {
+	case 0x3F000000: // 0.5
+		return 0, true
+	case 0x3F800000: // 1.0
+		return 1 << 5, true
+	}
+	return 0, false`, "enc_i1"},
+	`Is the floating-point immediate value,
+i1	<const>
+0	#0.5
+1	#2.0
+bit range mappings:
+i1: [5:6)
+`: {"encodeFimm0_5_2_0_56", `switch v {
+	case 0x3F000000: // 0.5
+		return 0, true
+	case 0x40000000: // 2.0
+		return 1 << 5, true
+	}
+	return 0, false`, "enc_i1"},
+	`Is the immediate shift amount, in the range 0 to number of bits per element minus 1, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [16:19)
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeShiftTsz1619Range0V1", `return codeShift161919212223, false`, "enc_tszh_tszl_imm3"},
+	`Is the immediate shift amount, in the range 0 to number of bits per element minus 1, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [16:19)
+tszh: [22:24)
+tszl: [19:21)
+`: {"encodeShiftTsz1619Range0V2", `return codeShift161919212224, false`, "enc_tszh_tszl_imm3"},
+	`Is the immediate shift amount, in the range 0 to number of bits per element minus 1, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [5:8)
+tszh: [22:24)
+tszl: [8:10)
+`: {"encodeShiftTsz58Range0", `return codeShift588102224, false`, "enc_tszh_tszl_imm3"},
+	`Is the immediate shift amount, in the range 1 to number of bits per element, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [16:19)
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeShiftTsz1619Range1V1", `return codeShift161919212223, false`, "enc_tszh_tszl_imm3"},
+	`Is the immediate shift amount, in the range 1 to number of bits per element, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [16:19)
+tszh: [22:24)
+tszl: [19:21)
+`: {"encodeShiftTsz1619Range1V2", `return codeShift161919212224, false`, "enc_tszh_tszl_imm3"},
+	`Is the immediate shift amount, in the range 1 to number of bits per element, encoded in "tszh:tszl:imm3".
+bit range mappings:
+imm3: [5:8)
+tszh: [22:24)
+tszl: [8:10)
+`: {"encodeShiftTsz58Range1", `return codeShift588102224, false`, "enc_tszh_tszl_imm3"},
+	`Is the name of the governing scalable predicate register, encoded in the "Pg" field.
+bit range mappings:
+Pg: [16:20)
+`: {"encodePg1620", `return v << 16, true`, "enc_Pg"},
+	`Is the second signed immediate operand, in the range -16 to 15, encoded in the "imm5b" field.
+bit range mappings:
+imm5b: [16:21)
+`: {"encodeImm5bSigned_1621", `if int32(v) >= -16 && int32(v) <= 15 {
+		return (v & 31) << 16, true
+	}
+	return 0, false`, "enc_imm5b"},
+	`Is the signed immediate operand, in the range -128 to 127, encoded in the "imm8" field.
+bit range mappings:
+imm8: [5:13)
+`: {"encodeImm8Signed_513", `if int32(v) >= -128 && int32(v) <= 127 {
+		return (v & 255) << 5, true
+	}
+	return 0, false`, "enc_imm8"},
+	`Is the signed immediate operand, in the range -16 to 15, encoded in the "imm5" field.
+bit range mappings:
+imm5: [16:21)
+`: {"encodeImm5Signed_1621V1", `if int32(v) >= -16 && int32(v) <= 15 {
+		return (v & 31) << 16, true
+	}
+	return 0, false`, "enc_imm5"},
+	`Is the signed immediate operand, in the range -16 to 15, encoded in the "imm5" field.
+bit range mappings:
+imm5: [5:10)
+`: {"encodeImm5Signed510Unique", `if int32(v) >= -16 && int32(v) <= 15 {
+		return (v & 31) << 5, true
+	}
+	return 0, false`, "enc_imm5"},
+	`Is the signed immediate operand, in the range -32 to 31, encoded in the "imm6" field.
+bit range mappings:
+imm6: [5:11)
+`: {"encodeImm6Signed_511", `if int32(v) >= -32 && int32(v) <= 31 {
+		return (v & 63) << 5, true
+	}
+	return 0, false`, "enc_imm6"},
+	`Is the size specifier,
+imm13	<T>
+0xxxxxx0xxxxx	S
+0xxxxxx10xxxx	H
+0xxxxxx110xxx	B
+0xxxxxx1110xx	B
+0xxxxxx11110x	B
+0xxxxxx11111x	RESERVED
+1xxxxxxxxxxxx	D
+bit range mappings:
+imm13: [5:18)
+`: {"encodeSizeImm13NoOp", `return codeNoOp, false`, "enc_imm13"},
+	`Is the size specifier,
+tszh	tszl	<T>
+0	00	RESERVED
+0	01	B
+0	1x	H
+1	xx	S
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeSizeBhsTsz1921", `switch v {
+	case ARNG_B:
+		return 1 << 19, true
+	case ARNG_H:
+		return 2 << 19, true
+	case ARNG_S:
+		return 1 << 22, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the size specifier,
+tszh	tszl	<T>
+0	00	RESERVED
+0	01	H
+0	1x	S
+1	xx	D
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeSizeHsdTsz1921", `switch v {
+	case ARNG_H:
+		return 1 << 19, true
+	case ARNG_S:
+		return 2 << 19, true
+	case ARNG_D:
+		return 1 << 22, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the size specifier,
+tszh	tszl	<T>
+00	00	RESERVED
+00	01	B
+00	01	H
+01	xx	S
+1x	xx	D
+bit range mappings:
+tszh: [22:24)
+tszl: [19:21)
+`: {"encodeSizeBhsdTsz1921", `switch v {
+	case ARNG_B:
+		return 1 << 19, true
+	case ARNG_H:
+		return 2 << 19, true
+	case ARNG_S:
+		return 1 << 22, true
+	case ARNG_D:
+		return 1 << 23, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the size specifier,
+tszh	tszl	<T>
+00	00	RESERVED
+00	01	B
+00	1x	H
+01	xx	S
+1x	xx	D
+bit range mappings:
+tszh: [22:24)
+tszl: [8:10)
+`: {"encodeSizeBhsdTsz810", `switch v {
+	case ARNG_B:
+		return 1 << 8, true
+	case ARNG_H:
+		return 2 << 8, true
+	case ARNG_S:
+		return 1 << 22, true
+	case ARNG_D:
+		return 1 << 23, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the size specifier,
+tszh	tszl	<Tb>
+0	00	RESERVED
+0	01	B
+0	1x	H
+1	xx	S
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeSizeBhsTsz1921Unique", `switch v {
+	case ARNG_B:
+		return 1 << 19, true
+	case ARNG_H:
+		return 2 << 19, true
+	case ARNG_S:
+		return 1 << 22, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the size specifier,
+tszh	tszl	<Tb>
+0	00	RESERVED
+0	01	H
+0	1x	S
+1	xx	D
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: {"encodeSizeHsdTsz1921Unique", `switch v {
+	case ARNG_H:
+		return 1 << 19, true
+	case ARNG_S:
+		return 2 << 19, true
+	case ARNG_D:
+		return 1 << 22, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
+	`Is the unsigned immediate operand, in the range 0 to 15, encoded in the "imm4" field.
+bit range mappings:
+imm4: [16:20)
+`: {"encodeImm4Unsigned_1620", `if v <= 15 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_imm4"},
+	`Is the unsigned immediate operand, in the range 0 to 255, encoded in the "imm8" field.
+bit range mappings:
+imm8: [5:13)
+`: {"encodeImm8Unsigned_513", `if v <= 255 {
+		return v << 5, true
+	}
+	return 0, false`, "enc_imm8"},
+	`Is the unsigned immediate operand, in the range 0 to 255, encoded in the "imm8h:imm8l" fields.
+bit range mappings:
+imm8h: [16:21)
+imm8l: [10:13)
+`: {"encodeImm8hImm8l_Unsigned", `if v <= 255 {
+		l := v & 7
+		h := v >> 3
+		return (l << 10) | (h << 16), true
+	}
+	return 0, false`, "enc_imm8h_imm8l"},
+	`Is the unsigned immediate operand, in the range 0 to 7, encoded in the "imm3" field.
+bit range mappings:
+imm3: [16:19)
+`: {"encodeImm3Unsigned_1619", `if v <= 7 {
+		return v << 16, true
+	}
+	return 0, false`, "enc_imm3"},
+	`Is the size specifier,
+tszh	tszl	<T>
+00	00	RESERVED
+00	01	B
+00	1x	H
+01	xx	S
+1x	xx	D
+bit range mappings:
+tszh: [22:24)
+tszl: [19:21)
+`: {"encodeSizeBhsdTsz1921", `switch v {
+	case ARNG_B:
+		return 1 << 19, true
+	case ARNG_H:
+		return 2 << 19, true
+	case ARNG_S:
+		return 1 << 22, true
+	case ARNG_D:
+		return 1 << 23, true
+	}
+	return 0, false`, "enc_tszh_tszl"},
 }
diff --git a/arm64/instgen/generator.go b/arm64/instgen/generator.go
index f9511e4..9f734be 100644
--- a/arm64/instgen/generator.go
+++ b/arm64/instgen/generator.go
@@ -58,6 +58,7 @@
 	Args      []argData
 	Ops       opCombData
 	Asm       string
+	highFeat  int
 }
 
 type argData struct {
@@ -205,6 +206,7 @@
 	"AC_ARNGIDX": 2,
 	"AC_ZREGIDX": 2,
 	"AC_PREGIDX": 2,
+	"AC_IMM":     3,
 }
 
 func readExistingGoOps(aoutPath string) map[string]bool {
@@ -364,10 +366,15 @@
 					if errData != nil {
 						e2eErrorData = append(e2eErrorData, *errData)
 					}
+					highFeat := 0
+					for _, op := range encoding.Operands[1:] {
+						highFeat = max(highFeat, operandTypeOrders[op.Typ])
+					}
 					instData := instData{
 						Asm:       encoding.GoOp[1:],
 						GoOp:      encoding.GoOp,
 						FixedBits: fmt.Sprintf("%#x", encoding.Binary),
+						highFeat:  highFeat,
 					}
 
 					for _, op := range encoding.Operands[1:] {
@@ -397,6 +404,7 @@
 										v = strings.ReplaceAll(v, ")", "")
 										v = strings.ReplaceAll(v, "[", "")
 										v = strings.ReplaceAll(v, "]", "")
+										v = strings.ReplaceAll(v, "#", "c")
 										enc = fmt.Sprintf("enc_%s", v)
 									}
 								}
@@ -454,6 +462,7 @@
 		v = strings.ReplaceAll(v, ")", "")
 		v = strings.ReplaceAll(v, "[", "")
 		v = strings.ReplaceAll(v, "]", "")
+		v = strings.ReplaceAll(v, "#", "c")
 		name := fmt.Sprintf("enc_%s", v)
 		encodedInMap[v0] = name
 		// Only add to output if used
@@ -563,6 +572,7 @@
 		s = strings.Replace(s, "]", "_", -1)
 		s = strings.Replace(s, "{", "", -1)
 		s = strings.Replace(s, "}", "", -1)
+		s = strings.Replace(s, "#", "c", -1)
 		return s
 	}
 
@@ -641,9 +651,21 @@
 	})
 
 	// Group instructions by name
-	// Also sort them by their Go mnemonic.
-	sort.Slice(data.Insts, func(i, j int) bool {
-		return data.Insts[i].GoOp < data.Insts[j].GoOp
+	// Also sort them by their Go mnemonic, stably.
+	slices.SortFunc(data.Insts, func(i, j instData) int {
+		if c := strings.Compare(i.GoOp, j.GoOp); c != 0 {
+			return c
+		}
+		if i.highFeat != j.highFeat {
+			return i.highFeat - j.highFeat
+		}
+		if c := strings.Compare(i.Ops.BetterName, j.Ops.BetterName); c != 0 {
+			return c
+		}
+		if c := strings.Compare(i.Ops.OpAsms, j.Ops.OpAsms); c != 0 {
+			return c
+		}
+		return strings.Compare(i.FixedBits, j.FixedBits)
 	})
 	ibn := []instsByName{}
 	prevGoOp := ""
@@ -821,6 +843,7 @@
 		// Key is the random number selected.
 		regCache := map[string]int{}
 		arrCache := map[string]int{}
+		immCache := map[string]int{}
 		cachedOrNew := func(cache map[string]int, key string, n int) int {
 			if v, ok := cache[key]; ok {
 				if errCase != nil || len(enc.Operands) == 0 {
@@ -1052,6 +1075,39 @@
 				}
 				gnuAsmOps = append([]string{gnuAsmOp}, gnuAsmOps...)
 				goAsmOps = append(goAsmOps, goAsmOp)
+			} else if op.Typ == "AC_IMM" {
+				var imm string
+				var isFloat bool
+				// Check if this is a float imm or an integer imm.
+				if op.Name == "#0.0" {
+					imm = "0.0"
+					isFloat = true
+				} else {
+					for _, e := range op.Elems {
+						if strings.Contains(e.TextExpWithRanges, "float") {
+							isFloat = true
+							break
+						}
+					}
+					if isFloat {
+						// Usually these floats are 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5...
+						imm = fmt.Sprintf("%.1f", float64(cachedOrNew(immCache, op.Name, 20))/5.0)
+					} else {
+						intImm := cachedOrNew(immCache, op.Name, 16)
+						if intImm > 12 {
+							// Some instructions likes 90, 270; let's generate them to make those instructions happy.
+							imm = fmt.Sprintf("%d", (intImm-12)*90)
+						} else {
+							imm = fmt.Sprintf("%d", intImm)
+						}
+					}
+				}
+				if isFloat {
+					goAsmOps = append(goAsmOps, fmt.Sprintf("$(%s)", imm))
+				} else {
+					goAsmOps = append(goAsmOps, fmt.Sprintf("$%s", imm))
+				}
+				gnuAsmOps = append([]string{fmt.Sprintf("#%s", imm)}, gnuAsmOps...)
 			}
 		}
 		// Try to assemble the GNU version.
@@ -1093,6 +1149,12 @@
 			continue
 		}
 
+		if enc.Asm == "INDEX  <Zd>.<T>, <R><n>, #<imm>" {
+			// Don't generate error case for this, the GNU assembler has a strict scalar and vector register width
+			// constraint, i.e. <R> must be W if <T> is B, H, S. The Go assembler doesn't have this constraint.
+			// So we can't generate an error case for this.
+			errCase = &e2eData{GoOp: enc.GoOp[1:], Asm: fmt.Sprintf("// TODO: %s", name), highFeat: highFeat}
+		}
 		return validCase, errCase
 	}
 	if name == "ADDQP" || name == "ADDSUBP" || name == "SCVTFLT" || name == "UCVTFLT" {
diff --git a/arm64/instgen/xmlspec/parser.go b/arm64/instgen/xmlspec/parser.go
index fc61da7..0c8194e 100644
--- a/arm64/instgen/xmlspec/parser.go
+++ b/arm64/instgen/xmlspec/parser.go
@@ -1016,7 +1016,7 @@
 	"AC_ZREGIDX": 3,
 	"AC_PREGIDX": 3,
 	// #<imm>, <shift>
-	"AC_IMM": 2,
+	"AC_IMM": 1,
 	// [<reg1>.<T1>, <reg2>.<T2>, <mod> <amount>]
 	"AC_MEMEXT": 6,
 	// [<reg>.<T>, #<imm>]
@@ -1153,15 +1153,16 @@
 		resolved := false
 		switch op.Name {
 		case "#0.0":
-			if el == 2 && len(op.Elems) == 0 {
-				op.Elems = make([]Element, 0, 2)
+			if el == 1 && len(op.Elems) == 0 {
+				op.Elems = make([]Element, 0, 1)
 				insertElmAt(0, "#0.0", "Check this is immediate 0.0")
-				insertElmAt(1, "nil", noOpCheck)
 				resolved = true
 			}
-		case "#<const>", "#<imm1>", "#<imm2>", "#<imm>", "<const>":
-			if el == 2 && len(op.Elems) == 1 {
-				insertElmAt(1, "nil", noOpCheck)
+		case "#<imm>{, <shift>}":
+			if el == 1 && len(op.Elems) == 2 {
+				// The 2 elements explanation need to be merged
+				insertElmAt(0, "#<imm>{, <shift>}", op.Elems[0].TextExpWithRanges+"\n"+op.Elems[1].TextExpWithRanges)
+				op.Elems = op.Elems[:1]
 				resolved = true
 			}
 		case "<Pd>", "<Pg>", "<Pn>", "<PNg>", "<Pt>", "<Pv>", "<Zd>", "<Zm>", "<Zn>", "<Zt>":