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>":