[binutils-gdb] aarch64: Add support for optional operand pairs

Victor Do Nascimento victorldn@sourceware.org
Tue Jan 9 10:18:02 GMT 2024


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=f89c290e23c1195bbe6f90980ace3f9a81fde6f1

commit f89c290e23c1195bbe6f90980ace3f9a81fde6f1
Author: Victor Do Nascimento <victor.donascimento@arm.com>
Date:   Wed Dec 13 14:27:31 2023 +0000

    aarch64: Add support for optional operand pairs
    
    Two of the instructions added by the `+d128' architectural extension
    add the flexibility to have two optional operands.  Prior to the
    addition of the `tlbip' and `sysp' instructions, no mnemonic allowed
    more than one such optional operand.
    
    With `tlbip' as an example, some TLBIP instruction names do not allow
    for any optional operands, while others allow for both to be optional.
    In the latter case, it is possible that either the second operand
    alone is omitted or both operands are omitted.
    Therefore, a considerable degree of flexibility needed to be added to
    the way operands were parsed.  It was, however, possible to achieve
    this with relatively few changes to existing code.
    
    it is noteworthy that opcode flags specifying the optional operand
    number are non-orthogonal. For example, we have:
    
           #define F_OPD1_OPT (2 << 12) : 0b10 << 12
           #define F_OPD2_OPT (3 << 12) : 0b11 << 12
    
    such that by virtue of the observation that
    
           (F_OPD1_OPT | F_OPD2_OPT) == F_OPD2_OPT
    
    it is impossible to mark both operands 1 and 2 as optional for an
    instruction and it is assumed that a maximum of 1 operand can ever be
    optional.  This is not overly-problematic given that, for optional
    pairs, the second optional operand is always found immediately after
    the first.  Thus, it suffices for us to flag that there is a second
    optional operand.  With this fact, we can infer its position in the
    mnemonic from the position of the first (e.g. if the second operand in
    the mnemonic is optional, we know the third is too).  We therefore
    define the `F_OPD_PAIR_OPT' flag and calculate its position in the
    mnemonic from the value encoded by the `F_OPD<n>_OPT' flag.
    
    Another observation is that there is a tight coupling between default
    values assigned to the two registers when one (or both) are omitted
    from the mnemonic.  Namely, if Xt1 has a value of 0x1f (the zero
    register is specified), Xt2 defaults to the same value, otherwise Xt2
    will be assigned Xt + 1.  This meant that where you have default value
    validation, in checking the second optional operand's value, it is
    also necessary to look at the value assigned to the
    previously-processed operand value before deciding its validity. Thus
    `process_omitted_operand' needs not only access to its `operand'
    argument, but also to the global `inst' struct.

Diff:
---
 gas/config/tc-aarch64.c  | 19 +++++++++++++++++--
 include/opcode/aarch64.h | 12 +++++++++++-
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 14687290720..29535e7d805 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -6168,6 +6168,17 @@ process_omitted_operand (enum aarch64_opnd type, const aarch64_opcode *opcode,
     case AARCH64_OPND_VnD1:
       operand->reg.regno = default_value;
       break;
+    case AARCH64_OPND_PAIRREG_OR_XZR:
+      if (inst.base.operands[idx - 1].reg.regno == 0x1f)
+	{
+	  operand->reg.regno = 0x1f;
+	  break;
+	}
+      operand->reg.regno = inst.base.operands[idx - 1].reg.regno + 1;
+      break;
+    case AARCH64_OPND_PAIRREG:
+      operand->reg.regno = inst.base.operands[idx - 1].reg.regno + 1;
+      break;
 
     case AARCH64_OPND_Ed:
     case AARCH64_OPND_En:
@@ -7864,6 +7875,12 @@ parse_operands (char *str, const aarch64_opcode *opcode)
 
       /* If we get here, this operand was successfully parsed.  */
       inst.base.operands[i].present = 1;
+
+      /* As instructions can have multiple optional operands, it is imporant to
+	 reset the backtrack_pos variable once we finish processing an operand
+	 successfully.  */
+      backtrack_pos = 0;
+
       continue;
 
     failure:
@@ -7885,8 +7902,6 @@ parse_operands (char *str, const aarch64_opcode *opcode)
 	char *tmp = backtrack_pos;
 	char endchar = END_OF_INSN;
 
-	if (i != (aarch64_num_of_operands (opcode) - 1))
-	  endchar = ',';
 	skip_past_char (&tmp, ',');
 
 	if (*tmp != endchar)
diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index 187a4287898..854bb7435d4 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -1220,7 +1220,11 @@ extern const aarch64_opcode aarch64_opcode_table[];
 /* This instruction has an extra constraint on it that imposes a requirement on
    subsequent instructions.  */
 #define F_SCAN (1ULL << 31)
-/* Next bit is 32.  */
+/* Instruction takes a pair of optional operands.  If we specify the Nth operand
+   to be optional, then we also implicitly specify (N+1)th operand to also be
+   optional.  */
+#define F_OPD_PAIR_OPT (1ULL << 32)
+/* Next bit is 33.  */
 
 /* Instruction constraints.  */
 /* This instruction has a predication constraint on the instruction at PC+4.  */
@@ -1259,9 +1263,15 @@ pseudo_opcode_p (const aarch64_opcode *opcode)
   return (opcode->flags & F_PSEUDO) != 0lu;
 }
 
+/* Deal with two possible scenarios: If F_OP_PAIR_OPT not set, as is the case
+   by default, F_OPDn_OPT must equal IDX + 1, else F_OPDn_OPT must be in range
+   [IDX, IDX + 1].  */
 static inline bool
 optional_operand_p (const aarch64_opcode *opcode, unsigned int idx)
 {
+  if (opcode->flags & F_OPD_PAIR_OPT)
+    return (((opcode->flags >> 12) & 0x7) == idx
+	    || ((opcode->flags >> 12) & 0x7) == idx + 1);
   return ((opcode->flags >> 12) & 0x7) == idx + 1;
 }


More information about the Binutils-cvs mailing list